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