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