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