KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
drc_test_provider_physical_clearance.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <common.h>
25#include <macros.h>
27#include <footprint.h>
28#include <pad.h>
29#include <pcb_track.h>
30#include <pcb_shape.h>
31#include <zone.h>
32#include <advanced_config.h>
34#include <geometry/seg.h>
36#include <drc/drc_engine.h>
37#include <drc/drc_rtree.h>
38#include <drc/drc_item.h>
39#include <drc/drc_rule.h>
41
42/*
43 Physical clearance tests.
44
45 Errors generated:
46 - DRCE_PHYSICAL_CLEARANCE
47 - DRCE_PHYSICAL_HOLE_CLEARANCE
48*/
49
51{
52public:
55 {
56 }
57
59 {
60 }
61
62 virtual bool Run() override;
63
64 virtual const wxString GetName() const override
65 {
66 return wxT( "physical_clearance" );
67 };
68
69 virtual const wxString GetDescription() const override
70 {
71 return wxT( "Tests item clearances irrespective of nets" );
72 }
73
74private:
75 int testItemAgainstItem( BOARD_ITEM* aItem, SHAPE* aItemShape, PCB_LAYER_ID aLayer,
76 BOARD_ITEM* aOther );
77
78 void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
79
80 void testShapeLineChain( const SHAPE_LINE_CHAIN& aOutline, int aLineWidth, PCB_LAYER_ID aLayer,
81 BOARD_ITEM* aParentItem, DRC_CONSTRAINT& aConstraint );
82
83 void testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer, DRC_CONSTRAINT& aConstraint );
84
85private:
87};
88
89
91{
94
95 int errorMax = m_board->GetDesignSettings().m_MaxError;
96
98 {
99 reportAux( wxT( "No physical clearance constraints found. Tests not run." ) );
100 return true; // continue with other tests
101 }
102
103 reportAux( wxT( "Largest physical clearance : %d nm" ), m_board->m_DRCMaxPhysicalClearance );
104
105 size_t progressDelta = 250;
106 size_t count = 0;
107 size_t ii = 0;
108
109 if( !reportPhase( _( "Gathering physical items..." ) ) )
110 return false; // DRC cancelled
111
112 static const std::vector<KICAD_T> itemTypes = {
115 PCB_PAD_T,
120 };
121
122 static const LSET courtyards( { F_CrtYd, B_CrtYd } );
123
124 //
125 // Generate a count for use in progress reporting.
126 //
127
129 [&]( BOARD_ITEM* item ) -> bool
130 {
131 ++count;
132 return true;
133 } );
134
135 //
136 // Generate a BOARD_ITEM RTree.
137 //
138
140 [&]( BOARD_ITEM* item ) -> bool
141 {
142 if( !reportProgress( ii++, count, progressDelta ) )
143 return false;
144
145 LSET layers = item->GetLayerSet();
146
147 // Special-case holes and edge-cuts which pierce all physical layers
148 if( item->HasHole() )
149 {
150 if( layers.Contains( F_Cu ) )
151 layers |= LSET::FrontBoardTechMask().set( F_CrtYd );
152
153 if( layers.Contains( B_Cu ) )
154 layers |= LSET::BackBoardTechMask().set( B_CrtYd );
155
156 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
157 layers |= LSET::AllCuMask();
158 }
159 else if( item->Type() == PCB_FOOTPRINT_T )
160 {
161 layers = courtyards;
162 }
163 else if( item->IsOnLayer( Edge_Cuts ) )
164 {
165 layers |= LSET::PhysicalLayersMask() | courtyards;
166 }
167
168 for( PCB_LAYER_ID layer : layers.Seq() )
170
171 return true;
172 } );
173
174 std::unordered_map<PTR_PTR_CACHE_KEY, LSET> checkedPairs;
175 progressDelta = 100;
176 ii = 0;
177
178 //
179 // Run clearance checks -between- items.
180 //
181
184 {
185 if( !reportPhase( _( "Checking physical clearances..." ) ) )
186 return false; // DRC cancelled
187
189 [&]( BOARD_ITEM* item ) -> bool
190 {
191 if( !reportProgress( ii++, count, progressDelta ) )
192 return false;
193
194 LSET layers = item->GetLayerSet();
195
196 if( item->Type() == PCB_FOOTPRINT_T )
197 layers = courtyards;
198
199 for( PCB_LAYER_ID layer : layers.Seq() )
200 {
201 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
202
203 m_itemTree.QueryColliding( item, layer, layer,
204 // Filter:
205 [&]( BOARD_ITEM* other ) -> bool
206 {
207 BOARD_ITEM* a = item;
208 BOARD_ITEM* b = other;
209
210 // store canonical order so we don't collide in both
211 // directions (a:b and b:a)
212 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
213 std::swap( a, b );
214
215 auto it = checkedPairs.find( { a, b } );
216
217 if( it != checkedPairs.end() && it->second.test( layer ) )
218 {
219 return false;
220 }
221 else
222 {
223 checkedPairs[ { a, b } ].set( layer );
224 return true;
225 }
226 },
227 // Visitor:
228 [&]( BOARD_ITEM* other ) -> bool
229 {
230 if( testItemAgainstItem( item, itemShape.get(), layer,
231 other ) > 0 )
232 {
233 BOARD_ITEM* a = item;
234 BOARD_ITEM* b = other;
235
236 // store canonical order
237 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
238 std::swap( a, b );
239
240 // Once we record one DRC for error for physical clearance
241 // we don't need to record more
242 checkedPairs[ { a, b } ].set();
243 }
244
245 return !m_drcEngine->IsCancelled();
246 },
248
249 testItemAgainstZones( item, layer );
250 }
251
252 return true;
253 } );
254 }
255
256 progressDelta = 100;
257 count = 0;
258 ii = 0;
259
260 //
261 // Generate a count for progress reporting.
262 //
263
264 forEachGeometryItem( { PCB_ZONE_T, PCB_SHAPE_T },
266 [&]( BOARD_ITEM* item ) -> bool
267 {
268 ZONE* zone = dynamic_cast<ZONE*>( item );
269
270 if( zone && zone->GetIsRuleArea() )
271 return true; // Continue with other items
272
273 count += ( item->GetLayerSet() & LSET::AllCuMask() ).count();
274
275 return true;
276 } );
277
278 //
279 // Run clearance checks -within- polygonal items.
280 //
281
282 forEachGeometryItem( { PCB_ZONE_T, PCB_SHAPE_T },
284 [&]( BOARD_ITEM* item ) -> bool
285 {
286 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
287 ZONE* zone = dynamic_cast<ZONE*>( item );
288
289 if( zone && zone->GetIsRuleArea() )
290 return true; // Continue with other items
291
292 for( PCB_LAYER_ID layer : item->GetLayerSet().Seq() )
293 {
294 if( IsCopperLayer( layer ) )
295 {
296 if( !reportProgress( ii++, count, progressDelta ) )
297 return false;
298
299 DRC_CONSTRAINT c = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT,
300 item, nullptr, layer );
301
302 if( shape )
303 {
304 switch( shape->GetShape() )
305 {
306 case SHAPE_T::POLY:
307 testShapeLineChain( shape->GetPolyShape().Outline( 0 ),
308 shape->GetWidth(), layer, item, c );
309 break;
310
311 case SHAPE_T::BEZIER:
312 {
313 SHAPE_LINE_CHAIN asPoly;
314
316
317 for( const VECTOR2I& pt : shape->GetBezierPoints() )
318 asPoly.Append( pt );
319
320 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
321 break;
322 }
323
324 case SHAPE_T::ARC:
325 {
326 SHAPE_LINE_CHAIN asPoly;
327
328 VECTOR2I center = shape->GetCenter();
329 EDA_ANGLE angle = -shape->GetArcAngle();
330 double r = shape->GetRadius();
331 int steps = GetArcToSegmentCount( r, errorMax, angle );
332
333 asPoly.Append( shape->GetStart() );
334
335 for( int step = 1; step <= steps; ++step )
336 {
337 EDA_ANGLE rotation = ( angle * step ) / steps;
338 VECTOR2I pt = shape->GetStart();
339
340 RotatePoint( pt, center, rotation );
341 asPoly.Append( pt );
342 }
343
344 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
345 break;
346 }
347
349 {
350 SHAPE_LINE_CHAIN asPoly;
351 std::vector<VECTOR2I> pts = shape->GetRectCorners();
352 asPoly.Append( pts[0] );
353 asPoly.Append( pts[1] );
354 asPoly.Append( pts[2] );
355 asPoly.Append( pts[3] );
356 asPoly.SetClosed( true );
357
358 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
359 break;
360 }
361
362 case SHAPE_T::SEGMENT:
363 {
364 SHAPE_LINE_CHAIN asPoly;
365 asPoly.Append( shape->GetStart() );
366 asPoly.Append( shape->GetEnd() );
367
368 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
369 break;
370 }
371
372 default:
374 }
375 }
376
377 if( zone )
378 testZoneLayer( static_cast<ZONE*>( item ), layer, c );
379 }
380
381 if( m_drcEngine->IsCancelled() )
382 return false;
383 }
384
385 return !m_drcEngine->IsCancelled();
386 } );
387
388 reportRuleStatistics();
389
390 return !m_drcEngine->IsCancelled();
391}
392
393
395 int aLineWidth, PCB_LAYER_ID aLayer,
396 BOARD_ITEM* aParentItem,
397 DRC_CONSTRAINT& aConstraint )
398{
399 // We don't want to collide with neighboring segments forming a curve until the concavity
400 // approaches 180 degrees.
401 double angleTolerance = DEG2RAD( 180.0 - ADVANCED_CFG::GetCfg().m_SliverAngleTolerance );
403 int count = aOutline.SegmentCount();
404 int clearance = aConstraint.GetValue().Min();
405
406 if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
407 return;
408
409 // Trigonometry is not cheap; cache seg angles
410 std::vector<double> angles;
411 angles.reserve( count );
412
413 auto angleDiff =
414 []( double a, double b ) -> double
415 {
416 if( a > b )
417 std::swap( a, b );
418
419 double diff = b - a;
420
421 if( diff > M_PI )
422 return 2 * M_PI - diff;
423 else
424 return diff;
425 };
426
427 for( int ii = 0; ii < count; ++ii )
428 {
429 const SEG& seg = aOutline.CSegment( ii );
430
431 // NB: don't store angles of really short segments (which could point anywhere)
432
433 if( seg.SquaredLength() > SEG::Square( epsilon * 2 ) )
434 {
435 angles.push_back( EDA_ANGLE( seg.B - seg.A ).AsRadians() );
436 }
437 else if( ii > 0 )
438 {
439 angles.push_back( angles.back() );
440 }
441 else
442 {
443 for( int jj = 1; jj < count; ++jj )
444 {
445 const SEG& following = aOutline.CSegment( jj );
446
447 if( following.SquaredLength() > SEG::Square( epsilon * 2 ) || jj == count - 1 )
448 {
449 angles.push_back( EDA_ANGLE( following.B - following.A ).AsRadians() );
450 break;
451 }
452 }
453 }
454 }
455
456 // Find collisions before reporting so that we can condense them into fewer reports.
457 std::vector< std::pair<VECTOR2I, int> > collisions;
458
459 for( int ii = 0; ii < count; ++ii )
460 {
461 const SEG seg = aOutline.CSegment( ii );
462 double segAngle = angles[ ii ];
463
464 // Exclude segments on either side of us until we reach the angle tolerance
465 int firstCandidate = ii + 1;
466 int lastCandidate = count - 1;
467
468 while( firstCandidate < count )
469 {
470 if( angleDiff( segAngle, angles[ firstCandidate ] ) < angleTolerance )
471 firstCandidate++;
472 else
473 break;
474 }
475
476 if( aOutline.IsClosed() )
477 {
478 if( ii > 0 )
479 lastCandidate = ii - 1;
480
481 while( lastCandidate != std::min( firstCandidate, count - 1 ) )
482 {
483 if( angleDiff( segAngle, angles[ lastCandidate ] ) < angleTolerance )
484 lastCandidate = ( lastCandidate == 0 ) ? count - 1 : lastCandidate - 1;
485 else
486 break;
487 }
488 }
489
490 // Now run the collision between seg and each candidate seg in the candidate range.
491 if( lastCandidate < ii )
492 lastCandidate = count - 1;
493
494 for( int jj = firstCandidate; jj <= lastCandidate; ++jj )
495 {
496 const SEG candidate = aOutline.CSegment( jj );
497 int actual;
498
499 if( seg.Collide( candidate, clearance + aLineWidth - epsilon, &actual ) )
500 {
501 VECTOR2I firstPoint = seg.NearestPoint( candidate );
502 VECTOR2I secondPoint = candidate.NearestPoint( seg );
503 VECTOR2I pos = ( firstPoint + secondPoint ) / 2;
504
505 if( !collisions.empty() &&
506 ( pos - collisions.back().first ).EuclideanNorm() < clearance * 2 )
507 {
508 if( actual < collisions.back().second )
509 {
510 collisions.back().first = pos;
511 collisions.back().second = actual;
512 }
513
514 continue;
515 }
516
517 collisions.push_back( { pos, actual } );
518 }
519 }
520 }
521
522 for( std::pair<VECTOR2I, int> collision : collisions )
523 {
524 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
525 VECTOR2I pt = collision.first;
526
527 if( FOOTPRINT* parentFP = aParentItem->GetParentFootprint() )
528 {
529 RotatePoint( pt, parentFP->GetOrientation() );
530 pt += parentFP->GetPosition();
531 }
532
533 wxString msg = formatMsg( _( "Internal clearance violation (%s clearance %s; actual %s)" ),
534 aConstraint.GetName(),
535 clearance,
536 collision.second );
537
538 drce->SetErrorMessage( msg );
539 drce->SetItems( aParentItem );
540 drce->SetViolatingRule( aConstraint.GetParentRule() );
541
542 reportViolation( drce, pt, aLayer );
543 }
544}
545
546
548 DRC_CONSTRAINT& aConstraint )
549{
551 int clearance = aConstraint.GetValue().Min();
552
553 if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
554 return;
555
556 SHAPE_POLY_SET fill = aZone->GetFilledPolysList( aLayer )->CloneDropTriangulation();
557
558 // Turn fractured fill into outlines and holes
559 fill.Simplify();
560
561 for( int outlineIdx = 0; outlineIdx < fill.OutlineCount(); ++outlineIdx )
562 {
563 SHAPE_LINE_CHAIN* firstOutline = &fill.Outline( outlineIdx );
564
565 //
566 // Step one: outline to outline clearance violations
567 //
568
569 for( int ii = outlineIdx + 1; ii < fill.OutlineCount(); ++ii )
570 {
571 SHAPE_LINE_CHAIN* secondOutline = &fill.Outline( ii );
572
573 for( int jj = 0; jj < secondOutline->SegmentCount(); ++jj )
574 {
575 SEG secondSeg = secondOutline->Segment( jj );
576 int actual;
577 VECTOR2I pos;
578
579 if( firstOutline->Collide( secondSeg, clearance - epsilon, &actual, &pos ) )
580 {
581 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
582 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
583 aConstraint.GetName(),
584 clearance,
585 actual );
586
587 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
588 drce->SetItems( aZone );
589 drce->SetViolatingRule( aConstraint.GetParentRule() );
590
591 reportViolation( drce, pos, aLayer );
592 }
593 }
594
595 if( m_drcEngine->IsCancelled() )
596 return;
597 }
598
599 //
600 // Step two: interior hole clearance violations
601 //
602
603 for( int holeIdx = 0; holeIdx < fill.HoleCount( outlineIdx ); ++holeIdx )
604 {
605 testShapeLineChain( fill.Hole( outlineIdx, holeIdx ), 0, aLayer, aZone, aConstraint );
606
607 if( m_drcEngine->IsCancelled() )
608 return;
609 }
610 }
611}
612
613
615 SHAPE* aItemShape,
616 PCB_LAYER_ID aLayer,
617 BOARD_ITEM* aOther )
618{
619 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
621 DRC_CONSTRAINT constraint;
622 int clearance = 0;
623 int actual;
624 int violations = 0;
625 VECTOR2I pos;
626
627 std::shared_ptr<SHAPE> otherShapeStorage = aOther->GetEffectiveShape( aLayer );
628 SHAPE* otherShape = otherShapeStorage.get();
629
630 if( testClearance )
631 {
632 constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, aOther, aLayer );
633 clearance = constraint.GetValue().Min();
634 }
635
636 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
637 {
638 // Collide (and generate violations) based on a well-defined order so that exclusion
639 // checking against previously-generated violations will work.
640 if( aItem->m_Uuid > aOther->m_Uuid )
641 {
642 std::swap( aItem, aOther );
643 std::swap( aItemShape, otherShape );
644 }
645
646 if( aItemShape->Collide( otherShape, clearance, &actual, &pos ) )
647 {
648 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
649 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
650 constraint.GetName(),
651 clearance,
652 actual );
653
654 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
655 drce->SetItems( aItem, aOther );
656 drce->SetViolatingRule( constraint.GetParentRule() );
657
658 reportViolation( drce, pos, aLayer );
659 ++violations;
660 }
661 }
662
663 if( testHoles )
664 {
665 std::shared_ptr<SHAPE_SEGMENT> itemHoleShape;
666 std::shared_ptr<SHAPE_SEGMENT> otherHoleShape;
667 clearance = 0;
668
669 if( aItem->Type() == PCB_VIA_T )
670 {
671 LSET layers = aItem->GetLayerSet();
672
673 if( layers.Contains( F_Cu ) )
674 layers |= LSET::FrontBoardTechMask().set( F_CrtYd );
675
676 if( layers.Contains( B_Cu ) )
677 layers |= LSET::BackBoardTechMask().set( B_CrtYd );
678
679 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
680 layers |= LSET::AllCuMask();
681
682 wxCHECK_MSG( layers.Contains( aLayer ), violations,
683 wxT( "Bug! Vias should only be checked for layers on which they exist" ) );
684
685 itemHoleShape = aItem->GetEffectiveHoleShape();
686 }
687 else if( aItem->HasHole() )
688 {
689 itemHoleShape = aItem->GetEffectiveHoleShape();
690 }
691
692 if( aOther->Type() == PCB_VIA_T )
693 {
694 LSET layers = aOther->GetLayerSet();
695
696 if( layers.Contains( F_Cu ) )
697 layers |= LSET::FrontBoardTechMask().set( F_CrtYd );
698
699 if( layers.Contains( B_Cu ) )
700 layers |= LSET::BackBoardTechMask().set( B_CrtYd );
701
702 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
703 layers |= LSET::AllCuMask();
704
705 wxCHECK_MSG( layers.Contains( aLayer ), violations,
706 wxT( "Bug! Vias should only be checked for layers on which they exist" ) );
707
708 otherHoleShape = aOther->GetEffectiveHoleShape();
709 }
710 else if( aOther->HasHole() )
711 {
712 otherHoleShape = aOther->GetEffectiveHoleShape();
713 }
714
715 if( itemHoleShape || otherHoleShape )
716 {
717 constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, aOther, aItem,
718 aLayer );
719 clearance = constraint.GetValue().Min();
720 }
721
722 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
723 {
724 if( itemHoleShape && itemHoleShape->Collide( otherShape, clearance, &actual, &pos ) )
725 {
726 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
727 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
728 constraint.GetName(),
729 clearance ,
730 actual );
731
732 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
733 drce->SetItems( aItem, aOther );
734 drce->SetViolatingRule( constraint.GetParentRule() );
735
736 reportViolation( drce, pos, aLayer );
737 ++violations;
738 }
739
740 if( otherHoleShape && otherHoleShape->Collide( aItemShape, clearance, &actual, &pos ) )
741 {
742 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
743 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
744 constraint.GetName(),
745 clearance,
746 actual );
747
748 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
749 drce->SetItems( aItem, aOther );
750 drce->SetViolatingRule( constraint.GetParentRule() );
751
752 reportViolation( drce, pos, aLayer );
753 ++violations;
754 }
755 }
756 }
757
758 return violations;
759}
760
761
763 PCB_LAYER_ID aLayer )
764{
765 for( ZONE* zone : m_board->m_DRCZones )
766 {
767 if( !zone->GetLayerSet().test( aLayer ) )
768 continue;
769
770 BOX2I itemBBox = aItem->GetBoundingBox();
771 BOX2I worstCaseBBox = itemBBox;
772
773 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
774
775 if( !worstCaseBBox.Intersects( zone->GetBoundingBox() ) )
776 continue;
777
778 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
780
781 if( !testClearance && !testHoles )
782 return;
783
784 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
785 DRC_CONSTRAINT constraint;
786 bool colliding;
787 int clearance = -1;
788 int actual;
789 VECTOR2I pos;
790
791 if( testClearance )
792 {
793 constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone,
794 aLayer );
795 clearance = constraint.GetValue().Min();
796 }
797
798 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
799 {
800 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
801
802 if( aItem->Type() == PCB_PAD_T )
803 {
804 PAD* pad = static_cast<PAD*>( aItem );
805
806 if( !pad->FlashLayer( aLayer ) )
807 {
808 if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
809 continue;
810
811 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
812 int size = hole->GetWidth();
813
814 itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
815 }
816 }
817
818 if( zoneTree )
819 {
820 colliding = zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, clearance,
821 &actual, &pos );
822 }
823 else
824 {
825 colliding = zone->Outline()->Collide( itemShape.get(), clearance, &actual, &pos );
826 }
827
828 if( colliding )
829 {
830 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
831 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
832 constraint.GetName(),
833 clearance,
834 actual );
835
836 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
837 drce->SetItems( aItem, zone );
838 drce->SetViolatingRule( constraint.GetParentRule() );
839
840 reportViolation( drce, pos, aLayer );
841 }
842 }
843
844 if( testHoles )
845 {
846 std::shared_ptr<SHAPE_SEGMENT> holeShape;
847
848 if( aItem->Type() == PCB_VIA_T )
849 {
850 if( aItem->GetLayerSet().Contains( aLayer ) )
851 holeShape = aItem->GetEffectiveHoleShape();
852 }
853 else if( aItem->HasHole() )
854 {
855 holeShape = aItem->GetEffectiveHoleShape();
856 }
857
858 if( holeShape )
859 {
861 zone, aLayer );
862 clearance = constraint.GetValue().Min();
863
864 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
865 && clearance > 0
866 && zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer, clearance,
867 &actual, &pos ) )
868 {
869 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
870 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
871 constraint.GetName(),
872 clearance,
873 actual );
874
875 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
876 drce->SetItems( aItem, zone );
877 drce->SetViolatingRule( constraint.GetParentRule() );
878
879 reportViolation( drce, pos, aLayer );
880 }
881 }
882 }
883
884 if( m_drcEngine->IsCancelled() )
885 return;
886 }
887}
888
889
890namespace detail
891{
893}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
BASE_SET & set(size_t pos)
Definition: base_set.h:116
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:78
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:311
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: board_item.cpp:279
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:299
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:249
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:289
virtual bool HasHole() const
Definition: board_item.h:153
int m_DRCMaxPhysicalClearance
Definition: board.h:1350
int m_DRCMaxClearance
Definition: board.h:1349
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1341
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:946
std::vector< ZONE * > m_DRCZones
Definition: board.h:1347
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
wxString GetName() const
Definition: drc_rule.h:160
SEVERITY GetSeverity() const
Definition: drc_rule.h:173
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:152
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:156
BOARD * GetBoard() const
Definition: drc_engine.h:96
bool IsErrorLimitExceeded(int error_code)
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:693
bool IsCancelled() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:393
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:48
void Insert(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, int aWorstClearance=0)
Insert an item into the tree on a particular layer with an optional worst clearance.
Definition: drc_rtree.h:104
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
Definition: drc_rtree.h:214
void clear()
Remove all items from the RTree.
Definition: drc_rtree.h:167
void testZoneLayer(ZONE *aZone, PCB_LAYER_ID aLayer, DRC_CONSTRAINT &aConstraint)
void testItemAgainstZones(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
virtual const wxString GetName() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
void testShapeLineChain(const SHAPE_LINE_CHAIN &aOutline, int aLineWidth, PCB_LAYER_ID aLayer, BOARD_ITEM *aParentItem, DRC_CONSTRAINT &aConstraint)
int testItemAgainstItem(BOARD_ITEM *aItem, SHAPE *aItemShape, PCB_LAYER_ID aLayer, BOARD_ITEM *aOther)
virtual const wxString GetDescription() const override
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual)
virtual bool reportPhase(const wxString &aStageName)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
DRC_ENGINE * m_drcEngine
void reportAux(const wxString &aMsg)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
double AsRadians() const
Definition: eda_angle.h:117
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:84
const KIID m_Uuid
Definition: eda_item.h:498
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:108
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:1033
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:337
int GetRadius() const
Definition: eda_shape.cpp:961
SHAPE_T GetShape() const
Definition: eda_shape.h:168
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:859
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:215
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:173
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1544
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:320
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:340
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllLayersMask()
Definition: lset.cpp:601
static LSET FrontBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on front side.
Definition: lset.cpp:629
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:572
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:297
static LSET BackBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on Back side.
Definition: lset.cpp:615
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:63
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:54
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:81
int GetWidth() const override
Definition: pcb_shape.cpp:375
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:327
static SEG::ecoord Square(int a)
Definition: seg.h:123
bool Collide(const SEG &aSeg, int aClearance, int *aActual=nullptr) const
Definition: seg.cpp:289
ecoord SquaredLength() const
Definition: seg.h:338
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if point aP lies closer to us than aClearance.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
Represent a set of closed polygons.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int OutlineCount() const
Return the number of outlines in the set.
An abstract shape on 2D plane.
Definition: shape.h:126
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition: shape.h:181
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:752
const std::shared_ptr< SHAPE_POLY_SET > & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:648
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:648
SHAPE_POLY_SET * Outline()
Definition: zone.h:368
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:136
The common library.
@ DRCE_HOLE_CLEARANCE
Definition: drc_item.h:54
@ DRCE_CLEARANCE
Definition: drc_item.h:43
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:75
@ PHYSICAL_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:74
#define _(s)
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
a few functions useful in geometry calculations.
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:663
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ Edge_Cuts
Definition: layer_ids.h:112
@ B_Cu
Definition: layer_ids.h:65
@ B_CrtYd
Definition: layer_ids.h:115
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
const double epsilon
VECTOR2I center
int clearance
int actual
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition: trigo.cpp:229
double DEG2RAD(double deg)
Definition: trigo.h:166
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition: typeinfo.h:90
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:100
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition: typeinfo.h:94
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96