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