KiCad PCB EDA Suite
Loading...
Searching...
No Matches
ratsnest_data.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) 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Maciej Suminski <[email protected]>
8 * @author Tomasz Wlostowski <[email protected]>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
28
29#ifdef PROFILE
30#include <core/profile.h>
31#endif
32
34#include <functional>
35using namespace std::placeholders;
36
37#include <algorithm>
38#include <cassert>
39#include <limits>
40#include <list>
41#include <span>
42#include <tuple>
43
44#include <core/filter_kruskal.h>
45#include <core/union_find.h>
46
47#include <delaunator.hpp>
48
49void RN_NET::kruskalMST( std::vector<CN_EDGE>& aEdges )
50{
51 m_rnEdges.clear();
52
53 int tag = 0;
54
55 for( const std::shared_ptr<CN_ANCHOR>& node : m_nodes )
56 node->SetTag( tag++ );
57
58 KI_UNION_FIND forest( m_nodes.size() );
59
60 // A zero-weight edge is a connection the board already makes and can never be a ratsnest
61 // line, so uniting it here keeps it out of the sort where on a routed net it would dominate
62 size_t candidateCount = 0;
63
64 for( size_t ii = 0; ii < aEdges.size(); ++ii )
65 {
66 const std::shared_ptr<const CN_ANCHOR>& source = aEdges[ii].GetSourceNode();
67 const std::shared_ptr<const CN_ANCHOR>& target = aEdges[ii].GetTargetNode();
68
69 wxCHECK2( source && !source->Dirty() && target && !target->Dirty(), continue );
70
71 if( aEdges[ii].GetWeight() > 0 )
72 {
73 if( candidateCount != ii )
74 aEdges[candidateCount] = std::move( aEdges[ii] );
75
76 ++candidateCount;
77 }
78 else
79 {
80 forest.Unite( source->GetTag(), target->GetTag() );
81 }
82 }
83
84 auto endpoints =
85 []( const CN_EDGE& aEdge )
86 {
87 return std::pair<size_t, size_t>( aEdge.GetSourceNode()->GetTag(),
88 aEdge.GetTargetNode()->GetTag() );
89 };
90
91 // Canonical because an edge is undirected and the triangulator may hand it to us either
92 // way round; the tag then separates distinct anchors that share a position
93 auto orderKey =
94 []( const CN_EDGE& aEdge )
95 {
96 const std::shared_ptr<const CN_ANCHOR>& source = aEdge.GetSourceNode();
97 const std::shared_ptr<const CN_ANCHOR>& target = aEdge.GetTargetNode();
98
99 std::tuple<int, int, int> first( source->Pos().x, source->Pos().y,
100 source->GetTag() );
101 std::tuple<int, int, int> second( target->Pos().x, target->Pos().y,
102 target->GetTag() );
103
104 if( second < first )
105 std::swap( first, second );
106
107 return std::tuple_cat( std::tuple( aEdge.GetWeight() ), first, second );
108 };
109
110 auto mstOrder =
111 [&orderKey]( const CN_EDGE& aLhs, const CN_EDGE& aRhs )
112 {
113 return orderKey( aLhs ) < orderKey( aRhs );
114 };
115
116 m_rnEdges.reserve( m_nodes.size() );
117
118 KI_MST::FilterKruskal<CN_EDGE>( std::span<CN_EDGE>( aEdges.data(), candidateCount ), forest,
119 mstOrder, endpoints,
120 [&]( const CN_EDGE& aEdge )
121 {
122 m_rnEdges.push_back( aEdge );
123 } );
124}
125
126
128{
129private:
130 std::multiset<std::shared_ptr<CN_ANCHOR>, CN_PTR_CMP> m_allNodes;
131
132
133 // Checks if all nodes in aNodes lie on a single line. Requires the nodes to
134 // have unique coordinates!
135 bool areNodesColinear( const std::vector<std::shared_ptr<CN_ANCHOR>>& aNodes ) const
136 {
137 if ( aNodes.size() <= 2 )
138 return true;
139
140 const VECTOR2I p0( aNodes[0]->Pos() );
141 const VECTOR2I v0( aNodes[1]->Pos() - p0 );
142
143 for( unsigned i = 2; i < aNodes.size(); i++ )
144 {
145 const VECTOR2I v1 = aNodes[i]->Pos() - p0;
146
147 if( v0.Cross( v1 ) != 0 )
148 return false;
149 }
150
151 return true;
152 }
153
154public:
155
156 void Clear()
157 {
158 m_allNodes.clear();
159 }
160
161 void AddNode( const std::shared_ptr<CN_ANCHOR>& aNode )
162 {
163 m_allNodes.insert( aNode );
164 }
165
166 void Triangulate( std::vector<CN_EDGE>& mstEdges )
167 {
168 std::vector<double> node_pts;
169 std::vector<std::shared_ptr<CN_ANCHOR>> anchors;
170 std::vector< std::vector<std::shared_ptr<CN_ANCHOR>> > anchorChains( m_allNodes.size() );
171
172 node_pts.reserve( 2 * m_allNodes.size() );
173 anchors.reserve( m_allNodes.size() );
174
175 auto addEdge =
176 [&]( const std::shared_ptr<CN_ANCHOR>& src, const std::shared_ptr<CN_ANCHOR>& dst )
177 {
178 mstEdges.emplace_back( src, dst, src->Dist( *dst ) );
179 };
180
181 std::shared_ptr<CN_ANCHOR> prev = nullptr;
182
183 for( const std::shared_ptr<CN_ANCHOR>& n : m_allNodes )
184 {
185 if( !prev || prev->Pos() != n->Pos() )
186 {
187 node_pts.push_back( n->Pos().x );
188 node_pts.push_back( n->Pos().y );
189 anchors.push_back( n );
190 prev = n;
191 }
192
193 anchorChains[anchors.size() - 1].push_back( n );
194 }
195
196 if( anchors.empty() )
197 {
198 return;
199 }
200 else if( anchors.size() == 1 )
201 {
202 // The anchors all have the same position, but may not have overlapping layers.
203 prev = nullptr;
204
205 for( const std::shared_ptr<CN_ANCHOR>& n : m_allNodes )
206 {
207 if( prev && !( prev->Parent()->GetLayerSet() & n->Parent()->GetLayerSet() ).any() )
208 {
209 // Use a minimal but non-zero distance or the edge will be ignored
210 mstEdges.emplace_back( prev, n, 1 );
211 }
212
213 prev = n;
214 }
215
216 return;
217 }
218 else if( areNodesColinear( anchors ) )
219 {
220 // special case: all nodes are on the same line - there's no
221 // triangulation for such set. In this case, we sort along any coordinate
222 // and chain the nodes together.
223 for( size_t i = 0; i < anchors.size() - 1; i++ )
224 addEdge( anchors[i], anchors[i + 1] );
225 }
226 else
227 {
228 delaunator::Delaunator delaunator( node_pts );
229 auto& triangles = delaunator.triangles;
230
231 // Half-edge e runs from triangles[e] to triangles[next(e)], so keeping the
232 // lower half of each opposite pair plus the hull edges visits each edge once
233 for( size_t e = 0; e < triangles.size(); e++ )
234 {
235 size_t opposite = delaunator.halfedges[e];
236
237 if( opposite != delaunator::INVALID_INDEX && opposite < e )
238 continue;
239
240 size_t next = ( e % 3 == 2 ) ? e - 2 : e + 1;
241
242 addEdge( anchors[triangles[e]], anchors[triangles[next]] );
243 }
244 }
245
246 for( size_t i = 0; i < anchorChains.size(); i++ )
247 {
248 std::vector<std::shared_ptr<CN_ANCHOR>>& chain = anchorChains[i];
249
250 if( chain.size() < 2 )
251 continue;
252
253 std::sort( chain.begin(), chain.end(),
254 [] ( const std::shared_ptr<CN_ANCHOR>& a, const std::shared_ptr<CN_ANCHOR>& b )
255 {
256 return a->GetCluster().get() < b->GetCluster().get();
257 } );
258
259 for( unsigned int j = 1; j < chain.size(); j++ )
260 {
261 const std::shared_ptr<CN_ANCHOR>& prevNode = chain[j - 1];
262 const std::shared_ptr<CN_ANCHOR>& curNode = chain[j];
263 int weight = prevNode->GetCluster() != curNode->GetCluster() ? 1 : 0;
264 mstEdges.emplace_back( prevNode, curNode, weight );
265 }
266 }
267 }
268};
269
270
272{
274}
275
276
278{
279 // Special cases do not need complicated algorithms (actually, it does not work well with
280 // the Delaunay triangulator)
281 if( m_nodes.size() <= 2 )
282 {
283 m_rnEdges.clear();
284
285 // Check if the only possible connection exists
286 if( m_boardEdges.size() == 0 && m_nodes.size() == 2 )
287 {
288 // There can be only one possible connection, but it is missing
289 auto it = m_nodes.begin();
290 const std::shared_ptr<CN_ANCHOR>& source = *it++;
291 const std::shared_ptr<CN_ANCHOR>& target = *it;
292
293 source->SetTag( 0 );
294 target->SetTag( 1 );
295 m_rnEdges.emplace_back( source, target );
296 }
297 else
298 {
299 // Set tags to m_nodes as connected
300 for( const std::shared_ptr<CN_ANCHOR>& node : m_nodes )
301 node->SetTag( 0 );
302 }
303
304 return;
305 }
306
307
308 m_triangulator->Clear();
309
310 for( const std::shared_ptr<CN_ANCHOR>& n : m_nodes )
311 m_triangulator->AddNode( n );
312
313 std::vector<CN_EDGE> triangEdges;
314
315 // A Delaunay triangulation of n points has at most 3n-6 edges
316 triangEdges.reserve( 3 * m_nodes.size() + m_boardEdges.size() );
317
318#ifdef PROFILE
319 PROF_TIMER cnt( "triangulate" );
320#endif
321 m_triangulator->Triangulate( triangEdges );
322#ifdef PROFILE
323 cnt.Show();
324#endif
325
326 for( const CN_EDGE& e : m_boardEdges )
327 triangEdges.emplace_back( e );
328
329// Get the minimal spanning tree
330#ifdef PROFILE
331 PROF_TIMER cnt2( "mst" );
332#endif
333 kruskalMST( triangEdges );
334#ifdef PROFILE
335 cnt2.Show();
336#endif
337}
338
339
341{
342 auto optimizeZoneAnchor =
343 [&]( const VECTOR2I& aPos, const LSET& aLayerSet,
344 const std::shared_ptr<const CN_ANCHOR>& aAnchor,
345 const std::function<void( std::shared_ptr<const CN_ANCHOR> )>& setOptimizedTo )
346 {
347 SEG::ecoord closest_dist_sq = ( aAnchor->Pos() - aPos ).SquaredEuclideanNorm();
348 VECTOR2I closest_pt;
349 CN_ITEM* closest_item = nullptr;
350
351 for( CN_ITEM* item : aAnchor->Item()->ConnectedItems() )
352 {
353 // Don't consider shorted items
354 if( aAnchor->Item()->Net() != item->Net() )
355 continue;
356
357 CN_ZONE_LAYER* zoneLayer = dynamic_cast<CN_ZONE_LAYER*>( item );
358
359 if( zoneLayer && aLayerSet.test( zoneLayer->GetBoardLayer() ) )
360 {
361 const std::vector<VECTOR2I>& pts = zoneLayer->GetOutline().CPoints();
362
363 for( const VECTOR2I& pt : pts )
364 {
365 SEG::ecoord dist_sq = ( pt - aPos ).SquaredEuclideanNorm();
366
367 if( dist_sq < closest_dist_sq )
368 {
369 closest_pt = pt;
370 closest_item = zoneLayer;
371 closest_dist_sq = dist_sq;
372 }
373 }
374 }
375 }
376
377 if( closest_item )
378 setOptimizedTo( std::make_shared<CN_ANCHOR>( closest_pt, closest_item ) );
379 };
380
381 auto optimizeZoneToZoneAnchors =
382 [&]( const std::shared_ptr<const CN_ANCHOR>& a,
383 const std::shared_ptr<const CN_ANCHOR>& b,
384 const std::function<void( const std::shared_ptr<const CN_ANCHOR>& )>&
385 setOptimizedATo,
386 const std::function<void( const std::shared_ptr<const CN_ANCHOR>& )>&
387 setOptimizedBTo )
388 {
389 struct CENTER
390 {
391 VECTOR2I pt;
392 bool valid = false;
393 };
394
395 struct DIST_PAIR
396 {
397 DIST_PAIR( int64_t aDistSq, size_t aIdA, size_t aIdB )
398 : dist_sq( aDistSq ), idA( aIdA ), idB( aIdB )
399 {}
400
401 int64_t dist_sq;
402 size_t idA;
403 size_t idB;
404 };
405
406 const std::vector<CN_ITEM*>& connectedItemsA = a->Item()->ConnectedItems();
407 const std::vector<CN_ITEM*>& connectedItemsB = b->Item()->ConnectedItems();
408
409 std::vector<CENTER> centersA( connectedItemsA.size() );
410 std::vector<CENTER> centersB( connectedItemsB.size() );
411
412 for( size_t i = 0; i < connectedItemsA.size(); i++ )
413 {
414 CN_ITEM* itemA = connectedItemsA[i];
415 CN_ZONE_LAYER* zoneLayerA = dynamic_cast<CN_ZONE_LAYER*>( itemA );
416
417 if( !zoneLayerA )
418 continue;
419
420 const SHAPE_LINE_CHAIN& shapeA = zoneLayerA->GetOutline();
421 centersA[i].pt = shapeA.BBox().GetCenter();
422 centersA[i].valid = true;
423 }
424
425 for( size_t i = 0; i < connectedItemsB.size(); i++ )
426 {
427 CN_ITEM* itemB = connectedItemsB[i];
428 CN_ZONE_LAYER* zoneLayerB = dynamic_cast<CN_ZONE_LAYER*>( itemB );
429
430 if( !zoneLayerB )
431 continue;
432
433 const SHAPE_LINE_CHAIN& shapeB = zoneLayerB->GetOutline();
434 centersB[i].pt = shapeB.BBox().GetCenter();
435 centersB[i].valid = true;
436 }
437
438 std::vector<DIST_PAIR> pairsToTest;
439
440 for( size_t ia = 0; ia < centersA.size(); ia++ )
441 {
442 for( size_t ib = 0; ib < centersB.size(); ib++ )
443 {
444 const CENTER& ca = centersA[ia];
445 const CENTER& cb = centersB[ib];
446
447 if( !ca.valid || !cb.valid )
448 continue;
449
450 VECTOR2L pA( ca.pt );
451 VECTOR2L pB( cb.pt );
452
453 int64_t dist_sq = ( pB - pA ).SquaredEuclideanNorm();
454 pairsToTest.emplace_back( dist_sq, ia, ib );
455 }
456 }
457
458 std::sort( pairsToTest.begin(), pairsToTest.end(),
459 []( const DIST_PAIR& dp_a, const DIST_PAIR& dp_b )
460 {
461 return dp_a.dist_sq < dp_b.dist_sq;
462 } );
463
464 const int c_polyPairsLimit = 3;
465
466 for( size_t i = 0; i < pairsToTest.size() && i < c_polyPairsLimit; i++ )
467 {
468 const DIST_PAIR& pair = pairsToTest[i];
469
470 CN_ZONE_LAYER* zoneLayerA = static_cast<CN_ZONE_LAYER*>( connectedItemsA[pair.idA] );
471 CN_ZONE_LAYER* zoneLayerB = static_cast<CN_ZONE_LAYER*>( connectedItemsB[pair.idB] );
472
473 if( zoneLayerA == zoneLayerB )
474 continue;
475
476 const SHAPE_LINE_CHAIN& shapeA = zoneLayerA->GetOutline();
477 const SHAPE_LINE_CHAIN& shapeB = zoneLayerB->GetOutline();
478
479 VECTOR2I ptA;
480 VECTOR2I ptB;
481
482 if( shapeA.ClosestSegmentsFast( shapeB, ptA, ptB ) )
483 {
484 setOptimizedATo( std::make_shared<CN_ANCHOR>( ptA, zoneLayerA ) );
485 setOptimizedBTo( std::make_shared<CN_ANCHOR>( ptB, zoneLayerB ) );
486 }
487 }
488 };
489
490 for( CN_EDGE& edge : m_rnEdges )
491 {
492 const std::shared_ptr<const CN_ANCHOR>& source = edge.GetSourceNode();
493 const std::shared_ptr<const CN_ANCHOR>& target = edge.GetTargetNode();
494
495 wxCHECK2( source && !source->Dirty() && target && !target->Dirty(), continue );
496
497 if( source->ConnectedItemsCount() == 0 )
498 {
499 optimizeZoneAnchor( source->Pos(), source->Parent()->GetLayerSet(), target,
500 [&]( const std::shared_ptr<const CN_ANCHOR>& optimized )
501 {
502 edge.SetTargetNode( optimized );
503 } );
504 }
505 else if( target->ConnectedItemsCount() == 0 )
506 {
507 optimizeZoneAnchor( target->Pos(), target->Parent()->GetLayerSet(), source,
508 [&]( const std::shared_ptr<const CN_ANCHOR>& optimized )
509 {
510 edge.SetSourceNode( optimized );
511 } );
512 }
513 else
514 {
515 optimizeZoneToZoneAnchors( source, target,
516 [&]( const std::shared_ptr<const CN_ANCHOR>& optimized )
517 {
518 edge.SetSourceNode( optimized );
519 },
520 [&]( const std::shared_ptr<const CN_ANCHOR>& optimized )
521 {
522 edge.SetTargetNode( optimized );
523 } );
524 }
525 }
526}
527
528
530{
531 compute();
532
533 m_dirty = false;
534}
535
536
538{
539 for( CN_EDGE& edge : m_rnEdges )
540 edge.RemoveInvalidRefs();
541
542 for( CN_EDGE& edge : m_boardEdges )
543 edge.RemoveInvalidRefs();
544
545 auto is_invalid = []( const CN_EDGE& edge )
546 {
547 return !edge.GetSourceNode() || !edge.GetTargetNode();
548 };
549
550 m_rnEdges.erase( std::remove_if( m_rnEdges.begin(), m_rnEdges.end(), is_invalid ), m_rnEdges.end() );
551 m_boardEdges.erase( std::remove_if( m_boardEdges.begin(), m_boardEdges.end(), is_invalid ),
552 m_boardEdges.end() );
553}
554
555
557{
558 m_rnEdges.clear();
559 m_boardEdges.clear();
560 m_nodes.clear();
561
562 m_dirty = true;
563}
564
565
566void RN_NET::AddCluster( std::shared_ptr<CN_CLUSTER> aCluster )
567{
568 std::shared_ptr<CN_ANCHOR> firstAnchor;
569
570 for( CN_ITEM* item : *aCluster )
571 {
572 std::vector<std::shared_ptr<CN_ANCHOR>>& anchors = item->Anchors();
573 unsigned int nAnchors = dynamic_cast<CN_ZONE_LAYER*>( item ) ? 1 : anchors.size();
574
575 if( nAnchors > anchors.size() )
576 nAnchors = anchors.size();
577
578 for( unsigned int i = 0; i < nAnchors; i++ )
579 {
580 anchors[i]->SetCluster( aCluster );
581 m_nodes.insert( anchors[i] );
582
583 if( firstAnchor )
584 {
585 if( firstAnchor != anchors[i] )
586 m_boardEdges.emplace_back( firstAnchor, anchors[i], 0 );
587 }
588 else
589 {
590 firstAnchor = anchors[i];
591 }
592 }
593 }
594}
595
596
597bool RN_NET::NearestBicoloredPair( RN_NET* aOtherNet, VECTOR2I& aPos1, VECTOR2I& aPos2 ) const
598{
599 bool rv = false;
600
602
603 auto verify =
604 [&]( const std::shared_ptr<CN_ANCHOR>& aTestNode1,
605 const std::shared_ptr<CN_ANCHOR>& aTestNode2 )
606 {
607 VECTOR2I diff = aTestNode1->Pos() - aTestNode2->Pos();
608 SEG::ecoord dist_sq = diff.SquaredEuclideanNorm();
609
610 if( dist_sq < distMax_sq )
611 {
612 rv = true;
613 distMax_sq = dist_sq;
614 aPos1 = aTestNode1->Pos();
615 aPos2 = aTestNode2->Pos();
616 }
617 };
618
622 for( const std::shared_ptr<CN_ANCHOR>& nodeA : aOtherNet->m_nodes )
623 {
624
625 if( nodeA->GetNoLine() )
626 continue;
627
631 auto fwd_it = m_nodes.lower_bound( nodeA );
632 auto rev_it = std::make_reverse_iterator( fwd_it );
633
634 for( ; fwd_it != m_nodes.end(); ++fwd_it )
635 {
636 const std::shared_ptr<CN_ANCHOR>& nodeB = *fwd_it;
637
638 if( nodeB->GetNoLine() )
639 continue;
640
641 SEG::ecoord distX_sq = SEG::Square( nodeA->Pos().x - nodeB->Pos().x );
642
645 if( distX_sq > distMax_sq )
646 break;
647
648 verify( nodeA, nodeB );
649 }
650
652 for( ; rev_it != m_nodes.rend(); ++rev_it )
653 {
654 const std::shared_ptr<CN_ANCHOR>& nodeB = *rev_it;
655
656 if( nodeB->GetNoLine() )
657 continue;
658
659 SEG::ecoord distX_sq = SEG::Square( nodeA->Pos().x - nodeB->Pos().x );
660
661 if( distX_sq > distMax_sq )
662 break;
663
664 verify( nodeA, nodeB );
665 }
666 }
667
668 return rv;
669}
constexpr const Vec GetCenter() const
Definition box2.h:227
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
const std::vector< CN_ITEM * > & ConnectedItems() const
PCB_LAYER_ID GetBoardLayer() const
const SHAPE_LINE_CHAIN & GetOutline() const
Lock-free disjoint-set over a dense range of indices.
Definition union_find.h:48
bool Unite(size_t aA, size_t aB)
Merge the components that hold aA and aB.
Definition union_find.h:82
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
A small class to help profiling.
Definition profile.h:46
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition profile.h:103
void AddNode(const std::shared_ptr< CN_ANCHOR > &aNode)
std::multiset< std::shared_ptr< CN_ANCHOR >, CN_PTR_CMP > m_allNodes
bool areNodesColinear(const std::vector< std::shared_ptr< CN_ANCHOR > > &aNodes) const
void Triangulate(std::vector< CN_EDGE > &mstEdges)
std::shared_ptr< TRIANGULATOR_STATE > m_triangulator
std::multiset< std::shared_ptr< CN_ANCHOR >, CN_PTR_CMP > m_nodes
< Vector of nodes
void RemoveInvalidRefs()
void UpdateNet()
Recompute ratsnest for a net.
std::vector< CN_EDGE > m_rnEdges
Flag indicating necessity of recalculation of ratsnest for a net.
void OptimizeRNEdges()
Find optimal ends of RNEdges.
bool NearestBicoloredPair(RN_NET *aOtherNet, VECTOR2I &aPos1, VECTOR2I &aPos2) const
bool m_dirty
std::vector< CN_EDGE > m_boardEdges
Vector of edges that makes ratsnest for a given net.
void compute()
< Recompute ratsnest from scratch.
void kruskalMST(std::vector< CN_EDGE > &aEdges)
void Clear()
void AddCluster(std::shared_ptr< CN_CLUSTER > aCluster)
VECTOR2I::extended_type ecoord
Definition seg.h:40
static SEG::ecoord Square(int a)
Definition seg.h:119
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool ClosestSegmentsFast(const SHAPE_LINE_CHAIN &aOther, VECTOR2I &aPt0, VECTOR2I &aPt1) const
Finds closest points between segments of this and the other line chain.
const std::vector< VECTOR2I > & CPoints() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition vector2d.h:534
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:303
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
size_t FilterKruskal(std::span< EDGE > aEdges, KI_UNION_FIND &aForest, LESS aLess, ENDPOINTS aEndpoints, EMIT aEmit)
Build a minimum spanning forest over aEdges.
CITER next(CITER it)
Definition ptree.cpp:120
Class that computes missing connections on a PCB.
VECTOR3I v1(5, 5, 5)
const SHAPE_LINE_CHAIN chain
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:684