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 int i = 0;
436#ifdef TOM_EXTRA_DEBUG
437 for( VERTEX* &v: vts )
438 {
439 if( v.indexh < 0 && v.type == ON_EDGE )
440 v.type = OUTSIDE; // hack
441
442 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 );
443 }
444#endif
445 // vts[0] = start point
446 VERTEX* v = &vts[0];
447 VERTEX* v_prev = nullptr;
449
450 int iterLimit = 1000;
451
452 // keep scanning the graph until we reach the end point of the path
453 while( v->indexp != ( pnew.PointCount() - 1 ) )
454 {
455 iterLimit--;
456
457 // I'm not 100% sure this algorithm doesn't have bugs that may cause it to freeze,
458 // so here's a temporary iteration limit
459 if( iterLimit == 0 )
460 return false;
461
462 if( v->visited )
463 {
464 // loop found? stop walking
465 break;
466 }
467
468#ifdef TOM_EXTRA_DEBUG
469 printf("---\nvisit ip %d ih %d type %d outs %d neig %d\n", v->indexp, v->indexh, v->type, out.PointCount(), v->neighbours.size() );
470#endif
471 out.Append( v->pos );
472
473 VERTEX* v_next = nullptr;
474
475 if( v->type == OUTSIDE )
476 {
477 // current vertex is outside? first look for any vertex further down the path
478 // that is not inside the hull
479 out.Append( v->pos );
480 VERTEX* v_next_fallback = nullptr;
481
482 for( VERTEX* vn : v->neighbours )
483 {
484 if( areNeighbours( vn->indexp , v->indexp, pnew.PointCount() )
485 && vn->type != INSIDE )
486 {
487 if( !vn->visited )
488 {
489 v_next = vn;
490 break;
491 }
492 else if( vn != v_prev )
493 {
494 v_next_fallback = vn;
495 }
496 }
497 }
498
499 if( !v_next )
500 v_next = v_next_fallback;
501
502 // such a vertex must always be present, if not, bummer.
503 if( !v_next )
504 {
505 #ifdef TOM_EXTRA_DEBUG
506 printf("FAIL VN fallback %p\n", v_next_fallback );
507 #endif
508 return false;
509 }
510 }
511 else if( v->type == ON_EDGE )
512 {
513 // look first for the first vertex outside the hull
514 for( VERTEX* vn : v->neighbours )
515 {
516#ifdef TOM_EXTRA_DEBUG
517 printf( "- OUT scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
518#endif
519
520 if( vn->type == OUTSIDE && !vn->visited )
521 {
522 v_next = vn;
523 break;
524 }
525 }
526
527 // no outside vertices found? continue traversing the hull
528 if( !v_next )
529 {
530 for( VERTEX* vn : v->neighbours )
531 {
532 #ifdef TOM_EXTRA_DEBUG
533 printf("- scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
534 #endif
535 if( vn->type == ON_EDGE && !vn->isHull &&
536 areNeighbours( vn->indexp, v->indexp, pnew.PointCount() ) &&
537 ( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) ) )
538 {
539 v_next = vn;
540 break;
541 }
542 }
543 }
544
545 // still nothing found? try to find the next (index-wise) point on the hull. I guess
546 // we should never reach this part of the code, but who really knows?
547 if( !v_next )
548 {
549#ifdef TOM_EXTRA_DEBUG
550 printf("still no v_next\n");
551#endif
552 for( VERTEX* vn : v->neighbours )
553 {
554 if( vn->type == ON_EDGE )
555 {
556 if( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) )
557 {
558 v_next = vn;
559 break;
560 }
561 }
562 }
563
564 if( v_next )
565 {
566 for( VERTEX &vt : vts )
567 {
568 if( vt.isHull )
569 vt.visited = false;
570 }
571 }
572
573#ifdef TOM_EXTRA_DEBUG
574 printf("v_next %p\n", v_next);
575#endif
576
577 // Did we get the next hull point but the end of the line is inside? Instead of walking
578 // around the hull some more (which will just end up taking us back to the start), lets
579 // just project the normal of the endpoint onto this next segment and call it quits.
580 if( inLast && v_next )
581 {
582 int d = ( v_next->pos - CLastPoint() ).SquaredEuclideanNorm();
583
584 if( d < lastDst )
585 {
586 lastDst = d;
587 }
588 else
589 {
590 VECTOR2I proj = SEG( v->pos, v_next->pos ).NearestPoint( CLastPoint() );
591 out.Append( proj );
592 appendV = false;
593 break;
594 }
595 }
596 }
597 }
598
599 v->visited = true;
600 v_prev = v;
601 v = v_next;
602
603 if( !v )
604 return false;
605 }
606
607 if( appendV )
608 out.Append( v->pos );
609
610 aPath = std::move( out );
611 return true;
612}
613
614
615const SHAPE_LINE_CHAIN SEGMENT::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
616{
617 /*DEBUG_DECORATOR* debugDecorator = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
618
619 PNS_DBG( debugDecorator, Message, wxString::Format( wxT( "seghull %d %d" ), aWalkaroundThickness, aClearance ) );
620 PNS_DBG(debugDecorator, AddShape, &m_seg, RED, 0, wxT("theseg") );
621 */
622
623 return SegmentHull( m_seg, aClearance, aWalkaroundThickness );
624}
625
627{
628 const int IterationLimit = 5;
629 int i;
630 LINE l( *this );
631
632 for( i = 0; i < IterationLimit; i++ )
633 {
634 NODE::OPT_OBSTACLE obs = aNode->NearestObstacle( &l );
635
636 if( obs )
637 {
638 l.RemoveVia();
639 VECTOR2I collisionPoint = obs->m_ipFirst;
640 int segIdx = l.Line().NearestSegment( collisionPoint );
641
642 if( l.Line().IsArcSegment( segIdx ) )
643 {
644 // Don't clip at arcs, start again
645 l.Line().Clear();
646 }
647 else
648 {
649 SEG nearestSegment = l.Line().CSegment( segIdx );
650 VECTOR2I nearestPt = nearestSegment.NearestPoint( collisionPoint );
651 int p = l.Line().Split( nearestPt );
652 l.Line().Remove( p + 1, -1 );
653 }
654 }
655 else
656 {
657 break;
658 }
659 }
660
661 if( i == IterationLimit )
662 l.Line().Clear();
663
664 return l;
665}
666
667
668
669SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECTOR2I& aP, DIRECTION_45 aPreferredEndingDirection = DIRECTION_45() )
670{
671 std::optional<SHAPE_LINE_CHAIN> picked;
672 int i;
673 int d = 2;
674
675 wxASSERT( aOrigin.PointCount() > 0 );
676
677 if( aOrigin.PointCount() == 1 )
678 {
679 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP );
680 }
681 else if( aOrigin.SegmentCount() == 1 )
682 {
683 DIRECTION_45 dir( aOrigin.CPoint( 0 ) - aOrigin.CPoint( 1 ) );
684
685 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
686 }
687
688
689 //if( aOrigin.CSegment( -1 ).Length() > 100000 * 30 ) // fixme: constant/parameter?
690 d = 1;
691
692 for( i = aOrigin.SegmentCount() - d; i >= 0; i-- )
693 {
694 DIRECTION_45 d_start( aOrigin.CSegment( i ) );
695 const VECTOR2I& p_start = aOrigin.CPoint( i );
696 SHAPE_LINE_CHAIN paths[2];
697 DIRECTION_45 dirs[2];
698 DIRECTION_45 d_prev = ( i > 0 ? DIRECTION_45( aOrigin.CSegment( i-1 ) )
699 : DIRECTION_45() );
700 int dirCount = 0;
701
702 for( int j = 0; j < 2; j++ )
703 {
704 paths[j] = d_start.BuildInitialTrace( p_start, aP, j );
705
706 if( paths[j].SegmentCount() < 1 )
707 continue;
708
709 assert( dirCount < int( sizeof( dirs ) / sizeof( dirs[0] ) ) );
710
711 dirs[dirCount] = DIRECTION_45( paths[j].CSegment( 0 ) );
712 ++dirCount;
713 }
714
715 if( aPreferredEndingDirection != DIRECTION_45::UNDEFINED )
716 {
717 for( int j = 0; j < dirCount; j++ )
718 {
719 DIRECTION_45 endingDir( paths[j].CSegment(-1) );
720 if( endingDir == aPreferredEndingDirection )
721 {
722 picked = paths[j];
723 break;
724 }
725 }
726 }
727
728 if( !picked )
729 {
730 for( int j = 0; j < dirCount; j++ )
731 {
732 if( dirs[j] == d_start )
733 {
734 picked = paths[j];
735 break;
736 }
737 }
738 }
739
740 if( picked )
741 break;
742
743 for( int j = 0; j < dirCount; j++ )
744 {
745 if( dirs[j].IsObtuse( d_prev ) )
746 {
747 picked = paths[j];
748 break;
749 }
750 }
751
752 if( picked )
753 break;
754 }
755
756 if( picked )
757 {
758 SHAPE_LINE_CHAIN path = aOrigin.Slice( 0, i );
759 path.Append( *picked );
760
761 return path;
762 }
763
764 DIRECTION_45 dir( aOrigin.CLastPoint() - aOrigin.CPoints()[ aOrigin.PointCount() - 2 ] );
765
766 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
767}
768
769
770void LINE::dragCorner45( const VECTOR2I& aP, int aIndex, DIRECTION_45 aPreferredEndingDirection )
771{
773
774 int width = m_line.Width();
775 VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex );
776
777 if( aIndex == 0 )
778 {
779 path = dragCornerInternal( m_line.Reverse(), snapped, aPreferredEndingDirection ).Reverse();
780 }
781 else if( aIndex == m_line.SegmentCount() )
782 {
783 path = dragCornerInternal( m_line, snapped, aPreferredEndingDirection );
784 }
785 else
786 {
787 // Are we next to an arc? Insert a new point so we slice correctly
788 if( m_line.IsPtOnArc( static_cast<size_t>( aIndex ) + 1 ) )
789 m_line.Insert( aIndex + 1, m_line.CPoint( aIndex + 1 ) );
790
791 // fixme: awkward behaviour for "outwards" drags
792 path = dragCornerInternal( m_line.Slice( 0, aIndex ), snapped, aPreferredEndingDirection );
793 SHAPE_LINE_CHAIN path_rev =
794 dragCornerInternal( m_line.Slice( aIndex, -1 ).Reverse(), snapped, aPreferredEndingDirection ).Reverse();
795 path.Append( path_rev );
796 }
797
798 path.Simplify();
799 path.SetWidth( width );
800 m_line = std::move( path );
801}
802
803
804void LINE::dragCornerFree( const VECTOR2I& aP, int aIndex )
805{
806 ssize_t idx = static_cast<ssize_t>( aIndex );
807 ssize_t numpts = static_cast<ssize_t>( m_line.PointCount() );
808
809 // If we're asked to drag the end of an arc, insert a new vertex to drag instead
810 if( m_line.IsPtOnArc( idx ) )
811 {
812 if( idx == 0 || ( idx > 0 && !m_line.IsPtOnArc( idx - 1 ) ) )
813 {
814 m_line.Insert( idx, m_line.GetPoint( idx ) );
815 }
816 else if( ( idx == numpts - 1 ) || ( idx < numpts - 1 && !m_line.IsArcSegment( idx ) ) )
817 {
818 idx++;
819 m_line.Insert( idx, m_line.GetPoint( idx ) );
820 }
821 else
822 {
823 wxASSERT_MSG( false, wxT( "Attempt to dragCornerFree in the middle of an arc!" ) );
824 }
825 }
826
827 m_line.SetPoint( idx, aP );
828 m_line.Simplify();
829}
830
831void LINE::DragCorner( const VECTOR2I& aP, int aIndex, bool aFreeAngle, DIRECTION_45 aPreferredEndingDirection )
832{
833 wxCHECK_RET( aIndex >= 0, wxT( "Negative index passed to LINE::DragCorner" ) );
834
835 if( aFreeAngle )
836 {
837 dragCornerFree( aP, aIndex );
838 }
839 else
840 {
841 dragCorner45( aP, aIndex, aPreferredEndingDirection );
842 }
843}
844
845void LINE::DragSegment( const VECTOR2I& aP, int aIndex, bool aFreeAngle )
846{
847 if( aFreeAngle )
848 {
849 assert( false );
850 }
851 else
852 {
853 dragSegment45( aP, aIndex );
854 }
855}
856
858 const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const
859{
860 int s_start = std::max( aIndex - 2, 0 );
861 int s_end = std::min( aIndex + 2, aPath.SegmentCount() - 1 );
862
863 int i, j;
864 int best_dist = INT_MAX;
865 VECTOR2I best_snap = aP;
866
867 if( m_snapThreshhold <= 0 )
868 return aP;
869
870 for( i = s_start; i <= s_end; i++ )
871 {
872 const SEG& a = aPath.CSegment( i );
873
874 for( j = s_start; j < i; j++ )
875 {
876 const SEG& b = aPath.CSegment( j );
877
878 if( !( DIRECTION_45( a ).IsObtuse( DIRECTION_45( b ) ) ) )
879 continue;
880
881 OPT_VECTOR2I ip = a.IntersectLines( b );
882
883 if( ip )
884 {
885 int dist = ( *ip - aP ).EuclideanNorm();
886
887 if( dist < m_snapThreshhold && dist < best_dist )
888 {
889 best_dist = dist;
890 best_snap = *ip;
891 }
892 }
893 }
894 }
895
896 return best_snap;
897}
898
900 const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const
901{
902 VECTOR2I snap_p[2];
903 DIRECTION_45 dragDir( aPath.CSegment( aIndex ) );
904 int snap_d[2] = { -1, -1 };
905
906 if( m_snapThreshhold == 0 )
907 return aP;
908
909 if( aIndex >= 2 )
910 {
911 SEG s = aPath.CSegment( aIndex - 2 );
912
913 if( DIRECTION_45( s ) == dragDir )
914 snap_d[0] = s.LineDistance( aP );
915
916 snap_p[0] = s.A;
917 }
918
919 if( aIndex < aPath.SegmentCount() - 2 )
920 {
921 SEG s = aPath.CSegment( aIndex + 2 );
922
923 if( DIRECTION_45( s ) == dragDir )
924 snap_d[1] = s.LineDistance( aP );
925
926 snap_p[1] = s.A;
927 }
928
929 VECTOR2I best = aP;
930 int minDist = INT_MAX;
931
932 for( int i = 0; i < 2; i++ )
933 {
934 if( snap_d[i] >= 0 && snap_d[i] < minDist && snap_d[i] <= m_snapThreshhold )
935 {
936 minDist = snap_d[i];
937 best = snap_p[i];
938 }
939 }
940
941 return best;
942}
943
944void LINE::dragSegment45( const VECTOR2I& aP, int aIndex )
945{
947 VECTOR2I target( aP );
948
949 wxASSERT( aIndex < m_line.PointCount() );
950
951 SEG guideA[2], guideB[2];
952 int index = aIndex;
953
954 target = snapToNeighbourSegments( path, aP, aIndex );
955
956 // We require a valid s_prev and s_next. If we are at the start or end of the line, we insert
957 // a new point at the start or end so there is a zero-length segment for prev or next (we will
958 // resize it as part of the drag operation). If we are next to an arc, we do this also, as we
959 // cannot drag away one of the arc's points.
960
961 if( index == 0 || path.IsPtOnArc( index ) )
962 {
963 path.Insert( index > 0 ? index + 1 : 0, path.CPoint( index ) );
964 index++;
965 }
966
967 if( index == path.SegmentCount() - 1 )
968 {
969 path.Insert( path.PointCount() - 1, path.CLastPoint() );
970 }
971 else if( path.IsPtOnArc( index + 1 ) )
972 {
973 path.Insert( index + 1, path.CPoint( index + 1 ) );
974 }
975
976 SEG dragged = path.CSegment( index );
977 DIRECTION_45 drag_dir( dragged );
978
979 SEG s_prev = path.CSegment( index - 1 );
980 SEG s_next = path.CSegment( index + 1 );
981
982 DIRECTION_45 dir_prev( s_prev );
983 DIRECTION_45 dir_next( s_next );
984
985 if( dir_prev == drag_dir )
986 {
987 dir_prev = dir_prev.Left();
988 path.Insert( index, path.CPoint( index ) );
989 index++;
990 }
991 else if( dir_prev == DIRECTION_45::UNDEFINED )
992 {
993 dir_prev = drag_dir.Left();
994 }
995
996 if( dir_next == drag_dir )
997 {
998 dir_next = dir_next.Right();
999 path.Insert( index + 1, path.CPoint( index + 1 ) );
1000 }
1001 else if( dir_next == DIRECTION_45::UNDEFINED )
1002 {
1003 dir_next = drag_dir.Right();
1004 }
1005
1006 s_prev = path.CSegment( index - 1 );
1007 s_next = path.CSegment( index + 1 );
1008 dragged = path.CSegment( index );
1009
1010 if( aIndex == 0 )
1011 {
1012 guideA[0] = SEG( dragged.A, dragged.A + drag_dir.Right().ToVector() );
1013 guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Left().ToVector() );
1014 }
1015 else
1016 {
1017 if( dir_prev.Angle( drag_dir )
1019 {
1020 guideA[0] = SEG( s_prev.A, s_prev.A + drag_dir.Left().ToVector() );
1021 guideA[1] = SEG( s_prev.A, s_prev.A + drag_dir.Right().ToVector() );
1022 }
1023 else
1024 guideA[0] = guideA[1] = SEG( dragged.A, dragged.A + dir_prev.ToVector() );
1025 }
1026
1027 if( aIndex == m_line.SegmentCount() - 1 )
1028 {
1029 guideB[0] = SEG( dragged.B, dragged.B + drag_dir.Right().ToVector() );
1030 guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Left().ToVector() );
1031 }
1032 else
1033 {
1034 if( dir_next.Angle( drag_dir )
1036 {
1037 guideB[0] = SEG( s_next.B, s_next.B + drag_dir.Left().ToVector() );
1038 guideB[1] = SEG( s_next.B, s_next.B + drag_dir.Right().ToVector() );
1039 }
1040 else
1041 guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + dir_next.ToVector() );
1042 }
1043
1044 SEG s_current( target, target + drag_dir.ToVector() );
1045
1046 int best_len = INT_MAX;
1047 SHAPE_LINE_CHAIN best;
1048
1049 for( int i = 0; i < 2; i++ )
1050 {
1051 for( int j = 0; j < 2; j++ )
1052 {
1053 OPT_VECTOR2I ip1 = s_current.IntersectLines( guideA[i] );
1054 OPT_VECTOR2I ip2 = s_current.IntersectLines( guideB[j] );
1055
1057
1058 if( !ip1 || !ip2 )
1059 continue;
1060
1061 SEG s1( s_prev.A, *ip1 );
1062 SEG s2( *ip1, *ip2 );
1063 SEG s3( *ip2, s_next.B );
1064
1065 OPT_VECTOR2I ip;
1066
1067 if( ( ip = s1.Intersect( s_next ) ) )
1068 {
1069 np.Append( s1.A );
1070 np.Append( *ip );
1071 np.Append( s_next.B );
1072 }
1073 else if( ( ip = s3.Intersect( s_prev ) ) )
1074 {
1075 np.Append( s_prev.A );
1076 np.Append( *ip );
1077 np.Append( s3.B );
1078 }
1079 else if( ( ip = s1.Intersect( s3 ) ) )
1080 {
1081 np.Append( s_prev.A );
1082 np.Append( *ip );
1083 np.Append( s_next.B );
1084 }
1085 else
1086 {
1087 np.Append( s_prev.A );
1088 np.Append( *ip1 );
1089 np.Append( *ip2 );
1090 np.Append( s_next.B );
1091 }
1092
1093 if( np.Length() < best_len )
1094 {
1095 best_len = np.Length();
1096 best = std::move( np );
1097 }
1098 }
1099 }
1100
1101 if( m_line.PointCount() == 1 )
1102 m_line = best;
1103 else if( aIndex == 0 )
1104 m_line.Replace( 0, 1, best );
1105 else if( aIndex == m_line.SegmentCount() - 1 )
1106 m_line.Replace( -2, -1, best );
1107 else
1108 m_line.Replace( aIndex, aIndex + 1, best );
1109
1110 m_line.Simplify();
1111}
1112
1113
1114bool LINE::CompareGeometry( const LINE& aOther )
1115{
1116 return m_line.CompareGeometry( aOther.m_line );
1117}
1118
1119
1121{
1122 m_line = m_line.Reverse();
1123
1124 std::reverse( m_links.begin(), m_links.end() );
1125}
1126
1127
1128void LINE::AppendVia( const VIA& aVia )
1129{
1130 if( m_line.PointCount() > 1 && aVia.Pos() == m_line.CPoint( 0 ) )
1131 {
1132 Reverse();
1133 }
1134
1135 m_via = aVia.Clone();
1136 m_via->SetOwner( this );
1137 m_via->SetNet( m_net );
1138}
1139
1140
1141void LINE::LinkVia( VIA* aVia )
1142{
1143 if( m_line.PointCount() > 1 && aVia->Pos() == m_line.CPoint( 0 ) )
1144 {
1145 Reverse();
1146 }
1147
1148 m_via = aVia;
1149 Link( aVia );
1150}
1151
1152
1153void LINE::SetRank( int aRank )
1154{
1155 m_rank = aRank;
1156
1157 for( auto s : m_links )
1158 s->SetRank( aRank );
1159
1160}
1161
1162
1163int LINE::Rank() const
1164{
1165 int min_rank = INT_MAX;
1166
1167 if( IsLinked() )
1168 {
1169 for( const LINKED_ITEM* item : m_links )
1170 min_rank = std::min( min_rank, item->Rank() );
1171 }
1172 else
1173 {
1174 min_rank = m_rank;
1175 }
1176
1177 int rank = ( min_rank == INT_MAX ) ? -1 : min_rank;
1178
1179 return rank;
1180}
1181
1182
1183void LINE::ClipVertexRange( int aStart, int aEnd )
1184{
1191 int firstLink = 0;
1192 int lastLink = std::max( 0, static_cast<int>( m_links.size() ) - 1 );
1193 int linkIdx = 0;
1194
1195 int numPoints = static_cast<int>( m_line.PointCount() );
1196
1197 for( int i = 0; i >= 0 && i < m_line.PointCount(); i = m_line.NextShape( i ) )
1198 {
1199 if( i <= aStart )
1200 firstLink = linkIdx;
1201
1202 if( i < 0 || i >= aEnd - 1 || linkIdx >= lastLink )
1203 {
1204 lastLink = linkIdx;
1205 break;
1206 }
1207
1208 linkIdx++;
1209 }
1210
1211 wxASSERT( lastLink >= firstLink );
1212
1213 m_line = m_line.Slice( aStart, aEnd );
1214
1215 if( IsLinked() )
1216 {
1217 wxASSERT( m_links.size() < INT_MAX );
1218 wxASSERT( static_cast<int>( m_links.size() ) >= ( lastLink - firstLink ) );
1219
1220 // Note: The range includes aEnd, but we have n-1 segments.
1221 std::rotate(
1222 m_links.begin(),
1223 m_links.begin() + firstLink,
1224 m_links.begin() + lastLink
1225 );
1226
1227 m_links.resize( lastLink - firstLink + 1 );
1228 }
1229}
1230
1231
1232bool LINE::HasLoops() const
1233{
1234 for( int i = 0; i < PointCount(); i++ )
1235 {
1236 for( int j = i + 2; j < PointCount(); j++ )
1237 {
1238 if( CPoint( i ) == CPoint( j ) )
1239 return true;
1240 }
1241 }
1242
1243 return false;
1244}
1245
1246
1247static void extendBox( BOX2I& aBox, bool& aDefined, const VECTOR2I& aP )
1248{
1249 if( aDefined )
1250 {
1251 aBox.Merge( aP );
1252 }
1253 else
1254 {
1255 aBox = BOX2I( aP, VECTOR2I( 0, 0 ) );
1256 aDefined = true;
1257 }
1258}
1259
1260
1261OPT_BOX2I LINE::ChangedArea( const LINE* aOther ) const
1262{
1263 BOX2I area;
1264 bool areaDefined = false;
1265
1266 int i_start = -1;
1267 int i_end_self = -1, i_end_other = -1;
1268
1269 SHAPE_LINE_CHAIN self( m_line );
1270 self.Simplify();
1271 SHAPE_LINE_CHAIN other( aOther->m_line );
1272 other.Simplify();
1273
1274 int np_self = self.PointCount();
1275 int np_other = other.PointCount();
1276
1277 int n = std::min( np_self, np_other );
1278
1279 for( int i = 0; i < n; i++ )
1280 {
1281 const VECTOR2I p1 = self.CPoint( i );
1282 const VECTOR2I p2 = other.CPoint( i );
1283
1284 if( p1 != p2 )
1285 {
1286 if( i != n - 1 )
1287 {
1288 SEG s = self.CSegment( i );
1289
1290 if( !s.Contains( p2 ) )
1291 {
1292 i_start = i;
1293 break;
1294 }
1295 }
1296 else
1297 {
1298 i_start = i;
1299 break;
1300 }
1301 }
1302 }
1303
1304 for( int i = 0; i < n; i++ )
1305 {
1306 const VECTOR2I p1 = self.CPoint( np_self - 1 - i );
1307 const VECTOR2I p2 = other.CPoint( np_other - 1 - i );
1308
1309 if( p1 != p2 )
1310 {
1311 i_end_self = np_self - 1 - i;
1312 i_end_other = np_other - 1 - i;
1313 break;
1314 }
1315 }
1316
1317 if( i_start < 0 )
1318 i_start = n;
1319
1320 if( i_end_self < 0 )
1321 i_end_self = np_self - 1;
1322
1323 if( i_end_other < 0 )
1324 i_end_other = np_other - 1;
1325
1326 for( int i = i_start; i <= i_end_self; i++ )
1327 extendBox( area, areaDefined, self.CPoint( i ) );
1328
1329 for( int i = i_start; i <= i_end_other; i++ )
1330 extendBox( area, areaDefined, other.CPoint( i ) );
1331
1332 if( areaDefined )
1333 {
1334 area.Inflate( std::max( Width(), aOther->Width() ) );
1335 return area;
1336 }
1337
1338 return OPT_BOX2I();
1339}
1340
1341
1343{
1344 for( const auto seg : m_links )
1345 {
1346 if( seg->Marker() & MK_LOCKED )
1347 return true;
1348 }
1349 return false;
1350}
1351
1352
1354{
1355 ClearLinks();
1356 RemoveVia();
1357 m_line.Clear();
1358}
1359
1360
1362{
1363 if( m_via )
1364 {
1365 if( ContainsLink( m_via ) )
1366 Unlink( m_via );
1367 if( m_via->BelongsTo( this ) )
1368 delete m_via;
1369 }
1370
1371 m_via = nullptr;
1372}
1373
1374
1375const std::string SEGMENT::Format( ) const
1376{
1377 std::stringstream ss;
1378 ss << ITEM::Format() << " ";
1379 ss << m_seg.Format( false );
1380 return ss.str();
1381}
1382
1383
1384int LINE::FindSegment( const SEGMENT* aSeg ) const
1385{
1386 for( int i = 0; i < m_line.SegmentCount(); i++)
1387 {
1388 const SEG&s = m_line.CSegment(i);
1389 if( s == aSeg->Seg() )
1390 return i;
1391 }
1392
1393 return -1;
1394}
1395
1396}
1397
1398
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:338
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:899
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:770
const LINE ClipToNearestObstacle(NODE *aNode) const
Clip the line to a given range of vertices.
Definition pns_line.cpp:626
VIA * m_via
Definition pns_line.h:265
virtual void Mark(int aMarker) const override
Definition pns_line.cpp:170
int m_width
Our width.
Definition pns_line.h:260
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:266
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:857
LINE & operator=(const LINE &aOther)
Definition pns_line.cpp:79
void dragSegment45(const VECTOR2I &aP, int aIndex)
Definition pns_line.cpp:944
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:831
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:263
SHAPE_LINE_CHAIN m_line
The actual shape of the line.
Definition pns_line.h:259
void DragSegment(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false)
Definition pns_line.cpp:845
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:804
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:232
std::optional< OBSTACLE > OPT_OBSTACLE
Definition pns_node.h:242
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
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:615
SHAPE_SEGMENT m_seg
const VECTOR2I & Pos() const
Definition pns_via.h:184
VIA * Clone() const override
Return a deep copy of the item.
Definition pns_via.cpp:244
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:717
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:604
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:437
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:669
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
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695