KiCad PCB EDA Suite
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 (C) 2021-2022 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>
33#include <geometry/seg.h>
35#include <drc/drc_engine.h>
36#include <drc/drc_rtree.h>
37#include <drc/drc_item.h>
38#include <drc/drc_rule.h>
40
41/*
42 Physical clearance tests.
43
44 Errors generated:
45 - DRCE_PHYSICAL_CLEARANCE
46 - DRCE_PHYSICAL_HOLE_CLEARANCE
47*/
48
50{
51public:
54 {
55 }
56
58 {
59 }
60
61 virtual bool Run() override;
62
63 virtual const wxString GetName() const override
64 {
65 return wxT( "physical_clearance" );
66 };
67
68 virtual const wxString GetDescription() const override
69 {
70 return wxT( "Tests item clearances irrespective of nets" );
71 }
72
73private:
74 bool testItemAgainstItem( BOARD_ITEM* item, SHAPE* itemShape, PCB_LAYER_ID layer,
75 BOARD_ITEM* other );
76
77 void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
78
79 void testShapeLineChain( const SHAPE_LINE_CHAIN& aOutline, int aLineWidth, PCB_LAYER_ID aLayer,
80 BOARD_ITEM* aParentItem, DRC_CONSTRAINT& aConstraint );
81
82 void testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer, DRC_CONSTRAINT& aConstraint );
83
84private:
86};
87
88
90{
93
94 int errorMax = m_board->GetDesignSettings().m_MaxError;
95
97 {
98 reportAux( wxT( "No physical clearance constraints found. Tests not run." ) );
99 return true; // continue with other tests
100 }
101
102 reportAux( wxT( "Largest physical clearance : %d nm" ), m_board->m_DRCMaxPhysicalClearance );
103
104 size_t progressDelta = 250;
105 size_t count = 0;
106 size_t ii = 0;
107
108 if( !reportPhase( _( "Gathering physical items..." ) ) )
109 return false; // DRC cancelled
110
111 static const std::vector<KICAD_T> itemTypes = {
114 PCB_PAD_T,
118 };
119
120 static const LSET courtyards( 2, F_CrtYd, B_CrtYd );
121
123 [&]( BOARD_ITEM* item ) -> bool
124 {
125 ++count;
126 return true;
127 } );
128
130 [&]( BOARD_ITEM* item ) -> bool
131 {
132 if( !reportProgress( ii++, count, progressDelta ) )
133 return false;
134
135 LSET layers = item->GetLayerSet();
136
137 // Special-case holes and edge-cuts which pierce all physical layers
138 if( item->HasHole() )
139 {
140 layers |= LSET::PhysicalLayersMask() | courtyards;
141 }
142 else if( item->Type() == PCB_FOOTPRINT_T )
143 {
144 layers = courtyards;
145 }
146 else if( item->IsOnLayer( Edge_Cuts ) )
147 {
148 layers |= LSET::PhysicalLayersMask() | courtyards;
149 }
150
151 for( PCB_LAYER_ID layer : layers.Seq() )
153
154 return true;
155 } );
156
157 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
158 progressDelta = 100;
159 ii = 0;
160
163 {
164 if( !reportPhase( _( "Checking physical clearances..." ) ) )
165 return false; // DRC cancelled
166
168 [&]( BOARD_ITEM* item ) -> bool
169 {
170 if( !reportProgress( ii++, count, progressDelta ) )
171 return false;
172
173 LSET layers = item->GetLayerSet();
174
175 if( item->Type() == PCB_FOOTPRINT_T )
176 layers = courtyards;
177
178 for( PCB_LAYER_ID layer : layers.Seq() )
179 {
180 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
181
182 m_itemTree.QueryColliding( item, layer, layer,
183 // Filter:
184 [&]( BOARD_ITEM* other ) -> bool
185 {
186 BOARD_ITEM* a = item;
187 BOARD_ITEM* b = other;
188
189 // store canonical order so we don't collide in both
190 // directions (a:b and b:a)
191 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
192 std::swap( a, b );
193
194 if( checkedPairs.count( { a, b } ) )
195 {
196 return false;
197 }
198 else
199 {
200 checkedPairs[ { a, b } ] = 1;
201 return true;
202 }
203 },
204 // Visitor:
205 [&]( BOARD_ITEM* other ) -> bool
206 {
207 return testItemAgainstItem( item, itemShape.get(), layer,
208 other );
209 },
211
212 testItemAgainstZones( item, layer );
213 }
214
215 return true;
216 } );
217 }
218
219 progressDelta = 100;
220 count = 0;
221 ii = 0;
222
223 forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T },
225 [&]( BOARD_ITEM* item ) -> bool
226 {
227 ZONE* zone = dynamic_cast<ZONE*>( item );
228
229 if( zone && zone->GetIsRuleArea() )
230 return true; // Continue with other items
231
232 count += ( item->GetLayerSet() & LSET::AllCuMask() ).count();
233
234 return true;
235 } );
236
237 forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T },
239 [&]( BOARD_ITEM* item ) -> bool
240 {
241 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
242 ZONE* zone = dynamic_cast<ZONE*>( item );
243
244 if( zone && zone->GetIsRuleArea() )
245 return true; // Continue with other items
246
247 for( PCB_LAYER_ID layer : item->GetLayerSet().Seq() )
248 {
249 if( IsCopperLayer( layer ) )
250 {
251 if( !reportProgress( ii++, count, progressDelta ) )
252 return false;
253
254 DRC_CONSTRAINT c = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT,
255 item, nullptr, layer );
256
257 if( shape )
258 {
259 switch( shape->GetShape() )
260 {
261 case SHAPE_T::POLY:
262 testShapeLineChain( shape->GetPolyShape().Outline( 0 ),
263 shape->GetWidth(), layer, item, c );
264 break;
265
266 case SHAPE_T::BEZIER:
267 {
268 SHAPE_LINE_CHAIN asPoly;
269
271
272 for( const VECTOR2I& pt : shape->GetBezierPoints() )
273 asPoly.Append( pt );
274
275 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
276 break;
277 }
278
279 case SHAPE_T::ARC:
280 {
281 SHAPE_LINE_CHAIN asPoly;
282
283 VECTOR2I center = shape->GetCenter();
284 EDA_ANGLE angle = -shape->GetArcAngle();
285 double r = shape->GetRadius();
286 int steps = GetArcToSegmentCount( r, errorMax, angle );
287
288 asPoly.Append( shape->GetStart() );
289
290 for( int step = 1; step <= steps; ++step )
291 {
292 EDA_ANGLE rotation = ( angle * step ) / steps;
293 VECTOR2I pt = shape->GetStart();
294
295 RotatePoint( pt, center, rotation );
296 asPoly.Append( pt );
297 }
298
299 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
300 break;
301 }
302
303 case SHAPE_T::RECT:
304 {
305 SHAPE_LINE_CHAIN asPoly;
306 std::vector<VECTOR2I> pts = shape->GetRectCorners();
307 asPoly.Append( pts[0] );
308 asPoly.Append( pts[1] );
309 asPoly.Append( pts[2] );
310 asPoly.Append( pts[3] );
311 asPoly.SetClosed( true );
312
313 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
314 break;
315 }
316
317 default:
319 }
320 }
321
322 if( zone )
323 testZoneLayer( static_cast<ZONE*>( item ), layer, c );
324 }
325
326 if( m_drcEngine->IsCancelled() )
327 return false;
328 }
329
330 return !m_drcEngine->IsCancelled();
331 } );
332
333 reportRuleStatistics();
334
335 return !m_drcEngine->IsCancelled();
336}
337
338
340 int aLineWidth, PCB_LAYER_ID aLayer,
341 BOARD_ITEM* aParentItem,
342 DRC_CONSTRAINT& aConstraint )
343{
344 // We don't want to collide with neighboring segments forming a curve until the concavity
345 // approaches 180 degrees.
346 double angleTolerance = DEG2RAD( 180.0 - ADVANCED_CFG::GetCfg().m_SliverAngleTolerance );
347 int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
348 int count = aOutline.SegmentCount();
349 int clearance = aConstraint.GetValue().Min();
350
351 if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
352 return;
353
354 // Trigonometry is not cheap; cache seg angles
355 std::vector<double> angles;
356 angles.reserve( count );
357
358 auto angleDiff =
359 []( double a, double b ) -> double
360 {
361 if( a > b )
362 std::swap( a, b );
363
364 double diff = b - a;
365
366 if( diff > M_PI )
367 return 2 * M_PI - diff;
368 else
369 return diff;
370 };
371
372 for( int ii = 0; ii < count; ++ii )
373 {
374 const SEG& seg = aOutline.CSegment( ii );
375
376 // NB: don't store angles of really short segments (which could point anywhere)
377
378 if( seg.SquaredLength() > SEG::Square( epsilon * 2 ) )
379 {
380 angles.push_back( EDA_ANGLE( seg.B - seg.A ).AsRadians() );
381 }
382 else if( ii > 0 )
383 {
384 angles.push_back( angles.back() );
385 }
386 else
387 {
388 for( int jj = 1; jj < count; ++jj )
389 {
390 const SEG& following = aOutline.CSegment( jj );
391
392 if( following.SquaredLength() > SEG::Square( epsilon * 2 ) || jj == count - 1 )
393 {
394 angles.push_back( EDA_ANGLE( following.B - following.A ).AsRadians() );
395 break;
396 }
397 }
398 }
399 }
400
401 // Find collisions before reporting so that we can condense them into fewer reports.
402 std::vector< std::pair<VECTOR2I, int> > collisions;
403
404 for( int ii = 0; ii < count; ++ii )
405 {
406 const SEG seg = aOutline.CSegment( ii );
407 double segAngle = angles[ ii ];
408
409 // Exclude segments on either side of us until we reach the angle tolerance
410 int firstCandidate = ii + 1;
411 int lastCandidate = count - 1;
412
413 while( firstCandidate < count )
414 {
415 if( angleDiff( segAngle, angles[ firstCandidate ] ) < angleTolerance )
416 firstCandidate++;
417 else
418 break;
419 }
420
421 if( aOutline.IsClosed() )
422 {
423 if( ii > 0 )
424 lastCandidate = ii - 1;
425
426 while( lastCandidate != std::min( firstCandidate, count - 1 ) )
427 {
428 if( angleDiff( segAngle, angles[ lastCandidate ] ) < angleTolerance )
429 lastCandidate = ( lastCandidate == 0 ) ? count - 1 : lastCandidate - 1;
430 else
431 break;
432 }
433 }
434
435 // Now run the collision between seg and each candidate seg in the candidate range.
436 if( lastCandidate < ii )
437 lastCandidate = count - 1;
438
439 for( int jj = firstCandidate; jj <= lastCandidate; ++jj )
440 {
441 const SEG candidate = aOutline.CSegment( jj );
442 int actual;
443
444 if( seg.Collide( candidate, clearance + aLineWidth - epsilon, &actual ) )
445 {
446 VECTOR2I firstPoint = seg.NearestPoint( candidate );
447 VECTOR2I secondPoint = candidate.NearestPoint( seg );
448 VECTOR2I pos = ( firstPoint + secondPoint ) / 2;
449
450 if( !collisions.empty() &&
451 ( pos - collisions.back().first ).EuclideanNorm() < clearance * 2 )
452 {
453 if( actual < collisions.back().second )
454 {
455 collisions.back().first = pos;
456 collisions.back().second = actual;
457 }
458
459 continue;
460 }
461
462 collisions.push_back( { pos, actual } );
463 }
464 }
465 }
466
467 for( std::pair<VECTOR2I, int> collision : collisions )
468 {
469 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
470 wxString msg;
471 VECTOR2I pt = collision.first;
472
473 if( aParentItem->GetParentFootprint() )
474 pt += aParentItem->GetParentFootprint()->GetPosition();
475
476 msg.Printf( _( "Internal clearance violation (%s clearance %s; actual %s)" ),
477 aConstraint.GetName(),
478 MessageTextFromValue( userUnits(), clearance ),
479 MessageTextFromValue( userUnits(), collision.second ) );
480
481 drce->SetErrorMessage( msg );
482 drce->SetItems( aParentItem );
483 drce->SetViolatingRule( aConstraint.GetParentRule() );
484
485 reportViolation( drce, pt, aLayer );
486 }
487}
488
489
491 DRC_CONSTRAINT& aConstraint )
492{
493 int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
494 int clearance = aConstraint.GetValue().Min();
495
496 if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
497 return;
498
499 SHAPE_POLY_SET fill = aZone->GetFilledPolysList( aLayer )->CloneDropTriangulation();
500
501 // Turn fractured fill into outlines and holes
503
504 for( int outlineIdx = 0; outlineIdx < fill.OutlineCount(); ++outlineIdx )
505 {
506 SHAPE_LINE_CHAIN* firstOutline = &fill.Outline( outlineIdx );
507
508 // Step one: outline to outline clearance violations
509
510 for( int ii = outlineIdx + 1; ii < fill.OutlineCount(); ++ii )
511 {
512 SHAPE_LINE_CHAIN* secondOutline = &fill.Outline( ii );
513
514 for( int jj = 0; jj < secondOutline->SegmentCount(); ++jj )
515 {
516 SEG secondSeg = secondOutline->Segment( jj );
517 int actual;
518 VECTOR2I pos;
519
520 if( firstOutline->Collide( secondSeg, clearance - epsilon, &actual, &pos ) )
521 {
522 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
523 wxString msg;
524
525 msg.Printf( _( "(%s clearance %s; actual %s)" ),
526 aConstraint.GetName(),
527 MessageTextFromValue( userUnits(), clearance ),
528 MessageTextFromValue( userUnits(), actual ) );
529
530 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
531 drce->SetItems( aZone );
532 drce->SetViolatingRule( aConstraint.GetParentRule() );
533
534 reportViolation( drce, pos, aLayer );
535 }
536 }
537
538 if( m_drcEngine->IsCancelled() )
539 return;
540 }
541
542 // Step two: interior hole clearance violations
543
544 for( int holeIdx = 0; holeIdx < fill.HoleCount( outlineIdx ); ++holeIdx )
545 {
546 testShapeLineChain( fill.Hole( outlineIdx, holeIdx ), 0, aLayer, aZone, aConstraint );
547
548 if( m_drcEngine->IsCancelled() )
549 return;
550 }
551 }
552}
553
554
556 SHAPE* itemShape,
557 PCB_LAYER_ID layer,
558 BOARD_ITEM* other )
559{
560 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
562 DRC_CONSTRAINT constraint;
563 int clearance = 0;
564 int actual;
565 VECTOR2I pos;
566
567 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( layer );
568
569 if( testClearance )
570 {
571 constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, item, other, layer );
572 clearance = constraint.GetValue().Min();
573 }
574
575 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
576 {
577 if( itemShape->Collide( otherShape.get(), clearance, &actual, &pos ) )
578 {
579 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
580 wxString msg;
581
582 msg.Printf( _( "(%s clearance %s; actual %s)" ),
583 constraint.GetName(),
584 MessageTextFromValue( userUnits(), clearance ),
585 MessageTextFromValue( userUnits(), actual ) );
586
587 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
588 drce->SetItems( item, other );
589 drce->SetViolatingRule( constraint.GetParentRule() );
590
591 reportViolation( drce, pos, layer );
592 }
593 }
594
595 if( testHoles )
596 {
597 std::shared_ptr<SHAPE_SEGMENT> itemHoleShape;
598 std::shared_ptr<SHAPE_SEGMENT> otherHoleShape;
599 clearance = 0;
600
601 if( item->Type() == PCB_VIA_T )
602 {
603 if( item->GetLayerSet().Contains( layer ) )
604 itemHoleShape = item->GetEffectiveHoleShape();
605 }
606 else if( item->HasHole() )
607 {
608 itemHoleShape = item->GetEffectiveHoleShape();
609 }
610
611 if( other->Type() == PCB_VIA_T )
612 {
613 if( other->GetLayerSet().Contains( layer ) )
614 otherHoleShape = other->GetEffectiveHoleShape();
615 }
616 else if( other->HasHole() )
617 {
618 otherHoleShape = other->GetEffectiveHoleShape();
619 }
620
621 if( itemHoleShape || otherHoleShape )
622 {
623 constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, other, item,
624 layer );
625 clearance = constraint.GetValue().Min();
626 }
627
628 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
629 {
630 if( itemHoleShape && itemHoleShape->Collide( otherShape.get(), clearance, &actual, &pos ) )
631 {
632 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
633 wxString msg;
634
635 msg.Printf( _( "(%s clearance %s; actual %s)" ),
636 constraint.GetName(),
637 MessageTextFromValue( userUnits(), clearance ),
638 MessageTextFromValue( userUnits(), actual ) );
639
640 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
641 drce->SetItems( item, other );
642 drce->SetViolatingRule( constraint.GetParentRule() );
643
644 reportViolation( drce, pos, layer );
645 }
646
647 if( otherHoleShape && otherHoleShape->Collide( itemShape, clearance, &actual, &pos ) )
648 {
649 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
650 wxString msg;
651
652 msg.Printf( _( "(%s clearance %s; actual %s)" ),
653 constraint.GetName(),
654 MessageTextFromValue( userUnits(), clearance ),
655 MessageTextFromValue( userUnits(), actual ) );
656
657 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
658 drce->SetItems( item, other );
659 drce->SetViolatingRule( constraint.GetParentRule() );
660
661 reportViolation( drce, pos, layer );
662 }
663 }
664 }
665
666 return !m_drcEngine->IsCancelled();
667}
668
669
671 PCB_LAYER_ID aLayer )
672{
673 for( ZONE* zone : m_board->m_DRCZones )
674 {
675 if( !zone->GetLayerSet().test( aLayer ) )
676 continue;
677
678 if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) )
679 {
680 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
682
683 if( !testClearance && !testHoles )
684 return;
685
686 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
687 EDA_RECT itemBBox = aItem->GetBoundingBox();
688 DRC_CONSTRAINT constraint;
689 bool colliding;
690 int clearance = -1;
691 int actual;
692 VECTOR2I pos;
693
694 if( testClearance )
695 {
696 constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone,
697 aLayer );
698 clearance = constraint.GetValue().Min();
699 }
700
701 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
702 {
703 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
704
705 if( aItem->Type() == PCB_PAD_T )
706 {
707 PAD* pad = static_cast<PAD*>( aItem );
708
709 if( !pad->FlashLayer( aLayer ) )
710 {
711 if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
712 continue;
713
714 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
715 int size = hole->GetWidth();
716
717 // Note: drill size represents finish size, which means the actual hole
718 // size is the plating thickness larger.
719 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
721
722 itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
723 }
724 }
725
726 if( zoneTree )
727 {
728 colliding = zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
729 clearance, &actual, &pos );
730 }
731 else
732 {
733 colliding = zone->Outline()->Collide( itemShape.get(), clearance, &actual,
734 &pos );
735 }
736
737 if( colliding )
738 {
739 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
740 wxString msg;
741
742 msg.Printf( _( "(%s clearance %s; actual %s)" ),
743 constraint.GetName(),
744 MessageTextFromValue( userUnits(), clearance ),
745 MessageTextFromValue( userUnits(), actual ) );
746
747 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
748 drce->SetItems( aItem, zone );
749 drce->SetViolatingRule( constraint.GetParentRule() );
750
751 reportViolation( drce, pos, aLayer );
752 }
753 }
754
755 if( testHoles )
756 {
757 std::shared_ptr<SHAPE_SEGMENT> holeShape;
758
759 if( aItem->Type() == PCB_VIA_T )
760 {
761 if( aItem->GetLayerSet().Contains( aLayer ) )
762 holeShape = aItem->GetEffectiveHoleShape();
763 }
764 else if( aItem->HasHole() )
765 {
766 holeShape = aItem->GetEffectiveHoleShape();
767 }
768
769 if( holeShape )
770 {
772 zone, aLayer );
773 clearance = constraint.GetValue().Min();
774
775 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
776 && clearance > 0
777 && zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
778 clearance, &actual, &pos ) )
779 {
780 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
781 wxString msg;
782
783 msg.Printf( _( "(%s clearance %s; actual %s)" ),
784 constraint.GetName(),
785 MessageTextFromValue( userUnits(), clearance ),
786 MessageTextFromValue( userUnits(), actual ) );
787
788 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
789 drce->SetItems( aItem, zone );
790 drce->SetViolatingRule( constraint.GetParentRule() );
791
792 reportViolation( drce, pos, aLayer );
793 }
794 }
795 }
796 }
797
798 if( m_drcEngine->IsCancelled() )
799 return;
800 }
801}
802
803
804namespace detail
805{
807}
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:103
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
int GetHolePlatingThickness() const
Pad & via drills are finish size.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:53
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:229
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:197
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:175
BOARD_ITEM_CONTAINER * GetParentFootprint() const
Definition: board_item.cpp:217
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:207
virtual bool HasHole() const
Definition: board_item.h:121
int m_DRCMaxPhysicalClearance
Definition: board.h:1135
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1128
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:619
std::vector< ZONE * > m_DRCZones
Definition: board.h:1132
wxString GetName() const
Definition: drc_rule.h:147
SEVERITY GetSeverity() const
Definition: drc_rule.h:160
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:139
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:143
BOARD * GetBoard() const
Definition: drc_engine.h:88
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:646
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:324
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:211
void clear()
Remove all items from the RTree.
Definition: drc_rtree.h:166
bool testItemAgainstItem(BOARD_ITEM *item, SHAPE *itemShape, PCB_LAYER_ID layer, BOARD_ITEM *other)
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)
virtual const wxString GetDescription() const override
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
virtual void reportAux(wxString fmt,...)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, PCB_LAYER_ID aMarkerLayer)
EDA_UNITS userUnits() const
DRC_ENGINE * m_drcEngine
double AsRadians() const
Definition: eda_angle.h:153
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:262
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
virtual const EDA_RECT GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:75
Handle the component boundary box.
Definition: eda_rect.h:44
bool Intersects(const EDA_RECT &aRect) const
Test for a common area between rectangles.
Definition: eda_rect.cpp:150
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:535
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:367
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:242
int GetRadius() const
Definition: eda_shape.cpp:473
SHAPE_T GetShape() const
Definition: eda_shape.h:110
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:115
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:982
int GetWidth() const
Definition: eda_shape.h:106
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:225
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:72
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:529
static LSET AllLayersMask()
Definition: lset.cpp:808
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
Definition: layer_ids.h:599
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:59
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:79
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:261
static SEG::ecoord Square(int a)
Definition: seg.h:123
bool Collide(const SEG &aSeg, int aClearance, int *aActual=nullptr) const
Definition: seg.cpp:223
ecoord SquaredLength() const
Definition: seg.h:356
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.
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.
SEG Segment(int aIndex)
Return a copy of the aIndex-th 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
Return the reference to aIndex-th outline in the set.
void Simplify(POLYGON_MODE aFastMode)
SHAPE_LINE_CHAIN & Outline(int aIndex)
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
An abstract shape on 2D plane.
Definition: shape.h:123
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:178
Handle a list of polygons defining a copper zone.
Definition: zone.h:58
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:698
const EDA_RECT GetCachedBoundingBox() const
ONLY TO BE USED BY CLIENTS WHICH SET UP THE CACHE!
Definition: zone.h:134
const std::shared_ptr< SHAPE_POLY_SET > & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:603
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.cpp:282
SHAPE_POLY_SET * Outline()
Definition: zone.h:311
The common library.
@ DRCE_HOLE_CLEARANCE
Definition: drc_item.h:53
@ DRCE_CLEARANCE
Definition: drc_item.h:43
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:70
@ PHYSICAL_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:69
#define _(s)
E_SERIE r
Definition: eserie.cpp:41
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:822
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ F_CrtYd
Definition: layer_ids.h:117
@ Edge_Cuts
Definition: layer_ids.h:113
@ B_CrtYd
Definition: layer_ids.h:116
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ PTH
Plated through hole pad.
@ RPT_SEVERITY_IGNORE
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
double DEG2RAD(double deg)
Definition: trigo.h:195
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:96
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:104
@ PCB_FP_TEXTBOX_T
class FP_TEXTBOX, wrapped text in a footprint
Definition: typeinfo.h:95
@ 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:114
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
@ PCB_FP_ZONE_T
class ZONE, managed by a footprint
Definition: typeinfo.h:102
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:89
@ PCB_FP_TEXT_T
class FP_TEXT, text in a footprint
Definition: typeinfo.h:94
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:105
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:107
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:103