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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <common.h>
21#include <macros.h>
23#include <footprint.h>
24#include <pad.h>
25#include <pcb_track.h>
26#include <pcb_shape.h>
27#include <zone.h>
28#include <advanced_config.h>
30#include <geometry/seg.h>
32#include <drc/drc_rtree.h>
33#include <drc/drc_item.h>
35
36/*
37 Physical clearance tests.
38
39 Errors generated:
40 - DRCE_PHYSICAL_CLEARANCE
41 - DRCE_PHYSICAL_HOLE_CLEARANCE
42*/
43
45{
46public:
50
52
53 virtual bool Run() override;
54
55 virtual const wxString GetName() const override { return wxT( "physical_clearance" ); };
56
57private:
58 int testItemAgainstItem( BOARD_ITEM* aItem, SHAPE* aItemShape, PCB_LAYER_ID aLayer,
59 BOARD_ITEM* aOther );
60
61 void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
62
63 void testShapeLineChain( const SHAPE_LINE_CHAIN& aOutline, int aLineWidth, PCB_LAYER_ID aLayer,
64 BOARD_ITEM* aParentItem, DRC_CONSTRAINT& aConstraint );
65
66 void testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer, DRC_CONSTRAINT& aConstraint );
67
68private:
70};
71
72
74{
75 m_board = m_drcEngine->GetBoard();
76 m_itemTree.clear();
77
78 int errorMax = m_board->GetDesignSettings().m_MaxError;
79 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
80
81 if( m_board->m_DRCMaxPhysicalClearance <= 0 )
82 {
83 REPORT_AUX( wxT( "No physical clearance constraints found. Tests not run." ) );
84 return true; // continue with other tests
85 }
86
87 size_t progressDelta = 250;
88 size_t count = 0;
89 size_t ii = 0;
90
91 if( !reportPhase( _( "Gathering physical items..." ) ) )
92 return false; // DRC cancelled
93
94 static const std::vector<KICAD_T> itemTypes = {
103 };
104
105 static const LSET courtyards( { F_CrtYd, B_CrtYd } );
106
107 //
108 // Generate a count for use in progress reporting.
109 //
110
112 [&]( BOARD_ITEM* item ) -> bool
113 {
114 if( isInvisibleText( item ) )
115 return true;
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( isInvisibleText( item ) )
129 return true;
130
131 if( !reportProgress( ii++, count, progressDelta ) )
132 return false;
133
134 LSET layers = item->GetLayerSet();
135
136 // Special-case holes and edge-cuts which pierce all physical layers
137 if( item->HasHole() )
138 {
139 if( layers.Contains( F_Cu ) )
140 layers |= LSET( LSET::FrontBoardTechMask() ).set( F_CrtYd );
141
142 if( layers.Contains( B_Cu ) )
143 layers |= LSET( LSET::BackBoardTechMask() ).set( B_CrtYd );
144
145 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
146 layers |= boardCopperLayers;
147 }
148 else if( item->Type() == PCB_FOOTPRINT_T )
149 {
150 layers = courtyards;
151 }
152 else if( item->IsOnLayer( Edge_Cuts ) )
153 {
154 layers |= LSET::PhysicalLayersMask() | courtyards;
155 }
156
157 for( PCB_LAYER_ID layer : layers )
158 m_itemTree.Insert( item, layer, m_board->m_DRCMaxPhysicalClearance, ATOMIC_TABLES );
159
160 return true;
161 } );
162
163 m_itemTree.Build();
164
165 std::unordered_map<PTR_PTR_CACHE_KEY, LSET> checkedPairs;
166 progressDelta = 100;
167 ii = 0;
168
169 //
170 // Run clearance checks -between- items.
171 //
172
173 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE )
174 || !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
175 {
176 if( !reportPhase( _( "Checking physical clearances..." ) ) )
177 return false; // DRC cancelled
178
180 [&]( BOARD_ITEM* item ) -> bool
181 {
182 if( isInvisibleText( item ) )
183 return true;
184
185 if( !reportProgress( ii++, count, progressDelta ) )
186 return false;
187
188 LSET layers = item->GetLayerSet();
189
190 if( item->Type() == PCB_FOOTPRINT_T )
191 layers = courtyards;
192
193 for( PCB_LAYER_ID layer : layers )
194 {
195 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
196
197 m_itemTree.QueryColliding( item, layer, layer,
198 // Filter:
199 [&]( BOARD_ITEM* other ) -> bool
200 {
201 if( item->Type() == PCB_TABLECELL_T && item->GetParent() == other )
202 return false;
203
204 BOARD_ITEM* a = item;
205 BOARD_ITEM* b = other;
206
207 // store canonical order so we don't collide in both
208 // directions (a:b and b:a)
209 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
210 std::swap( a, b );
211
212 auto it = checkedPairs.find( { a, b } );
213
214 if( it != checkedPairs.end() && it->second.test( layer ) )
215 {
216 return false;
217 }
218 else
219 {
220 checkedPairs[ { a, b } ].set( layer );
221 return true;
222 }
223 },
224 // Visitor:
225 [&]( BOARD_ITEM* other ) -> bool
226 {
227 if( testItemAgainstItem( item, itemShape.get(), layer,
228 other ) > 0 )
229 {
230 BOARD_ITEM* a = item;
231 BOARD_ITEM* b = other;
232
233 // store canonical order
234 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
235 std::swap( a, b );
236
237 // Once we record one DRC for error for physical clearance
238 // we don't need to record more
239 checkedPairs[ { a, b } ].set();
240 }
241
242 return !m_drcEngine->IsCancelled();
243 },
244 m_board->m_DRCMaxPhysicalClearance );
245
246 testItemAgainstZones( item, layer );
247 }
248
249 return true;
250 } );
251 }
252
253 progressDelta = 100;
254 count = 0;
255 ii = 0;
256
257 //
258 // Generate a count for progress reporting.
259 //
260
261 forEachGeometryItem( { PCB_ZONE_T, PCB_SHAPE_T }, boardCopperLayers,
262 [&]( BOARD_ITEM* item ) -> bool
263 {
264 ZONE* zone = dynamic_cast<ZONE*>( item );
265
266 if( zone && zone->GetIsRuleArea() )
267 return true; // Continue with other items
268
269 count += ( item->GetLayerSet() & boardCopperLayers ).count();
270
271 return true;
272 } );
273
274 //
275 // Run clearance checks -within- polygonal items.
276 //
277
278 forEachGeometryItem( { PCB_ZONE_T, PCB_SHAPE_T }, boardCopperLayers,
279 [&]( BOARD_ITEM* item ) -> bool
280 {
281 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
282 ZONE* zone = dynamic_cast<ZONE*>( item );
283
284 if( zone && zone->GetIsRuleArea() )
285 return true; // Continue with other items
286
287 for( PCB_LAYER_ID layer : item->GetLayerSet() )
288 {
289 if( IsCopperLayer( layer ) )
290 {
291 if( !reportProgress( ii++, count, progressDelta ) )
292 return false;
293
294 DRC_CONSTRAINT c = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, item, nullptr,
295 layer );
296
297 if( shape )
298 {
299 switch( shape->GetShape() )
300 {
301 case SHAPE_T::POLY:
302 testShapeLineChain( shape->GetPolyShape().Outline( 0 ), shape->GetWidth(), layer,
303 item, c );
304 break;
305
306 case SHAPE_T::BEZIER:
307 {
308 SHAPE_LINE_CHAIN asPoly;
309
310 shape->RebuildBezierToSegmentsPointsList( errorMax );
311
312 for( const VECTOR2I& pt : shape->GetBezierPoints() )
313 asPoly.Append( pt );
314
315 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
316 break;
317 }
318
319 case SHAPE_T::ARC:
320 {
321 SHAPE_LINE_CHAIN asPoly;
322
323 VECTOR2I center = shape->GetCenter();
324 EDA_ANGLE angle = -shape->GetArcAngle();
325 double r = shape->GetRadius();
326 int steps = GetArcToSegmentCount( r, errorMax, angle );
327
328 asPoly.Append( shape->GetStart() );
329
330 for( int step = 1; step <= steps; ++step )
331 {
332 EDA_ANGLE rotation = ( angle * step ) / steps;
333 VECTOR2I pt = shape->GetStart();
334
335 RotatePoint( pt, center, rotation );
336 asPoly.Append( pt );
337 }
338
339 testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c );
340 break;
341 }
342
343 // Simple shapes can't create self-intersections, and I'm not sure a user
344 // would want a report that one side of their rectangle was too close to
345 // the other side.
347 case SHAPE_T::SEGMENT:
348 case SHAPE_T::CIRCLE:
349 case SHAPE_T::ELLIPSE:
350 case SHAPE_T::ELLIPSE_ARC: break;
351
352 default:
354 }
355 }
356
357 if( zone )
358 testZoneLayer( static_cast<ZONE*>( item ), layer, c );
359 }
360
361 if( m_drcEngine->IsCancelled() )
362 return false;
363 }
364
365 return !m_drcEngine->IsCancelled();
366 } );
367
368 m_itemTree.clear();
369
370 return !m_drcEngine->IsCancelled();
371}
372
373
375 int aLineWidth, PCB_LAYER_ID aLayer,
376 BOARD_ITEM* aParentItem,
377 DRC_CONSTRAINT& aConstraint )
378{
379 // We don't want to collide with neighboring segments forming a curve until the concavity
380 // approaches 180 degrees.
381 double angleTolerance = DEG2RAD( 180.0 - ADVANCED_CFG::GetCfg().m_SliverAngleTolerance );
382 int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
383 int count = aOutline.SegmentCount();
384 int clearance = aConstraint.GetValue().Min();
385
386 if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
387 return;
388
389 // Trigonometry is not cheap; cache seg angles
390 std::vector<double> angles;
391 angles.reserve( count );
392
393 auto angleDiff =
394 []( double a, double b ) -> double
395 {
396 if( a > b )
397 std::swap( a, b );
398
399 double diff = b - a;
400
401 if( diff > M_PI )
402 return 2 * M_PI - diff;
403 else
404 return diff;
405 };
406
407 for( int ii = 0; ii < count; ++ii )
408 {
409 const SEG& seg = aOutline.CSegment( ii );
410
411 // NB: don't store angles of really short segments (which could point anywhere)
412
413 if( seg.SquaredLength() > SEG::Square( epsilon * 2 ) )
414 {
415 angles.push_back( EDA_ANGLE( seg.B - seg.A ).AsRadians() );
416 }
417 else if( ii > 0 )
418 {
419 angles.push_back( angles.back() );
420 }
421 else
422 {
423 for( int jj = 1; jj < count; ++jj )
424 {
425 const SEG& following = aOutline.CSegment( jj );
426
427 if( following.SquaredLength() > SEG::Square( epsilon * 2 ) || jj == count - 1 )
428 {
429 angles.push_back( EDA_ANGLE( following.B - following.A ).AsRadians() );
430 break;
431 }
432 }
433 }
434 }
435
436 // Find collisions before reporting so that we can condense them into fewer reports.
437 std::vector< std::pair<VECTOR2I, int> > collisions;
438
439 for( int ii = 0; ii < count; ++ii )
440 {
441 const SEG seg = aOutline.CSegment( ii );
442 double segAngle = angles[ ii ];
443
444 // Exclude segments on either side of us until we reach the angle tolerance
445 int firstCandidate = ii + 1;
446 int lastCandidate = count - 1;
447
448 while( firstCandidate < count )
449 {
450 if( angleDiff( segAngle, angles[ firstCandidate ] ) < angleTolerance )
451 firstCandidate++;
452 else
453 break;
454 }
455
456 if( aOutline.IsClosed() )
457 {
458 if( ii > 0 )
459 lastCandidate = ii - 1;
460
461 while( lastCandidate != std::min( firstCandidate, count - 1 ) )
462 {
463 if( angleDiff( segAngle, angles[ lastCandidate ] ) < angleTolerance )
464 lastCandidate = ( lastCandidate == 0 ) ? count - 1 : lastCandidate - 1;
465 else
466 break;
467 }
468 }
469
470 // Now run the collision between seg and each candidate seg in the candidate range.
471 if( lastCandidate < ii )
472 lastCandidate = count - 1;
473
474 for( int jj = firstCandidate; jj <= lastCandidate; ++jj )
475 {
476 const SEG candidate = aOutline.CSegment( jj );
477 int actual;
478
479 if( seg.Collide( candidate, clearance + aLineWidth - epsilon, &actual ) )
480 {
481 VECTOR2I firstPoint = seg.NearestPoint( candidate );
482 VECTOR2I secondPoint = candidate.NearestPoint( seg );
483 VECTOR2I pos = ( firstPoint + secondPoint ) / 2;
484
485 if( !collisions.empty() && ( pos - collisions.back().first ).EuclideanNorm() < clearance * 2 )
486 {
487 if( actual < collisions.back().second )
488 {
489 collisions.back().first = pos;
490 collisions.back().second = actual;
491 }
492
493 continue;
494 }
495
496 collisions.push_back( { pos, actual } );
497 }
498 }
499 }
500
501 for( const std::pair<VECTOR2I, int>& collision : collisions )
502 {
503 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
504 VECTOR2I pt = collision.first;
505
506 if( FOOTPRINT* parentFP = aParentItem->GetParentFootprint() )
507 {
508 RotatePoint( pt, parentFP->GetOrientation() );
509 pt += parentFP->GetPosition();
510 }
511
512 wxString msg = formatMsg( _( "Internal clearance violation (%s clearance %s; actual %s)" ),
513 aConstraint.GetName(),
514 clearance,
515 collision.second );
516
517 drcItem->SetErrorMessage( msg );
518 drcItem->SetItems( aParentItem );
519 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
520
521 reportViolation( drcItem, pt, aLayer );
522 }
523}
524
525
527 DRC_CONSTRAINT& aConstraint )
528{
529 int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
530 int clearance = aConstraint.GetValue().Min();
531
532 if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
533 return;
534
536
537 // Turn fractured fill into outlines and holes
538 fill.Simplify();
539
540 for( int outlineIdx = 0; outlineIdx < fill.OutlineCount(); ++outlineIdx )
541 {
542 SHAPE_LINE_CHAIN* firstOutline = &fill.Outline( outlineIdx );
543
544 //
545 // Step one: outline to outline clearance violations
546 //
547
548 for( int ii = outlineIdx + 1; ii < fill.OutlineCount(); ++ii )
549 {
550 SHAPE_LINE_CHAIN* secondOutline = &fill.Outline( ii );
551
552 for( int jj = 0; jj < secondOutline->SegmentCount(); ++jj )
553 {
554 SEG secondSeg = secondOutline->Segment( jj );
555 int actual;
556 VECTOR2I pos;
557
558 if( firstOutline->Collide( secondSeg, clearance - epsilon, &actual, &pos ) )
559 {
560 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
561 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
562 aConstraint.GetName(),
563 clearance,
564 actual ) );
565 drcItem->SetItems( aZone );
566 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
567 reportViolation( drcItem, pos, aLayer );
568 }
569 }
570
571 if( m_drcEngine->IsCancelled() )
572 return;
573 }
574
575 //
576 // Step two: interior hole clearance violations
577 //
578
579 for( int holeIdx = 0; holeIdx < fill.HoleCount( outlineIdx ); ++holeIdx )
580 {
581 testShapeLineChain( fill.Hole( outlineIdx, holeIdx ), 0, aLayer, aZone, aConstraint );
582
583 if( m_drcEngine->IsCancelled() )
584 return;
585 }
586 }
587}
588
589
591 PCB_LAYER_ID aLayer, BOARD_ITEM* aOther )
592{
593 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
594 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
595 DRC_CONSTRAINT constraint;
596 int clearance = 0;
597 int actual;
598 int violations = 0;
599 VECTOR2I pos;
600 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
601
602 std::shared_ptr<SHAPE> otherShapeStorage = aOther->GetEffectiveShape( aLayer );
603 SHAPE* otherShape = otherShapeStorage.get();
604
605 if( testClearance )
606 {
607 constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, aOther, aLayer );
608 clearance = constraint.GetValue().Min();
609 }
610
611 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
612 {
613 // Collide (and generate violations) based on a well-defined order so that exclusion
614 // checking against previously-generated violations will work.
615 if( aItem->m_Uuid > aOther->m_Uuid )
616 {
617 std::swap( aItem, aOther );
618 std::swap( aItemShape, otherShape );
619 }
620
621 if( aItemShape->Collide( otherShape, clearance, &actual, &pos ) )
622 {
623 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
624 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
625 constraint.GetName(),
626 clearance,
627 actual ) );
628 drcItem->SetItems( aItem, aOther );
629 drcItem->SetViolatingRule( constraint.GetParentRule() );
630 reportTwoShapeGeometry( drcItem, pos, aItemShape, otherShape, aLayer, actual );
631 ++violations;
632 }
633 }
634
635 if( testHoles )
636 {
637 std::shared_ptr<SHAPE_SEGMENT> itemHoleShape;
638 std::shared_ptr<SHAPE_SEGMENT> otherHoleShape;
639 clearance = 0;
640
641 if( aItem->Type() == PCB_VIA_T )
642 {
643 LSET layers = aItem->GetLayerSet();
644
645 if( layers.Contains( F_Cu ) )
646 layers |= LSET( LSET::FrontBoardTechMask() ).set( F_CrtYd );
647
648 if( layers.Contains( B_Cu ) )
649 layers |= LSET( LSET::BackBoardTechMask() ).set( B_CrtYd );
650
651 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
652 layers |= boardCopperLayers;
653
654 wxCHECK_MSG( layers.Contains( aLayer ), violations,
655 wxT( "Bug! Vias should only be checked for layers on which they exist" ) );
656
657 itemHoleShape = aItem->GetEffectiveHoleShape();
658 }
659 else if( aItem->HasHole() )
660 {
661 itemHoleShape = aItem->GetEffectiveHoleShape();
662 }
663
664 if( aOther->Type() == PCB_VIA_T )
665 {
666 LSET layers = aOther->GetLayerSet();
667
668 if( layers.Contains( F_Cu ) )
669 layers |= LSET( LSET::FrontBoardTechMask() ).set( F_CrtYd );
670
671 if( layers.Contains( B_Cu ) )
672 layers |= LSET( LSET::BackBoardTechMask() ).set( B_CrtYd );
673
674 if( layers.Contains( F_Cu ) && layers.Contains( B_Cu ) )
675 layers |= boardCopperLayers;
676
677 wxCHECK_MSG( layers.Contains( aLayer ), violations,
678 wxT( "Bug! Vias should only be checked for layers on which they exist" ) );
679
680 otherHoleShape = aOther->GetEffectiveHoleShape();
681 }
682 else if( aOther->HasHole() )
683 {
684 otherHoleShape = aOther->GetEffectiveHoleShape();
685 }
686
687 if( itemHoleShape || otherHoleShape )
688 {
689 constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, aOther, aItem, aLayer );
690 clearance = constraint.GetValue().Min();
691 }
692
693 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
694 {
695 if( itemHoleShape && itemHoleShape->Collide( otherShape, clearance, &actual, &pos ) )
696 {
697 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
698 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
699 constraint.GetName(),
700 clearance ,
701 actual ) );
702 drcItem->SetItems( aItem, aOther );
703 drcItem->SetViolatingRule( constraint.GetParentRule() );
704 reportTwoShapeGeometry( drcItem, pos, itemHoleShape.get(), otherShape, aLayer, actual );
705 ++violations;
706 }
707
708 if( otherHoleShape && otherHoleShape->Collide( aItemShape, clearance, &actual, &pos ) )
709 {
710 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
711 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
712 constraint.GetName(),
713 clearance,
714 actual ) );
715 drcItem->SetItems( aItem, aOther );
716 drcItem->SetViolatingRule( constraint.GetParentRule() );
717 reportTwoShapeGeometry( drcItem, pos, otherHoleShape.get(), aItemShape, aLayer, actual );
718 ++violations;
719 }
720 }
721 }
722
723 return violations;
724}
725
726
728 PCB_LAYER_ID aLayer )
729{
730 for( ZONE* zone : m_board->m_DRCZones )
731 {
732 if( !zone->GetLayerSet().test( aLayer ) )
733 continue;
734
735 BOX2I itemBBox = aItem->GetBoundingBox();
736 BOX2I worstCaseBBox = itemBBox;
737
738 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
739
740 if( !worstCaseBBox.Intersects( zone->GetBoundingBox() ) )
741 continue;
742
743 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
744 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
745
746 if( !testClearance && !testHoles )
747 return;
748
749 DRC_RTREE* zoneRTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
750 DRC_CONSTRAINT constraint;
751 bool colliding;
752 int clearance = -1;
753 int actual;
754 VECTOR2I pos;
755
756 if( testClearance )
757 {
758 constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone, aLayer );
759 clearance = constraint.GetValue().Min();
760 }
761
762 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
763 {
764 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
765
766 if( aItem->Type() == PCB_PAD_T )
767 {
768 PAD* pad = static_cast<PAD*>( aItem );
769
770 if( !pad->FlashLayer( aLayer ) )
771 {
772 if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
773 continue;
774
775 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
776 int size = hole->GetWidth();
777
778 itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
779 }
780 }
781
782 if( IsCopperLayer( aLayer ) && zoneRTree )
783 {
784 colliding = zoneRTree->QueryColliding( itemBBox, itemShape.get(), aLayer, clearance,
785 &actual, &pos );
786 }
787 else
788 {
789 SHAPE_POLY_SET boardOutline = zone->GetBoardOutline();
790 colliding = boardOutline.Collide( itemShape.get(), clearance, &actual, &pos );
791 }
792
793 if( colliding )
794 {
795 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
796 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
797 constraint.GetName(),
798 clearance,
799 actual ) );
800 drcItem->SetItems( aItem, zone );
801 drcItem->SetViolatingRule( constraint.GetParentRule() );
802 reportTwoItemGeometry( drcItem, pos, aItem, zone, aLayer, actual );
803 }
804 }
805
806 if( testHoles )
807 {
808 std::shared_ptr<SHAPE_SEGMENT> holeShape;
809
810 if( aItem->Type() == PCB_VIA_T )
811 {
812 if( aItem->GetLayerSet().Contains( aLayer ) )
813 holeShape = aItem->GetEffectiveHoleShape();
814 }
815 else if( aItem->HasHole() )
816 {
817 holeShape = aItem->GetEffectiveHoleShape();
818 }
819
820 if( holeShape )
821 {
822 constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, aItem, zone, aLayer );
823 clearance = constraint.GetValue().Min();
824
825 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
826 {
827 if( IsCopperLayer( aLayer ) && zoneRTree )
828 {
829 colliding = zoneRTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
830 clearance, &actual, &pos );
831 }
832 else
833 {
834 SHAPE_POLY_SET boardOutline = zone->GetBoardOutline();
835 colliding = boardOutline.Collide( holeShape.get(), clearance, &actual, &pos );
836 }
837
838 if( colliding )
839 {
840 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
841 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
842 constraint.GetName(),
843 clearance,
844 actual ) );
845 drcItem->SetItems( aItem, zone );
846 drcItem->SetViolatingRule( constraint.GetParentRule() );
847
848 std::shared_ptr<SHAPE> zoneShape = zone->GetEffectiveShape( aLayer );
849 reportTwoShapeGeometry( drcItem, pos, holeShape.get(), zoneShape.get(), aLayer, actual );
850 }
851 }
852 }
853 }
854
855 if( m_drcEngine->IsCancelled() )
856 return;
857 }
858}
859
860
861namespace detail
862{
864}
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
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
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:347
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.
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:285
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:231
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
virtual bool HasHole() const
Definition board_item.h:177
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:307
wxString GetName() const
Definition drc_rule.h:204
SEVERITY GetSeverity() const
Definition drc_rule.h:217
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:196
DRC_RULE * GetParentRule() const
Definition drc_rule.h:200
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:417
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition drc_rtree.h:45
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:225
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 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)
void reportTwoShapeGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const SHAPE *aShape1, const SHAPE *aShape2, PCB_LAYER_ID aLayer, int aDistance)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
void reportTwoItemGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const BOARD_ITEM *aItem1, const BOARD_ITEM *aItem2, PCB_LAYER_ID aLayer, int aDistance)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
bool isInvisibleText(const BOARD_ITEM *aItem) const
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:135
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
EDA_ANGLE GetArcAngle() const
SHAPE_POLY_SET & GetPolyShape()
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:185
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:404
wxString SHAPE_T_asString() const
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
static const LSET & FrontBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on front side.
Definition lset.cpp:665
static const LSET & AllLayersMask()
Definition lset.cpp:637
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:693
static const LSET & BackBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on Back side.
Definition lset.cpp:651
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:29
Definition pad.h:61
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:78
int GetWidth() const override
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:629
static SEG::ecoord Square(int a)
Definition seg.h:119
bool Collide(const SEG &aSeg, int aClearance, int *aActual=nullptr) const
Definition seg.cpp:538
ecoord SquaredLength() const
Definition seg.h:344
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
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.
SHAPE_POLY_SET CloneDropTriangulation() const
An abstract shape on 2D plane.
Definition shape.h:124
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:179
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:811
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:697
const BOX2I GetBoundingBox() const override
Definition zone.cpp:737
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:835
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition zone.cpp:1841
The common library.
@ DRCE_HOLE_CLEARANCE
Definition drc_item.h:51
@ DRCE_CLEARANCE
Definition drc_item.h:40
#define ATOMIC_TABLES
Definition drc_rtree.h:38
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:83
@ PHYSICAL_CLEARANCE_CONSTRAINT
Definition drc_rule.h:82
#define REPORT_AUX(s)
#define _(s)
@ ELLIPSE
Definition eda_shape.h:52
@ SEGMENT
Definition eda_shape.h:46
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
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:675
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ B_Cu
Definition layer_ids.h:61
@ B_CrtYd
Definition layer_ids.h:111
@ F_Cu
Definition layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
const double epsilon
VECTOR2I center
int clearance
int actual
#define M_PI
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:225
double DEG2RAD(double deg)
Definition trigo.h:162
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:86
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:94
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:88
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:93
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:87
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683