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