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