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
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <optional>
23#include <core/typeinfo.h>
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#include <geometry/circle.h>
36#include <trigo.h>
37#include <advanced_config.h>
38#include <base_units.h>
39
40namespace PNS {
41
42LINE::LINE( const LINE& aOther ) :
43 LINK_HOLDER( aOther ),
44 m_line( aOther.m_line ),
45 m_width( aOther.m_width ),
47{
48 m_net = aOther.m_net;
49 m_movable = aOther.m_movable;
50 m_layers = aOther.m_layers;
51
52 m_via = nullptr;
53 m_ownsVia = false;
54 copyVia( aOther );
55
56 m_marker = aOther.m_marker;
57 m_rank = aOther.m_rank;
59
60 copyLinks( &aOther );
61}
62
63
65{
66 dropVia();
67}
68
69
70void LINE::copyVia( const LINE& aOther )
71{
72 if( !aOther.m_via )
73 return;
74
75 if( aOther.m_ownsVia )
76 {
77 m_via = aOther.m_via->Clone();
78 m_via->SetOwner( this );
79 m_via->SetNet( m_net );
80 m_ownsVia = true;
81 }
82 else
83 {
84 m_via = aOther.m_via;
85 }
86}
87
88
90{
91 // A borrowed via may already be freed, so only the flag says whose it is
92 if( m_ownsVia )
93 delete m_via;
94
95 m_via = nullptr;
96 m_ownsVia = false;
97}
98
99
100LINE& LINE::operator=( const LINE& aOther )
101{
102 if( this == &aOther )
103 return *this;
104
105 m_parent = aOther.m_parent;
106 m_sourceItem = aOther.m_sourceItem;
107
108 m_line = aOther.m_line;
109 m_width = aOther.m_width;
110 m_net = aOther.m_net;
111 m_movable = aOther.m_movable;
112 m_layers = aOther.m_layers;
113
114 dropVia();
115 copyVia( aOther );
116
117 m_marker = aOther.m_marker;
118 m_rank = aOther.m_rank;
119 m_routable = aOther.m_routable;
120 m_owner = aOther.m_owner;
123
124 copyLinks( &aOther );
125
126 return *this;
127}
128
129
130LINE& LINE::operator=( LINE&& aOther ) noexcept
131{
132 if (this != &aOther)
133 {
134 m_parent = aOther.m_parent;
135 m_sourceItem = aOther.m_sourceItem;
136
137 m_line = std::move( aOther.m_line );
138 m_width = aOther.m_width;
139 m_net = aOther.m_net;
140 m_movable = aOther.m_movable;
141 m_layers = aOther.m_layers;
142
143 dropVia();
144 copyVia( aOther );
145
146 m_marker = aOther.m_marker;
147 m_rank = aOther.m_rank;
148 m_routable = aOther.m_routable;
149 m_owner = aOther.m_owner;
150 m_snapThreshhold = aOther.m_snapThreshhold;
151 m_blockingObstacle = aOther.m_blockingObstacle;
152
153 m_links = std::move( aOther.m_links );
154 }
155
156 return *this;
157}
158
159
161{
162 LINE* l = new LINE( *this );
163
164 return l;
165}
166
167
168void LINE::Mark( int aMarker ) const
169{
170 m_marker = aMarker;
171
172 for( const LINKED_ITEM* s : m_links )
173 s->Mark( aMarker );
174
175}
176
177
178void LINE::Unmark( int aMarker ) const
179{
180 for( const LINKED_ITEM* s : m_links )
181 s->Unmark( aMarker );
182
183 m_marker = 0;
184}
185
186
187int LINE::Marker() const
188{
189 int marker = m_marker;
190
191 for( LINKED_ITEM* s : m_links )
192 marker |= s->Marker();
193
194 return marker;
195}
196
197
199{
200 SEGMENT* s = new SEGMENT( *this );
201
202 s->m_seg = m_seg;
203 s->m_net = m_net;
204 s->m_layers = m_layers;
205 s->m_marker = m_marker;
206 s->m_rank = m_rank;
207
208 return s;
209}
210
211
212int LINE::CountCorners( int aAngles ) const
213{
214 int count = 0;
215
216 for( int i = 0; i < m_line.SegmentCount() - 1; i++ )
217 {
218 const SEG seg1 = m_line.CSegment( i );
219 const SEG seg2 = m_line.CSegment( i + 1 );
220
221 const DIRECTION_45 dir1( seg1 );
222 const DIRECTION_45 dir2( seg2 );
223
224 DIRECTION_45::AngleType a = dir1.Angle( dir2 );
225
226 if( a & aAngles )
227 count++;
228 }
229
230 return count;
231}
232
233static int areNeighbours( int x, int y, int max = 0 )
234{
235 if( x > 0 && x - 1 == y )
236 return true;
237
238 if( x < max - 1 && x + 1 == y )
239 return true;
240
241 return false;
242}
243
244#ifdef TOM_EXTRA_DEBUG
245SHAPE_LINE_CHAIN g_pnew, g_hnew;
246#endif
247
248
250{
251 if( aOriginal.ArcCount() == 0 )
252 return;
253
254 const int origCount = aOriginal.PointCount();
255 const int pathCount = aPath.PointCount();
256
257 int head = 0;
258
259 while( head < origCount && head < pathCount
260 && aOriginal.CPoint( head ) == aPath.CPoint( head ) )
261 {
262 head++;
263 }
264
265 int tail = 0;
266
267 while( tail < origCount - head && tail < pathCount - head
268 && aOriginal.CPoint( origCount - 1 - tail ) == aPath.CPoint( pathCount - 1 - tail ) )
269 {
270 tail++;
271 }
272
273 if( head == 0 && tail == 0 )
274 return;
275
276 SHAPE_LINE_CHAIN rebuilt;
277
278 if( head > 0 )
279 rebuilt = aOriginal.Slice( 0, head - 1 );
280
281 if( head + tail < pathCount )
282 rebuilt.Append( aPath.Slice( std::max( head - 1, 0 ), pathCount - tail - 1 ) );
283
284 if( tail > 0 )
285 rebuilt.Append( aOriginal.Slice( origCount - tail, origCount - 1 ) );
286
287 aPath = std::move( rebuilt );
288}
289
290
291bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPath, bool aCw ) const
292{
293 const SHAPE_LINE_CHAIN& line( CLine() );
294
295 if( line.SegmentCount() < 1 )
296 {
297 return false;
298 }
299
300 const VECTOR2I pFirst = line.CPoint(0);
301
302 bool inFirst = aObstacle.PointInside( pFirst ) && !aObstacle.PointOnEdge( pFirst );
303
304 // We can't really walk around if the beginning of the path lies inside the obstacle hull.
305 // Double check if it's not on the hull itself as this triggers many unroutable corner cases.
306 if( inFirst )
307 {
308 return false;
309 }
310
311 enum VERTEX_TYPE { INSIDE = 0, OUTSIDE, ON_EDGE };
312
313 // Represents an entry in directed graph of hull/path vertices. Scanning this graph
314 // starting from the path's first point results (if possible) with a correct walkaround path
315 struct VERTEX
316 {
317 // vertex classification (inside/outside/exactly on the hull)
318 VERTEX_TYPE type;
319 // true = vertex coming from the hull primitive
320 bool isHull;
321 // position
322 VECTOR2I pos;
323 // list of neighboring vertices
324 std::vector<VERTEX*> neighbours;
325 // index of this vertex in path (pnew)
326 int indexp = -1;
327 // index of this vertex in the hull (hnew)
328 int indexh = -1;
329 // visited indicator (for BFS search)
330 bool visited = false;
331 };
332
333 std::vector<SHAPE_LINE_CHAIN::INTERSECTION> ips;
334
335 HullIntersection( aObstacle, line, ips );
336
337 SHAPE_LINE_CHAIN pnew( CLine() ), hnew( aObstacle );
338
339 std::vector<VERTEX> vts;
340
341 auto findVertex =
342 [&]( const VECTOR2I& pos ) -> VERTEX*
343 {
344 for( VERTEX& v : vts )
345 {
346 if( v.pos == pos )
347 return &v;
348 }
349
350 return nullptr;
351 };
352
353 // corner case for loopy tracks: insert the end loop point back into the hull
354 if( const std::optional<SHAPE_LINE_CHAIN::INTERSECTION> isect = pnew.SelfIntersecting() )
355 {
356 if( isect->p != pnew.CLastPoint() )
357 pnew.Split( isect->p );
358 }
359
360 // insert all intersections found into the new hull/path SLCs
361 for( SHAPE_LINE_CHAIN::INTERSECTION& ip : ips )
362 {
363 if( pnew.Find( ip.p, 1 ) < 0)
364 pnew.Split(ip.p);
365
366 if( hnew.Find( ip.p, 1 ) < 0 )
367 hnew.Split(ip.p);
368 }
369
370 for( int i = 0; i < pnew.PointCount(); i++ )
371 {
372 const VECTOR2I& p = pnew.CPoint( i );
373 bool onEdge = hnew.PointOnEdge( p );
374
375 if ( !onEdge )
376 continue;
377
378 int idx = hnew.Find( p );
379
380 if(idx < 0 )
381 hnew.Split( p );
382 }
383
384 #ifdef TOM_EXTRA_DEBUG
385 for( auto& ip : ips )
386 {
387 printf("Chk: %d %d\n", pnew.Find( ip.p ), hnew.Find(ip.p) );
388 }
389 #endif
390
391 // we assume the default orientation of the hulls is clockwise, so just reverse the vertex
392 // order if the caller wants a counter-clockwise walkaround
393 if ( !aCw )
394 hnew = hnew.Reverse();
395
396 vts.reserve( 2 * ( hnew.PointCount() + pnew.PointCount() ) );
397
398 // create a graph of hull/path vertices and classify them (inside/on edge/outside the hull)
399 for( int i = 0; i < pnew.PointCount(); i++ )
400 {
401 const VECTOR2I& p = pnew.CPoint(i);
402 bool onEdge = hnew.PointOnEdge( p );
403 bool inside = hnew.PointInside( p );
404
405 #ifdef TOM_EXTRA_DEBUG
406 printf("pnew %d inside %d onedge %d\n", i, !!inside, !!onEdge );
407 #endif
408
409 VERTEX v;
410
411 v.indexp = i;
412 v.isHull = false;
413 v.pos = p;
414 v.type = inside && !onEdge ? INSIDE : onEdge ? ON_EDGE : OUTSIDE;
415 vts.push_back( v );
416 }
417
418 #ifdef TOM_EXTRA_DEBUG
419 g_pnew = pnew;
420 g_hnew = hnew;
421 #endif
422
423 // each path vertex neighbour list points for sure to the next vertex in the path
424 for( int i = 0; i < pnew.PointCount() - 1; i++ )
425 {
426 vts[i].neighbours.push_back( &vts[ i+1 ] );
427 }
428
429 // each path vertex neighbour list points for sure to the next vertex in the path
430 for( int i = 1; i < pnew.PointCount() ; i++ )
431 {
432 vts[i].neighbours.push_back( &vts[ i-1 ] );
433 }
434
435 // insert hull vertices into the graph
436 for( int i = 0; i < hnew.PointCount(); i++ )
437 {
438 const VECTOR2I& hp = hnew.CPoint( i );
439 VERTEX* vn = findVertex( hp );
440
441 // if vertex already present (it's very likely that in recursive shoving hull and path vertices will overlap)
442 // just mark it as a path vertex that also belongs to the hull
443 if( vn )
444 {
445 vn->isHull = true;
446 vn->indexh = i;
447 }
448 else // new hull vertex
449 {
450 VERTEX v;
451 v.pos = hp;
452 v.type = ON_EDGE;
453 v.indexh = i;
454 v.isHull = true;
455 vts.push_back( v );
456 }
457 }
458
459 // go around the hull and fix up the neighbour link lists
460 for( int i = 0; i < hnew.PointCount(); i++ )
461 {
462 VERTEX* vc = findVertex( hnew.CPoint( i ) );
463 VERTEX* vnext = findVertex( hnew.CPoint( i+1 ) );
464
465 if( vc && vnext )
466 vc->neighbours.push_back( vnext );
467 }
468
469 // In the case that the initial path ends *inside* the current obstacle (i.e. the mouse cursor
470 // is somewhere inside the hull for the current obstacle) we want to end the walkaround at the
471 // point closest to the cursor
472 bool inLast = aObstacle.PointInside( CLastPoint() ) && !aObstacle.PointOnEdge( CLastPoint() );
473 bool appendV = true;
474 int lastDst = INT_MAX;
475
476#ifdef TOM_EXTRA_DEBUG
477 int i = 0;
478
479 for( VERTEX* &v: vts )
480 {
481 if( v.indexh < 0 && v.type == ON_EDGE )
482 v.type = OUTSIDE; // hack
483
484 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 );
485 }
486#endif
487 // vts[0] = start point
488 VERTEX* v = &vts[0];
489 VERTEX* v_prev = nullptr;
491
492 int iterLimit = 1000;
493
494 // keep scanning the graph until we reach the end point of the path
495 while( v->indexp != ( pnew.PointCount() - 1 ) )
496 {
497 iterLimit--;
498
499 // I'm not 100% sure this algorithm doesn't have bugs that may cause it to freeze,
500 // so here's a temporary iteration limit
501 if( iterLimit == 0 )
502 return false;
503
504 if( v->visited )
505 {
506 // loop found? stop walking
507 break;
508 }
509
510#ifdef TOM_EXTRA_DEBUG
511 printf("---\nvisit ip %d ih %d type %d outs %d neig %d\n", v->indexp, v->indexh, v->type, out.PointCount(), v->neighbours.size() );
512#endif
513 out.Append( v->pos );
514
515 VERTEX* v_next = nullptr;
516
517 if( v->type == OUTSIDE )
518 {
519 // current vertex is outside? first look for any vertex further down the path
520 // that is not inside the hull
521 out.Append( v->pos );
522 VERTEX* v_next_fallback = nullptr;
523
524 for( VERTEX* vn : v->neighbours )
525 {
526 if( areNeighbours( vn->indexp , v->indexp, pnew.PointCount() )
527 && vn->type != INSIDE )
528 {
529 if( !vn->visited )
530 {
531 v_next = vn;
532 break;
533 }
534 else if( vn != v_prev )
535 {
536 v_next_fallback = vn;
537 }
538 }
539 }
540
541 if( !v_next )
542 v_next = v_next_fallback;
543
544 // such a vertex must always be present, if not, bummer.
545 if( !v_next )
546 {
547 #ifdef TOM_EXTRA_DEBUG
548 printf("FAIL VN fallback %p\n", v_next_fallback );
549 #endif
550 return false;
551 }
552 }
553 else if( v->type == ON_EDGE )
554 {
555 // look first for the first vertex outside the hull
556 for( VERTEX* vn : v->neighbours )
557 {
558#ifdef TOM_EXTRA_DEBUG
559 printf( "- OUT scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
560#endif
561
562 if( vn->type == OUTSIDE && !vn->visited )
563 {
564 v_next = vn;
565 break;
566 }
567 }
568
569 // no outside vertices found? continue traversing the hull
570 if( !v_next )
571 {
572 for( VERTEX* vn : v->neighbours )
573 {
574 #ifdef TOM_EXTRA_DEBUG
575 printf("- scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
576 #endif
577 if( vn->type == ON_EDGE && !vn->isHull &&
578 areNeighbours( vn->indexp, v->indexp, pnew.PointCount() ) &&
579 ( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) ) )
580 {
581 v_next = vn;
582 break;
583 }
584 }
585 }
586
587 // still nothing found? try to find the next (index-wise) point on the hull. I guess
588 // we should never reach this part of the code, but who really knows?
589 if( !v_next )
590 {
591#ifdef TOM_EXTRA_DEBUG
592 printf("still no v_next\n");
593#endif
594 for( VERTEX* vn : v->neighbours )
595 {
596 if( vn->type == ON_EDGE )
597 {
598 if( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) )
599 {
600 v_next = vn;
601 break;
602 }
603 }
604 }
605
606 if( v_next )
607 {
608 for( VERTEX &vt : vts )
609 {
610 if( vt.isHull )
611 vt.visited = false;
612 }
613 }
614
615#ifdef TOM_EXTRA_DEBUG
616 printf("v_next %p\n", v_next);
617#endif
618
619 // Did we get the next hull point but the end of the line is inside? Instead of walking
620 // around the hull some more (which will just end up taking us back to the start), lets
621 // just project the normal of the endpoint onto this next segment and call it quits.
622 if( inLast && v_next )
623 {
624 int d = ( v_next->pos - CLastPoint() ).SquaredEuclideanNorm();
625
626 if( d < lastDst )
627 {
628 lastDst = d;
629 }
630 else
631 {
632 VECTOR2I proj = SEG( v->pos, v_next->pos ).NearestPoint( CLastPoint() );
633 out.Append( proj );
634 appendV = false;
635 break;
636 }
637 }
638 }
639 }
640
641 v->visited = true;
642 v_prev = v;
643 v = v_next;
644
645 if( !v )
646 return false;
647 }
648
649 if( appendV )
650 out.Append( v->pos );
651
652 // Vertices outside the hull are emitted twice; the duplicates have to go before the point
653 // runs can be matched against the original
654 out.Simplify2( false );
655 restoreUntouchedArcs( out, pnew );
656
657 aPath = std::move( out );
658 return true;
659}
660
661
662const SHAPE_LINE_CHAIN SEGMENT::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
663{
664 /*DEBUG_DECORATOR* debugDecorator = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
665
666 PNS_DBG( debugDecorator, Message, wxString::Format( wxT( "seghull %d %d" ), aWalkaroundThickness, aClearance ) );
667 PNS_DBG(debugDecorator, AddShape, &m_seg, RED, 0, wxT("theseg") );
668 */
669
670 return SegmentHull( m_seg, aClearance, aWalkaroundThickness );
671}
672
674{
675 const int IterationLimit = 5;
676 int i;
677 LINE l( *this );
678
679 for( i = 0; i < IterationLimit; i++ )
680 {
681 NODE::OPT_OBSTACLE obs = aNode->NearestObstacle( &l );
682
683 if( obs )
684 {
685 l.RemoveVia();
686 VECTOR2I collisionPoint = obs->m_ipFirst;
687 int segIdx = l.Line().NearestSegment( collisionPoint );
688
689 if( l.Line().IsArcSegment( segIdx ) )
690 {
691 // Don't clip at arcs, start again
692 l.Line().Clear();
693 }
694 else
695 {
696 SEG nearestSegment = l.Line().CSegment( segIdx );
697 VECTOR2I nearestPt = nearestSegment.NearestPoint( collisionPoint );
698 int p = l.Line().Split( nearestPt );
699 l.Line().Remove( p + 1, -1 );
700 }
701 }
702 else
703 {
704 break;
705 }
706 }
707
708 if( i == IterationLimit )
709 l.Line().Clear();
710
711 return l;
712}
713
714
715
716SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECTOR2I& aP, DIRECTION_45 aPreferredEndingDirection = DIRECTION_45() )
717{
718 std::optional<SHAPE_LINE_CHAIN> picked;
719 int i;
720 int d = 2;
721
722 wxASSERT( aOrigin.PointCount() > 0 );
723
724 if( aOrigin.PointCount() == 1 )
725 {
726 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP );
727 }
728 else if( aOrigin.SegmentCount() == 1 )
729 {
730 DIRECTION_45 dir( aOrigin.CPoint( 0 ) - aOrigin.CPoint( 1 ) );
731
732 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
733 }
734
735
736 //if( aOrigin.CSegment( -1 ).Length() > 100000 * 30 ) // fixme: constant/parameter?
737 d = 1;
738
739 for( i = aOrigin.SegmentCount() - d; i >= 0; i-- )
740 {
741 DIRECTION_45 d_start( aOrigin.CSegment( i ) );
742 const VECTOR2I& p_start = aOrigin.CPoint( i );
743 SHAPE_LINE_CHAIN paths[2];
744 DIRECTION_45 dirs[2];
745 DIRECTION_45 d_prev = ( i > 0 ? DIRECTION_45( aOrigin.CSegment( i-1 ) )
746 : DIRECTION_45() );
747 int dirCount = 0;
748
749 for( int j = 0; j < 2; j++ )
750 {
751 paths[j] = d_start.BuildInitialTrace( p_start, aP, j );
752
753 if( paths[j].SegmentCount() < 1 )
754 continue;
755
756 assert( dirCount < int( sizeof( dirs ) / sizeof( dirs[0] ) ) );
757
758 dirs[dirCount] = DIRECTION_45( paths[j].CSegment( 0 ) );
759 ++dirCount;
760 }
761
762 if( aPreferredEndingDirection != DIRECTION_45::UNDEFINED )
763 {
764 for( int j = 0; j < dirCount; j++ )
765 {
766 DIRECTION_45 endingDir( paths[j].CSegment(-1) );
767 if( endingDir == aPreferredEndingDirection )
768 {
769 picked = paths[j];
770 break;
771 }
772 }
773 }
774
775 if( !picked )
776 {
777 for( int j = 0; j < dirCount; j++ )
778 {
779 if( dirs[j] == d_start )
780 {
781 picked = paths[j];
782 break;
783 }
784 }
785 }
786
787 if( picked )
788 break;
789
790 for( int j = 0; j < dirCount; j++ )
791 {
792 if( dirs[j].IsObtuse( d_prev ) )
793 {
794 picked = paths[j];
795 break;
796 }
797 }
798
799 if( picked )
800 break;
801 }
802
803 if( picked )
804 {
805 SHAPE_LINE_CHAIN path = aOrigin.Slice( 0, i );
806 path.Append( *picked );
807
808 return path;
809 }
810
811 DIRECTION_45 dir( aOrigin.CLastPoint() - aOrigin.CPoints()[ aOrigin.PointCount() - 2 ] );
812
813 return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
814}
815
816
817void LINE::dragCorner45( const VECTOR2I& aP, int aIndex, DIRECTION_45 aPreferredEndingDirection )
818{
820
821 int width = m_line.Width();
822 VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex );
823
824 if( aIndex == 0 )
825 {
826 path = dragCornerInternal( m_line.Reverse(), snapped, aPreferredEndingDirection ).Reverse();
827 }
828 else if( aIndex == m_line.SegmentCount() )
829 {
830 path = dragCornerInternal( m_line, snapped, aPreferredEndingDirection );
831 }
832 else
833 {
834 // Are we next to an arc? Insert a new point so we slice correctly
835 if( m_line.IsPtOnArc( static_cast<size_t>( aIndex ) + 1 ) )
836 m_line.Insert( aIndex + 1, m_line.CPoint( aIndex + 1 ) );
837
838 // fixme: awkward behaviour for "outwards" drags
839 path = dragCornerInternal( m_line.Slice( 0, aIndex ), snapped, aPreferredEndingDirection );
840 SHAPE_LINE_CHAIN path_rev =
841 dragCornerInternal( m_line.Slice( aIndex, -1 ).Reverse(), snapped, aPreferredEndingDirection ).Reverse();
842 path.Append( path_rev );
843 }
844
845 path.Simplify();
846 path.SetWidth( width );
847 m_line = std::move( path );
848}
849
850
851void LINE::dragCornerFree( const VECTOR2I& aP, int aIndex )
852{
853 ssize_t idx = static_cast<ssize_t>( aIndex );
854 ssize_t numpts = static_cast<ssize_t>( m_line.PointCount() );
855
856 // If we're asked to drag the end of an arc, insert a new vertex to drag instead
857 if( m_line.IsPtOnArc( idx ) )
858 {
859 if( idx == 0 || ( idx > 0 && !m_line.IsPtOnArc( idx - 1 ) ) )
860 {
861 m_line.Insert( idx, m_line.GetPoint( idx ) );
862 }
863 else if( ( idx == numpts - 1 ) || ( idx < numpts - 1 && !m_line.IsArcSegment( idx ) ) )
864 {
865 idx++;
866 m_line.Insert( idx, m_line.GetPoint( idx ) );
867 }
868 else
869 {
870 wxASSERT_MSG( false, wxT( "Attempt to dragCornerFree in the middle of an arc!" ) );
871 }
872 }
873
874 m_line.SetPoint( idx, aP );
875 m_line.Simplify();
876}
877
878void LINE::DragCorner( const VECTOR2I& aP, int aIndex, bool aFreeAngle, DIRECTION_45 aPreferredEndingDirection )
879{
880 wxCHECK_RET( aIndex >= 0, wxT( "Negative index passed to LINE::DragCorner" ) );
881
882 if( aFreeAngle )
883 {
884 dragCornerFree( aP, aIndex );
885 }
886 else
887 {
888 dragCorner45( aP, aIndex, aPreferredEndingDirection );
889 }
890}
891
892void LINE::DragSegment( const VECTOR2I& aP, int aIndex, bool aFreeAngle )
893{
894 if( aFreeAngle )
895 {
896 assert( false );
897 }
898 else
899 {
900 dragSegment45( aP, aIndex );
901 }
902}
903
904
905void LINE::DragArc( const VECTOR2I& aP, int aIndex )
906{
907 if( aIndex < 0 || aIndex >= m_line.PointCount() )
908 return;
909
910 ssize_t arcIdx = m_line.ArcIndex( aIndex );
911
912 if( arcIdx < 0 )
913 return;
914
915 int firstArcPt = -1;
916 int lastArcPt = -1;
917
918 for( int i = 0; i < m_line.PointCount(); i++ )
919 {
920 if( m_line.ArcIndex( i ) == arcIdx )
921 {
922 if( firstArcPt < 0 )
923 firstArcPt = i;
924
925 lastArcPt = i;
926 }
927 }
928
929 if( firstArcPt < 0 || lastArcPt < 0 )
930 return;
931
932 const SHAPE_ARC& oldArc = m_line.CArcs()[arcIdx];
933 int width = oldArc.GetWidth();
934
935 auto tangentLineAtArcEndpoint = [&]( const VECTOR2I& aEndpoint ) -> SEG
936 {
937 VECTOR2I center = oldArc.GetCenter();
938 VECTOR2I radial = aEndpoint - center;
939 VECTOR2I perp( -radial.y, radial.x );
940 return SEG( aEndpoint - perp, aEndpoint + perp );
941 };
942
943 auto isCollinearTo = [&]( const SEG& aA, const SEG& aB, double aMaxDeviationDeg ) -> bool
944 {
945 VECTOR2D dirA( aA.B - aA.A );
946 VECTOR2D dirB( aB.B - aB.A );
947 double magA = dirA.EuclideanNorm();
948 double magB = dirB.EuclideanNorm();
949
950 if( magA <= 0 || magB <= 0 )
951 return false;
952
953 double crossMag = std::abs( dirA.x * dirB.y - dirA.y * dirB.x );
954 double sinAngle = crossMag / ( magA * magB );
955 double angleDeg = std::asin( std::clamp( sinAngle, 0.0, 1.0 ) ) * 180.0 / M_PI;
956
957 return angleDeg <= aMaxDeviationDeg;
958 };
959
960 double maxDeviation = ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation;
961 SEG arcLineStart = tangentLineAtArcEndpoint( oldArc.GetP0() );
962 SEG arcLineEnd = tangentLineAtArcEndpoint( oldArc.GetP1() );
963
964 bool useChainStart = false;
965 bool useChainEnd = false;
966
967 if( firstArcPt > 0 )
968 {
969 SEG candidate( m_line.CPoint( firstArcPt - 1 ), m_line.CPoint( firstArcPt ) );
970
971 if( isCollinearTo( candidate, arcLineStart, maxDeviation ) )
972 useChainStart = true;
973 }
974
975 if( lastArcPt < m_line.PointCount() - 1 )
976 {
977 SEG candidate( m_line.CPoint( lastArcPt ), m_line.CPoint( lastArcPt + 1 ) );
978
979 if( isCollinearTo( candidate, arcLineEnd, maxDeviation ) )
980 useChainEnd = true;
981 }
982
983 OPT_VECTOR2I arcOwnTanIntersect = arcLineStart.IntersectLines( arcLineEnd );
984
985 SEG tanStartSeg, tanEndSeg;
986
987 if( useChainStart )
988 {
989 tanStartSeg = SEG( m_line.CPoint( firstArcPt - 1 ), m_line.CPoint( firstArcPt ) );
990 }
991 else
992 {
993 if( !arcOwnTanIntersect )
994 return;
995
996 tanStartSeg = SEG( *arcOwnTanIntersect, oldArc.GetP0() );
997 }
998
999 if( useChainEnd )
1000 {
1001 tanEndSeg = SEG( m_line.CPoint( lastArcPt ), m_line.CPoint( lastArcPt + 1 ) );
1002 }
1003 else
1004 {
1005 if( !arcOwnTanIntersect )
1006 return;
1007
1008 tanEndSeg = SEG( *arcOwnTanIntersect, oldArc.GetP1() );
1009 }
1010
1011 OPT_VECTOR2I tanIntersect = tanStartSeg.IntersectLines( tanEndSeg );
1012
1013 if( !tanIntersect )
1014 return; // parallel tangents have no tangent-circle solution
1015
1016 // Reorient tangent segments so they emanate from the intersection point, so the
1017 // constraint math below operates on the (intersect, arc-endpoint) directed segments.
1018 SEG tanStartFromIntersect = SEG( *tanIntersect, oldArc.GetP0() );
1019 SEG tanEndFromIntersect = SEG( *tanIntersect, oldArc.GetP1() );
1020
1021 auto furthestFromIntersect = [&]( const VECTOR2I& aA, const VECTOR2I& aB ) -> VECTOR2I
1022 {
1023 return ( aA - *tanIntersect ).EuclideanNorm() > ( aB - *tanIntersect ).EuclideanNorm() ? aA : aB;
1024 };
1025
1026 VECTOR2I tanStartFar = furthestFromIntersect( tanStartSeg.A, tanStartSeg.B );
1027 VECTOR2I tanEndFar = furthestFromIntersect( tanEndSeg.A, tanEndSeg.B );
1028 VECTOR2I tempTangentPoint = furthestFromIntersect( tanStartFar, tanEndFar ) == tanEndFar ? tanStartFar : tanEndFar;
1029
1030 CIRCLE maxTanCircle;
1031 maxTanCircle.ConstructFromTanTanPt( tanStartFromIntersect, tanEndFromIntersect, tempTangentPoint );
1032
1033 VECTOR2I maxTanPtStart = tanStartFromIntersect.LineProject( maxTanCircle.Center );
1034 VECTOR2I maxTanPtEnd = tanEndFromIntersect.LineProject( maxTanCircle.Center );
1035
1036 SEG cSegTanStart( maxTanPtStart, *tanIntersect );
1037 SEG cSegTanEnd( maxTanPtEnd, *tanIntersect );
1038 SEG cSegChord( maxTanPtStart, maxTanPtEnd );
1039
1040 VECTOR2I oldMid = oldArc.GetArcMid();
1041 int cSegTanStartSide = cSegTanStart.Side( oldMid );
1042 int cSegTanEndSide = cSegTanEnd.Side( oldMid );
1043 int cSegChordSide = cSegChord.Side( oldMid );
1044
1045 VECTOR2I cursor = aP;
1046
1047 if( cSegTanStartSide != cSegTanStart.Side( cursor ) || cSegTanEndSide != cSegTanEnd.Side( cursor )
1048 || cSegChordSide != cSegChord.Side( cursor ) )
1049 {
1050 VECTOR2I best = cSegTanStart.NearestPoint( cursor );
1051
1052 for( const VECTOR2I& candidate : { cSegTanEnd.NearestPoint( cursor ), cSegChord.NearestPoint( cursor ) } )
1053 {
1054 if( ( candidate - cursor ).SquaredEuclideanNorm() < ( best - cursor ).SquaredEuclideanNorm() )
1055 {
1056 best = candidate;
1057 }
1058 }
1059
1060 cursor = best;
1061 }
1062
1063 if( ( cursor - maxTanCircle.Center ).EuclideanNorm() < maxTanCircle.Radius )
1064 cursor = maxTanCircle.NearestPoint( cursor );
1065
1066 CIRCLE c;
1067 c.ConstructFromTanTanPt( tanStartSeg, tanEndSeg, cursor );
1068
1069 if( c.Radius <= 0 )
1070 return;
1071
1072 VECTOR2I newCenter = c.Center;
1073 VECTOR2I newStart = tanStartSeg.LineProject( newCenter );
1074 VECTOR2I newEnd = tanEndSeg.LineProject( newCenter );
1075
1076 // Non-tangent side keeps the original arc endpoint in the chain so the corner
1077 // stays put while a new tangent stub grows out to newStart.
1078 int maxStubIU = KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * pcbIUScale.IU_PER_MM );
1079
1080 int prefixCutoff = useChainStart ? ( firstArcPt - 1 ) : firstArcPt;
1081 int suffixCutoff = useChainEnd ? ( lastArcPt + 1 ) : lastArcPt;
1082
1083 if( ( newEnd - newStart ).EuclideanNorm() <= maxStubIU )
1084 {
1085 SHAPE_LINE_CHAIN rebuilt;
1086 rebuilt.SetWidth( m_line.Width() );
1087
1088 if( prefixCutoff >= 0 )
1089 rebuilt.Append( m_line.Slice( 0, prefixCutoff ) );
1090
1091 if( suffixCutoff <= m_line.PointCount() - 1 )
1092 rebuilt.Append( m_line.Slice( suffixCutoff, m_line.PointCount() - 1 ) );
1093
1094 m_line = rebuilt;
1095 return;
1096 }
1097
1098 if( firstArcPt > 0 )
1099 {
1100 VECTOR2I anchor = useChainStart ? m_line.CPoint( firstArcPt - 1 ) : m_line.CPoint( firstArcPt );
1101
1102 if( ( anchor - newStart ).EuclideanNorm() <= maxStubIU )
1103 {
1104 newStart = anchor;
1105 prefixCutoff = useChainStart ? ( firstArcPt - 2 ) : ( firstArcPt - 1 );
1106 }
1107 }
1108
1109 if( lastArcPt < m_line.PointCount() - 1 )
1110 {
1111 VECTOR2I anchor = useChainEnd ? m_line.CPoint( lastArcPt + 1 ) : m_line.CPoint( lastArcPt );
1112
1113 if( ( anchor - newEnd ).EuclideanNorm() <= maxStubIU )
1114 {
1115 newEnd = anchor;
1116 suffixCutoff = useChainEnd ? ( lastArcPt + 2 ) : ( lastArcPt + 1 );
1117 }
1118 }
1119
1120 VECTOR2I newMid = CalcArcMid( newStart, newEnd, newCenter );
1121 SHAPE_ARC newArc( newStart, newMid, newEnd, width );
1122
1123 SHAPE_LINE_CHAIN rebuilt;
1124 rebuilt.SetWidth( m_line.Width() );
1125
1126 if( prefixCutoff >= 0 )
1127 rebuilt.Append( m_line.Slice( 0, prefixCutoff ) );
1128
1129 rebuilt.Append( newArc );
1130
1131 if( suffixCutoff <= m_line.PointCount() - 1 )
1132 rebuilt.Append( m_line.Slice( suffixCutoff, m_line.PointCount() - 1 ) );
1133
1134 m_line = rebuilt;
1135}
1136
1138 const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const
1139{
1140 int s_start = std::max( aIndex - 2, 0 );
1141 int s_end = std::min( aIndex + 2, aPath.SegmentCount() - 1 );
1142
1143 int i, j;
1144 int best_dist = INT_MAX;
1145 VECTOR2I best_snap = aP;
1146
1147 if( m_snapThreshhold <= 0 )
1148 return aP;
1149
1150 for( i = s_start; i <= s_end; i++ )
1151 {
1152 const SEG& a = aPath.CSegment( i );
1153
1154 for( j = s_start; j < i; j++ )
1155 {
1156 const SEG& b = aPath.CSegment( j );
1157
1158 if( !( DIRECTION_45( a ).IsObtuse( DIRECTION_45( b ) ) ) )
1159 continue;
1160
1161 OPT_VECTOR2I ip = a.IntersectLines( b );
1162
1163 if( ip )
1164 {
1165 int dist = ( *ip - aP ).EuclideanNorm();
1166
1167 if( dist < m_snapThreshhold && dist < best_dist )
1168 {
1169 best_dist = dist;
1170 best_snap = *ip;
1171 }
1172 }
1173 }
1174 }
1175
1176 return best_snap;
1177}
1178
1180 const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const
1181{
1182 VECTOR2I snap_p[2];
1183 DIRECTION_45 dragDir( aPath.CSegment( aIndex ) );
1184 int snap_d[2] = { -1, -1 };
1185
1186 if( m_snapThreshhold == 0 )
1187 return aP;
1188
1189 if( aIndex >= 2 )
1190 {
1191 SEG s = aPath.CSegment( aIndex - 2 );
1192
1193 if( DIRECTION_45( s ) == dragDir )
1194 snap_d[0] = s.LineDistance( aP );
1195
1196 snap_p[0] = s.A;
1197 }
1198
1199 if( aIndex < aPath.SegmentCount() - 2 )
1200 {
1201 SEG s = aPath.CSegment( aIndex + 2 );
1202
1203 if( DIRECTION_45( s ) == dragDir )
1204 snap_d[1] = s.LineDistance( aP );
1205
1206 snap_p[1] = s.A;
1207 }
1208
1209 VECTOR2I best = aP;
1210 int minDist = INT_MAX;
1211
1212 for( int i = 0; i < 2; i++ )
1213 {
1214 if( snap_d[i] >= 0 && snap_d[i] < minDist && snap_d[i] <= m_snapThreshhold )
1215 {
1216 minDist = snap_d[i];
1217 best = snap_p[i];
1218 }
1219 }
1220
1221 return best;
1222}
1223
1224void LINE::dragSegment45( const VECTOR2I& aP, int aIndex )
1225{
1227 VECTOR2I target( aP );
1228
1229 wxASSERT( aIndex < m_line.PointCount() );
1230
1231 SEG guideA[2], guideB[2];
1232 int index = aIndex;
1233
1234 target = snapToNeighbourSegments( path, aP, aIndex );
1235
1236 // We require a valid s_prev and s_next. If we are at the start or end of the line, we insert
1237 // a new point at the start or end so there is a zero-length segment for prev or next (we will
1238 // resize it as part of the drag operation). If we are next to an arc, we do this also, as we
1239 // cannot drag away one of the arc's points.
1240
1241 if( index == 0 || path.IsPtOnArc( index ) )
1242 {
1243 path.Insert( index > 0 ? index + 1 : 0, path.CPoint( index ) );
1244 index++;
1245 }
1246
1247 if( index == path.SegmentCount() - 1 )
1248 {
1249 path.Insert( path.PointCount() - 1, path.CLastPoint() );
1250 }
1251 else if( path.IsPtOnArc( index + 1 ) )
1252 {
1253 path.Insert( index + 1, path.CPoint( index + 1 ) );
1254 }
1255
1256 SEG dragged = path.CSegment( index );
1257 DIRECTION_45 drag_dir( dragged );
1258
1259 SEG s_prev = path.CSegment( index - 1 );
1260 SEG s_next = path.CSegment( index + 1 );
1261
1262 DIRECTION_45 dir_prev( s_prev );
1263 DIRECTION_45 dir_next( s_next );
1264
1265 if( dir_prev == drag_dir )
1266 {
1267 dir_prev = dir_prev.Left();
1268 path.Insert( index, path.CPoint( index ) );
1269 index++;
1270 }
1271 else if( dir_prev == DIRECTION_45::UNDEFINED )
1272 {
1273 dir_prev = drag_dir.Left();
1274 }
1275
1276 if( dir_next == drag_dir )
1277 {
1278 dir_next = dir_next.Right();
1279 path.Insert( index + 1, path.CPoint( index + 1 ) );
1280 }
1281 else if( dir_next == DIRECTION_45::UNDEFINED )
1282 {
1283 dir_next = drag_dir.Right();
1284 }
1285
1286 s_prev = path.CSegment( index - 1 );
1287 s_next = path.CSegment( index + 1 );
1288 dragged = path.CSegment( index );
1289
1290 if( aIndex == 0 )
1291 {
1292 guideA[0] = SEG( dragged.A, dragged.A + drag_dir.Right().ToVector() );
1293 guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Left().ToVector() );
1294 }
1295 else
1296 {
1297 if( dir_prev.Angle( drag_dir )
1299 {
1300 guideA[0] = SEG( s_prev.A, s_prev.A + drag_dir.Left().ToVector() );
1301 guideA[1] = SEG( s_prev.A, s_prev.A + drag_dir.Right().ToVector() );
1302 }
1303 else
1304 guideA[0] = guideA[1] = SEG( dragged.A, dragged.A + dir_prev.ToVector() );
1305 }
1306
1307 if( aIndex == m_line.SegmentCount() - 1 )
1308 {
1309 guideB[0] = SEG( dragged.B, dragged.B + drag_dir.Right().ToVector() );
1310 guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Left().ToVector() );
1311 }
1312 else
1313 {
1314 if( dir_next.Angle( drag_dir )
1316 {
1317 guideB[0] = SEG( s_next.B, s_next.B + drag_dir.Left().ToVector() );
1318 guideB[1] = SEG( s_next.B, s_next.B + drag_dir.Right().ToVector() );
1319 }
1320 else
1321 guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + dir_next.ToVector() );
1322 }
1323
1324 SEG s_current( target, target + drag_dir.ToVector() );
1325
1326 int best_len = INT_MAX;
1327 SHAPE_LINE_CHAIN best;
1328
1329 for( int i = 0; i < 2; i++ )
1330 {
1331 for( int j = 0; j < 2; j++ )
1332 {
1333 OPT_VECTOR2I ip1 = s_current.IntersectLines( guideA[i] );
1334 OPT_VECTOR2I ip2 = s_current.IntersectLines( guideB[j] );
1335
1337
1338 if( !ip1 || !ip2 )
1339 continue;
1340
1341 SEG s1( s_prev.A, *ip1 );
1342 SEG s2( *ip1, *ip2 );
1343 SEG s3( *ip2, s_next.B );
1344
1345 OPT_VECTOR2I ip;
1346
1347 if( ( ip = s1.Intersect( s_next ) ) )
1348 {
1349 np.Append( s1.A );
1350 np.Append( *ip );
1351 np.Append( s_next.B );
1352 }
1353 else if( ( ip = s3.Intersect( s_prev ) ) )
1354 {
1355 np.Append( s_prev.A );
1356 np.Append( *ip );
1357 np.Append( s3.B );
1358 }
1359 else if( ( ip = s1.Intersect( s3 ) ) )
1360 {
1361 np.Append( s_prev.A );
1362 np.Append( *ip );
1363 np.Append( s_next.B );
1364 }
1365 else
1366 {
1367 np.Append( s_prev.A );
1368 np.Append( *ip1 );
1369 np.Append( *ip2 );
1370 np.Append( s_next.B );
1371 }
1372
1373 if( np.Length() < best_len )
1374 {
1375 best_len = np.Length();
1376 best = std::move( np );
1377 }
1378 }
1379 }
1380
1381 if( m_line.PointCount() == 1 )
1382 m_line = best;
1383 else if( aIndex == 0 )
1384 m_line.Replace( 0, 1, best );
1385 else if( aIndex == m_line.SegmentCount() - 1 )
1386 m_line.Replace( -2, -1, best );
1387 else
1388 m_line.Replace( aIndex, aIndex + 1, best );
1389
1390 m_line.Simplify();
1391}
1392
1393
1394bool LINE::CompareGeometry( const LINE& aOther )
1395{
1396 return m_line.CompareGeometry( aOther.m_line );
1397}
1398
1399
1401{
1402 m_line = m_line.Reverse();
1403
1404 std::reverse( m_links.begin(), m_links.end() );
1405}
1406
1407
1408void LINE::AppendVia( const VIA& aVia )
1409{
1410 if( m_line.PointCount() > 1 && aVia.Pos() == m_line.CPoint( 0 ) )
1411 {
1412 Reverse();
1413 }
1414
1415 dropVia();
1416
1417 m_via = aVia.Clone();
1418 m_via->SetOwner( this );
1419 m_via->SetNet( m_net );
1420 m_ownsVia = true;
1421}
1422
1423
1424void LINE::LinkVia( VIA* aVia )
1425{
1426 if( m_line.PointCount() > 1 && aVia->Pos() == m_line.CPoint( 0 ) )
1427 {
1428 Reverse();
1429 }
1430
1431 dropVia();
1432
1433 m_via = aVia;
1434 Link( aVia );
1435}
1436
1437
1438void LINE::SetRank( int aRank )
1439{
1440 m_rank = aRank;
1441
1442 for( auto s : m_links )
1443 s->SetRank( aRank );
1444
1445}
1446
1447
1448int LINE::Rank() const
1449{
1450 int min_rank = INT_MAX;
1451
1452 if( IsLinked() )
1453 {
1454 for( const LINKED_ITEM* item : m_links )
1455 min_rank = std::min( min_rank, item->Rank() );
1456 }
1457 else
1458 {
1459 min_rank = m_rank;
1460 }
1461
1462 int rank = ( min_rank == INT_MAX ) ? -1 : min_rank;
1463
1464 return rank;
1465}
1466
1467
1468void LINE::ClipVertexRange( int aStart, int aEnd )
1469{
1476 int firstLink = 0;
1477 int lastLink = std::max( 0, static_cast<int>( m_links.size() ) - 1 );
1478 int linkIdx = 0;
1479
1480 for( int i = 0; i >= 0 && i < m_line.PointCount(); i = m_line.NextShape( i ) )
1481 {
1482 if( i <= aStart )
1483 firstLink = linkIdx;
1484
1485 if( i < 0 || i >= aEnd - 1 || linkIdx >= lastLink )
1486 {
1487 lastLink = linkIdx;
1488 break;
1489 }
1490
1491 linkIdx++;
1492 }
1493
1494 wxASSERT( lastLink >= firstLink );
1495
1496 m_line = m_line.Slice( aStart, aEnd );
1497
1498 if( IsLinked() )
1499 {
1500 wxASSERT( m_links.size() < INT_MAX );
1501 wxASSERT( static_cast<int>( m_links.size() ) >= ( lastLink - firstLink ) );
1502
1503 // Note: The range includes aEnd, but we have n-1 segments.
1504 std::rotate(
1505 m_links.begin(),
1506 m_links.begin() + firstLink,
1507 m_links.begin() + lastLink
1508 );
1509
1510 m_links.resize( lastLink - firstLink + 1 );
1511 }
1512}
1513
1514
1515bool LINE::HasLoops() const
1516{
1517 for( int i = 0; i < PointCount(); i++ )
1518 {
1519 for( int j = i + 2; j < PointCount(); j++ )
1520 {
1521 if( CPoint( i ) == CPoint( j ) )
1522 return true;
1523 }
1524 }
1525
1526 return false;
1527}
1528
1529
1530static void extendBox( BOX2I& aBox, bool& aDefined, const VECTOR2I& aP )
1531{
1532 if( aDefined )
1533 {
1534 aBox.Merge( aP );
1535 }
1536 else
1537 {
1538 aBox = BOX2I( aP, VECTOR2I( 0, 0 ) );
1539 aDefined = true;
1540 }
1541}
1542
1543
1544OPT_BOX2I LINE::ChangedArea( const LINE* aOther ) const
1545{
1546 BOX2I area;
1547 bool areaDefined = false;
1548
1549 int i_start = -1;
1550 int i_end_self = -1, i_end_other = -1;
1551
1552 SHAPE_LINE_CHAIN self( m_line );
1553 self.Simplify();
1554 SHAPE_LINE_CHAIN other( aOther->m_line );
1555 other.Simplify();
1556
1557 int np_self = self.PointCount();
1558 int np_other = other.PointCount();
1559
1560 int n = std::min( np_self, np_other );
1561
1562 for( int i = 0; i < n; i++ )
1563 {
1564 const VECTOR2I p1 = self.CPoint( i );
1565 const VECTOR2I p2 = other.CPoint( i );
1566
1567 if( p1 != p2 )
1568 {
1569 if( i != n - 1 )
1570 {
1571 SEG s = self.CSegment( i );
1572
1573 if( !s.Contains( p2 ) )
1574 {
1575 i_start = i;
1576 break;
1577 }
1578 }
1579 else
1580 {
1581 i_start = i;
1582 break;
1583 }
1584 }
1585 }
1586
1587 for( int i = 0; i < n; i++ )
1588 {
1589 const VECTOR2I p1 = self.CPoint( np_self - 1 - i );
1590 const VECTOR2I p2 = other.CPoint( np_other - 1 - i );
1591
1592 if( p1 != p2 )
1593 {
1594 i_end_self = np_self - 1 - i;
1595 i_end_other = np_other - 1 - i;
1596 break;
1597 }
1598 }
1599
1600 if( i_start < 0 )
1601 i_start = n;
1602
1603 if( i_end_self < 0 )
1604 i_end_self = np_self - 1;
1605
1606 if( i_end_other < 0 )
1607 i_end_other = np_other - 1;
1608
1609 for( int i = i_start; i <= i_end_self; i++ )
1610 extendBox( area, areaDefined, self.CPoint( i ) );
1611
1612 for( int i = i_start; i <= i_end_other; i++ )
1613 extendBox( area, areaDefined, other.CPoint( i ) );
1614
1615 if( areaDefined )
1616 {
1617 area.Inflate( std::max( Width(), aOther->Width() ) );
1618 return area;
1619 }
1620
1621 return OPT_BOX2I();
1622}
1623
1624
1626{
1627 for( const auto seg : m_links )
1628 {
1629 if( seg->Marker() & MK_LOCKED )
1630 return true;
1631 }
1632 return false;
1633}
1634
1635
1637{
1638 ClearLinks();
1639 RemoveVia();
1640 m_line.Clear();
1641}
1642
1643
1645{
1646 if( m_via && ContainsLink( m_via ) )
1647 Unlink( m_via );
1648
1649 dropVia();
1650}
1651
1652
1653const std::string SEGMENT::Format( ) const
1654{
1655 std::stringstream ss;
1656 ss << ITEM::Format() << " ";
1657 ss << m_seg.Format( false );
1658 return ss.str();
1659}
1660
1661
1662int LINE::FindSegment( const SEGMENT* aSeg ) const
1663{
1664 for( int i = 0; i < m_line.SegmentCount(); i++)
1665 {
1666 const SEG&s = m_line.CSegment(i);
1667 if( s == aSeg->Seg() )
1668 return i;
1669 }
1670
1671 return -1;
1672}
1673
1674
1676{
1677 for( auto lnk : Links() )
1678 {
1679 if( auto seg = dyn_cast<SEGMENT*>( lnk ) )
1680 {
1681 if( seg->Seg() == aSeg || seg->Seg() == aSeg.Reversed() )
1682 return seg;
1683 }
1684 }
1685
1686 return nullptr;
1687}
1688
1689
1691{
1692 for( auto lnk : Links() )
1693 {
1694 if( auto seg = dyn_cast<SEGMENT*>( lnk ) )
1695 {
1696 if( seg->Seg().Contains( aP ) )
1697 {
1698 return seg;
1699 }
1700 }
1701 }
1702
1703 return nullptr;
1704}
1705
1706
1707}
1708
1709
int index
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
Represent basic circle geometry with utility geometry functions.
Definition circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition circle.h:150
int Radius
Public to make access simpler.
Definition circle.h:149
CIRCLE & ConstructFromTanTanPt(const SEG &aLineA, const SEG &aLineB, const VECTOR2I &aP)
Construct this circle such that it is tangent to the given segments and passes through the given poin...
Definition circle.cpp:51
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute the point on the circumference of the circle that is the closest to aP.
Definition circle.cpp:197
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:323
virtual const std::string Format() const
Definition pns_item.cpp:338
bool m_movable
Definition pns_item.h:329
PNS_LAYER_RANGE m_layers
Definition pns_item.h:327
bool m_routable
Definition pns_item.h:333
NET_HANDLE m_net
Definition pns_item.h:330
int m_marker
Definition pns_item.h:331
BOARD_ITEM * m_parent
Definition pns_item.h:321
int m_rank
Definition pns_item.h:332
VECTOR2I snapToNeighbourSegments(const SHAPE_LINE_CHAIN &aPath, const VECTOR2I &aP, int aIndex) const
void ClipVertexRange(int aStart, int aEnd)
Clip the line to a given range of vertices.
int FindSegment(const SEGMENT *aSeg) const
const VECTOR2I & CPoint(int aIdx) const
Return the aIdx-th point of the line.
Definition pns_line.h:157
bool HasLoops() const
OPT_BOX2I ChangedArea(const LINE *aOther) const
bool HasLockedSegments() const
void dropVia()
Free a via this line owns and forget whichever via it had.
Definition pns_line.cpp:89
int Rank() const override
void dragCorner45(const VECTOR2I &aP, int aIndex, DIRECTION_45 aPreferredEndingDirection)
Definition pns_line.cpp:817
const LINE ClipToNearestObstacle(NODE *aNode) const
Clip the line to the nearest obstacle, traversing from the line's start vertex (0).
Definition pns_line.cpp:673
VIA * m_via
Definition pns_line.h:305
void DragArc(const VECTOR2I &aP, int aIndex)
Definition pns_line.cpp:905
virtual void Mark(int aMarker) const override
Definition pns_line.cpp:168
int m_width
Our width.
Definition pns_line.h:294
bool CompareGeometry(const LINE &aOther)
Return true if the line is geometrically identical as line aOther.
void LinkVia(VIA *aVia)
ITEM * m_blockingObstacle
For mark obstacle mode.
Definition pns_line.h:311
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:149
VECTOR2I snapDraggedCorner(const SHAPE_LINE_CHAIN &aPath, const VECTOR2I &aP, int aIndex) const
LINE & operator=(const LINE &aOther)
Definition pns_line.cpp:100
void dragSegment45(const VECTOR2I &aP, int aIndex)
const VECTOR2I & CLastPoint() const
Definition pns_line.h:158
void RemoveVia()
int CountCorners(int aAngles) const
Return the number of corners of angles specified by mask aAngles.
Definition pns_line.cpp:212
void SetRank(int aRank) override
LINE()
Makes an empty line.
Definition pns_line.h:67
bool Walkaround(SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN &aPrePath, SHAPE_LINE_CHAIN &aWalk, SHAPE_LINE_CHAIN &aPost, bool aCw) const
Calculate a line tightly wrapping a convex hull of an obstacle object (aObstacle).
SHAPE_LINE_CHAIN & Line()
Modifiable accessor to the underlying shape.
Definition pns_line.h:148
void DragCorner(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition pns_line.cpp:878
bool m_ownsVia
A via made by AppendVia() or copied from a line that owned its via is ours to free.
Definition pns_line.h:309
virtual int Marker() const override
Definition pns_line.cpp:187
void AppendVia(const VIA &aVia)
SEGMENT * FindLinkedSegment(const SEG &aSeg) const
Finds a linked SEGMENT whose endpoints match aSeg (either direction)
virtual void Unmark(int aMarker=-1) const override
Definition pns_line.cpp:178
int PointCount() const
Definition pns_line.h:152
SEGMENT * FindLinkContainingVertex(const VECTOR2I &aP) const
int m_snapThreshhold
Width to smooth out jagged segments.
Definition pns_line.h:297
SHAPE_LINE_CHAIN m_line
The actual shape of the line.
Definition pns_line.h:293
void DragSegment(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false)
Definition pns_line.cpp:892
void Reverse()
Reverse the point/vertex order.
void dragCornerFree(const VECTOR2I &aP, int aIndex)
Definition pns_line.cpp:851
virtual LINE * Clone() const override
Return a deep copy of the item.
Definition pns_line.cpp:160
void copyVia(const LINE &aOther)
Take a copy of another line's via if it owns one, or share the via it borrows.
Definition pns_line.cpp:70
int Width() const
Return line width.
Definition pns_line.h:169
void Clear()
void restoreUntouchedArcs(SHAPE_LINE_CHAIN &aPath, const SHAPE_LINE_CHAIN &aOriginal) const
Used to rebuild arcs in the walkaround since the graph only stores vertices.
Definition pns_line.cpp:249
Keep the router "world" - i.e.
Definition pns_node.h:244
std::optional< OBSTACLE > OPT_OBSTACLE
Definition pns_node.h:254
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:304
const ITEM_OWNER * m_owner
Definition pns_item.h:88
virtual const std::string Format() const override
const SEG & Seg() const
SEGMENT * Clone() const override
Return a deep copy of the item.
Definition pns_line.cpp:198
const SHAPE_LINE_CHAIN Hull(int aClearance, int aWalkaroundThickness, int aLayer=-1) const override
Definition pns_line.cpp:662
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:269
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
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:710
VECTOR2I B
Definition seg.h:46
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:599
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:401
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:217
bool Contains(const SEG &aSeg) const
Definition seg.h:321
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition seg.cpp:651
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition seg.h:139
SEG Reversed() const
Definition seg.h:370
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:116
int GetWidth() const override
Definition shape_arc.h:215
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
const VECTOR2I & GetCenter() const
bool PointOnEdge(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of the line chain.
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.
void SetWidth(int aWidth) override
Set the width of all segments in the chain.
int NearestSegment(const VECTOR2I &aP) const
Find the segment nearest the given point.
SHAPE_LINE_CHAIN & Simplify2(bool aRemoveColinear=true)
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.
size_t ArcCount() const
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
bool IsArcSegment(size_t aSegment) const
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
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
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
double m_MaxTangentAngleDeviation
Maximum angle between the tangent line of an arc track and a connected straight track in order to com...
Push and Shove diff pair dimensions (gap) settings dialog.
static void extendBox(BOX2I &aBox, bool &aDefined, const VECTOR2I &aP)
static int areNeighbours(int x, int y, int max=0)
Definition pns_line.cpp:233
SHAPE_LINE_CHAIN dragCornerInternal(const SHAPE_LINE_CHAIN &aOrigin, const VECTOR2I &aP, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition pns_line.cpp:716
const SHAPE_LINE_CHAIN SegmentHull(const SHAPE_SEGMENT &aSeg, int aClearance, int aWalkaroundThickness)
void HullIntersection(const SHAPE_LINE_CHAIN &hull, const SHAPE_LINE_CHAIN &line, std::vector< SHAPE_LINE_CHAIN::INTERSECTION > &ips)
@ MK_LOCKED
Definition pns_item.h:45
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ 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:35
Represent an intersection between two line segments.
CADSTAR_ARCHIVE_PARSER::VERTEX_TYPE vt
std::string path
VECTOR2I center
#define M_PI
const VECTOR2I CalcArcMid(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aMinArcAngle=true)
Return the middle point of an arc, half-way between aStart and aEnd.
Definition trigo.cpp:207
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:55
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707