KiCad PCB EDA Suite
drc_test_provider_connection_width.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) 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 3
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/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 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 <algorithm>
25#include <atomic>
26#include <deque>
27#include <optional>
28#include <utility>
29
30#include <wx/debug.h>
31
32#include <board.h>
35#include <drc/drc_rule.h>
36#include <drc/drc_item.h>
38#include <drc/drc_rtree.h>
40#include <footprint.h>
41#include <geometry/seg.h>
43#include <math/box2.h>
44#include <math/vector2d.h>
45#include <pcb_shape.h>
46#include <progress_reporter.h>
47#include <thread_pool.h>
48#include <pcb_track.h>
49#include <pad.h>
50#include <zone.h>
51
52/*
53 Checks for copper connections that are less than the specified minimum width
54
55 Errors generated:
56 - DRCE_CONNECTION_WIDTH
57*/
58
60{
63
64 bool operator==(const NETCODE_LAYER_CACHE_KEY& other) const
65 {
66 return Netcode == other.Netcode && Layer == other.Layer;
67 }
68};
69
70
71namespace std
72{
73 template <>
75 {
76 std::size_t operator()( const NETCODE_LAYER_CACHE_KEY& k ) const
77 {
78 constexpr std::size_t prime = 19937;
79
80 return hash<int>()( k.Netcode ) ^ ( hash<int>()( k.Layer ) * prime );
81 }
82 };
83}
84
85
87{
88public:
90 {
91 }
92
94 {
95 }
96
97 virtual bool Run() override;
98
99 virtual const wxString GetName() const override
100 {
101 return wxT( "copper width" );
102 };
103
104 virtual const wxString GetDescription() const override
105 {
106 return wxT( "Checks copper nets for connections less than a specified minimum" );
107 }
108
109private:
110 wxString layerDesc( PCB_LAYER_ID aLayer );
111};
112
113
115{
116public:
117 POLYGON_TEST( int aLimit, int aErrorLimit ) :
118 m_limit( aLimit ), m_max_error( aErrorLimit )
119 {
120 };
121
122 bool FindPairs( const SHAPE_LINE_CHAIN& aPoly )
123 {
124 m_hits.clear();
125 m_vertices.clear();
126 m_bbox = aPoly.BBox();
127
128 createList( aPoly );
129
130 m_vertices.front().updateList();
131
132 Vertex* p = m_vertices.front().next;
133 std::set<Vertex*> all_hits;
134
135 while( p != &m_vertices.front() )
136 {
137 Vertex* match = nullptr;
138
139 // Only run the expensive search if we don't already have a match for the point
140 if( ( all_hits.empty() || all_hits.count( p ) == 0 ) && ( match = getKink( p ) ) )
141 {
142 if( !all_hits.count( match ) && m_hits.emplace( p->i, match->i ).second )
143 {
144 all_hits.emplace( p );
145 all_hits.emplace( match );
146 all_hits.emplace( p->next );
147 all_hits.emplace( p->prev );
148 all_hits.emplace( match->next );
149 all_hits.emplace( match->prev );
150 }
151 }
152
153 p = p->next;
154 }
155
156 return !m_hits.empty();
157 }
158
159 std::set<std::pair<int, int>>& GetVertices()
160 {
161 return m_hits;
162 }
163
164
165private:
166 struct Vertex
167 {
168 Vertex( int aIndex, double aX, double aY, POLYGON_TEST* aParent ) :
169 i( aIndex ),
170 x( aX ),
171 y( aY ),
172 parent( aParent )
173 {
174 }
175
176 Vertex& operator=( const Vertex& ) = delete;
177 Vertex& operator=( Vertex&& ) = delete;
178
179 bool operator==( const Vertex& rhs ) const
180 {
181 return this->x == rhs.x && this->y == rhs.y;
182 }
183 bool operator!=( const Vertex& rhs ) const { return !( *this == rhs ); }
184
188 void remove()
189 {
190 next->prev = prev;
191 prev->next = next;
192
193 if( prevZ )
194 prevZ->nextZ = nextZ;
195
196 if( nextZ )
197 nextZ->prevZ = prevZ;
198
199 next = nullptr;
200 prev = nullptr;
201 nextZ = nullptr;
202 prevZ = nullptr;
203 }
204
206 {
207 if( !z )
208 z = parent->zOrder( x, y );
209 }
210
216 {
217 Vertex* p = next;
218
219 while( p != this )
220 {
224 if( *p == *p->next )
225 {
226 p = p->prev;
227 p->next->remove();
228
229 if( p == p->next )
230 break;
231 }
232
233 p->updateOrder();
234 p = p->next;
235 };
236
237 updateOrder();
238 zSort();
239 }
240
244 void zSort()
245 {
246 std::deque<Vertex*> queue;
247
248 queue.push_back( this );
249
250 for( Vertex* p = next; p && p != this; p = p->next )
251 queue.push_back( p );
252
253 std::sort( queue.begin(), queue.end(), []( const Vertex* a, const Vertex* b )
254 {
255 return a->z < b->z || ( a->z == b->z
256 && ( ( a->x < b->x )
257 || ( a->y < b->y )
258 || ( a->i < b->i ) ) );
259 } );
260
261 Vertex* prev_elem = nullptr;
262
263 for( Vertex* elem : queue )
264 {
265 if( prev_elem )
266 prev_elem->nextZ = elem;
267
268 elem->prevZ = prev_elem;
269 prev_elem = elem;
270 }
271
272 prev_elem->nextZ = nullptr;
273 }
274
275 const int i;
276 const double x;
277 const double y;
279
280 // previous and next vertices nodes in a polygon ring
281 Vertex* prev = nullptr;
282 Vertex* next = nullptr;
283
284 // z-order curve value
285 int32_t z = 0;
286
287 // previous and next nodes in z-order
288 Vertex* prevZ = nullptr;
289 Vertex* nextZ = nullptr;
290 };
291
297 int32_t zOrder( const double aX, const double aY ) const
298 {
299 int32_t x = static_cast<int32_t>( 32767.0 * ( aX - m_bbox.GetX() ) / m_bbox.GetWidth() );
300 int32_t y = static_cast<int32_t>( 32767.0 * ( aY - m_bbox.GetY() ) / m_bbox.GetHeight() );
301
302 x = ( x | ( x << 8 ) ) & 0x00FF00FF;
303 x = ( x | ( x << 4 ) ) & 0x0F0F0F0F;
304 x = ( x | ( x << 2 ) ) & 0x33333333;
305 x = ( x | ( x << 1 ) ) & 0x55555555;
306
307 y = ( y | ( y << 8 ) ) & 0x00FF00FF;
308 y = ( y | ( y << 4 ) ) & 0x0F0F0F0F;
309 y = ( y | ( y << 2 ) ) & 0x33333333;
310 y = ( y | ( y << 1 ) ) & 0x55555555;
311
312 return x | ( y << 1 );
313 }
314
315 constexpr bool same_point( const Vertex* aA, const Vertex* aB ) const
316 {
317 return aA && aB && aA->x == aB->x && aA->y == aB->y;
318 }
319
320 Vertex* getNextOutlineVertex( const Vertex* aPt ) const
321 {
322 Vertex* nz = aPt->nextZ;
323 Vertex* pz = aPt->prevZ;
324
325 // If we hit a fracture point, we want to continue around the
326 // edge we are working on and not switch to the pair edge
327 // However, this will depend on which direction the initial
328 // fracture hit is. If we find that we skip directly to
329 // a new fracture point, then we know that we are proceeding
330 // in the wrong direction from the fracture and should
331 // fall through to the next point
332 if( same_point( aPt, nz )
333 && !( same_point( nz->next, nz->next->prevZ ) || same_point( nz->next, nz->next->nextZ ) ) )
334 {
335 return nz->next;
336 }
337 else if( same_point( aPt, pz )
338 && !( same_point( pz->next, pz->next->prevZ ) || same_point( pz->next, pz->next->nextZ ) ) )
339 {
340 return pz->next;
341 }
342
343 return aPt->next;
344 }
345
346 Vertex* getPrevOutlineVertex( const Vertex* aPt ) const
347 {
348 Vertex* nz = aPt->nextZ;
349 Vertex* pz = aPt->prevZ;
350
351 // If we hit a fracture point, we want to continue around the
352 // edge we are working on and not switch to the pair edge
353 // However, this will depend on which direction the initial
354 // fracture hit is. If we find that we skip directly to
355 // a new fracture point, then we know that we are proceeding
356 // in the wrong direction from the fracture and should
357 // fall through to the next point
358 if( same_point( aPt, nz )
359 && !( same_point( nz->prev, nz->prev->nextZ ) || same_point( nz->prev, nz->prev->prevZ ) ) )
360 {
361 return nz->prev;
362 }
363 else if( same_point( aPt, pz )
364 && !( same_point( pz->prev, pz->prev->nextZ ) || same_point( pz->prev, pz->prev->prevZ ) ) )
365 {
366 return pz->prev;
367 }
368
369 return aPt->prev;
370
371 }
372
381 bool isSubstantial( const Vertex* aA, const Vertex* aB ) const
382 {
383 // `directions` is a bitfield where
384 // bit 0 = pos y
385 // bit 1 = neg y
386 // bit 2 = pos x
387 // bit 3 = neg x
388 // So, once directions = 15, we have all directions
389 int directions = 0;
390 constexpr int all_dirs = 0b1111;
391
392 // This is a failsafe in case of invalid lists. Never check
393 // more than the total number of points in m_vertices
394 size_t checked = 0;
395 size_t total_pts = m_vertices.size();
396
397 const Vertex* p0 = aA;
398 const Vertex* p = getNextOutlineVertex( p0 );
399
400
401 while( !same_point( p0, aB ) && checked < total_pts && directions != all_dirs )
402 {
403 double diff_x = std::abs( p->x - p0->x );
404 double diff_y = std::abs( p->y - p0->y );
405
406 // Floating point zeros can have a negative sign, so we need to
407 // ensure that only substantive diversions count for a direction
408 // change
409 if( diff_x > m_max_error )
410 directions |= ( 1 << ( 2 + std::signbit( p->x - p0->x ) ) );
411
412 if( diff_y > m_max_error )
413 directions |= ( 1 << std::signbit( p->y - p0->y ) );
414
415 // In the case of a circle, we need to eventually get the direction
416 // so keep the p0 at the same point
417 if( diff_x > m_max_error || diff_y > m_max_error || p == aB )
418 p0 = p;
419
420 if( same_point( p, p->nextZ ) )
421 p = p->nextZ->next;
422 else if( same_point( p, p->prevZ ) )
423 p = p->prevZ->next;
424 else
425 p = p->next;
426
427 ++checked;
428 }
429
430 wxCHECK_MSG( checked < total_pts, false, wxT( "Invalid polygon detected. Missing points to check" ) );
431
432 if( directions != all_dirs )
433 return false;
434
435 p0 = aA;
436 p = getPrevOutlineVertex( p0 );
437
438 directions = 0;
439 checked = 0;
440
441 while( !same_point( p0, aB ) && checked < total_pts && directions != all_dirs )
442 {
443 double diff_x = std::abs( p->x - p0->x );
444 double diff_y = std::abs( p->y - p0->y );
445
446 // Floating point zeros can have a negative sign, so we need to
447 // ensure that only substantive diversions count for a direction
448 // change
449 if( diff_x > m_max_error )
450 directions |= ( 1 << ( 2 + std::signbit( p->x - p0->x ) ) );
451
452 if( diff_y > m_max_error )
453 directions |= ( 1 << std::signbit( p->y - p0->y ) );
454
455 // In the case of a circle, we need to eventually get the direction
456 // so keep the p0 at the same point
457 if( diff_x > m_max_error || diff_y > m_max_error || p == aB )
458 p0 = p;
459
460 if( same_point( p, p->nextZ ) )
461 p = p->nextZ->prev;
462 else if( same_point( p, p->prevZ ) )
463 p = p->prevZ->prev;
464 else
465 p = p->prev;
466
467 ++checked;
468 }
469
470 wxCHECK_MSG( checked < total_pts, false, wxT( "Invalid polygon detected. Missing points to check" ) );
471
472 return ( directions == all_dirs );
473 }
474
479 {
480 Vertex* tail = nullptr;
481 double sum = 0.0;
482
483 // Check for winding order
484 for( int i = 0; i < points.PointCount(); i++ )
485 {
486 VECTOR2D p1 = points.CPoint( i );
487 VECTOR2D p2 = points.CPoint( i + 1 );
488
489 sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) );
490 }
491
492 if( sum > 0.0 )
493 {
494 for( int i = points.PointCount() - 1; i >= 0; i--)
495 tail = insertVertex( i, points.CPoint( i ), tail );
496 }
497 else
498 {
499 for( int i = 0; i < points.PointCount(); i++ )
500 tail = insertVertex( i, points.CPoint( i ), tail );
501 }
502
503 if( tail && ( *tail == *tail->next ) )
504 {
505 tail->next->remove();
506 }
507
508 return tail;
509 }
510
511 Vertex* getKink( Vertex* aPt ) const
512 {
513 // The point needs to be at a concave surface
514 if( locallyInside( aPt->prev, aPt->next ) )
515 return nullptr;
516
517 // z-order range for the current point ± limit bounding box
518 const int32_t maxZ = zOrder( aPt->x + m_limit, aPt->y + m_limit );
519 const int32_t minZ = zOrder( aPt->x - m_limit, aPt->y - m_limit );
520 const SEG::ecoord limit2 = SEG::Square( m_limit );
521
522 // first look for points in increasing z-order
523 Vertex* p = aPt->nextZ;
524 SEG::ecoord min_dist = std::numeric_limits<SEG::ecoord>::max();
525 Vertex* retval = nullptr;
526
527 while( p && p->z <= maxZ )
528 {
529 int delta_i = std::abs( p->i - aPt->i );
530 VECTOR2D diff( p->x - aPt->x, p->y - aPt->y );
531 SEG::ecoord dist2 = diff.SquaredEuclideanNorm();
532
533 if( delta_i > 1 && dist2 < limit2 && dist2 < min_dist && dist2 > 0.0
534 && locallyInside( p, aPt ) && isSubstantial( p, aPt ) )
535 {
536 min_dist = dist2;
537 retval = p;
538 }
539
540 p = p->nextZ;
541 }
542
543 p = aPt->prevZ;
544
545 while( p && p->z >= minZ )
546 {
547 int delta_i = std::abs( p->i - aPt->i );
548 VECTOR2D diff( p->x - aPt->x, p->y - aPt->y );
549 SEG::ecoord dist2 = diff.SquaredEuclideanNorm();
550
551 if( delta_i > 1 && dist2 < limit2 && dist2 < min_dist && dist2 > 0.0
552 && locallyInside( p, aPt ) && isSubstantial( p, aPt ) )
553 {
554 min_dist = dist2;
555 retval = p;
556 }
557
558 p = p->prevZ;
559 }
560 return retval;
561 }
562
563
567 double area( const Vertex* p, const Vertex* q, const Vertex* r ) const
568 {
569 return ( q->y - p->y ) * ( r->x - q->x ) - ( q->x - p->x ) * ( r->y - q->y );
570 }
571
572
582 bool locallyInside( const Vertex* a, const Vertex* b ) const
583 {
584 const Vertex* an = getNextOutlineVertex( a );
585 const Vertex* ap = getPrevOutlineVertex( a );
586
587 if( area( ap, a, an ) < 0 )
588 return area( a, b, an ) >= 0 && area( a, ap, b ) >= 0;
589 else
590 return area( a, b, ap ) < 0 || area( a, an, b ) < 0;
591 }
592
599 Vertex* insertVertex( int aIndex, const VECTOR2I& pt, Vertex* last )
600 {
601 m_vertices.emplace_back( aIndex, pt.x, pt.y, this );
602
603 Vertex* p = &m_vertices.back();
604
605 if( !last )
606 {
607 p->prev = p;
608 p->next = p;
609 }
610 else
611 {
612 p->next = last->next;
613 p->prev = last;
614 last->next->prev = p;
615 last->next = p;
616 }
617 return p;
618 }
619
620private:
624 std::deque<Vertex> m_vertices;
625 std::set<std::pair<int, int>> m_hits;
626};
627
628
630{
631 return wxString::Format( wxT( "(%s)" ), m_drcEngine->GetBoard()->GetLayerName( aLayer ) );
632}
633
634
636{
638 return true; // Continue with other tests
639
640 if( !reportPhase( _( "Checking nets for minimum connection width..." ) ) )
641 return false; // DRC cancelled
642
643 LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask();
644 LSEQ copperLayers = copperLayerSet.Seq();
645 BOARD* board = m_drcEngine->GetBoard();
646
647 /*
648 * Build a set of distinct minWidths specified by various DRC rules. We'll run a test for
649 * each distinct minWidth, and then decide if any copper which failed that minWidth actually
650 * was required to abide by it or not.
651 */
652 std::set<int> distinctMinWidths
654
655 if( m_drcEngine->IsCancelled() )
656 return false; // DRC cancelled
657
658 struct ITEMS_POLY
659 {
660 std::set<BOARD_ITEM*> Items;
661 SHAPE_POLY_SET Poly;
662 };
663
664 std::unordered_map<NETCODE_LAYER_CACHE_KEY, ITEMS_POLY> dataset;
665 std::atomic<size_t> done( 1 );
666
667 auto calc_effort =
668 [&]( const std::set<BOARD_ITEM*>& items, PCB_LAYER_ID aLayer ) -> size_t
669 {
670 size_t effort = 0;
671
672 for( BOARD_ITEM* item : items )
673 {
674 if( item->Type() == PCB_ZONE_T )
675 {
676 ZONE* zone = static_cast<ZONE*>( item );
677 effort += zone->GetFilledPolysList( aLayer )->FullPointCount();
678 }
679 else
680 {
681 effort += 4;
682 }
683 }
684
685 return effort;
686 };
687
688 /*
689 * For each net, on each layer, build a polygonSet which contains all the copper associated
690 * with that net on that layer.
691 */
692 auto build_netlayer_polys =
693 [&]( int aNetcode, const PCB_LAYER_ID aLayer ) -> size_t
694 {
695 if( m_drcEngine->IsCancelled() )
696 return 0;
697
698 ITEMS_POLY& itemsPoly = dataset[ { aNetcode, aLayer } ];
699
700 for( BOARD_ITEM* item : itemsPoly.Items )
701 {
702 item->TransformShapeToPolygon( itemsPoly.Poly, aLayer, 0, ARC_HIGH_DEF,
704 }
705
706 itemsPoly.Poly.Fracture( SHAPE_POLY_SET::PM_FAST );
707
708 done.fetch_add( calc_effort( itemsPoly.Items, aLayer ) );
709
710 return 1;
711 };
712
713 /*
714 * Examine all necks in a given polygonSet which fail a given minWidth.
715 */
716 auto min_checker =
717 [&]( const ITEMS_POLY& aItemsPoly, const PCB_LAYER_ID aLayer, int aMinWidth ) -> size_t
718 {
719 if( m_drcEngine->IsCancelled() )
720 return 0;
721
723
724 for( int ii = 0; ii < aItemsPoly.Poly.OutlineCount(); ++ii )
725 {
726 const SHAPE_LINE_CHAIN& chain = aItemsPoly.Poly.COutline( ii );
727
728 test.FindPairs( chain );
729 auto& ret = test.GetVertices();
730
731 for( const std::pair<int, int>& pt : ret )
732 {
733 /*
734 * We've found a neck that fails the given aMinWidth. We now need to know
735 * if the objects the produced the copper at this location are required to
736 * abide by said aMinWidth or not. (If so, we have a violation.)
737 *
738 * We find the contributingItems by hit-testing at the choke point (the
739 * centre point of the neck), and then run the rules engine on those
740 * contributingItems. If the reported constraint matches aMinWidth, then
741 * we've got a violation.
742 */
743 SEG span( chain.CPoint( pt.first ), chain.CPoint( pt.second ) );
744 VECTOR2I location = ( span.A + span.B ) / 2;
745 int dist = ( span.A - span.B ).EuclideanNorm();
746
747 std::vector<BOARD_ITEM*> contributingItems;
748
749 for( auto* item : board->m_CopperItemRTreeCache->GetObjectsAt( location,
750 aLayer,
751 aMinWidth ) )
752 {
753 if( item->HitTest( location, aMinWidth ) )
754 contributingItems.push_back( item );
755 }
756
757 for( auto& [ zone, rtree ] : board->m_CopperZoneRTreeCache )
758 {
759 if( !rtree->GetObjectsAt( location, aLayer, aMinWidth ).empty()
760 && zone->HitTestFilledArea( aLayer, location, aMinWidth ) )
761 {
762 contributingItems.push_back( zone );
763 }
764 }
765
766 if( !contributingItems.empty() )
767 {
768 BOARD_ITEM* item1 = contributingItems[0];
769 BOARD_ITEM* item2 = contributingItems.size() > 1 ? contributingItems[1]
770 : nullptr;
772 item1, item2, aLayer );
773
774 if( c.Value().Min() == aMinWidth )
775 {
777 wxString msg;
778
779 msg = formatMsg( _( "(%s minimum connection width %s; actual %s)" ),
780 c.GetName(),
781 aMinWidth,
782 dist );
783
784 msg += wxS( " " ) + layerDesc( aLayer );
785
786 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
787 drce->SetViolatingRule( c.GetParentRule() );
788
789 for( BOARD_ITEM* item : contributingItems )
790 drce->AddItem( item );
791
792 reportViolation( drce, location, aLayer );
793 }
794 }
795 }
796 }
797
798 done.fetch_add( calc_effort( aItemsPoly.Items, aLayer ) );
799
800 return 1;
801 };
802
803 for( PCB_LAYER_ID layer : copperLayers )
804 {
805 for( ZONE* zone : board->m_DRCCopperZones )
806 {
807 if( !zone->GetIsRuleArea() && zone->IsOnLayer( layer ) )
808 dataset[ { zone->GetNetCode(), layer } ].Items.emplace( zone );
809 }
810
811 for( PCB_TRACK* track : board->Tracks() )
812 {
813 if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( track ) )
814 {
815 if( via->FlashLayer( static_cast<int>( layer ) ) )
816 dataset[ { via->GetNetCode(), layer } ].Items.emplace( via );
817 }
818 else if( track->IsOnLayer( layer ) )
819 {
820 dataset[ { track->GetNetCode(), layer } ].Items.emplace( track );
821 }
822 }
823
824 for( FOOTPRINT* fp : board->Footprints() )
825 {
826 for( PAD* pad : fp->Pads() )
827 {
828 if( pad->FlashLayer( static_cast<int>( layer ) ) )
829 dataset[ { pad->GetNetCode(), layer } ].Items.emplace( pad );
830 }
831
832 // Footprint zones are also in the m_DRCCopperZones cache
833 }
834 }
835
837 std::vector<std::future<size_t>> returns;
838 size_t total_effort = 0;
839
840 for( const auto& [ netLayer, itemsPoly ] : dataset )
841 total_effort += calc_effort( itemsPoly.Items, netLayer.Layer );
842
843 total_effort += std::max( (size_t) 1, total_effort ) * distinctMinWidths.size();
844
845 returns.reserve( dataset.size() );
846
847 for( const auto& [ netLayer, itemsPoly ] : dataset )
848 {
849 returns.emplace_back( tp.submit( build_netlayer_polys, netLayer.Netcode, netLayer.Layer ) );
850 }
851
852 for( std::future<size_t>& ret : returns )
853 {
854 std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
855
856 while( status != std::future_status::ready )
857 {
858 m_drcEngine->ReportProgress( static_cast<double>( done ) / total_effort );
859 status = ret.wait_for( std::chrono::milliseconds( 250 ) );
860 }
861 }
862
863 returns.clear();
864 returns.reserve( dataset.size() * distinctMinWidths.size() );
865
866 for( const auto& [ netLayer, itemsPoly ] : dataset )
867 {
868 for( int minWidth : distinctMinWidths )
869 returns.emplace_back( tp.submit( min_checker, itemsPoly, netLayer.Layer, minWidth ) );
870 }
871
872 for( std::future<size_t>& ret : returns )
873 {
874 std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
875
876 while( status != std::future_status::ready )
877 {
878 m_drcEngine->ReportProgress( static_cast<double>( done ) / total_effort );
879 status = ret.wait_for( std::chrono::milliseconds( 250 ) );
880 }
881 }
882
883 return true;
884}
885
886
887namespace detail
888{
890}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:121
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
Definition: board_item.cpp:196
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:569
std::vector< ZONE * > m_DRCCopperZones
Definition: board.h:1151
FOOTPRINTS & Footprints()
Definition: board.h:307
std::unique_ptr< DRC_RTREE > m_CopperItemRTreeCache
Definition: board.h:1146
TRACKS & Tracks()
Definition: board.h:304
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:456
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1145
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetY() const
Definition: box2.h:181
coord_type GetWidth() const
Definition: box2.h:187
coord_type GetX() const
Definition: box2.h:180
wxString GetName() const
Definition: drc_rule.h:149
MINOPTMAX< int > & Value()
Definition: drc_rule.h:142
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:145
BOARD * GetBoard() const
Definition: drc_engine.h:89
std::set< int > QueryDistinctConstraints(DRC_CONSTRAINT_T aConstraintId)
bool ReportProgress(double aProgress)
BOARD_DESIGN_SETTINGS * GetDesignSettings() const
Definition: drc_engine.h:92
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:673
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:325
virtual const wxString GetDescription() const override
virtual const wxString GetName() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out #DRC_ITEMs and po...
virtual bool reportPhase(const wxString &aStageName)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
DRC_ENGINE * m_drcEngine
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, int aConstraint, int aActual)
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: layer_ids.h:491
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
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:411
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:59
Vertex * createList(const SHAPE_LINE_CHAIN &points)
Take a SHAPE_LINE_CHAIN and links each point into a circular, doubly-linked list.
bool FindPairs(const SHAPE_LINE_CHAIN &aPoly)
Vertex * getNextOutlineVertex(const Vertex *aPt) const
int32_t zOrder(const double aX, const double aY) const
Calculate the Morton code of the Vertex http://www.graphics.stanford.edu/~seander/bithacks....
double area(const Vertex *p, const Vertex *q, const Vertex *r) const
Return the twice the signed area of the triangle formed by vertices p, q, and r.
Vertex * getPrevOutlineVertex(const Vertex *aPt) const
std::set< std::pair< int, int > > & GetVertices()
std::set< std::pair< int, int > > m_hits
bool isSubstantial(const Vertex *aA, const Vertex *aB) const
Checks to see if there is a "substantial" protrusion in each polygon produced by the cut from aA to a...
bool locallyInside(const Vertex *a, const Vertex *b) const
Check whether the segment from vertex a -> vertex b is inside the polygon around the immediate area o...
Vertex * getKink(Vertex *aPt) const
constexpr bool same_point(const Vertex *aA, const Vertex *aB) const
Vertex * insertVertex(int aIndex, const VECTOR2I &pt, Vertex *last)
Create an entry in the vertices lookup and optionally inserts the newly created vertex into an existi...
POLYGON_TEST(int aLimit, int aErrorLimit)
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I::extended_type ecoord
Definition: seg.h:44
VECTOR2I B
Definition: seg.h:50
static SEG::ecoord Square(int a)
Definition: seg.h:123
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:300
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:697
const std::shared_ptr< SHAPE_POLY_SET > & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:602
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:317
@ DRCE_CONNECTION_WIDTH
Definition: drc_item.h:56
@ CONNECTION_WIDTH_CONSTRAINT
Definition: drc_rule.h:73
#define _(s)
@ ERROR_OUTSIDE
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
bool signbit(T v)
Integral version of std::signbit that works all compilers.
Definition: kicad_algo.h:197
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
Definition: bitmap.cpp:64
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:412
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
bool operator==(const NETCODE_LAYER_CACHE_KEY &other) const
Vertex & operator=(Vertex &&)=delete
void remove()
Remove the node from the linked list and z-ordered linked list.
void zSort()
Sort all vertices in this vertex's list by their Morton code.
Vertex & operator=(const Vertex &)=delete
void updateList()
After inserting or changing nodes, this function should be called to remove duplicate vertices and en...
Vertex(int aIndex, double aX, double aY, POLYGON_TEST *aParent)
bool operator!=(const Vertex &rhs) const
bool operator==(const Vertex &rhs) const
std::size_t operator()(const NETCODE_LAYER_CACHE_KEY &k) const
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:32
static thread_pool * tp
Definition: thread_pool.cpp:30
BS::thread_pool thread_pool
Definition: thread_pool.h:30
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:112