KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_line.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2017 CERN
5 * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <optional>
23
24#include <math/box2.h>
25#include <math/vector2d.h>
26
27#include "pns_line.h"
28#include "pns_node.h"
29#include "pns_via.h"
30#include "pns_utils.h"
31#include "pns_router.h"
32#include "pns_debug_decorator.h"
33
34#include <geometry/shape_rect.h>
35
36namespace PNS {
37
38LINE::LINE( const LINE& aOther )
39 : LINK_HOLDER( aOther ),
40 m_line( aOther.m_line ),
41 m_width( aOther.m_width ),
42 m_snapThreshhold( aOther.m_snapThreshhold )
43{
44 m_net = aOther.m_net;
45 m_movable = aOther.m_movable;
46 m_layers = aOther.m_layers;
47
48 m_via = nullptr;
49
50 if( aOther.m_via )
51 {
52 if( aOther.m_via->BelongsTo( &aOther ) )
53 {
54 m_via = aOther.m_via->Clone();
55 m_via->SetOwner( this );
56 m_via->SetNet( m_net );
57 }
58 else
59 {
60 m_via = aOther.m_via;
61 }
62 }
63
64 m_marker = aOther.m_marker;
65 m_rank = aOther.m_rank;
67
68 copyLinks( &aOther );
69}
70
71
73{
74 if( m_via && m_via->BelongsTo( this ) )
75 delete m_via;
76}
77
78
79LINE& LINE::operator=( const LINE& aOther )
80{
81 m_line = aOther.m_line;
82 m_width = aOther.m_width;
83 m_net = aOther.m_net;
84 m_movable = aOther.m_movable;
85 m_layers = aOther.m_layers;
86
87 m_via = nullptr;
88
89 if( aOther.m_via )
90 {
91 if( aOther.m_via->BelongsTo( &aOther ) )
92 {
93 m_via = aOther.m_via->Clone();
94 m_via->SetOwner( this );
95 m_via->SetNet( m_net );
96 }
97 else
98 {
99 m_via = aOther.m_via;
100 }
101 }
102
103 m_marker = aOther.m_marker;
104 m_rank = aOther.m_rank;
105 m_owner = aOther.m_owner;
108
109 copyLinks( &aOther );
110
111 return *this;
112}
113
114
116{
117 LINE* l = new LINE( *this );
118
119 return l;
120}
121
122
123void LINE::Mark( int aMarker ) const
124{
125 m_marker = aMarker;
126
127 for( const LINKED_ITEM* s : m_links )
128 s->Mark( aMarker );
129
130}
131
132
133void LINE::Unmark( int aMarker ) const
134{
135 for( const LINKED_ITEM* s : m_links )
136 s->Unmark( aMarker );
137
138 m_marker = 0;
139}
140
141
142int LINE::Marker() const
143{
144 int marker = m_marker;
145
146 for( LINKED_ITEM* s : m_links )
147 marker |= s->Marker();
148
149 return marker;
150}
151
152
154{
155 SEGMENT* s = new SEGMENT( *this );
156
157 s->m_seg = m_seg;
158 s->m_net = m_net;
159 s->m_layers = m_layers;
160 s->m_marker = m_marker;
161 s->m_rank = m_rank;
162
163 return s;
164}
165
166
167int LINE::CountCorners( int aAngles ) const
168{
169 int count = 0;
170
171 for( int i = 0; i < m_line.SegmentCount() - 1; i++ )
172 {
173 const SEG seg1 = m_line.CSegment( i );
174 const SEG seg2 = m_line.CSegment( i + 1 );
175
176 const DIRECTION_45 dir1( seg1 );
177 const DIRECTION_45 dir2( seg2 );
178
179 DIRECTION_45::AngleType a = dir1.Angle( dir2 );
180
181 if( a & aAngles )
182 count++;
183 }
184
185 return count;
186}
187
188static int areNeighbours( int x, int y, int max = 0 )
189{
190 if( x > 0 && x - 1 == y )
191 return true;
192
193 if( x < max - 1 && x + 1 == y )
194 return true;
195
196 return false;
197}
198
199#ifdef TOM_EXTRA_DEBUG
200SHAPE_LINE_CHAIN g_pnew, g_hnew;
201#endif
202
203bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPath, bool aCw ) const
204{
205 const SHAPE_LINE_CHAIN& line( CLine() );
206
207 if( line.SegmentCount() < 1 )
208 {
209 return false;
210 }
211
212 const VECTOR2I pFirst = line.CPoint(0);
213
214 bool inFirst = aObstacle.PointInside( pFirst ) && !aObstacle.PointOnEdge( pFirst );
215
216 // We can't really walk around if the beginning of the path lies inside the obstacle hull.
217 // Double check if it's not on the hull itself as this triggers many unroutable corner cases.
218 if( inFirst )
219 {
220 return false;
221 }
222
223 enum VERTEX_TYPE { INSIDE = 0, OUTSIDE, ON_EDGE };
224
225 // Represents an entry in directed graph of hull/path vertices. Scanning this graph
226 // starting from the path's first point results (if possible) with a correct walkaround path
227 struct VERTEX
228 {
229 // vertex classification (inside/outside/exactly on the hull)
230 VERTEX_TYPE type;
231 // true = vertex coming from the hull primitive
232 bool isHull;
233 // position
234 VECTOR2I pos;
235 // list of neighboring vertices
236 std::vector<VERTEX*> neighbours;
237 // index of this vertex in path (pnew)
238 int indexp = -1;
239 // index of this vertex in the hull (hnew)
240 int indexh = -1;
241 // visited indicator (for BFS search)
242 bool visited = false;
243 };
244
246
247 HullIntersection( aObstacle, line, ips );
248
249 SHAPE_LINE_CHAIN pnew( CLine() ), hnew( aObstacle );
250
251 std::vector<VERTEX> vts;
252
253 auto findVertex =
254 [&]( const VECTOR2I& pos ) -> VERTEX*
255 {
256 for( VERTEX& v : vts )
257 {
258 if( v.pos == pos )
259 return &v;
260 }
261
262 return nullptr;
263 };
264
265 // corner case for loopy tracks: insert the end loop point back into the hull
266 if( const std::optional<SHAPE_LINE_CHAIN::INTERSECTION> isect = pnew.SelfIntersecting() )
267 {
268 if( isect->p != pnew.CPoint( -1 ) )
269 pnew.Split( isect->p );
270 }
271
272 // insert all intersections found into the new hull/path SLCs
273 for( SHAPE_LINE_CHAIN::INTERSECTION& ip : ips )
274 {
275 if( pnew.Find( ip.p, 1 ) < 0)
276 pnew.Split(ip.p);
277
278 if( hnew.Find( ip.p, 1 ) < 0 )
279 hnew.Split(ip.p);
280 }
281
282 for( int i = 0; i < pnew.PointCount(); i++ )
283 {
284 const VECTOR2I& p = pnew.CPoint( i );
285 bool onEdge = hnew.PointOnEdge( p );
286
287 if ( !onEdge )
288 continue;
289
290 int idx = hnew.Find( p );
291
292 if(idx < 0 )
293 hnew.Split( p );
294 }
295
296 #ifdef TOM_EXTRA_DEBUG
297 for( auto& ip : ips )
298 {
299 printf("Chk: %d %d\n", pnew.Find( ip.p ), hnew.Find(ip.p) );
300 }
301 #endif
302
303 // we assume the default orientation of the hulls is clockwise, so just reverse the vertex
304 // order if the caller wants a counter-clockwise walkaround
305 if ( !aCw )
306 hnew = hnew.Reverse();
307
308 vts.reserve( 2 * ( hnew.PointCount() + pnew.PointCount() ) );
309
310 // create a graph of hull/path vertices and classify them (inside/on edge/outside the hull)
311 for( int i = 0; i < pnew.PointCount(); i++ )
312 {
313 const VECTOR2I& p = pnew.CPoint(i);
314 bool onEdge = hnew.PointOnEdge( p );
315 bool inside = hnew.PointInside( p );
316
317 #ifdef TOM_EXTRA_DEBUG
318 printf("pnew %d inside %d onedge %d\n", i, !!inside, !!onEdge );
319 #endif
320
321 VERTEX v;
322
323 v.indexp = i;
324 v.isHull = false;
325 v.pos = p;
326 v.type = inside && !onEdge ? INSIDE : onEdge ? ON_EDGE : OUTSIDE;
327 vts.push_back( v );
328 }
329
330 #ifdef TOM_EXTRA_DEBUG
331 g_pnew = pnew;
332 g_hnew = hnew;
333 #endif
334
335 // each path vertex neighbour list points for sure to the next vertex in the path
336 for( int i = 0; i < pnew.PointCount() - 1; i++ )
337 {
338 vts[i].neighbours.push_back( &vts[ i+1 ] );
339 }
340
341 // each path vertex neighbour list points for sure to the next vertex in the path
342 for( int i = 1; i < pnew.PointCount() ; i++ )
343 {
344 vts[i].neighbours.push_back( &vts[ i-1 ] );
345 }
346
347 // insert hull vertices into the graph
348 for( int i = 0; i < hnew.PointCount(); i++ )
349 {
350 const VECTOR2I& hp = hnew.CPoint( i );
351 VERTEX* vn = findVertex( hp );
352
353 // if vertex already present (it's very likely that in recursive shoving hull and path vertices will overlap)
354 // just mark it as a path vertex that also belongs to the hull
355 if( vn )
356 {
357 vn->isHull = true;
358 vn->indexh = i;
359 }
360 else // new hull vertex
361 {
362 VERTEX v;
363 v.pos = hp;
364 v.type = ON_EDGE;
365 v.indexh = i;
366 v.isHull = true;
367 vts.push_back( v );
368 }
369 }
370
371 // go around the hull and fix up the neighbour link lists
372 for( int i = 0; i < hnew.PointCount(); i++ )
373 {
374 VERTEX* vc = findVertex( hnew.CPoint( i ) );
375 VERTEX* vnext = findVertex( hnew.CPoint( i+1 ) );
376
377 if( vc && vnext )
378 vc->neighbours.push_back( vnext );
379 }
380
381 // In the case that the initial path ends *inside* the current obstacle (i.e. the mouse cursor
382 // is somewhere inside the hull for the current obstacle) we want to end the walkaround at the
383 // point closest to the cursor
384 bool inLast = aObstacle.PointInside( CPoint( -1 ) ) && !aObstacle.PointOnEdge( CPoint( -1 ) );
385 bool appendV = true;
386 int lastDst = INT_MAX;
387
388 int i = 0;
389#ifdef TOM_EXTRA_DEBUG
390 for( VERTEX* &v: vts )
391 {
392 if( v.indexh < 0 && v.type == ON_EDGE )
393 v.type = OUTSIDE; // hack
394
395 printf("V %d pos %d %d ip %d ih %d type %d\n", i++, v.pos.x, v.pos.y, v.indexp, v.indexh, v.type );
396 }
397#endif
398 // vts[0] = start point
399 VERTEX* v = &vts[0];
400 VERTEX* v_prev = nullptr;
402
403 int iterLimit = 1000;
404
405 // keep scanning the graph until we reach the end point of the path
406 while( v->indexp != ( pnew.PointCount() - 1 ) )
407 {
408 iterLimit--;
409
410 // I'm not 100% sure this algorithm doesn't have bugs that may cause it to freeze,
411 // so here's a temporary iteration limit
412 if( iterLimit == 0 )
413 return false;
414
415 if( v->visited )
416 {
417 // loop found? stop walking
418 break;
419 }
420
421#ifdef TOM_EXTRA_DEBUG
422 printf("---\nvisit ip %d ih %d type %d outs %d neig %d\n", v->indexp, v->indexh, v->type, out.PointCount(), v->neighbours.size() );
423#endif
424 out.Append( v->pos );
425
426 VERTEX* v_next = nullptr;
427
428 if( v->type == OUTSIDE )
429 {
430 // current vertex is outside? first look for any vertex further down the path
431 // that is not inside the hull
432 out.Append( v->pos );
433 VERTEX* v_next_fallback = nullptr;
434
435 for( VERTEX* vn : v->neighbours )
436 {
437 if( areNeighbours( vn->indexp , v->indexp, pnew.PointCount() )
438 && vn->type != INSIDE )
439 {
440 if( !vn->visited )
441 {
442 v_next = vn;
443 break;
444 }
445 else if( vn != v_prev )
446 {
447 v_next_fallback = vn;
448 }
449 }
450 }
451
452 if( !v_next )
453 v_next = v_next_fallback;
454
455 // such a vertex must always be present, if not, bummer.
456 if( !v_next )
457 {
458 #ifdef TOM_EXTRA_DEBUG
459 printf("FAIL VN fallback %p\n", v_next_fallback );
460 #endif
461 return false;
462 }
463 }
464 else if( v->type == ON_EDGE )
465 {
466 // look first for the first vertex outside the hull
467 for( VERTEX* vn : v->neighbours )
468 {
469#ifdef TOM_EXTRA_DEBUG
470 printf( "- OUT scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
471#endif
472
473 if( vn->type == OUTSIDE && !vn->visited )
474 {
475 v_next = vn;
476 break;
477 }
478 }
479
480 // no outside vertices found? continue traversing the hull
481 if( !v_next )
482 {
483 for( VERTEX* vn : v->neighbours )
484 {
485 #ifdef TOM_EXTRA_DEBUG
486 printf("- scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
487 #endif
488 if( vn->type == ON_EDGE && !vn->isHull &&
489 areNeighbours( vn->indexp, v->indexp, pnew.PointCount() ) &&
490 ( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) ) )
491 {
492 v_next = vn;
493 break;
494 }
495 }
496 }
497
498 // still nothing found? try to find the next (index-wise) point on the hull. I guess
499 // we should never reach this part of the code, but who really knows?
500 if( !v_next )
501 {
502#ifdef TOM_EXTRA_DEBUG
503 printf("still no v_next\n");
504#endif
505 for( VERTEX* vn : v->neighbours )
506 {
507 if( vn->type == ON_EDGE )
508 {
509 if( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) )
510 {
511 v_next = vn;
512 break;
513 }
514 }
515 }
516
517 if( v_next )
518 {
519 for( VERTEX &vt : vts )
520 {
521 if( vt.isHull )
522 vt.visited = false;
523 }
524 }
525
526#ifdef TOM_EXTRA_DEBUG
527 printf("v_next %p\n", v_next);
528#endif
529
530 // Did we get the next hull point but the end of the line is inside? Instead of walking
531 // around the hull some more (which will just end up taking us back to the start), lets
532 // just project the normal of the endpoint onto this next segment and call it quits.
533 if( inLast && v_next )
534 {
535 int d = ( v_next->pos - CPoint( -1 ) ).SquaredEuclideanNorm();
536
537 if( d < lastDst )
538 {
539 lastDst = d;
540 }
541 else
542 {
543 VECTOR2I proj = SEG( v->pos, v_next->pos ).NearestPoint( CPoint( -1 ) );
544 out.Append( proj );
545 appendV = false;
546 break;
547 }
548 }
549 }
550 }
551
552 v->visited = true;
553 v_prev = v;
554 v = v_next;
555
556 if( !v )
557 return false;
558 }
559
560 if( appendV )
561 out.Append( v->pos );
562
563 aPath = out;
564
565 return true;
566}
567
568
569const SHAPE_LINE_CHAIN SEGMENT::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
570{
571 /*DEBUG_DECORATOR* debugDecorator = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
572
573 PNS_DBG( debugDecorator, Message, wxString::Format( wxT( "seghull %d %d" ), aWalkaroundThickness, aClearance ) );
574 PNS_DBG(debugDecorator, AddShape, &m_seg, RED, 0, wxT("theseg") );
575 */
576
577 return SegmentHull( m_seg, aClearance, aWalkaroundThickness );
578}
579
581{
582 const int IterationLimit = 5;
583 int i;
584 LINE l( *this );
585
586 for( i = 0; i < IterationLimit; i++ )
587 {
588 NODE::OPT_OBSTACLE obs = aNode->NearestObstacle( &l );
589
590 if( obs )
591 {
592 l.RemoveVia();
593 VECTOR2I collisionPoint = obs->m_ipFirst;
594 int segIdx = l.Line().NearestSegment( collisionPoint );
595
596 if( l.Line().IsArcSegment( segIdx ) )
597 {
598 // Don't clip at arcs, start again
599 l.Line().Clear();
600 }
601 else
602 {
603 SEG nearestSegment = l.Line().CSegment( segIdx );
604 VECTOR2I nearestPt = nearestSegment.NearestPoint( collisionPoint );
605 int p = l.Line().Split( nearestPt );
606 l.Line().Remove( p + 1, -1 );
607 }
608 }
609 else
610 {
611 break;
612 }
613 }
614
615 if( i == IterationLimit )
616 l.Line().Clear();
617
618 return l;
619}
620
621
622
623SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECTOR2I& aP, DIRECTION_45 aPreferredEndingDirection = DIRECTION_45() )
624{
625 std::optional<SHAPE_LINE_CHAIN> picked;
626 int i;
627 int d = 2;
628
629 wxASSERT( aOrigin.PointCount() > 0 );
630
631 if( aOrigin.PointCount() == 1 )
632 {
633 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP );
634 }
635 else if( aOrigin.SegmentCount() == 1 )
636 {
637 DIRECTION_45 dir( aOrigin.CPoint( 0 ) - aOrigin.CPoint( 1 ) );
638
639 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
640 }
641
642
643 //if( aOrigin.CSegment( -1 ).Length() > 100000 * 30 ) // fixme: constant/parameter?
644 d = 1;
645
646 for( i = aOrigin.SegmentCount() - d; i >= 0; i-- )
647 {
648 DIRECTION_45 d_start( aOrigin.CSegment( i ) );
649 const VECTOR2I& p_start = aOrigin.CPoint( i );
650 SHAPE_LINE_CHAIN paths[2];
651 DIRECTION_45 dirs[2];
652 DIRECTION_45 d_prev = ( i > 0 ? DIRECTION_45( aOrigin.CSegment( i-1 ) )
653 : DIRECTION_45() );
654 int dirCount = 0;
655
656 for( int j = 0; j < 2; j++ )
657 {
658 paths[j] = d_start.BuildInitialTrace( p_start, aP, j );
659
660 if( paths[j].SegmentCount() < 1 )
661 continue;
662
663 assert( dirCount < int( sizeof( dirs ) / sizeof( dirs[0] ) ) );
664
665 dirs[dirCount] = DIRECTION_45( paths[j].CSegment( 0 ) );
666 ++dirCount;
667 }
668
669 if( aPreferredEndingDirection != DIRECTION_45::UNDEFINED )
670 {
671 for( int j = 0; j < dirCount; j++ )
672 {
673 DIRECTION_45 endingDir( paths[j].CSegment(-1) );
674 if( endingDir == aPreferredEndingDirection )
675 {
676 picked = paths[j];
677 break;
678 }
679 }
680 }
681
682 if( !picked )
683 {
684 for( int j = 0; j < dirCount; j++ )
685 {
686 if( dirs[j] == d_start )
687 {
688 picked = paths[j];
689 break;
690 }
691 }
692 }
693
694 if( picked )
695 break;
696
697 for( int j = 0; j < dirCount; j++ )
698 {
699 if( dirs[j].IsObtuse( d_prev ) )
700 {
701 picked = paths[j];
702 break;
703 }
704 }
705
706 if( picked )
707 break;
708 }
709
710 if( picked )
711 {
712 SHAPE_LINE_CHAIN path = aOrigin.Slice( 0, i );
713 path.Append( *picked );
714
715 return path;
716 }
717
718 DIRECTION_45 dir( aOrigin.CPoint( -1 ) - aOrigin.CPoint( -2 ) );
719
720 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
721}
722
723
724void LINE::dragCorner45( const VECTOR2I& aP, int aIndex, DIRECTION_45 aPreferredEndingDirection )
725{
727
728 int width = m_line.Width();
729 VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex );
730
731 if( aIndex == 0 )
732 {
733 path = dragCornerInternal( m_line.Reverse(), snapped, aPreferredEndingDirection ).Reverse();
734 }
735 else if( aIndex == m_line.SegmentCount() )
736 {
737 path = dragCornerInternal( m_line, snapped, aPreferredEndingDirection );
738 }
739 else
740 {
741 // Are we next to an arc? Insert a new point so we slice correctly
742 if( m_line.IsPtOnArc( static_cast<size_t>( aIndex ) + 1 ) )
743 m_line.Insert( aIndex + 1, m_line.CPoint( aIndex + 1 ) );
744
745 // fixme: awkward behaviour for "outwards" drags
746 path = dragCornerInternal( m_line.Slice( 0, aIndex ), snapped, aPreferredEndingDirection );
747 SHAPE_LINE_CHAIN path_rev =
748 dragCornerInternal( m_line.Slice( aIndex, -1 ).Reverse(), snapped, aPreferredEndingDirection ).Reverse();
749 path.Append( path_rev );
750 }
751
752 path.Simplify();
753 path.SetWidth( width );
754 m_line = path;
755}
756
757
758void LINE::dragCornerFree( const VECTOR2I& aP, int aIndex )
759{
760 ssize_t idx = static_cast<ssize_t>( aIndex );
761 ssize_t numpts = static_cast<ssize_t>( m_line.PointCount() );
762
763 // If we're asked to drag the end of an arc, insert a new vertex to drag instead
764 if( m_line.IsPtOnArc( idx ) )
765 {
766 if( idx == 0 || ( idx > 0 && !m_line.IsPtOnArc( idx - 1 ) ) )
767 {
768 m_line.Insert( idx, m_line.GetPoint( idx ) );
769 }
770 else if( ( idx == numpts - 1 ) || ( idx < numpts - 1 && !m_line.IsArcSegment( idx ) ) )
771 {
772 idx++;
773 m_line.Insert( idx, m_line.GetPoint( idx ) );
774 }
775 else
776 {
777 wxASSERT_MSG( false, wxT( "Attempt to dragCornerFree in the middle of an arc!" ) );
778 }
779 }
780
781 m_line.SetPoint( idx, aP );
783}
784
785void LINE::DragCorner( const VECTOR2I& aP, int aIndex, bool aFreeAngle, DIRECTION_45 aPreferredEndingDirection )
786{
787 wxCHECK_RET( aIndex >= 0, wxT( "Negative index passed to LINE::DragCorner" ) );
788
789 if( aFreeAngle )
790 {
791 dragCornerFree( aP, aIndex );
792 }
793 else
794 {
795 dragCorner45( aP, aIndex, aPreferredEndingDirection );
796 }
797}
798
799void LINE::DragSegment( const VECTOR2I& aP, int aIndex, bool aFreeAngle )
800{
801 if( aFreeAngle )
802 {
803 assert( false );
804 }
805 else
806 {
807 dragSegment45( aP, aIndex );
808 }
809}
810
812 const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const
813{
814 int s_start = std::max( aIndex - 2, 0 );
815 int s_end = std::min( aIndex + 2, aPath.SegmentCount() - 1 );
816
817 int i, j;
818 int best_dist = INT_MAX;
819 VECTOR2I best_snap = aP;
820
821 if( m_snapThreshhold <= 0 )
822 return aP;
823
824 for( i = s_start; i <= s_end; i++ )
825 {
826 const SEG& a = aPath.CSegment( i );
827
828 for( j = s_start; j < i; j++ )
829 {
830 const SEG& b = aPath.CSegment( j );
831
832 if( !( DIRECTION_45( a ).IsObtuse( DIRECTION_45( b ) ) ) )
833 continue;
834
835 OPT_VECTOR2I ip = a.IntersectLines( b );
836
837 if( ip )
838 {
839 int dist = ( *ip - aP ).EuclideanNorm();
840
841 if( dist < m_snapThreshhold && dist < best_dist )
842 {
843 best_dist = dist;
844 best_snap = *ip;
845 }
846 }
847 }
848 }
849
850 return best_snap;
851}
852
854 const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const
855{
856 VECTOR2I snap_p[2];
857 DIRECTION_45 dragDir( aPath.CSegment( aIndex ) );
858 int snap_d[2] = { -1, -1 };
859
860 if( m_snapThreshhold == 0 )
861 return aP;
862
863 if( aIndex >= 2 )
864 {
865 SEG s = aPath.CSegment( aIndex - 2 );
866
867 if( DIRECTION_45( s ) == dragDir )
868 snap_d[0] = s.LineDistance( aP );
869
870 snap_p[0] = s.A;
871 }
872
873 if( aIndex < aPath.SegmentCount() - 2 )
874 {
875 SEG s = aPath.CSegment( aIndex + 2 );
876
877 if( DIRECTION_45( s ) == dragDir )
878 snap_d[1] = s.LineDistance( aP );
879
880 snap_p[1] = s.A;
881 }
882
883 VECTOR2I best = aP;
884 int minDist = INT_MAX;
885
886 for( int i = 0; i < 2; i++ )
887 {
888 if( snap_d[i] >= 0 && snap_d[i] < minDist && snap_d[i] <= m_snapThreshhold )
889 {
890 minDist = snap_d[i];
891 best = snap_p[i];
892 }
893 }
894
895 return best;
896}
897
898void LINE::dragSegment45( const VECTOR2I& aP, int aIndex )
899{
901 VECTOR2I target( aP );
902
903 wxASSERT( aIndex < m_line.PointCount() );
904
905 SEG guideA[2], guideB[2];
906 int index = aIndex;
907
908 target = snapToNeighbourSegments( path, aP, aIndex );
909
910 // We require a valid s_prev and s_next. If we are at the start or end of the line, we insert
911 // a new point at the start or end so there is a zero-length segment for prev or next (we will
912 // resize it as part of the drag operation). If we are next to an arc, we do this also, as we
913 // cannot drag away one of the arc's points.
914
915 if( index == 0 || path.IsPtOnArc( index ) )
916 {
917 path.Insert( index > 0 ? index + 1 : 0, path.CPoint( index ) );
918 index++;
919 }
920
921 if( index == path.SegmentCount() - 1 )
922 {
923 path.Insert( path.PointCount() - 1, path.CPoint( -1 ) );
924 }
925 else if( path.IsPtOnArc( index + 1 ) )
926 {
927 path.Insert( index + 1, path.CPoint( index + 1 ) );
928 }
929
930 SEG dragged = path.CSegment( index );
931 DIRECTION_45 drag_dir( dragged );
932
933 SEG s_prev = path.CSegment( index - 1 );
934 SEG s_next = path.CSegment( index + 1 );
935
936 DIRECTION_45 dir_prev( s_prev );
937 DIRECTION_45 dir_next( s_next );
938
939 if( dir_prev == drag_dir )
940 {
941 dir_prev = dir_prev.Left();
942 path.Insert( index, path.CPoint( index ) );
943 index++;
944 }
945 else if( dir_prev == DIRECTION_45::UNDEFINED )
946 {
947 dir_prev = drag_dir.Left();
948 }
949
950 if( dir_next == drag_dir )
951 {
952 dir_next = dir_next.Right();
953 path.Insert( index + 1, path.CPoint( index + 1 ) );
954 }
955 else if( dir_next == DIRECTION_45::UNDEFINED )
956 {
957 dir_next = drag_dir.Right();
958 }
959
960 s_prev = path.CSegment( index - 1 );
961 s_next = path.CSegment( index + 1 );
962 dragged = path.CSegment( index );
963
964 if( aIndex == 0 )
965 {
966 guideA[0] = SEG( dragged.A, dragged.A + drag_dir.Right().ToVector() );
967 guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Left().ToVector() );
968 }
969 else
970 {
971 if( dir_prev.Angle( drag_dir )
973 {
974 guideA[0] = SEG( s_prev.A, s_prev.A + drag_dir.Left().ToVector() );
975 guideA[1] = SEG( s_prev.A, s_prev.A + drag_dir.Right().ToVector() );
976 }
977 else
978 guideA[0] = guideA[1] = SEG( dragged.A, dragged.A + dir_prev.ToVector() );
979 }
980
981 if( aIndex == m_line.SegmentCount() - 1 )
982 {
983 guideB[0] = SEG( dragged.B, dragged.B + drag_dir.Right().ToVector() );
984 guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Left().ToVector() );
985 }
986 else
987 {
988 if( dir_next.Angle( drag_dir )
990 {
991 guideB[0] = SEG( s_next.B, s_next.B + drag_dir.Left().ToVector() );
992 guideB[1] = SEG( s_next.B, s_next.B + drag_dir.Right().ToVector() );
993 }
994 else
995 guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + dir_next.ToVector() );
996 }
997
998 SEG s_current( target, target + drag_dir.ToVector() );
999
1000 int best_len = INT_MAX;
1001 SHAPE_LINE_CHAIN best;
1002
1003 for( int i = 0; i < 2; i++ )
1004 {
1005 for( int j = 0; j < 2; j++ )
1006 {
1007 OPT_VECTOR2I ip1 = s_current.IntersectLines( guideA[i] );
1008 OPT_VECTOR2I ip2 = s_current.IntersectLines( guideB[j] );
1009
1011
1012 if( !ip1 || !ip2 )
1013 continue;
1014
1015 SEG s1( s_prev.A, *ip1 );
1016 SEG s2( *ip1, *ip2 );
1017 SEG s3( *ip2, s_next.B );
1018
1019 OPT_VECTOR2I ip;
1020
1021 if( ( ip = s1.Intersect( s_next ) ) )
1022 {
1023 np.Append( s1.A );
1024 np.Append( *ip );
1025 np.Append( s_next.B );
1026 }
1027 else if( ( ip = s3.Intersect( s_prev ) ) )
1028 {
1029 np.Append( s_prev.A );
1030 np.Append( *ip );
1031 np.Append( s3.B );
1032 }
1033 else if( ( ip = s1.Intersect( s3 ) ) )
1034 {
1035 np.Append( s_prev.A );
1036 np.Append( *ip );
1037 np.Append( s_next.B );
1038 }
1039 else
1040 {
1041 np.Append( s_prev.A );
1042 np.Append( *ip1 );
1043 np.Append( *ip2 );
1044 np.Append( s_next.B );
1045 }
1046
1047 if( np.Length() < best_len )
1048 {
1049 best_len = np.Length();
1050 best = np;
1051 }
1052 }
1053 }
1054
1055 if( m_line.PointCount() == 1 )
1056 m_line = best;
1057 else if( aIndex == 0 )
1058 m_line.Replace( 0, 1, best );
1059 else if( aIndex == m_line.SegmentCount() - 1 )
1060 m_line.Replace( -2, -1, best );
1061 else
1062 m_line.Replace( aIndex, aIndex + 1, best );
1063
1064 m_line.Simplify();
1065}
1066
1067
1068bool LINE::CompareGeometry( const LINE& aOther )
1069{
1070 return m_line.CompareGeometry( aOther.m_line );
1071}
1072
1073
1075{
1076 m_line = m_line.Reverse();
1077
1078 std::reverse( m_links.begin(), m_links.end() );
1079}
1080
1081
1082void LINE::AppendVia( const VIA& aVia )
1083{
1084 if( m_line.PointCount() > 1 && aVia.Pos() == m_line.CPoint( 0 ) )
1085 {
1086 Reverse();
1087 }
1088
1089 m_via = aVia.Clone();
1090 m_via->SetOwner( this );
1091 m_via->SetNet( m_net );
1092}
1093
1094
1095void LINE::LinkVia( VIA* aVia )
1096{
1097 if( m_line.PointCount() > 1 && aVia->Pos() == m_line.CPoint( 0 ) )
1098 {
1099 Reverse();
1100 }
1101
1102 m_via = aVia;
1103 Link( aVia );
1104}
1105
1106
1107void LINE::SetRank( int aRank )
1108{
1109 m_rank = aRank;
1110
1111 for( auto s : m_links )
1112 s->SetRank( aRank );
1113
1114}
1115
1116
1117int LINE::Rank() const
1118{
1119 int min_rank = INT_MAX;
1120
1121 if( IsLinked() )
1122 {
1123 for( const LINKED_ITEM* item : m_links )
1124 min_rank = std::min( min_rank, item->Rank() );
1125 }
1126 else
1127 {
1128 min_rank = m_rank;
1129 }
1130
1131 int rank = ( min_rank == INT_MAX ) ? -1 : min_rank;
1132
1133 return rank;
1134}
1135
1136
1137void LINE::ClipVertexRange( int aStart, int aEnd )
1138{
1145 int firstLink = 0;
1146 int lastLink = std::max( 0, static_cast<int>( m_links.size() ) - 1 );
1147 int linkIdx = 0;
1148
1149 int numPoints = static_cast<int>( m_line.PointCount() );
1150
1151 for( int i = 0; i >= 0 && i < m_line.PointCount(); i = m_line.NextShape( i ) )
1152 {
1153 if( i <= aStart )
1154 firstLink = linkIdx;
1155
1156 if( i < 0 || i >= aEnd - 1 || linkIdx >= lastLink )
1157 {
1158 lastLink = linkIdx;
1159 break;
1160 }
1161
1162 linkIdx++;
1163 }
1164
1165 wxASSERT( lastLink >= firstLink );
1166
1167 m_line = m_line.Slice( aStart, aEnd );
1168
1169 if( IsLinked() )
1170 {
1171 wxASSERT( m_links.size() < INT_MAX );
1172 wxASSERT( static_cast<int>( m_links.size() ) >= ( lastLink - firstLink ) );
1173
1174 // Note: The range includes aEnd, but we have n-1 segments.
1175 std::rotate(
1176 m_links.begin(),
1177 m_links.begin() + firstLink,
1178 m_links.begin() + lastLink
1179 );
1180
1181 m_links.resize( lastLink - firstLink + 1 );
1182 }
1183}
1184
1185
1186bool LINE::HasLoops() const
1187{
1188 for( int i = 0; i < PointCount(); i++ )
1189 {
1190 for( int j = i + 2; j < PointCount(); j++ )
1191 {
1192 if( CPoint( i ) == CPoint( j ) )
1193 return true;
1194 }
1195 }
1196
1197 return false;
1198}
1199
1200
1201static void extendBox( BOX2I& aBox, bool& aDefined, const VECTOR2I& aP )
1202{
1203 if( aDefined )
1204 {
1205 aBox.Merge( aP );
1206 }
1207 else
1208 {
1209 aBox = BOX2I( aP, VECTOR2I( 0, 0 ) );
1210 aDefined = true;
1211 }
1212}
1213
1214
1215OPT_BOX2I LINE::ChangedArea( const LINE* aOther ) const
1216{
1217 BOX2I area;
1218 bool areaDefined = false;
1219
1220 int i_start = -1;
1221 int i_end_self = -1, i_end_other = -1;
1222
1223 SHAPE_LINE_CHAIN self( m_line );
1224 self.Simplify();
1225 SHAPE_LINE_CHAIN other( aOther->m_line );
1226 other.Simplify();
1227
1228 int np_self = self.PointCount();
1229 int np_other = other.PointCount();
1230
1231 int n = std::min( np_self, np_other );
1232
1233 for( int i = 0; i < n; i++ )
1234 {
1235 const VECTOR2I p1 = self.CPoint( i );
1236 const VECTOR2I p2 = other.CPoint( i );
1237
1238 if( p1 != p2 )
1239 {
1240 if( i != n - 1 )
1241 {
1242 SEG s = self.CSegment( i );
1243
1244 if( !s.Contains( p2 ) )
1245 {
1246 i_start = i;
1247 break;
1248 }
1249 }
1250 else
1251 {
1252 i_start = i;
1253 break;
1254 }
1255 }
1256 }
1257
1258 for( int i = 0; i < n; i++ )
1259 {
1260 const VECTOR2I p1 = self.CPoint( np_self - 1 - i );
1261 const VECTOR2I p2 = other.CPoint( np_other - 1 - i );
1262
1263 if( p1 != p2 )
1264 {
1265 i_end_self = np_self - 1 - i;
1266 i_end_other = np_other - 1 - i;
1267 break;
1268 }
1269 }
1270
1271 if( i_start < 0 )
1272 i_start = n;
1273
1274 if( i_end_self < 0 )
1275 i_end_self = np_self - 1;
1276
1277 if( i_end_other < 0 )
1278 i_end_other = np_other - 1;
1279
1280 for( int i = i_start; i <= i_end_self; i++ )
1281 extendBox( area, areaDefined, self.CPoint( i ) );
1282
1283 for( int i = i_start; i <= i_end_other; i++ )
1284 extendBox( area, areaDefined, other.CPoint( i ) );
1285
1286 if( areaDefined )
1287 {
1288 area.Inflate( std::max( Width(), aOther->Width() ) );
1289 return area;
1290 }
1291
1292 return OPT_BOX2I();
1293}
1294
1295
1297{
1298 for( const auto seg : m_links )
1299 {
1300 if( seg->Marker() & MK_LOCKED )
1301 return true;
1302 }
1303 return false;
1304}
1305
1306
1308{
1309 ClearLinks();
1310 RemoveVia();
1311 m_line.Clear();
1312}
1313
1314
1316{
1317 if( m_via )
1318 {
1319 if( ContainsLink( m_via ) )
1320 Unlink( m_via );
1321 if( m_via->BelongsTo( this ) )
1322 delete m_via;
1323 }
1324
1325 m_via = nullptr;
1326}
1327
1328
1329const std::string SEGMENT::Format( ) const
1330{
1331 std::stringstream ss;
1332 ss << ITEM::Format() << " ";
1333 ss << m_seg.Format( false );
1334 return ss.str();
1335}
1336
1337
1338int LINE::FindSegment( const SEGMENT* aSeg ) const
1339{
1340 for( int i = 0; i < m_line.SegmentCount(); i++)
1341 {
1342 const SEG&s = m_line.CSegment(i);
1343 if( s == aSeg->Seg() )
1344 return i;
1345 }
1346
1347 return -1;
1348}
1349
1350}
1351
1352
BOX2< VECTOR2I > BOX2I
Definition: box2.h:922
std::optional< BOX2I > OPT_BOX2I
Definition: box2.h:926
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 BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:658
Represent route directions & corner angles in a 45-degree metric.
Definition: direction45.h:37
const SHAPE_LINE_CHAIN BuildInitialTrace(const VECTOR2I &aP0, const VECTOR2I &aP1, bool aStartDiagonal=false, CORNER_MODE aMode=CORNER_MODE::MITERED_45) const
Build a 2-segment line chain between points aP0 and aP1 and following 45-degree routing regime.
AngleType Angle(const DIRECTION_45 &aOther) const
Return the type of angle between directions (this) and aOther.
Definition: direction45.h:181
const DIRECTION_45 Left() const
Return the direction on the left side of this (i.e.
Definition: direction45.h:269
const VECTOR2I ToVector() const
Definition: direction45.h:287
AngleType
Represent kind of angle formed by vectors heading in two DIRECTION_45s.
Definition: direction45.h:78
bool IsDiagonal() const
Returns true if the direction is diagonal (e.g.
Definition: direction45.h:213
const DIRECTION_45 Right() const
Return the direction on the right side of this (i.e.
Definition: direction45.h:251
virtual const std::string Format() const
Definition: pns_item.cpp:329
bool m_movable
Definition: pns_item.h:309
PNS_LAYER_RANGE m_layers
Definition: pns_item.h:307
void SetNet(NET_HANDLE aNet)
Definition: pns_item.h:197
NET_HANDLE m_net
Definition: pns_item.h:310
int m_marker
Definition: pns_item.h:311
int m_rank
Definition: pns_item.h:312
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition: pns_line.h:62
VECTOR2I snapToNeighbourSegments(const SHAPE_LINE_CHAIN &aPath, const VECTOR2I &aP, int aIndex) const
Definition: pns_line.cpp:853
void ClipVertexRange(int aStart, int aEnd)
Return the number of corners of angles specified by mask aAngles.
Definition: pns_line.cpp:1137
int FindSegment(const SEGMENT *aSeg) const
Definition: pns_line.cpp:1338
const VECTOR2I & CPoint(int aIdx) const
Definition: pns_line.h:146
bool HasLoops() const
Definition: pns_line.cpp:1186
OPT_BOX2I ChangedArea(const LINE *aOther) const
Definition: pns_line.cpp:1215
bool HasLockedSegments() const
Definition: pns_line.cpp:1296
int Rank() const override
Definition: pns_line.cpp:1117
void dragCorner45(const VECTOR2I &aP, int aIndex, DIRECTION_45 aPreferredEndingDirection)
Definition: pns_line.cpp:724
const LINE ClipToNearestObstacle(NODE *aNode) const
Clip the line to a given range of vertices.
Definition: pns_line.cpp:580
VIA * m_via
Definition: pns_line.h:260
virtual void Mark(int aMarker) const override
Definition: pns_line.cpp:123
int m_width
Our width.
Definition: pns_line.h:255
bool CompareGeometry(const LINE &aOther)
Reverse the point/vertex order.
Definition: pns_line.cpp:1068
void LinkVia(VIA *aVia)
Definition: pns_line.cpp:1095
ITEM * m_blockingObstacle
For mark obstacle mode.
Definition: pns_line.h:261
const SHAPE_LINE_CHAIN & CLine() const
Definition: pns_line.h:138
VECTOR2I snapDraggedCorner(const SHAPE_LINE_CHAIN &aPath, const VECTOR2I &aP, int aIndex) const
Definition: pns_line.cpp:811
LINE & operator=(const LINE &aOther)
Definition: pns_line.cpp:79
void dragSegment45(const VECTOR2I &aP, int aIndex)
Definition: pns_line.cpp:898
void RemoveVia()
Definition: pns_line.cpp:1315
int CountCorners(int aAngles) const
Definition: pns_line.cpp:167
void SetRank(int aRank) override
Definition: pns_line.cpp:1107
LINE()
Makes an empty line.
Definition: pns_line.h:67
SHAPE_LINE_CHAIN & Line()
Definition: pns_line.h:137
void DragCorner(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition: pns_line.cpp:785
virtual int Marker() const override
Definition: pns_line.cpp:142
void AppendVia(const VIA &aVia)
Definition: pns_line.cpp:1082
virtual void Unmark(int aMarker=-1) const override
Definition: pns_line.cpp:133
int PointCount() const
Definition: pns_line.h:141
int m_snapThreshhold
Width to smooth out jagged segments.
Definition: pns_line.h:258
SHAPE_LINE_CHAIN m_line
The actual shape of the line.
Definition: pns_line.h:254
void DragSegment(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false)
Definition: pns_line.cpp:799
bool Walkaround(SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN &aPre, SHAPE_LINE_CHAIN &aWalk, SHAPE_LINE_CHAIN &aPost, bool aCw) const
Calculate a line tightly wrapping a convex hull of an obstacle object (aObstacle).
void Reverse()
Clip the line to the nearest obstacle, traversing from the line's start vertex (0).
Definition: pns_line.cpp:1074
void dragCornerFree(const VECTOR2I &aP, int aIndex)
Definition: pns_line.cpp:758
virtual LINE * Clone() const override
Return a deep copy of the item.
Definition: pns_line.cpp:115
int Width() const
Return true if the line is geometrically identical as line aOther.
Definition: pns_line.h:157
void Clear()
Definition: pns_line.cpp:1307
Keep the router "world" - i.e.
Definition: pns_node.h:231
std::optional< OBSTACLE > OPT_OBSTACLE
Definition: pns_node.h:241
OPT_OBSTACLE NearestObstacle(const LINE *aLine, const COLLISION_SEARCH_OPTIONS &aOpts=COLLISION_SEARCH_OPTIONS())
Follow the line in search of an obstacle that is nearest to the starting to the line's starting point...
Definition: pns_node.cpp:286
void SetOwner(const ITEM_OWNER *aOwner)
Set the node that owns this item.
Definition: pns_item.h:77
const ITEM_OWNER * m_owner
Definition: pns_item.h:88
bool BelongsTo(const ITEM_OWNER *aNode) const
Definition: pns_item.h:82
virtual const std::string Format() const override
Definition: pns_line.cpp:1329
const SEG & Seg() const
Definition: pns_segment.h:90
SEGMENT * Clone() const override
Return a deep copy of the item.
Definition: pns_line.cpp:153
const SHAPE_LINE_CHAIN Hull(int aClearance, int aWalkaroundThickness, int aLayer=-1) const override
Definition: pns_line.cpp:569
SHAPE_SEGMENT m_seg
Definition: pns_segment.h:134
const VECTOR2I & Pos() const
Definition: pns_via.h:175
VIA * Clone() const override
Return a deep copy of the item.
Definition: pns_via.cpp:235
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
int LineDistance(const VECTOR2I &aP, bool aDetermineSide=false) const
Return the closest Euclidean distance between point aP and the line defined by the ends of segment (t...
Definition: seg.cpp:428
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:327
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition: seg.cpp:254
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:220
bool Contains(const SEG &aSeg) const
Definition: seg.h:314
bool PointOnEdge(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of the line chain.
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
bool IsPtOnArc(size_t aPtIndex) const
int Width() const
Get the current width of the segments in the chain.
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
int Split(const VECTOR2I &aP, bool aExact=false)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
void Simplify(int aMaxError=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Replace(int aStartIndex, int aEndIndex, const VECTOR2I &aP)
Replace points with indices in range [start_index, end_index] with a single point aP.
bool CompareGeometry(const SHAPE_LINE_CHAIN &aOther) const
void Clear()
Remove all points from the line chain.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex=-1) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
int NearestSegment(const VECTOR2I &aP) const
Find the segment nearest the given point.
int NextShape(int aPointIndex) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
bool IsArcSegment(size_t aSegment) const
void Insert(size_t aVertex, const VECTOR2I &aP)
std::vector< INTERSECTION > INTERSECTIONS
long long int Length() const
Return length of the line chain in Euclidean metric.
int Find(const VECTOR2I &aP, int aThreshold=0) const
Search for point aP.
virtual const std::string Format(bool aCplusPlus=true) const override
Push and Shove diff pair dimensions (gap) settings dialog.
static void extendBox(BOX2I &aBox, bool &aDefined, const VECTOR2I &aP)
Definition: pns_line.cpp:1201
void HullIntersection(const SHAPE_LINE_CHAIN &hull, const SHAPE_LINE_CHAIN &line, SHAPE_LINE_CHAIN::INTERSECTIONS &ips)
Definition: pns_utils.cpp:394
static int areNeighbours(int x, int y, int max=0)
Definition: pns_line.cpp:188
SHAPE_LINE_CHAIN dragCornerInternal(const SHAPE_LINE_CHAIN &aOrigin, const VECTOR2I &aP, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition: pns_line.cpp:623
const SHAPE_LINE_CHAIN SegmentHull(const SHAPE_SEGMENT &aSeg, int aClearance, int aWalkaroundThickness)
Definition: pns_utils.cpp:179
@ MK_LOCKED
Definition: pns_item.h:45
@ OUTSIDE
Text appears outside the dimension line (default)
static std::pair< bool, SHAPE_POLY_SET::VERTEX_INDEX > findVertex(SHAPE_POLY_SET &aPolySet, const EDIT_POINT &aPoint)
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
Represent an intersection between two line segments.
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691