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