1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2007-2008 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5 * Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25 #include <collectors.h>
26 #include <board_item.h> // class BOARD_ITEM
27
28 #include <footprint.h>
29 #include <fp_shape.h>
30 #include <pad.h>
31 #include <pcb_track.h>
32 #include <pcb_marker.h>
33 #include <pcb_dimension.h>
34 #include <zone.h>
35 #include <pcb_shape.h>
36 #include <pcb_group.h>
37 #include <macros.h>
38 #include <math/util.h> // for KiROUND
39
40 /*
41 * This module contains out of line member functions for classes given in
42 * collectors.h. Those classes augment the functionality of class PCB_EDIT_FRAME.
43 */
44
45
46 const KICAD_T GENERAL_COLLECTOR::AllBoardItems[] = {
47 // there are some restrictions on the order of items in the general case.
48 // all items in m_Drawings for instance should be contiguous.
49 // *** all items in a same list (shown here) must be contiguous ****
50 PCB_MARKER_T, // in m_markers
51 PCB_TEXT_T, // in m_drawings
52 PCB_SHAPE_T, // in m_drawings
53 PCB_DIM_ALIGNED_T, // in m_drawings
54 PCB_DIM_CENTER_T, // in m_drawings
55 PCB_DIM_ORTHOGONAL_T, // in m_drawings
56 PCB_DIM_LEADER_T, // in m_drawings
57 PCB_TARGET_T, // in m_drawings
58 PCB_VIA_T, // in m_tracks
59 PCB_TRACE_T, // in m_tracks
60 PCB_ARC_T, // in m_tracks
61 PCB_PAD_T, // in footprints
62 PCB_FP_TEXT_T, // in footprints
63 PCB_FOOTPRINT_T, // in m_footprints
64 PCB_GROUP_T, // in m_groups
65 PCB_ZONE_T, // in m_zones
66 EOT
67 };
68
69
70 const KICAD_T GENERAL_COLLECTOR::BoardLevelItems[] = {
71 PCB_MARKER_T,
72 PCB_TEXT_T,
73 PCB_SHAPE_T,
74 PCB_DIM_ALIGNED_T,
75 PCB_DIM_ORTHOGONAL_T,
76 PCB_DIM_CENTER_T,
77 PCB_DIM_LEADER_T,
78 PCB_TARGET_T,
79 PCB_VIA_T,
80 PCB_ARC_T,
81 PCB_TRACE_T,
82 PCB_FOOTPRINT_T,
83 PCB_GROUP_T,
84 PCB_ZONE_T,
85 EOT
86 };
87
88
89 const KICAD_T GENERAL_COLLECTOR::Footprints[] = {
90 PCB_FOOTPRINT_T,
91 EOT
92 };
93
94
95 const KICAD_T GENERAL_COLLECTOR::PadsOrTracks[] = {
96 PCB_PAD_T,
97 PCB_VIA_T,
98 PCB_TRACE_T,
99 PCB_ARC_T,
100 EOT
101 };
102
103
104 const KICAD_T GENERAL_COLLECTOR::FootprintItems[] = {
105 PCB_FP_TEXT_T,
106 PCB_FP_SHAPE_T,
107 PCB_PAD_T,
108 PCB_FP_ZONE_T,
109 PCB_GROUP_T,
110 EOT
111 };
112
113
114 const KICAD_T GENERAL_COLLECTOR::Tracks[] = {
115 PCB_TRACE_T,
116 PCB_ARC_T,
117 PCB_VIA_T,
118 EOT
119 };
120
121
122 const KICAD_T GENERAL_COLLECTOR::LockableItems[] = {
123 PCB_FOOTPRINT_T,
124 PCB_GROUP_T, // Can a group be locked?
125 PCB_TRACE_T,
126 PCB_ARC_T,
127 PCB_VIA_T,
128 EOT
129 };
130
131
132 const KICAD_T GENERAL_COLLECTOR::Zones[] = {
133 PCB_ZONE_T,
134 PCB_FP_ZONE_T,
135 EOT
136 };
137
138
139 const KICAD_T GENERAL_COLLECTOR::Dimensions[] = {
140 PCB_DIM_ALIGNED_T,
141 PCB_DIM_LEADER_T,
142 PCB_DIM_ORTHOGONAL_T,
143 PCB_DIM_CENTER_T,
144 EOT
145 };
146
147
148 const KICAD_T GENERAL_COLLECTOR::DraggableItems[] = {
149 PCB_TRACE_T,
150 PCB_VIA_T,
151 PCB_FOOTPRINT_T,
152 PCB_ARC_T,
153 EOT
154 };
155
156
Inspect(EDA_ITEM * testItem,void * testData)157 SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
158 {
159 BOARD_ITEM* item = (BOARD_ITEM*) testItem;
160 FOOTPRINT* footprint = nullptr;
161 PCB_GROUP* group = nullptr;
162 PAD* pad = nullptr;
163 bool pad_through = false;
164 PCB_VIA* via = nullptr;
165 PCB_MARKER* marker = nullptr;
166 ZONE* zone = nullptr;
167 PCB_SHAPE* shape = nullptr;
168 PCB_DIMENSION_BASE* dimension = nullptr;
169
170 #if 0 // debugging
171 static int breakhere = 0;
172
173 switch( item->Type() )
174 {
175 case PCB_PAD_T:
176 {
177 FOOTPRINT* footprint = (FOOTPRINT*) item->GetParent();
178
179 if( footprint->GetReference() == wxT( "Y2" ) )
180 breakhere++;
181 }
182 break;
183
184 case PCB_VIA_T:
185 breakhere++;
186 break;
187
188 case PCB_TRACE_T:
189 case PCB_ARC_T:
190 breakhere++;
191 break;
192
193 case PCB_TEXT_T:
194 breakhere++;
195 break;
196
197 case PCB_SHAPE_T:
198 breakhere++;
199 break;
200
201 case PCB_DIM_ALIGNED_T:
202 breakhere++;
203 break;
204
205 case PCB_FP_TEXT_T:
206 {
207 FP_TEXT* fpText = (FP_TEXT*) item;
208
209 if( fpText->GetText() == wxT( "10uH" ) )
210 breakhere++;
211 }
212 break;
213
214 case PCB_FOOTPRINT_T:
215 {
216 FOOTPRINT* footprint = (FOOTPRINT*) item;
217
218 if( footprint->GetReference() == wxT( "C98" ) )
219 breakhere++;
220 }
221 break;
222
223 case PCB_MARKER_T:
224 breakhere++;
225 break;
226
227 default:
228 breakhere++;
229 break;
230 }
231
232 #endif
233
234 switch( item->Type() )
235 {
236 case PCB_PAD_T:
237 // there are pad specific visibility controls.
238 // Criteria to select a pad is:
239 // for smd pads: the footprint parent must be visible, and pads on the corresponding
240 // board side must be visible
241 // if pad is a thru hole, then it can be visible when its parent footprint is not.
242 // for through pads: pads on Front or Back board sides must be visible
243 pad = static_cast<PAD*>( item );
244
245 if( ( pad->GetAttribute() != PAD_ATTRIB::SMD ) &&
246 ( pad->GetAttribute() != PAD_ATTRIB::CONN ) ) // a hole is present, so multiple layers
247 {
248 // proceed to the common tests below, but without the parent footprint test,
249 // by leaving footprint==NULL, but having pad != null
250 pad_through = true;
251 }
252 else // smd, so use pads test after footprint test
253 {
254 footprint = static_cast<FOOTPRINT*>( item->GetParent() );
255 }
256
257 break;
258
259 case PCB_VIA_T: // vias are on many layers, so layer test is specific
260 via = static_cast<PCB_VIA*>( item );
261 break;
262
263 case PCB_TRACE_T:
264 case PCB_ARC_T:
265 if( m_Guide->IgnoreTracks() )
266 goto exit;
267
268 break;
269
270 case PCB_FP_ZONE_T:
271 footprint = static_cast<FOOTPRINT*>( item->GetParent() );
272
273 // Fallthrough to get the zone as well
274 KI_FALLTHROUGH;
275
276 case PCB_ZONE_T:
277 zone = static_cast<ZONE*>( item );
278 break;
279
280 case PCB_TEXT_T:
281 break;
282
283 case PCB_SHAPE_T:
284 shape = static_cast<PCB_SHAPE*>( item );
285 break;
286
287 case PCB_DIM_ALIGNED_T:
288 case PCB_DIM_CENTER_T:
289 case PCB_DIM_ORTHOGONAL_T:
290 case PCB_DIM_LEADER_T:
291 dimension = static_cast<PCB_DIMENSION_BASE*>( item );
292 break;
293
294 case PCB_TARGET_T:
295 break;
296
297 case PCB_FP_TEXT_T:
298 {
299 FP_TEXT *text = static_cast<FP_TEXT*>( item );
300
301 if( m_Guide->IgnoreHiddenFPText() && !text->IsVisible() )
302 goto exit;
303
304 if( m_Guide->IgnoreFPTextOnBack() && IsBackLayer( text->GetLayer() ) )
305 goto exit;
306
307 if( m_Guide->IgnoreFPTextOnFront() && IsFrontLayer( text->GetLayer() ) )
308 goto exit;
309
310 /* The three text types have different criteria: reference
311 * and value have their own ignore flags; user text instead
312 * follows their layer visibility. Checking this here is
313 * simpler than later (when layer visibility is checked for
314 * other entities) */
315
316 switch( text->GetType() )
317 {
318 case FP_TEXT::TEXT_is_REFERENCE:
319 if( m_Guide->IgnoreFPReferences() )
320 goto exit;
321
322 break;
323
324 case FP_TEXT::TEXT_is_VALUE:
325 if( m_Guide->IgnoreFPValues() )
326 goto exit;
327
328 break;
329
330 case FP_TEXT::TEXT_is_DIVERS:
331 if( !m_Guide->IsLayerVisible( text->GetLayer() )
332 && m_Guide->IgnoreNonVisibleLayers() )
333 goto exit;
334
335 break;
336 }
337
338 // Extract the footprint since it could be hidden
339 footprint = static_cast<FOOTPRINT*>( item->GetParent() );
340 break;
341 }
342
343 case PCB_FP_SHAPE_T:
344 shape = static_cast<FP_SHAPE*>( item );
345 break;
346
347 case PCB_FOOTPRINT_T:
348 footprint = static_cast<FOOTPRINT*>( item );
349 break;
350
351 case PCB_GROUP_T:
352 group = static_cast<PCB_GROUP*>( item );
353 break;
354
355 case PCB_MARKER_T:
356 marker = static_cast<PCB_MARKER*>( item );
357 break;
358
359 default:
360 break;
361 }
362
363 // common tests:
364
365 if( footprint ) // true from case PCB_PAD_T, PCB_FP_TEXT_T, or PCB_FOOTPRINT_T
366 {
367 if( m_Guide->IgnoreFootprintsOnBack() && ( footprint->GetLayer() == B_Cu ) )
368 goto exit;
369
370 if( m_Guide->IgnoreFootprintsOnFront() && ( footprint->GetLayer() == F_Cu ) )
371 goto exit;
372 }
373
374 // Pads are not sensitive to the layer visibility controls.
375 // They all have their own separate visibility controls
376 // skip them if not visible
377 if( pad )
378 {
379 if( m_Guide->IgnorePads() )
380 goto exit;
381
382 if( ! pad_through )
383 {
384 if( m_Guide->IgnorePadsOnFront() && pad->IsOnLayer(F_Cu ) )
385 goto exit;
386
387 if( m_Guide->IgnorePadsOnBack() && pad->IsOnLayer(B_Cu ) )
388 goto exit;
389 }
390 }
391
392 if( marker )
393 {
394 // Markers are not sensitive to the layer
395 if( marker->HitTest( m_refPos ) )
396 Append( item );
397
398 goto exit;
399 }
400
401 if( group )
402 {
403 // Groups are not sensitive to the layer ... ?
404 if( group->HitTest( m_refPos ) )
405 Append( item );
406
407 goto exit;
408 }
409
410 if( via )
411 {
412 auto type = via->GetViaType();
413
414 if( ( m_Guide->IgnoreThroughVias() && type == VIATYPE::THROUGH )
415 || ( m_Guide->IgnoreBlindBuriedVias() && type == VIATYPE::BLIND_BURIED )
416 || ( m_Guide->IgnoreMicroVias() && type == VIATYPE::MICROVIA ) )
417 {
418 goto exit;
419 }
420 }
421
422 if( item->IsOnLayer( m_Guide->GetPreferredLayer() ) || m_Guide->IgnorePreferredLayer() )
423 {
424 PCB_LAYER_ID layer = item->GetLayer();
425
426 // footprints and their subcomponents: reference, value and pads are not sensitive
427 // to the layer visibility controls. They all have their own separate visibility
428 // controls for vias, GetLayer() has no meaning, but IsOnLayer() works fine. User
429 // text in a footprint *is* sensitive to layer visibility but that was already handled.
430
431 if( via || footprint || pad || m_Guide->IsLayerVisible( layer )
432 || !m_Guide->IgnoreNonVisibleLayers() )
433 {
434 if( !m_Guide->IsLayerLocked( layer ) || !m_Guide->IgnoreLockedLayers() )
435 {
436 if( !item->IsLocked() || !m_Guide->IgnoreLockedItems() )
437 {
438 int accuracy = KiROUND( 5 * m_Guide->OnePixelInIU() );
439
440 if( zone )
441 {
442 bool testFill = !m_Guide->IgnoreZoneFills();
443
444 if( zone->HitTestForCorner( m_refPos, accuracy * 2 )
445 || zone->HitTestForEdge( m_refPos, accuracy )
446 || ( testFill && zone->HitTestFilledArea( layer, m_refPos ) ) )
447 {
448 Append( item );
449 goto exit;
450 }
451 }
452 else if( item->Type() == PCB_FOOTPRINT_T )
453 {
454 if( footprint->HitTest( m_refPos, accuracy )
455 && footprint->HitTestAccurate( m_refPos, accuracy ) )
456 {
457 Append( item );
458 goto exit;
459 }
460 }
461 else if( shape )
462 {
463 if( shape->HitTest( m_refPos, accuracy ) )
464 {
465 Append( shape );
466 goto exit;
467 }
468 }
469 else if( dimension )
470 {
471 // Dimensions feel particularly hard to select, probably due to their
472 // noisy shape making it feel like they should have a larger boundary.
473 if( dimension->HitTest( m_refPos, KiROUND( accuracy * 1.5 ) ) )
474 {
475 Append( dimension );
476 goto exit;
477 }
478 }
479 else
480 {
481 if( item->HitTest( m_refPos, accuracy ) )
482 {
483 Append( item );
484 goto exit;
485 }
486 }
487 }
488 }
489 }
490 }
491
492 if( m_Guide->IncludeSecondary() )
493 {
494 // for now, "secondary" means "tolerate any layer". It has
495 // no effect on other criteria, since there is a separate "ignore" control for
496 // those in the COLLECTORS_GUIDE
497
498 PCB_LAYER_ID layer = item->GetLayer();
499
500 // footprints and their subcomponents: reference, value and pads are not sensitive
501 // to the layer visibility controls. They all have their own separate visibility
502 // controls for vias, GetLayer() has no meaning, but IsOnLayer() works fine. User
503 // text in a footprint *is* sensitive to layer visibility but that was already handled.
504
505 if( via || footprint || pad || zone || m_Guide->IsLayerVisible( layer )
506 || !m_Guide->IgnoreNonVisibleLayers() )
507 {
508 if( !m_Guide->IsLayerLocked( layer ) || !m_Guide->IgnoreLockedLayers() )
509 {
510 if( !item->IsLocked() || !m_Guide->IgnoreLockedItems() )
511 {
512 int accuracy = KiROUND( 5 * m_Guide->OnePixelInIU() );
513
514 if( zone )
515 {
516 bool testFill = !m_Guide->IgnoreZoneFills();
517
518 if( zone->HitTestForCorner( m_refPos, accuracy * 2 )
519 || zone->HitTestForEdge( m_refPos, accuracy )
520 || ( testFill && zone->HitTestFilledArea( layer, m_refPos ) ) )
521 {
522 Append2nd( item );
523 goto exit;
524 }
525 }
526 else if( item->Type() == PCB_FOOTPRINT_T )
527 {
528 if( footprint->HitTest( m_refPos, accuracy )
529 && footprint->HitTestAccurate( m_refPos, accuracy ) )
530 {
531 Append2nd( item );
532 goto exit;
533 }
534 }
535 else if( shape )
536 {
537 if( shape->HitTest( m_refPos, accuracy ) )
538 {
539 Append2nd( shape );
540 goto exit;
541 }
542 }
543 else if( dimension )
544 {
545 // Dimensions feels particularly hard to select, probably due to their
546 // noisy shape making it feel like they should have a larger boundary.
547 if( dimension->HitTest( m_refPos, KiROUND( accuracy * 1.5 ) ) )
548 {
549 Append2nd( dimension );
550 goto exit;
551 }
552 }
553 else
554 {
555 if( item->HitTest( m_refPos, 0 ) )
556 {
557 Append2nd( item );
558 goto exit;
559 }
560 }
561 }
562 }
563 }
564 }
565
566 exit:
567 return SEARCH_RESULT::CONTINUE; // always when collecting
568 }
569
570
Collect(BOARD_ITEM * aItem,const KICAD_T aScanList[],const wxPoint & aRefPos,const COLLECTORS_GUIDE & aGuide)571 void GENERAL_COLLECTOR::Collect( BOARD_ITEM* aItem, const KICAD_T aScanList[],
572 const wxPoint& aRefPos, const COLLECTORS_GUIDE& aGuide )
573 {
574 Empty(); // empty the collection, primary criteria list
575 Empty2nd(); // empty the collection, secondary criteria list
576
577 // remember guide, pass it to Inspect()
578 SetGuide( &aGuide );
579
580 SetScanTypes( aScanList );
581
582 // remember where the snapshot was taken from and pass refPos to
583 // the Inspect() function.
584 SetRefPos( aRefPos );
585
586 aItem->Visit( m_inspector, nullptr, m_scanTypes );
587
588 // record the length of the primary list before concatenating on to it.
589 m_PrimaryLength = m_list.size();
590
591 // append 2nd list onto end of the first list
592 for( unsigned i = 0; i<m_List2nd.size(); ++i )
593 Append( m_List2nd[i] );
594
595 Empty2nd();
596 }
597
598
Inspect(EDA_ITEM * testItem,void * testData)599 SEARCH_RESULT PCB_TYPE_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
600 {
601 // The Visit() function only visits the testItem if its type was in the
602 // the scanList, so therefore we can collect anything given to us here.
603 Append( testItem );
604
605 return SEARCH_RESULT::CONTINUE; // always when collecting
606 }
607
608
Collect(BOARD_ITEM * aBoard,const KICAD_T aScanList[])609 void PCB_TYPE_COLLECTOR::Collect( BOARD_ITEM* aBoard, const KICAD_T aScanList[] )
610 {
611 Empty(); // empty any existing collection
612
613 aBoard->Visit( m_inspector, nullptr, aScanList );
614 }
615
616
Inspect(EDA_ITEM * testItem,void * testData)617 SEARCH_RESULT PCB_LAYER_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
618 {
619 BOARD_ITEM* item = (BOARD_ITEM*) testItem;
620
621 if( item->IsOnLayer( m_layer_id ) )
622 Append( testItem );
623
624 return SEARCH_RESULT::CONTINUE;
625 }
626
627
Collect(BOARD_ITEM * aBoard,const KICAD_T aScanList[])628 void PCB_LAYER_COLLECTOR::Collect( BOARD_ITEM* aBoard, const KICAD_T aScanList[] )
629 {
630 Empty();
631
632 aBoard->Visit( m_inspector, nullptr, aScanList );
633 }
634