KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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_rtree.h>
37#include <drc/drc_item.h>
39
40/*
41 Physical clearance tests.
42
43 Errors generated:
44 - DRCE_PHYSICAL_CLEARANCE
45 - DRCE_PHYSICAL_HOLE_CLEARANCE
46*/
47
49{
50public:
53 {}
54
56
57 virtual bool Run() override;
58
59 virtual const wxString GetName() const override { return wxT( "physical_clearance" ); };
60
61private:
62 int testItemAgainstItem( BOARD_ITEM* aItem, SHAPE* aItemShape, PCB_LAYER_ID aLayer,
63 BOARD_ITEM* aOther );
64
65 void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
66
67 void testShapeLineChain( const SHAPE_LINE_CHAIN& aOutline, int aLineWidth, PCB_LAYER_ID aLayer,
68 BOARD_ITEM* aParentItem, DRC_CONSTRAINT& aConstraint );
69
70 void testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer, DRC_CONSTRAINT& aConstraint );
71
72private:
74};
75
76
78{
81
82 int errorMax = m_board->GetDesignSettings().m_MaxError;
83 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
84
86 {
87 REPORT_AUX( wxT( "No physical clearance constraints found. Tests not run." ) );
88 return true; // continue with other tests
89 }
90
91 size_t progressDelta = 250;
92 size_t count = 0;
93 size_t ii = 0;
94
95 if( !reportPhase( _( "Gathering physical items..." ) ) )
96 return false; // DRC cancelled
97
98 static const std::vector<KICAD_T> itemTypes = {
101 PCB_PAD_T,
106 };
107
108 static const LSET courtyards( { F_CrtYd, B_CrtYd } );
109
110 //
111 // Generate a count for use in progress reporting.
112 //
113
115 [&]( BOARD_ITEM* item ) -> bool
116 {
117 ++count;
118 return true;
119 } );
120
121 //
122 // Generate a BOARD_ITEM RTree.
123 //
124
126 [&]( BOARD_ITEM* item ) -> bool
127 {
128 if( !reportProgress( ii++, count, progressDelta ) )
129 return false;
130
131 LSET layers = item->GetLayerSet();
132
133 // Special-case holes and edge-cuts which pierce all physical layers
134 if( item->HasHole() )
135 {
136 if( layers.Contains( F_Cu ) )
137 layers |= LSET( LSET::FrontBoardTechMask() ).set( F_CrtYd );
138
139 if( layers.Contains( B_Cu ) )
140 layers |= LSET( LSET::BackBoardTechMask() ).set( B_CrtYd );
141
142 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
143 layers |= boardCopperLayers;
144 }
145 else if( item->Type() == PCB_FOOTPRINT_T )
146 {
147 layers = courtyards;
148 }
149 else if( item->IsOnLayer( Edge_Cuts ) )
150 {
151 layers |= LSET::PhysicalLayersMask() | courtyards;
152 }
153
154 for( PCB_LAYER_ID layer : layers )
156
157 return true;
158 } );
159
160 std::unordered_map<PTR_PTR_CACHE_KEY, LSET> checkedPairs;
161 progressDelta = 100;
162 ii = 0;
163
164 //
165 // Run clearance checks -between- items.
166 //
167
170 {
171 if( !reportPhase( _( "Checking physical clearances..." ) ) )
172 return false; // DRC cancelled
173
175 [&]( BOARD_ITEM* item ) -> bool
176 {
177 if( !reportProgress( ii++, count, progressDelta ) )
178 return false;
179
180 LSET layers = item->GetLayerSet();
181
182 if( item->Type() == PCB_FOOTPRINT_T )
183 layers = courtyards;
184
185 for( PCB_LAYER_ID layer : layers )
186 {
187 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
188
189 m_itemTree.QueryColliding( item, layer, layer,
190 // Filter:
191 [&]( BOARD_ITEM* other ) -> bool
192 {
193 BOARD_ITEM* a = item;
194 BOARD_ITEM* b = other;
195
196 // store canonical order so we don't collide in both
197 // directions (a:b and b:a)
198 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
199 std::swap( a, b );
200
201 auto it = checkedPairs.find( { a, b } );
202
203 if( it != checkedPairs.end() && it->second.test( layer ) )
204 {
205 return false;
206 }
207 else
208 {
209 checkedPairs[ { a, b } ].set( layer );
210 return true;
211 }
212 },
213 // Visitor:
214 [&]( BOARD_ITEM* other ) -> bool
215 {
216 if( testItemAgainstItem( item, itemShape.get(), layer,
217 other ) > 0 )
218 {
219 BOARD_ITEM* a = item;
220 BOARD_ITEM* b = other;
221
222 // store canonical order
223 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
224 std::swap( a, b );
225
226 // Once we record one DRC for error for physical clearance
227 // we don't need to record more
228 checkedPairs[ { a, b } ].set();
229 }
230
231 return !m_drcEngine->IsCancelled();
232 },
234
235 testItemAgainstZones( item, layer );
236 }
237
238 return true;
239 } );
240 }
241
242 progressDelta = 100;
243 count = 0;
244 ii = 0;
245
246 //
247 // Generate a count for progress reporting.
248 //
249
250 forEachGeometryItem( { PCB_ZONE_T, PCB_SHAPE_T }, boardCopperLayers,
251 [&]( BOARD_ITEM* item ) -> bool
252 {
253 ZONE* zone = dynamic_cast<ZONE*>( item );
254
255 if( zone && zone->GetIsRuleArea() )
256 return true; // Continue with other items
257
258 count += ( item->GetLayerSet() & boardCopperLayers ).count();
259
260 return true;
261 } );
262
263 //
264 // Run clearance checks -within- polygonal items.
265 //
266
267 forEachGeometryItem( { PCB_ZONE_T, PCB_SHAPE_T }, boardCopperLayers,
268 [&]( BOARD_ITEM* item ) -> bool
269 {
270 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
271 ZONE* zone = dynamic_cast<ZONE*>( item );
272
273 if( zone && zone->GetIsRuleArea() )
274 return true; // Continue with other items
275
276 for( PCB_LAYER_ID layer : item->GetLayerSet() )
277 {
278 if( IsCopperLayer( layer ) )
279 {
280 if( !reportProgress( ii++, count, progressDelta ) )
281 return false;
282
283 DRC_CONSTRAINT c = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, item, nullptr,
284 layer );
285
286 if( shape )
287 {
288 switch( shape->GetShape() )
289 {
290 case SHAPE_T::POLY:
291 testShapeLineChain( shape->GetPolyShape().Outline( 0 ), shape->GetWidth(), layer,
292 item, c );
293 break;
294
295 case SHAPE_T::BEZIER:
296 {
297 SHAPE_LINE_CHAIN asPoly;
298
299 shape->RebuildBezierToSegmentsPointsList( errorMax );
300
301 for( const VECTOR2I& pt : shape->GetBezierPoints() )
302 asPoly.Append( pt );
303
304 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
305 break;
306 }
307
308 case SHAPE_T::ARC:
309 {
310 SHAPE_LINE_CHAIN asPoly;
311
312 VECTOR2I center = shape->GetCenter();
313 EDA_ANGLE angle = -shape->GetArcAngle();
314 double r = shape->GetRadius();
315 int steps = GetArcToSegmentCount( r, errorMax, angle );
316
317 asPoly.Append( shape->GetStart() );
318
319 for( int step = 1; step <= steps; ++step )
320 {
321 EDA_ANGLE rotation = ( angle * step ) / steps;
322 VECTOR2I pt = shape->GetStart();
323
324 RotatePoint( pt, center, rotation );
325 asPoly.Append( pt );
326 }
327
328 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
329 break;
330 }
331
333 {
334 SHAPE_LINE_CHAIN asPoly;
335 std::vector<VECTOR2I> pts = shape->GetRectCorners();
336 asPoly.Append( pts[0] );
337 asPoly.Append( pts[1] );
338 asPoly.Append( pts[2] );
339 asPoly.Append( pts[3] );
340 asPoly.SetClosed( true );
341
342 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
343 break;
344 }
345
346 case SHAPE_T::SEGMENT:
347 {
348 SHAPE_LINE_CHAIN asPoly;
349 asPoly.Append( shape->GetStart() );
350 asPoly.Append( shape->GetEnd() );
351
352 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
353 break;
354 }
355
356 default:
358 }
359 }
360
361 if( zone )
362 testZoneLayer( static_cast<ZONE*>( item ), layer, c );
363 }
364
365 if( m_drcEngine->IsCancelled() )
366 return false;
367 }
368
369 return !m_drcEngine->IsCancelled();
370 } );
371
372 return !m_drcEngine->IsCancelled();
373}
374
375
377 int aLineWidth, PCB_LAYER_ID aLayer,
378 BOARD_ITEM* aParentItem,
379 DRC_CONSTRAINT& aConstraint )
380{
381 // We don't want to collide with neighboring segments forming a curve until the concavity
382 // approaches 180 degrees.
383 double angleTolerance = DEG2RAD( 180.0 - ADVANCED_CFG::GetCfg().m_SliverAngleTolerance );
385 int count = aOutline.SegmentCount();
386 int clearance = aConstraint.GetValue().Min();
387
388 if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
389 return;
390
391 // Trigonometry is not cheap; cache seg angles
392 std::vector<double> angles;
393 angles.reserve( count );
394
395 auto angleDiff =
396 []( double a, double b ) -> double
397 {
398 if( a > b )
399 std::swap( a, b );
400
401 double diff = b - a;
402
403 if( diff > M_PI )
404 return 2 * M_PI - diff;
405 else
406 return diff;
407 };
408
409 for( int ii = 0; ii < count; ++ii )
410 {
411 const SEG& seg = aOutline.CSegment( ii );
412
413 // NB: don't store angles of really short segments (which could point anywhere)
414
415 if( seg.SquaredLength() > SEG::Square( epsilon * 2 ) )
416 {
417 angles.push_back( EDA_ANGLE( seg.B - seg.A ).AsRadians() );
418 }
419 else if( ii > 0 )
420 {
421 angles.push_back( angles.back() );
422 }
423 else
424 {
425 for( int jj = 1; jj < count; ++jj )
426 {
427 const SEG& following = aOutline.CSegment( jj );
428
429 if( following.SquaredLength() > SEG::Square( epsilon * 2 ) || jj == count - 1 )
430 {
431 angles.push_back( EDA_ANGLE( following.B - following.A ).AsRadians() );
432 break;
433 }
434 }
435 }
436 }
437
438 // Find collisions before reporting so that we can condense them into fewer reports.
439 std::vector< std::pair<VECTOR2I, int> > collisions;
440
441 for( int ii = 0; ii < count; ++ii )
442 {
443 const SEG seg = aOutline.CSegment( ii );
444 double segAngle = angles[ ii ];
445
446 // Exclude segments on either side of us until we reach the angle tolerance
447 int firstCandidate = ii + 1;
448 int lastCandidate = count - 1;
449
450 while( firstCandidate < count )
451 {
452 if( angleDiff( segAngle, angles[ firstCandidate ] ) < angleTolerance )
453 firstCandidate++;
454 else
455 break;
456 }
457
458 if( aOutline.IsClosed() )
459 {
460 if( ii > 0 )
461 lastCandidate = ii - 1;
462
463 while( lastCandidate != std::min( firstCandidate, count - 1 ) )
464 {
465 if( angleDiff( segAngle, angles[ lastCandidate ] ) < angleTolerance )
466 lastCandidate = ( lastCandidate == 0 ) ? count - 1 : lastCandidate - 1;
467 else
468 break;
469 }
470 }
471
472 // Now run the collision between seg and each candidate seg in the candidate range.
473 if( lastCandidate < ii )
474 lastCandidate = count - 1;
475
476 for( int jj = firstCandidate; jj <= lastCandidate; ++jj )
477 {
478 const SEG candidate = aOutline.CSegment( jj );
479 int actual;
480
481 if( seg.Collide( candidate, clearance + aLineWidth - epsilon, &actual ) )
482 {
483 VECTOR2I firstPoint = seg.NearestPoint( candidate );
484 VECTOR2I secondPoint = candidate.NearestPoint( seg );
485 VECTOR2I pos = ( firstPoint + secondPoint ) / 2;
486
487 if( !collisions.empty() && ( pos - collisions.back().first ).EuclideanNorm() < clearance * 2 )
488 {
489 if( actual < collisions.back().second )
490 {
491 collisions.back().first = pos;
492 collisions.back().second = actual;
493 }
494
495 continue;
496 }
497
498 collisions.push_back( { pos, actual } );
499 }
500 }
501 }
502
503 for( const std::pair<VECTOR2I, int>& collision : collisions )
504 {
505 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
506 VECTOR2I pt = collision.first;
507
508 if( FOOTPRINT* parentFP = aParentItem->GetParentFootprint() )
509 {
510 RotatePoint( pt, parentFP->GetOrientation() );
511 pt += parentFP->GetPosition();
512 }
513
514 wxString msg = formatMsg( _( "Internal clearance violation (%s clearance %s; actual %s)" ),
515 aConstraint.GetName(),
516 clearance,
517 collision.second );
518
519 drce->SetErrorMessage( msg );
520 drce->SetItems( aParentItem );
521 drce->SetViolatingRule( aConstraint.GetParentRule() );
522
523 reportViolation( drce, pt, aLayer );
524 }
525}
526
527
529 DRC_CONSTRAINT& aConstraint )
530{
532 int clearance = aConstraint.GetValue().Min();
533
534 if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
535 return;
536
537 SHAPE_POLY_SET fill = aZone->GetFilledPolysList( aLayer )->CloneDropTriangulation();
538
539 // Turn fractured fill into outlines and holes
540 fill.Simplify();
541
542 for( int outlineIdx = 0; outlineIdx < fill.OutlineCount(); ++outlineIdx )
543 {
544 SHAPE_LINE_CHAIN* firstOutline = &fill.Outline( outlineIdx );
545
546 //
547 // Step one: outline to outline clearance violations
548 //
549
550 for( int ii = outlineIdx + 1; ii < fill.OutlineCount(); ++ii )
551 {
552 SHAPE_LINE_CHAIN* secondOutline = &fill.Outline( ii );
553
554 for( int jj = 0; jj < secondOutline->SegmentCount(); ++jj )
555 {
556 SEG secondSeg = secondOutline->Segment( jj );
557 int actual;
558 VECTOR2I pos;
559
560 if( firstOutline->Collide( secondSeg, clearance - epsilon, &actual, &pos ) )
561 {
562 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
563 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
564 aConstraint.GetName(),
565 clearance,
566 actual );
567
568 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
569 drce->SetItems( aZone );
570 drce->SetViolatingRule( aConstraint.GetParentRule() );
571
572 reportViolation( drce, pos, aLayer );
573 }
574 }
575
576 if( m_drcEngine->IsCancelled() )
577 return;
578 }
579
580 //
581 // Step two: interior hole clearance violations
582 //
583
584 for( int holeIdx = 0; holeIdx < fill.HoleCount( outlineIdx ); ++holeIdx )
585 {
586 testShapeLineChain( fill.Hole( outlineIdx, holeIdx ), 0, aLayer, aZone, aConstraint );
587
588 if( m_drcEngine->IsCancelled() )
589 return;
590 }
591 }
592}
593
594
596 PCB_LAYER_ID aLayer, BOARD_ITEM* aOther )
597{
598 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
600 DRC_CONSTRAINT constraint;
601 int clearance = 0;
602 int actual;
603 int violations = 0;
604 VECTOR2I pos;
605 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
606
607 std::shared_ptr<SHAPE> otherShapeStorage = aOther->GetEffectiveShape( aLayer );
608 SHAPE* otherShape = otherShapeStorage.get();
609
610 if( testClearance )
611 {
612 constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, aOther, aLayer );
613 clearance = constraint.GetValue().Min();
614 }
615
616 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
617 {
618 // Collide (and generate violations) based on a well-defined order so that exclusion
619 // checking against previously-generated violations will work.
620 if( aItem->m_Uuid > aOther->m_Uuid )
621 {
622 std::swap( aItem, aOther );
623 std::swap( aItemShape, otherShape );
624 }
625
626 if( aItemShape->Collide( otherShape, clearance, &actual, &pos ) )
627 {
628 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
629 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
630 constraint.GetName(),
631 clearance,
632 actual );
633
634 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
635 drce->SetItems( aItem, aOther );
636 drce->SetViolatingRule( constraint.GetParentRule() );
637
638 reportViolation( drce, pos, aLayer );
639 ++violations;
640 }
641 }
642
643 if( testHoles )
644 {
645 std::shared_ptr<SHAPE_SEGMENT> itemHoleShape;
646 std::shared_ptr<SHAPE_SEGMENT> otherHoleShape;
647 clearance = 0;
648
649 if( aItem->Type() == PCB_VIA_T )
650 {
651 LSET layers = aItem->GetLayerSet();
652
653 if( layers.Contains( F_Cu ) )
654 layers |= LSET( LSET::FrontBoardTechMask() ).set( F_CrtYd );
655
656 if( layers.Contains( B_Cu ) )
657 layers |= LSET( LSET::BackBoardTechMask() ).set( B_CrtYd );
658
659 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
660 layers |= boardCopperLayers;
661
662 wxCHECK_MSG( layers.Contains( aLayer ), violations,
663 wxT( "Bug! Vias should only be checked for layers on which they exist" ) );
664
665 itemHoleShape = aItem->GetEffectiveHoleShape();
666 }
667 else if( aItem->HasHole() )
668 {
669 itemHoleShape = aItem->GetEffectiveHoleShape();
670 }
671
672 if( aOther->Type() == PCB_VIA_T )
673 {
674 LSET layers = aOther->GetLayerSet();
675
676 if( layers.Contains( F_Cu ) )
677 layers |= LSET( LSET::FrontBoardTechMask() ).set( F_CrtYd );
678
679 if( layers.Contains( B_Cu ) )
680 layers |= LSET( LSET::BackBoardTechMask() ).set( B_CrtYd );
681
682 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
683 layers |= boardCopperLayers;
684
685 wxCHECK_MSG( layers.Contains( aLayer ), violations,
686 wxT( "Bug! Vias should only be checked for layers on which they exist" ) );
687
688 otherHoleShape = aOther->GetEffectiveHoleShape();
689 }
690 else if( aOther->HasHole() )
691 {
692 otherHoleShape = aOther->GetEffectiveHoleShape();
693 }
694
695 if( itemHoleShape || otherHoleShape )
696 {
697 constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, aOther, aItem,
698 aLayer );
699 clearance = constraint.GetValue().Min();
700 }
701
702 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
703 {
704 if( itemHoleShape && itemHoleShape->Collide( otherShape, clearance, &actual, &pos ) )
705 {
706 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
707 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
708 constraint.GetName(),
709 clearance ,
710 actual );
711
712 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
713 drce->SetItems( aItem, aOther );
714 drce->SetViolatingRule( constraint.GetParentRule() );
715
716 reportViolation( drce, pos, aLayer );
717 ++violations;
718 }
719
720 if( otherHoleShape && otherHoleShape->Collide( aItemShape, clearance, &actual, &pos ) )
721 {
722 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
723 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
724 constraint.GetName(),
725 clearance,
726 actual );
727
728 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
729 drce->SetItems( aItem, aOther );
730 drce->SetViolatingRule( constraint.GetParentRule() );
731
732 reportViolation( drce, pos, aLayer );
733 ++violations;
734 }
735 }
736 }
737
738 return violations;
739}
740
741
743 PCB_LAYER_ID aLayer )
744{
745 for( ZONE* zone : m_board->m_DRCZones )
746 {
747 if( !zone->GetLayerSet().test( aLayer ) )
748 continue;
749
750 BOX2I itemBBox = aItem->GetBoundingBox();
751 BOX2I worstCaseBBox = itemBBox;
752
753 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
754
755 if( !worstCaseBBox.Intersects( zone->GetBoundingBox() ) )
756 continue;
757
758 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
760
761 if( !testClearance && !testHoles )
762 return;
763
764 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
765 DRC_CONSTRAINT constraint;
766 bool colliding;
767 int clearance = -1;
768 int actual;
769 VECTOR2I pos;
770
771 if( testClearance )
772 {
773 constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone,
774 aLayer );
775 clearance = constraint.GetValue().Min();
776 }
777
778 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
779 {
780 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
781
782 if( aItem->Type() == PCB_PAD_T )
783 {
784 PAD* pad = static_cast<PAD*>( aItem );
785
786 if( !pad->FlashLayer( aLayer ) )
787 {
788 if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
789 continue;
790
791 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
792 int size = hole->GetWidth();
793
794 itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
795 }
796 }
797
798 if( zoneTree )
799 {
800 colliding = zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, clearance,
801 &actual, &pos );
802 }
803 else
804 {
805 colliding = zone->Outline()->Collide( itemShape.get(), clearance, &actual, &pos );
806 }
807
808 if( colliding )
809 {
810 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
811 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
812 constraint.GetName(),
813 clearance,
814 actual );
815
816 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
817 drce->SetItems( aItem, zone );
818 drce->SetViolatingRule( constraint.GetParentRule() );
819
820 reportViolation( drce, pos, aLayer );
821 }
822 }
823
824 if( testHoles )
825 {
826 std::shared_ptr<SHAPE_SEGMENT> holeShape;
827
828 if( aItem->Type() == PCB_VIA_T )
829 {
830 if( aItem->GetLayerSet().Contains( aLayer ) )
831 holeShape = aItem->GetEffectiveHoleShape();
832 }
833 else if( aItem->HasHole() )
834 {
835 holeShape = aItem->GetEffectiveHoleShape();
836 }
837
838 if( holeShape )
839 {
841 zone, aLayer );
842 clearance = constraint.GetValue().Min();
843
844 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
845 && clearance > 0
846 && zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer, clearance,
847 &actual, &pos ) )
848 {
849 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
850 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
851 constraint.GetName(),
852 clearance,
853 actual );
854
855 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
856 drce->SetItems( aItem, zone );
857 drce->SetViolatingRule( constraint.GetParentRule() );
858
859 reportViolation( drce, pos, aLayer );
860 }
861 }
862 }
863
864 if( m_drcEngine->IsCancelled() )
865 return;
866 }
867}
868
869
870namespace detail
871{
873}
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:79
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:314
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:326
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:97
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:252
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:336
virtual bool HasHole() const
Definition: board_item.h:156
int GetCopperLayerCount() const
Definition: board.cpp:859
int m_DRCMaxPhysicalClearance
Definition: board.h:1384
int m_DRCMaxClearance
Definition: board.h:1383
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1375
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:1024
std::vector< ZONE * > m_DRCZones
Definition: board.h:1381
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:168
SEVERITY GetSeverity() const
Definition: drc_rule.h:181
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:160
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:164
BOARD * GetBoard() const
Definition: drc_engine.h:100
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:706
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)
virtual ~DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE()=default
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 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
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
double AsRadians() const
Definition: eda_angle.h:120
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:110
const KIID m_Uuid
Definition: eda_item.h:516
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:110
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:1080
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:337
int GetRadius() const
Definition: eda_shape.cpp:1005
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:903
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:1599
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:320
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:343
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static const LSET & FrontBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on front side.
Definition: lset.cpp:652
static const LSET & AllLayersMask()
Definition: lset.cpp:624
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition: lset.cpp:591
static const LSET & BackBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on Back side.
Definition: lset.cpp:638
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:385
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:606
static SEG::ecoord Square(int a)
Definition: seg.h:123
bool Collide(const SEG &aSeg, int aClearance, int *aActual=nullptr) const
Definition: seg.cpp:538
ecoord SquaredLength() const
Definition: seg.h:348
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:704
const std::shared_ptr< SHAPE_POLY_SET > & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:600
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:621
SHAPE_POLY_SET * Outline()
Definition: zone.h:335
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:78
@ PHYSICAL_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:77
#define REPORT_AUX(s)
#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:665
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