KiCad PCB EDA Suite
Loading...
Searching...
No Matches
shape_line_chain.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <limits>
24#include <math.h> // for hypot
25#include <map>
26#include <string> // for basic_string
27
28#include <clipper2/clipper.h>
29#include <core/kicad_algo.h> // for alg::run_on_pair
30#include <geometry/circle.h>
31#include <geometry/seg.h> // for SEG, OPT_VECTOR2I
34#include <math/box2.h> // for BOX2I
35#include <math/util.h> // for rescale
36#include <math/vector2d.h> // for VECTOR2, VECTOR2I
37#include <math/box2_minmax.h>
38#include <trigo.h> // for RotatePoint
39
40class SHAPE;
41
42const ssize_t SHAPE_LINE_CHAIN::SHAPE_IS_PT = -1;
43const std::pair<ssize_t, ssize_t> SHAPE_LINE_CHAIN::SHAPES_ARE_PT = { SHAPE_IS_PT, SHAPE_IS_PT };
44
45namespace
46{
47// Compact bounding-box record used inside SHAPE_LINE_CHAIN::Intersect.
48// Hoisted to file scope so thread_local scratch vectors can reference the type.
49struct SEG_EXTENT
50{
51 int minX, maxX, minY, maxY;
52 int segIdx;
53};
54}
55
56
58{
59 // This polyline will only be used for display. The native arc is still used for output.
60 // We therefore want to use a higher definition than the typical maxError.
62}
63
64
65SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector<int>& aV) :
67 m_accuracy( 0 ),
68 m_closed( false ),
69 m_width( 0 )
70{
71 for(size_t i = 0; i < aV.size(); i+= 2 )
72 {
73 Append( aV[i], aV[i+1] );
74 }
75}
76
77
78SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector<VECTOR2I>& aV, bool aClosed ) :
80 m_accuracy( 0 ),
81 m_closed( false ),
82 m_width( 0 )
83{
84 m_points = aV;
85 m_shapes = std::vector<std::pair<ssize_t, ssize_t>>( aV.size(), SHAPES_ARE_PT );
86 SetClosed( aClosed );
87}
88
89
90SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const SHAPE_ARC& aArc, bool aClosed, std::optional<int> aMaxError ) :
92 m_accuracy( 0 ),
93 m_closed( false ),
94 m_width( aArc.GetWidth() )
95{
96 if( aMaxError.has_value() )
97 Append( aArc, aMaxError.value() );
98 else
99 Append( aArc );
100
101 SetClosed( aClosed );
102}
103
104
105SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const Clipper2Lib::Path64& aPath,
106 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
107 const std::vector<SHAPE_ARC>& aArcBuffer ) :
109 m_accuracy( 0 ),
110 m_closed( true ),
111 m_width( 0 )
112{
113 std::map<ssize_t, ssize_t> loadedArcs;
114 m_points.reserve( aPath.size() );
115 m_shapes.reserve( aPath.size() );
116
117 auto loadArc =
118 [&]( ssize_t aArcIndex ) -> ssize_t
119 {
120 if( aArcIndex == SHAPE_IS_PT )
121 {
122 return SHAPE_IS_PT;
123 }
124 else if( loadedArcs.count( aArcIndex ) == 0 )
125 {
126 loadedArcs.insert( { aArcIndex, m_arcs.size() } );
127 m_arcs.push_back( aArcBuffer.at( aArcIndex ) );
128 }
129
130 return loadedArcs.at( aArcIndex );
131 };
132
133 for( size_t ii = 0; ii < aPath.size(); ++ii )
134 {
135 Append( aPath[ii].x, aPath[ii].y );
136
137 // Add arc info (if exists)
138 int idx_z = aPath[ii].z;
139
140 if( idx_z < 0 || idx_z >= (int)aZValueBuffer.size() )
141 continue;
142
143 m_shapes[ii].first = loadArc( aZValueBuffer[idx_z].m_FirstArcIdx );
144 m_shapes[ii].second = loadArc( aZValueBuffer[idx_z].m_SecondArcIdx );
145 }
146
147 // Clipper shouldn't return duplicate contiguous points. if it did, these would be
148 // removed during Append() and we would have different number of shapes to points
149 wxASSERT( m_shapes.size() == m_points.size() );
150
151 // Clipper might mess up the rotation of the indices such that an arc can be split between
152 // the end point and wrap around to the start point. Lets fix the indices up now
153 fixIndicesRotation();
154}
155
156
157Clipper2Lib::Path64 SHAPE_LINE_CHAIN::convertToClipper2( bool aRequiredOrientation,
158 std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
159 std::vector<SHAPE_ARC>& aArcBuffer ) const
160{
161 Clipper2Lib::Path64 c_path;
162 SHAPE_LINE_CHAIN input;
163 bool orientation = Area( false ) >= 0;
164 ssize_t shape_offset = aArcBuffer.size();
165
166 if( orientation != aRequiredOrientation )
167 input = Reverse();
168 else
169 input = *this;
170
171 int pointCount = input.PointCount();
172 c_path.reserve( pointCount );
173
174 for( int i = 0; i < pointCount; i++ )
175 {
176 const VECTOR2I& vertex = input.CPoint( i );
177
178 CLIPPER_Z_VALUE z_value( input.m_shapes[i], shape_offset );
179 size_t z_value_ptr = aZValueBuffer.size();
180 aZValueBuffer.push_back( z_value );
181
182 c_path.emplace_back( vertex.x, vertex.y, z_value_ptr );
183 }
184
185 aArcBuffer.insert( aArcBuffer.end(), input.m_arcs.begin(), input.m_arcs.end() );
186
187 return c_path;
188}
189
190
192{
193 wxCHECK( m_shapes.size() == m_points.size(), /*void*/ );
194
195 if( m_shapes.size() <= 1 )
196 return;
197
198 size_t rotations = 0;
199
200 while( ArcIndex( 0 ) != SHAPE_IS_PT
201 && !IsArcStart( 0 ) )
202 {
203 // Rotate right
204 std::rotate( m_points.rbegin(), m_points.rbegin() + 1, m_points.rend() );
205 std::rotate( m_shapes.rbegin(), m_shapes.rbegin() + 1, m_shapes.rend() );
206
207 // Sanity check - avoid infinite loops (NB: wxCHECK is not thread-safe)
208 if( rotations++ > m_shapes.size() )
209 return;
210 }
211}
212
213
215{
216 if( m_closed )
217 {
218 if( m_points.size() > 1 && m_points.front() == m_points.back() )
219 {
220 if( ArcIndex( m_shapes.size() - 1 ) != SHAPE_IS_PT )
221 {
222 m_shapes.front().second = m_shapes.front().first;
223 m_shapes.front().first = ArcIndex( m_shapes.size() - 1 ) ;
224 }
225
226 m_points.pop_back();
227 m_shapes.pop_back();
228
230 }
231 }
232 else
233 {
234 if( m_points.size() > 1 && IsSharedPt( 0 ) )
235 {
236 // Create a duplicate point at the end
237 m_points.push_back( m_points.front() );
238 m_shapes.push_back( { m_shapes.front().first, SHAPE_IS_PT } );
239 m_shapes.front().first = m_shapes.front().second;
240 m_shapes.front().second = SHAPE_IS_PT;
241 }
242 }
243}
244
245
246void SHAPE_LINE_CHAIN::convertArc( ssize_t aArcIndex )
247{
248 if( aArcIndex < 0 )
249 aArcIndex += m_arcs.size();
250
251 if( aArcIndex >= static_cast<ssize_t>( m_arcs.size() ) )
252 return;
253
254 // Clear the shapes references
255 for( auto& sh : m_shapes )
256 {
258 [&]( ssize_t& aShapeIndex )
259 {
260 if( aShapeIndex == aArcIndex )
261 aShapeIndex = SHAPE_IS_PT;
262
263 if( aShapeIndex > aArcIndex )
264 --aShapeIndex;
265 } );
266
267 if( sh.second != SHAPE_IS_PT && sh.first == SHAPE_IS_PT )
268 std::swap( sh.first, sh.second );
269 }
270
271 m_arcs.erase( m_arcs.begin() + aArcIndex );
272}
273
274
275void SHAPE_LINE_CHAIN::amendArc( size_t aArcIndex, const VECTOR2I& aNewStart,
276 const VECTOR2I& aNewEnd )
277{
278 wxCHECK_MSG( aArcIndex < m_arcs.size(), /* void */,
279 wxT( "Invalid arc index requested." ) );
280
281 SHAPE_ARC& theArc = m_arcs[aArcIndex];
282
283 // Try to preseve the centre of the original arc
284 SHAPE_ARC newArc;
285 newArc.ConstructFromStartEndCenter( aNewStart, aNewEnd, theArc.GetCenter(),
286 theArc.IsClockwise() );
287
288 m_arcs[aArcIndex] = newArc;
289}
290
291
292void SHAPE_LINE_CHAIN::splitArc( ssize_t aPtIndex, bool aCoincident )
293{
294 if( aPtIndex < 0 )
295 aPtIndex += m_shapes.size();
296
297 if( !IsSharedPt( aPtIndex ) && IsArcStart( aPtIndex ) )
298 return; // Nothing to do
299
300 if( !IsPtOnArc( aPtIndex ) )
301 return; // Nothing to do
302
303 wxCHECK_MSG( aPtIndex < static_cast<ssize_t>( m_shapes.size() ), /* void */,
304 wxT( "Invalid point index requested." ) );
305
306 if( IsSharedPt( aPtIndex ) || IsArcEnd( aPtIndex ) )
307 {
308 if( aCoincident || aPtIndex == 0 )
309 return; // nothing to do
310
311 ssize_t firstArcIndex = m_shapes[aPtIndex].first;
312
313 const VECTOR2I& newStart = m_arcs[firstArcIndex].GetP0(); // don't amend the start
314 const VECTOR2I& newEnd = m_points[aPtIndex - 1];
315 amendArc( firstArcIndex, newStart, newEnd );
316
317 if( IsSharedPt( aPtIndex ) )
318 {
319 m_shapes[aPtIndex].first = m_shapes[aPtIndex].second;
320 m_shapes[aPtIndex].second = SHAPE_IS_PT;
321 }
322 else
323 {
324 m_shapes[aPtIndex] = SHAPES_ARE_PT;
325 }
326
327 return;
328 }
329
330 ssize_t currArcIdx = ArcIndex( aPtIndex );
331 SHAPE_ARC& currentArc = m_arcs[currArcIdx];
332
333 SHAPE_ARC newArc1;
334 SHAPE_ARC newArc2;
335
336 VECTOR2I arc1End = ( aCoincident ) ? m_points[aPtIndex] : m_points[aPtIndex - 1];
337 VECTOR2I arc2Start = m_points[aPtIndex];
338
339 newArc1.ConstructFromStartEndCenter( currentArc.GetP0(), arc1End, currentArc.GetCenter(),
340 currentArc.IsClockwise() );
341
342 newArc2.ConstructFromStartEndCenter( arc2Start, currentArc.GetP1(), currentArc.GetCenter(),
343 currentArc.IsClockwise() );
344
345 if( !aCoincident && ArcIndex( aPtIndex - 1 ) != currArcIdx )
346 {
347 //Ignore newArc1 as it has zero points
348 m_arcs[currArcIdx] = newArc2;
349 }
350 else
351 {
352 m_arcs[currArcIdx] = newArc1;
353 m_arcs.insert( m_arcs.begin() + currArcIdx + 1, newArc2 );
354
355 if( aCoincident )
356 {
357 m_shapes[aPtIndex].second = currArcIdx + 1;
358 aPtIndex++;
359 }
360
361 // Only change the arc indices for the second half of the point range
362 for( int i = aPtIndex; i < PointCount(); i++ )
363 {
364 alg::run_on_pair( m_shapes[i], [&]( ssize_t& aIndex ) {
365 if( aIndex != SHAPE_IS_PT )
366 aIndex++;
367 } );
368 }
369 }
370}
371
372
373bool SHAPE_LINE_CHAIN_BASE::Collide( const VECTOR2I& aP, int aClearance, int* aActual,
374 VECTOR2I* aLocation ) const
375{
376 if( IsClosed() && PointInside( aP, aClearance ) )
377 {
378 if( aLocation )
379 *aLocation = aP;
380
381 if( aActual )
382 *aActual = 0;
383
384 return true;
385 }
386
387 SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX;
388 SEG::ecoord clearance_sq = SEG::Square( aClearance );
389 VECTOR2I nearest;
390
391 for( size_t i = 0; i < GetSegmentCount(); i++ )
392 {
393 const SEG& s = GetSegment( i );
394 VECTOR2I pn = s.NearestPoint( aP );
395 SEG::ecoord dist_sq = ( pn - aP ).SquaredEuclideanNorm();
396
397 if( dist_sq < closest_dist_sq )
398 {
399 nearest = pn;
400 closest_dist_sq = dist_sq;
401
402 if( closest_dist_sq == 0 )
403 break;
404
405 // If we're not looking for aActual then any collision will do
406 if( closest_dist_sq < clearance_sq && !aActual )
407 break;
408 }
409 }
410
411 if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
412 {
413 if( aLocation )
414 *aLocation = nearest;
415
416 if( aActual )
417 *aActual = sqrt( closest_dist_sq );
418
419 return true;
420 }
421
422 return false;
423}
424
425
426bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance, int* aActual,
427 VECTOR2I* aLocation ) const
428{
429 if( IsClosed() && PointInside( aP, aClearance ) )
430 {
431 if( aLocation )
432 *aLocation = aP;
433
434 if( aActual )
435 *aActual = 0;
436
437 return true;
438 }
439
440 SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX;
441 SEG::ecoord clearance_sq = SEG::Square( aClearance );
442 VECTOR2I nearest;
443
444 // Collide line segments
445 for( size_t i = 0; i < GetSegmentCount(); i++ )
446 {
447 if( IsArcSegment( i ) )
448 continue;
449
450 const SEG& s = GetSegment( i );
451 VECTOR2I pn = s.NearestPoint( aP );
452 SEG::ecoord dist_sq = ( pn - aP ).SquaredEuclideanNorm();
453
454 if( dist_sq < closest_dist_sq )
455 {
456 nearest = pn;
457 closest_dist_sq = dist_sq;
458
459 if( closest_dist_sq == 0 )
460 break;
461
462 // If we're not looking for aActual then any collision will do
463 if( closest_dist_sq < clearance_sq && !aActual )
464 break;
465 }
466 }
467
468 if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
469 {
470 if( aLocation )
471 *aLocation = nearest;
472
473 if( aActual )
474 *aActual = sqrt( closest_dist_sq );
475
476 return true;
477 }
478
479 // Collide arc segments
480 for( size_t i = 0; i < ArcCount(); i++ )
481 {
482 const SHAPE_ARC& arc = Arc( i );
483
484 // The arcs in the chain should have zero width
485 wxASSERT_MSG( arc.GetWidth() == 0, wxT( "Invalid arc width - should be zero" ) );
486
487 if( arc.Collide( aP, aClearance, aActual, aLocation ) )
488 return true;
489 }
490
491 return false;
492}
493
494
495void SHAPE_LINE_CHAIN::Rotate( const EDA_ANGLE& aAngle, const VECTOR2I& aCenter )
496{
497 for( VECTOR2I& pt : m_points )
498 RotatePoint( pt, aCenter, aAngle );
499
500 for( SHAPE_ARC& arc : m_arcs )
501 arc.Rotate( aAngle, aCenter );
502}
503
504
506 VECTOR2I& aPt1 ) const
507{
508 const std::vector<VECTOR2I>& myPts = m_points;
509 const std::vector<VECTOR2I>& otherPts = aOther.m_points;
510
511 const int c_maxBoxes = 100;
512 const int c_minPtsPerBox = 20;
513
514 int myPointsPerBox = std::max( c_minPtsPerBox, int( myPts.size() / c_maxBoxes ) + 1 );
515 int otherPointsPerBox = std::max( c_minPtsPerBox, int( otherPts.size() / c_maxBoxes ) + 1 );
516
517 int myNumBoxes = ( myPts.size() + myPointsPerBox - 1 ) / myPointsPerBox;
518 int otherNumBoxes = ( otherPts.size() + otherPointsPerBox - 1 ) / otherPointsPerBox;
519
520 struct BOX
521 {
522 BOX2I_MINMAX bbox;
524 int radius;
525 bool valid = false;
526 };
527
528 std::vector<BOX> myBoxes( myNumBoxes );
529 std::vector<BOX> otherBoxes( otherNumBoxes );
530
531 // Calculate bounding boxes
532 for( size_t i = 0; i < myPts.size(); i++ )
533 {
534 const VECTOR2I pt = myPts[i];
535 BOX& box = myBoxes[i / myPointsPerBox];
536
537 if( box.valid )
538 {
539 box.bbox.Merge( pt );
540 }
541 else
542 {
543 box.bbox = BOX2I_MINMAX( pt );
544 box.valid = true;
545 }
546 }
547
548 for( size_t i = 0; i < otherPts.size(); i++ )
549 {
550 const VECTOR2I pt = otherPts[i];
551 BOX& box = otherBoxes[i / otherPointsPerBox];
552
553 if( box.valid )
554 {
555 box.bbox.Merge( pt );
556 }
557 else
558 {
559 box.bbox = BOX2I_MINMAX( pt );
560 box.valid = true;
561 }
562 }
563
564 // Store centers and radiuses
565 for( BOX& box : myBoxes )
566 {
567 box.center = box.bbox.GetCenter();
568 box.radius = int( box.bbox.GetDiameter() / 2 );
569 }
570
571 for( BOX& box : otherBoxes )
572 {
573 box.center = box.bbox.GetCenter();
574 box.radius = int( box.bbox.GetDiameter() / 2 );
575 }
576
577 // Find closest pairs
578 struct DIST_PAIR
579 {
580 DIST_PAIR( int64_t aDistSq, size_t aIdA, size_t aIdB ) :
581 dist( aDistSq ), idA( aIdA ), idB( aIdB )
582 {
583 }
584
585 int64_t dist;
586 size_t idA;
587 size_t idB;
588 };
589
590 std::vector<DIST_PAIR> pairsToTest;
591
592 for( size_t ia = 0; ia < myBoxes.size(); ia++ )
593 {
594 for( size_t ib = 0; ib < otherBoxes.size(); ib++ )
595 {
596 const BOX& ca = myBoxes[ia];
597 const BOX& cb = otherBoxes[ib];
598
599 if( !ca.valid || !cb.valid )
600 continue;
601
602 VECTOR2L pA( ca.center );
603 VECTOR2L pB( cb.center );
604
605 int64_t dist = ( pB - pA ).EuclideanNorm();
606
607 dist -= ca.radius;
608 dist -= cb.radius;
609
610 pairsToTest.emplace_back( dist, ia, ib );
611 }
612 }
613
614 std::sort( pairsToTest.begin(), pairsToTest.end(),
615 []( const DIST_PAIR& a, const DIST_PAIR& b )
616 {
617 return a.dist < b.dist;
618 } );
619
620 const int c_polyPairsLimit = 5;
621
622 // Find closest segments in tested pairs
623 int64_t total_closest_dist_sq = VECTOR2I::ECOORD_MAX;
624
625 for( size_t pairId = 0; pairId < pairsToTest.size() && pairId < c_polyPairsLimit; pairId++ )
626 {
627 const DIST_PAIR& pair = pairsToTest[pairId];
628
629 VECTOR2I ptA;
630 VECTOR2I ptB;
631 int64_t dist_sq;
632
633 size_t myStartId = pair.idA * myPointsPerBox;
634 size_t myEndId = myStartId + myPointsPerBox;
635
636 if( myEndId > myPts.size() )
637 myEndId = myPts.size();
638
639 VECTOR2I myPrevPt = myPts[myStartId == 0 ? myPts.size() - 1 : myStartId - 1];
640
641 size_t otherStartId = pair.idB * otherPointsPerBox;
642 size_t otherEndId = otherStartId + otherPointsPerBox;
643
644 if( otherEndId > otherPts.size() )
645 otherEndId = otherPts.size();
646
647 VECTOR2I otherPrevPt = otherPts[otherStartId == 0 ? otherPts.size() - 1 : otherStartId - 1];
648
649 if( ClosestSegments( myPrevPt, myPts.begin() + myStartId, myPts.begin() + myEndId,
650 otherPrevPt, otherPts.begin() + otherStartId,
651 otherPts.begin() + otherEndId, ptA, ptB, dist_sq ) )
652 {
653 if( dist_sq < total_closest_dist_sq )
654 {
655 total_closest_dist_sq = dist_sq;
656 aPt0 = ptA;
657 aPt1 = ptB;
658 }
659 }
660 }
661
662 return total_closest_dist_sq != VECTOR2I::ECOORD_MAX;
663}
664
665
666bool SHAPE_LINE_CHAIN::ClosestSegments( const VECTOR2I& aMyPrevPt, const point_citer& aMyStart,
667 const point_citer& aMyEnd, const VECTOR2I& aOtherPrevPt,
668 const point_citer& aOtherStart,
669 const point_citer& aOtherEnd, VECTOR2I& aPt0,
670 VECTOR2I& aPt1, int64_t& aDistSq )
671{
672 if( aMyStart == aMyEnd )
673 return false;
674
675 if( aOtherStart == aOtherEnd )
676 return false;
677
678 int64_t closest_dist_sq = VECTOR2I::ECOORD_MAX;
679 VECTOR2I lastPtA = aMyPrevPt;
680
681 for( point_citer itA = aMyStart; itA != aMyEnd; itA++ )
682 {
683 const VECTOR2I& ptA = *itA;
684 VECTOR2I lastPtB = aOtherPrevPt;
685
686 for( point_citer itB = aOtherStart; itB != aOtherEnd; itB++ )
687 {
688 const VECTOR2I& ptB = *itB;
689
690 SEG segA( lastPtA, ptA );
691 SEG segB( lastPtB, ptB );
692
693 VECTOR2I nearestA, nearestB;
694
695 int64_t dist_sq;
696
697 if( segA.NearestPoints( segB, nearestA, nearestB, dist_sq ) )
698 {
699 if( dist_sq < closest_dist_sq )
700 {
701 closest_dist_sq = dist_sq;
702 aPt0 = nearestA;
703 aPt1 = nearestB;
704 }
705 }
706
707 lastPtB = ptB;
708 }
709
710 lastPtA = ptA;
711 }
712
713 aDistSq = closest_dist_sq;
714 return closest_dist_sq != VECTOR2I::ECOORD_MAX;
715}
716
717
718bool SHAPE_LINE_CHAIN::ClosestPoints( const point_citer& aMyStart, const point_citer& aMyEnd,
719 const point_citer& aOtherStart, const point_citer& aOtherEnd,
720 VECTOR2I& aPt0, VECTOR2I& aPt1, int64_t& aDistSq )
721{
722 int64_t closest_dist_sq = VECTOR2I::ECOORD_MAX;
723
724 for( point_citer itA = aMyStart; itA != aMyEnd; itA++ )
725 {
726 const VECTOR2I& ptA = *itA;
727
728 for( point_citer itB = aOtherStart; itB != aOtherEnd; itB++ )
729 {
730 const VECTOR2I& ptB = *itB;
731
732 ecoord dx = (ecoord) ptB.x - ptA.x;
733 ecoord dy = (ecoord) ptB.y - ptA.y;
734
735 SEG::ecoord dist_sq = dx * dx + dy * dy;
736
737 if( dist_sq < closest_dist_sq )
738 {
739 closest_dist_sq = dist_sq;
740 aPt0 = ptA;
741 aPt1 = ptB;
742 }
743 }
744 }
745
746 aDistSq = closest_dist_sq;
747 return closest_dist_sq != VECTOR2I::ECOORD_MAX;
748}
749
750
752 VECTOR2I& aPt1 ) const
753{
754 ecoord dist_sq;
755
756 return ClosestPoints( m_points.cbegin(), m_points.cend(), aOther.m_points.cbegin(),
757 aOther.m_points.cend(), aPt0, aPt1, dist_sq );
758}
759
760
761bool SHAPE_LINE_CHAIN_BASE::Collide( const SEG& aSeg, int aClearance, int* aActual,
762 VECTOR2I* aLocation ) const
763{
764 if( IsClosed() && PointInside( aSeg.A ) )
765 {
766 if( aLocation )
767 *aLocation = aSeg.A;
768
769 if( aActual )
770 *aActual = 0;
771
772 return true;
773 }
774
775 SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX;
776 SEG::ecoord clearance_sq = SEG::Square( aClearance );
777 VECTOR2I nearest;
778
779 for( size_t i = 0; i < GetSegmentCount(); i++ )
780 {
781 const SEG& s = GetSegment( i );
782 SEG::ecoord dist_sq = s.SquaredDistance( aSeg );
783
784 if( dist_sq < closest_dist_sq )
785 {
786 if( aLocation )
787 nearest = s.NearestPoint( aSeg );
788
789 closest_dist_sq = dist_sq;
790
791 if( closest_dist_sq == 0)
792 break;
793
794 // If we're not looking for aActual then any collision will do
795 if( closest_dist_sq < clearance_sq && !aActual )
796 break;
797 }
798 }
799
800 if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
801 {
802 if( aLocation )
803 *aLocation = nearest;
804
805 if( aActual )
806 *aActual = sqrt( closest_dist_sq );
807
808 return true;
809 }
810
811 return false;
812}
813
814
815bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance, int* aActual,
816 VECTOR2I* aLocation ) const
817{
818 if( IsClosed() && PointInside( aSeg.A ) )
819 {
820 if( aLocation )
821 *aLocation = aSeg.A;
822
823 if( aActual )
824 *aActual = 0;
825
826 return true;
827 }
828
829 SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX;
830 SEG::ecoord clearance_sq = SEG::Square( aClearance );
831 VECTOR2I nearest;
832
833 // Collide line segments
834 for( size_t i = 0; i < GetSegmentCount(); i++ )
835 {
836 if( IsArcSegment( i ) )
837 continue;
838
839 const SEG& s = GetSegment( i );
840 SEG::ecoord dist_sq = s.SquaredDistance( aSeg );
841
842 if( dist_sq < closest_dist_sq )
843 {
844 if( aLocation )
845 nearest = s.NearestPoint( aSeg );
846
847 closest_dist_sq = dist_sq;
848
849 if( closest_dist_sq == 0 )
850 break;
851
852 // If we're not looking for aActual then any collision will do
853 if( closest_dist_sq < clearance_sq && !aActual )
854 break;
855 }
856 }
857
858 if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
859 {
860 if( aLocation )
861 *aLocation = nearest;
862
863 if( aActual )
864 *aActual = sqrt( closest_dist_sq );
865
866 return true;
867 }
868
869 int dist = std::numeric_limits<int>::max();
870 SEG::ecoord closest_dist = sqrt( closest_dist_sq );
871
872 // Collide arc segments
873 for( size_t i = 0; i < ArcCount(); i++ )
874 {
875 const SHAPE_ARC& arc = Arc( i );
876 VECTOR2I pos;
877
878 // The arcs in the chain should have zero width
879 wxASSERT_MSG( arc.GetWidth() == 0, wxT( "Invalid arc width - should be zero" ) );
880
881 if( arc.Collide( aSeg, aClearance, aActual || aLocation ? &dist : nullptr,
882 aLocation ? &pos : nullptr ) )
883 {
884 if( !aActual )
885 return true;
886
887 if( dist < closest_dist )
888 {
889 closest_dist = dist;
890 nearest = pos;
891 }
892 }
893 }
894
895 if( closest_dist == 0 || closest_dist < aClearance )
896 {
897 if( aLocation )
898 *aLocation = nearest;
899
900 if( aActual )
901 *aActual = closest_dist;
902
903 return true;
904 }
905
906 return false;
907}
908
909
911{
912 SHAPE_LINE_CHAIN a( *this );
913
914 reverse( a.m_points.begin(), a.m_points.end() );
915 reverse( a.m_shapes.begin(), a.m_shapes.end() );
916 reverse( a.m_arcs.begin(), a.m_arcs.end() );
917
918 for( auto& sh : a.m_shapes )
919 {
920 if( sh != SHAPES_ARE_PT )
921 {
923 [&]( ssize_t& aShapeIndex )
924 {
925 if( aShapeIndex != SHAPE_IS_PT )
926 aShapeIndex = a.m_arcs.size() - aShapeIndex - 1;
927 } );
928
929 if( sh.second != SHAPE_IS_PT )
930 {
931 // If the second element is populated, the first one should be too!
932 assert( sh.first != SHAPE_IS_PT );
933
934 // Switch round first and second in shared points, as part of reversing the chain
935 std::swap( sh.first, sh.second );
936 }
937 }
938 }
939
940 for( SHAPE_ARC& arc : a.m_arcs )
941 arc.Reverse();
942
943 a.m_closed = m_closed;
944
945 return a;
946}
947
948
950{
951 for( ssize_t arcIndex = m_arcs.size() - 1; arcIndex >= 0; --arcIndex )
952 convertArc( arcIndex );
953}
954
955
956long long int SHAPE_LINE_CHAIN::Length() const
957{
958 long long int l = 0;
959
960 for( int i = 0; i < SegmentCount(); i++ )
961 {
962 // Only include segments that aren't part of arc shapes
963 if( !IsArcSegment(i) )
964 l += CSegment( i ).Length();
965 }
966
967 for( size_t i = 0; i < ArcCount(); i++ )
968 l += CArcs()[i].GetLength();
969
970 return l;
971}
972
973
974void SHAPE_LINE_CHAIN::Mirror( const VECTOR2I& aRef, FLIP_DIRECTION aFlipDirection )
975{
976 for( auto& pt : m_points )
977 {
978 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
979 pt.x = -pt.x + 2 * aRef.x;
980 else
981 pt.y = -pt.y + 2 * aRef.y;
982 }
983
984 for( auto& arc : m_arcs )
985 arc.Mirror( aRef, aFlipDirection );
986}
987
988
989void SHAPE_LINE_CHAIN::Mirror( const SEG& axis )
990{
991 for( auto& pt : m_points )
992 pt = axis.ReflectPoint( pt );
993
994 for( auto& arc : m_arcs )
995 arc.Mirror( axis );
996}
997
998
999void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I& aP )
1000{
1001 Remove( aStartIndex, aEndIndex );
1002 Insert( aStartIndex, aP );
1003 assert( m_shapes.size() == m_points.size() );
1004}
1005
1006
1007void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE_CHAIN& aLine )
1008{
1009 if( aEndIndex < 0 )
1010 aEndIndex += PointCount();
1011
1012 if( aStartIndex < 0 )
1013 aStartIndex += PointCount();
1014
1015 // We only process lines in order in this house
1016 wxASSERT( aStartIndex <= aEndIndex );
1017 wxASSERT( aEndIndex < static_cast<int>( m_points.size() ) );
1018
1019 SHAPE_LINE_CHAIN newLine = aLine;
1020
1021 // Zero points to add?
1022 if( newLine.PointCount() == 0 )
1023 {
1024 Remove( aStartIndex, aEndIndex );
1025 return;
1026 }
1027
1028 // Remove coincident points in the new line
1029 if( newLine.m_points.front() == m_points[aStartIndex] )
1030 {
1031 aStartIndex++;
1032 newLine.Remove( 0 );
1033
1034 // Zero points to add?
1035 if( newLine.PointCount() == 0 )
1036 {
1037 Remove( aStartIndex, aEndIndex );
1038 return;
1039 }
1040 }
1041
1042 if( newLine.m_points.back() == m_points[aEndIndex] && aEndIndex > 0 )
1043 {
1044 aEndIndex--;
1045 newLine.Remove( -1 );
1046 }
1047
1048 Remove( aStartIndex, aEndIndex );
1049
1050 // Zero points to add?
1051 if( newLine.PointCount() == 0 )
1052 return;
1053
1054 // The total new arcs index is added to the new arc indices
1055 size_t prev_arc_count = m_arcs.size();
1056 std::vector<std::pair<ssize_t, ssize_t>> new_shapes = newLine.m_shapes;
1057
1058 for( std::pair<ssize_t, ssize_t>& shape_pair : new_shapes )
1059 {
1060 alg::run_on_pair( shape_pair,
1061 [&]( ssize_t& aShape )
1062 {
1063 if( aShape != SHAPE_IS_PT )
1064 aShape += prev_arc_count;
1065 } );
1066 }
1067
1068 m_shapes.insert( m_shapes.begin() + aStartIndex, new_shapes.begin(), new_shapes.end() );
1069 m_points.insert( m_points.begin() + aStartIndex, newLine.m_points.begin(),
1070 newLine.m_points.end() );
1071 m_arcs.insert( m_arcs.end(), newLine.m_arcs.begin(), newLine.m_arcs.end() );
1072
1073 assert( m_shapes.size() == m_points.size() );
1074}
1075
1076
1077void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex )
1078{
1079 wxCHECK( m_shapes.size() == m_points.size(), /*void*/ );
1080
1081 // Unwrap the chain first (correctly handling removing arc at
1082 // end of chain coincident with start)
1083 bool closedState = IsClosed();
1084 SetClosed( false );
1085
1086 if( aEndIndex < 0 )
1087 aEndIndex += PointCount();
1088
1089 if( aStartIndex < 0 )
1090 aStartIndex += PointCount();
1091
1092 if( aStartIndex >= PointCount() || aEndIndex >= PointCount() || aStartIndex > aEndIndex)
1093 {
1094 SetClosed( closedState );
1095 return;
1096 }
1097
1098
1099 // Split arcs, making arcs coincident
1100 if( !IsArcStart( aStartIndex ) && IsPtOnArc( aStartIndex ) )
1101 splitArc( aStartIndex, false );
1102
1103 if( IsSharedPt( aStartIndex ) ) // Don't delete the shared point
1104 aStartIndex += 1;
1105
1106 if( !IsArcEnd( aEndIndex ) && IsPtOnArc( aEndIndex ) && aEndIndex < PointCount() - 1 )
1107 splitArc( aEndIndex + 1, true );
1108
1109 if( IsSharedPt( aEndIndex ) ) // Don't delete the shared point
1110 aEndIndex -= 1;
1111
1112 if( aStartIndex > aEndIndex )
1113 {
1114 SetClosed( closedState );
1115 return;
1116 }
1117
1118 std::set<size_t> extra_arcs;
1119 auto logArcIdxRemoval = [&]( ssize_t& aShapeIndex )
1120 {
1121 if( aShapeIndex != SHAPE_IS_PT )
1122 extra_arcs.insert( aShapeIndex );
1123 };
1124
1125 // Remove any overlapping arcs in the point range
1126 for( int i = aStartIndex; i <= aEndIndex; i++ )
1127 {
1128 if( IsSharedPt( i ) )
1129 {
1130 if( i == aStartIndex )
1131 {
1132 logArcIdxRemoval( m_shapes[i].second ); // Only remove the arc on the second index
1133
1134 // Ensure that m_shapes has been built correctly.
1135 assert( i < aEndIndex || m_shapes[i + 1].first == m_shapes[i].second );
1136
1137 continue;
1138 }
1139 else if( i == aEndIndex )
1140 {
1141 logArcIdxRemoval( m_shapes[i].first ); // Only remove the arc on the first index
1142
1143 // Ensure that m_shapes has been built correctly.
1144 assert( i > aStartIndex || ( IsSharedPt( i - 1 )
1145 ? m_shapes[i - 1].second == m_shapes[i].first
1146 : m_shapes[i - 1].first == m_shapes[i].first ) );
1147 continue;
1148 }
1149 }
1150 else
1151 {
1152 alg::run_on_pair( m_shapes[i], logArcIdxRemoval );
1153 }
1154 }
1155
1156 for( auto arc : extra_arcs )
1157 convertArc( arc );
1158
1159 m_shapes.erase( m_shapes.begin() + aStartIndex, m_shapes.begin() + aEndIndex + 1 );
1160 m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 );
1161 assert( m_shapes.size() == m_points.size() );
1162
1163 SetClosed( closedState );
1164}
1165
1166
1168{
1170
1171 if( IsClosed() && PointInside( aP ) && !aOutlineOnly )
1172 return 0;
1173
1174 for( size_t s = 0; s < GetSegmentCount(); s++ )
1175 d = std::min( d, GetSegment( s ).SquaredDistance( aP ) );
1176
1177 return d;
1178}
1179
1180
1181int SHAPE_LINE_CHAIN::Split( const VECTOR2I& aP, bool aExact )
1182{
1183 int ii = -1;
1184 int min_dist = 2;
1185
1186 int found_index = Find( aP );
1187
1188 if( found_index >= 0 && aExact )
1189 return found_index;
1190
1191 for( int s = 0; s < SegmentCount(); s++ )
1192 {
1193 const SEG seg = CSegment( s );
1194 int dist = seg.Distance( aP );
1195
1196 // make sure we are not producing a 'slightly concave' primitive. This might happen
1197 // if aP lies very close to one of already existing points.
1198 if( dist < min_dist && seg.A != aP && seg.B != aP )
1199 {
1200 min_dist = dist;
1201 if( found_index < 0 )
1202 ii = s;
1203 else if( s < found_index )
1204 ii = s;
1205 }
1206 }
1207
1208 if( ii < 0 )
1209 ii = found_index;
1210
1211 if( ii >= 0 )
1212 {
1213 // Don't create duplicate points
1214 if( GetPoint( ii ) == aP )
1215 return ii;
1216
1217 size_t newIndex = static_cast<size_t>( ii ) + 1;
1218
1219 if( IsArcSegment( ii ) )
1220 {
1221 m_points.insert( m_points.begin() + newIndex, aP );
1222 m_shapes.insert( m_shapes.begin() + newIndex, { ArcIndex( ii ), SHAPE_IS_PT } );
1223 splitArc( newIndex, true ); // Make the inserted point a shared point
1224 }
1225 else
1226 {
1227 Insert( newIndex, aP );
1228 }
1229
1230 return newIndex;
1231 }
1232
1233 return -1;
1234}
1235
1236
1237int SHAPE_LINE_CHAIN::Find( const VECTOR2I& aP, int aThreshold ) const
1238{
1239 for( int s = 0; s < PointCount(); s++ )
1240 {
1241 if( aThreshold == 0 )
1242 {
1243 if( CPoint( s ) == aP )
1244 return s;
1245 }
1246 else
1247 {
1248 if( (CPoint( s ) - aP).EuclideanNorm() <= aThreshold )
1249 return s;
1250 }
1251 }
1252
1253 return -1;
1254}
1255
1256
1257int SHAPE_LINE_CHAIN::FindSegment( const VECTOR2I& aP, int aThreshold ) const
1258{
1259 for( int s = 0; s < SegmentCount(); s++ )
1260 {
1261 if( CSegment( s ).Distance( aP ) <= aThreshold )
1262 return s;
1263 }
1264
1265 return -1;
1266}
1267
1268
1270{
1271 wxCHECK2_MSG( m_points.size() == m_shapes.size(), return 0, "Invalid chain!" );
1272
1273 if( m_points.size() < 2 )
1274 return 0;
1275
1276 int numShapes = 1;
1277
1278 for( int i = NextShape( 0 ); i != -1; i = NextShape( i ) )
1279 numShapes++;
1280
1281 return numShapes;
1282}
1283
1284
1286{
1287 int segCount = SegmentCount();
1288
1289 if( aIndex < 0 )
1290 aIndex += segCount;
1291
1292 wxCHECK( aIndex < segCount && aIndex >= 0,
1293 m_points.size() > 0 ? SEG( m_points.back(), m_points.back() ) : SEG( 0, 0, 0, 0 ) );
1294
1295 if( aIndex == (int) ( m_points.size() - 1 ) && m_closed )
1296 return SEG( m_points[aIndex], m_points[0], aIndex );
1297 else
1298 return SEG( m_points[aIndex], m_points[aIndex + 1], aIndex );
1299}
1300
1301
1302int SHAPE_LINE_CHAIN::NextShape( int aPointIndex ) const
1303{
1304 if( aPointIndex < 0 )
1305 aPointIndex += PointCount();
1306
1307 if( aPointIndex < 0 )
1308 return -1;
1309
1310 int lastIndex = PointCount() - 1;
1311
1312 // Last point?
1313 if( aPointIndex >= lastIndex )
1314 return -1; // we don't want to wrap around
1315
1316 if( m_shapes[aPointIndex] == SHAPES_ARE_PT )
1317 {
1318 if( aPointIndex == lastIndex - 1 )
1319 {
1320 if( m_closed )
1321 return lastIndex;
1322 else
1323 return -1;
1324 }
1325 else
1326 {
1327 return aPointIndex + 1;
1328 }
1329 }
1330
1331 int arcStart = aPointIndex;
1332
1333 // The second element should only get populated when the point is shared between two shapes.
1334 // If not a shared point, then the index should always go on the first element.
1335 wxCHECK2_MSG( m_shapes[aPointIndex].first != SHAPE_IS_PT, return -1, "malformed chain!" );
1336
1337 ssize_t currentArcIdx = ArcIndex( aPointIndex );
1338
1339 // Now skip the rest of the arc
1340 while( aPointIndex < lastIndex && ArcIndex( aPointIndex ) == currentArcIdx )
1341 aPointIndex += 1;
1342
1343 bool indexStillOnArc = alg::pair_contains( m_shapes[aPointIndex], currentArcIdx );
1344
1345 // We want the last vertex of the arc if the initial point was the start of one
1346 // Well-formed arcs should generate more than one point to travel above
1347 if( aPointIndex - arcStart > 1 && !indexStillOnArc )
1348 aPointIndex -= 1;
1349
1350 if( aPointIndex == lastIndex )
1351 {
1352 if( !m_closed || IsArcSegment( aPointIndex ) )
1353 return -1; //no shape
1354 else
1355 return lastIndex; // Segment between last point and the start of the chain
1356 }
1357
1358 return aPointIndex;
1359}
1360
1361
1362void SHAPE_LINE_CHAIN::SetPoint( int aIndex, const VECTOR2I& aPos )
1363{
1364 if( aIndex < 0 )
1365 aIndex += PointCount();
1366 else if( aIndex >= PointCount() )
1367 aIndex -= PointCount();
1368
1369 m_points[aIndex] = aPos;
1370
1371 alg::run_on_pair( m_shapes[aIndex],
1372 [&]( ssize_t& aIdx )
1373 {
1374 if( aIdx != SHAPE_IS_PT )
1375 convertArc( aIdx );
1376 } );
1377}
1378
1379
1380void SHAPE_LINE_CHAIN::RemoveShape( int aPointIndex )
1381{
1382 if( aPointIndex < 0 )
1383 aPointIndex += PointCount();
1384
1385 if( aPointIndex >= PointCount() || aPointIndex < 0 )
1386 return; // Invalid index, fail gracefully
1387
1388 if( m_shapes[aPointIndex] == SHAPES_ARE_PT )
1389 {
1390 Remove( aPointIndex );
1391 return;
1392 }
1393
1394 int start = aPointIndex;
1395 int end = aPointIndex;
1396 int arcIdx = ArcIndex( aPointIndex );
1397
1398 if( !IsArcStart( start ) )
1399 {
1400 // aPointIndex is not a shared point, so iterate backwards to find the start of the arc
1401 while( start > 0 && ArcIndex( static_cast<ssize_t>( start ) - 1 ) == arcIdx )
1402 start--;
1403 }
1404
1405 if( !IsArcEnd( end ) || start == end )
1406 end = NextShape( end ); // can be -1 to indicate end of chain
1407
1408 Remove( start, end );
1409}
1410
1411
1412const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex ) const
1413{
1414 return Slice( aStartIndex, aEndIndex, getArcPolygonizationMaxError() );
1415}
1416
1417
1418const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex, int aMaxError ) const
1419{
1421
1422 if( aEndIndex < 0 )
1423 aEndIndex += PointCount();
1424
1425 if( aStartIndex < 0 )
1426 aStartIndex += PointCount();
1427
1428 // Bad programmer checks
1429 wxCHECK( aStartIndex >= 0, SHAPE_LINE_CHAIN() );
1430 wxCHECK( aEndIndex >= 0, SHAPE_LINE_CHAIN() );
1431 wxCHECK( aStartIndex < PointCount(), SHAPE_LINE_CHAIN() );
1432 wxCHECK( aEndIndex < PointCount(), SHAPE_LINE_CHAIN() );
1433 wxCHECK( aEndIndex >= aStartIndex, SHAPE_LINE_CHAIN() );
1434
1435 int numPoints = static_cast<int>( PointCount() );
1436
1437 if( IsArcSegment( aStartIndex ) && !IsArcStart( aStartIndex ) )
1438 {
1439 // Cutting in middle of an arc, lets split it
1440 ssize_t arcToSplitIndex = ArcIndex( aStartIndex );
1441 const SHAPE_ARC& arcToSplit = Arc( arcToSplitIndex );
1442
1443 // Copy the points as arc points
1444 for( int i = aStartIndex; i < aEndIndex && arcToSplitIndex == ArcIndex( i ); i++ )
1445 {
1446 rv.m_points.push_back( m_points[i] );
1447 rv.m_shapes.push_back( { rv.m_arcs.size(), SHAPE_IS_PT } );
1448 rv.m_bbox.Merge( m_points[i] );
1449 }
1450
1451 // Create a new arc from the existing one, with different start point.
1452 SHAPE_ARC newArc;
1453 VECTOR2I newArcStart = m_points[aStartIndex];
1454
1455 newArc.ConstructFromStartEndCenter( newArcStart, arcToSplit.GetP1(), arcToSplit.GetCenter(),
1456 arcToSplit.IsClockwise() );
1457
1458 rv.m_arcs.push_back( newArc );
1459
1460 aStartIndex += rv.PointCount();
1461 }
1462
1463 for( int i = aStartIndex; i <= aEndIndex && i < numPoints; i = NextShape( i ) )
1464 {
1465 if( i == -1 )
1466 return rv; // NextShape reached the end
1467
1468 int nextShape = NextShape( i );
1469 bool isLastShape = nextShape < 0;
1470
1471 if( IsArcStart( i ) )
1472 {
1473 if( ( isLastShape && aEndIndex != ( numPoints - 1 ) )
1474 || ( nextShape > aEndIndex ) )
1475 {
1476 if( i == aEndIndex )
1477 {
1478 // Single point on an arc, just append the single point
1479 rv.Append( m_points[i] );
1480 return rv;
1481 }
1482
1483 // Cutting in middle of an arc, lets split it
1484 ssize_t arcIndex = ArcIndex( i );
1485 const SHAPE_ARC& currentArc = Arc( arcIndex );
1486
1487 // Copy the points as arc points
1488 for( ; i <= aEndIndex && i < numPoints; i++ )
1489 {
1490 if( arcIndex != ArcIndex( i ) )
1491 break;
1492
1493 rv.m_points.push_back( m_points[i] );
1494 rv.m_shapes.push_back( { rv.m_arcs.size(), SHAPE_IS_PT } );
1495 rv.m_bbox.Merge( m_points[i] );
1496 }
1497
1498 // Create a new arc from the existing one, with different end point.
1499 SHAPE_ARC newArc;
1500 VECTOR2I newArcEnd = m_points[aEndIndex];
1501
1502 newArc.ConstructFromStartEndCenter( currentArc.GetP0(), newArcEnd, currentArc.GetCenter(),
1503 currentArc.IsClockwise() );
1504
1505 rv.m_arcs.push_back( newArc );
1506
1507 return rv;
1508 }
1509 else
1510 {
1511 // append the whole arc
1512 const SHAPE_ARC& currentArc = Arc( ArcIndex( i ) );
1513 rv.Append( currentArc, aMaxError );
1514 }
1515
1516 if( isLastShape )
1517 return rv;
1518 }
1519 else
1520 {
1521 wxASSERT_MSG( !IsArcSegment( i ), wxT( "Still on an arc segment, we missed something..." ) );
1522
1523 if( i == aStartIndex )
1524 rv.Append( m_points[i] );
1525
1526 bool nextPointIsArc = isLastShape ? false : IsArcSegment( nextShape );
1527
1528 if( !nextPointIsArc && i < SegmentCount() && i < aEndIndex )
1529 rv.Append( GetSegment( i ).B );
1530 }
1531 }
1532
1533 wxASSERT( rv.m_points.size() == rv.m_shapes.size() );
1534
1535 return rv;
1536}
1537
1538
1540{
1541 assert( m_shapes.size() == m_points.size() );
1542
1543 if( aOtherLine.PointCount() == 0 )
1544 {
1545 return;
1546 }
1547
1548 size_t num_arcs = m_arcs.size();
1549 m_arcs.insert( m_arcs.end(), aOtherLine.m_arcs.begin(), aOtherLine.m_arcs.end() );
1550
1551 auto fixShapeIndices =
1552 [&]( const std::pair<ssize_t, ssize_t>& aShapeIndices ) -> std::pair<ssize_t, ssize_t>
1553 {
1554 std::pair<ssize_t, ssize_t> retval = aShapeIndices;
1555
1556 alg::run_on_pair( retval, [&]( ssize_t& aIndex )
1557 {
1558 if( aIndex != SHAPE_IS_PT )
1559 aIndex = aIndex + num_arcs;
1560 } );
1561
1562 return retval;
1563 };
1564
1565 if( PointCount() == 0 || aOtherLine.CPoint( 0 ) != CLastPoint() )
1566 {
1567 const VECTOR2I& p = aOtherLine.CPoint( 0 );
1568 m_points.push_back( p );
1569 m_shapes.push_back( fixShapeIndices( aOtherLine.CShapes()[0] ) );
1570 m_bbox.Merge( p );
1571 }
1572 else if( aOtherLine.IsArcSegment( 0 ) )
1573 {
1574 // Associate the new arc shape with the last point of this chain
1575 if( m_shapes.back() == SHAPES_ARE_PT )
1576 m_shapes.back().first = aOtherLine.CShapes()[0].first + num_arcs;
1577 else
1578 m_shapes.back().second = aOtherLine.CShapes()[0].first + num_arcs;
1579 }
1580
1581
1582 for( int i = 1; i < aOtherLine.PointCount(); i++ )
1583 {
1584 const VECTOR2I& p = aOtherLine.CPoint( i );
1585 m_points.push_back( p );
1586
1587 ssize_t arcIndex = aOtherLine.ArcIndex( i );
1588
1589 if( arcIndex != ssize_t( SHAPE_IS_PT ) )
1590 {
1591 m_shapes.push_back( fixShapeIndices( aOtherLine.m_shapes[i] ) );
1592 }
1593 else
1594 m_shapes.push_back( SHAPES_ARE_PT );
1595
1596 m_bbox.Merge( p );
1597 }
1598
1600
1601 assert( m_shapes.size() == m_points.size() );
1602}
1603
1604
1606{
1608}
1609
1610
1611void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc, int aMaxError )
1612{
1613 SHAPE_LINE_CHAIN chain = aArc.ConvertToPolyline( aMaxError );
1614
1615 if( chain.PointCount() > 2 )
1616 {
1617 chain.m_arcs.push_back( aArc );
1618 chain.m_arcs.back().SetWidth( 0 );
1619
1620 for( auto& sh : chain.m_shapes )
1621 sh.first = 0;
1622 }
1623
1624 Append( chain );
1625
1626 assert( m_shapes.size() == m_points.size() );
1627}
1628
1629
1630void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const VECTOR2I& aP )
1631{
1632 if( aVertex == m_points.size() )
1633 {
1634 Append( aP );
1635 return;
1636 }
1637
1638 wxCHECK( aVertex < m_points.size(), /* void */ );
1639
1640 if( aVertex > 0 && IsPtOnArc( aVertex ) )
1641 splitArc( aVertex );
1642
1643 //@todo need to check we aren't creating duplicate points
1644 m_points.insert( m_points.begin() + aVertex, aP );
1645 m_shapes.insert( m_shapes.begin() + aVertex, SHAPES_ARE_PT );
1646
1647 assert( m_shapes.size() == m_points.size() );
1648}
1649
1650
1651void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc )
1652{
1653 Insert( aVertex, aArc, getArcPolygonizationMaxError() );
1654}
1655
1656
1657void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc, int aMaxError )
1658{
1659 wxCHECK( aVertex < m_points.size(), /* void */ );
1660
1661 if( aVertex > 0 && IsPtOnArc( aVertex ) )
1662 splitArc( aVertex );
1663
1665 ssize_t arc_pos = m_arcs.size();
1666
1667 for( auto arc_it = m_shapes.rbegin() ;
1668 arc_it != m_shapes.rend() + aVertex;
1669 arc_it++ )
1670 {
1671 if( *arc_it != SHAPES_ARE_PT )
1672 {
1673 arc_pos = std::max( ( *arc_it ).first, ( *arc_it ).second );
1674 arc_pos++;
1675 }
1676 }
1677
1678 //Increment all arc indices before inserting the new arc
1679 for( auto& sh : m_shapes )
1680 {
1681 alg::run_on_pair( sh,
1682 [&]( ssize_t& aIndex )
1683 {
1684 if( aIndex >= arc_pos )
1685 aIndex++;
1686 } );
1687 }
1688
1689 SHAPE_ARC arcCopy( aArc );
1690 arcCopy.SetWidth( 0 );
1691 m_arcs.insert( m_arcs.begin() + arc_pos, arcCopy );
1692
1694 //@todo need to check we aren't creating duplicate points at start or end
1695 auto& chain = aArc.ConvertToPolyline( aMaxError );
1696 m_points.insert( m_points.begin() + aVertex, chain.CPoints().begin(), chain.CPoints().end() );
1697
1699 //@todo need to check we aren't creating duplicate points at start or end
1700 std::vector<std::pair<ssize_t, ssize_t>> new_points( chain.PointCount(),
1701 { arc_pos, SHAPE_IS_PT } );
1702
1703 m_shapes.insert( m_shapes.begin() + aVertex, new_points.begin(), new_points.end() );
1704 assert( m_shapes.size() == m_points.size() );
1705}
1706
1707
1709{
1711 m_origin( aOrigin ) {};
1712
1714 const SHAPE_LINE_CHAIN::INTERSECTION& aB ) const
1715 {
1716 return ( VECTOR2L( m_origin ) - aA.p ).SquaredEuclideanNorm()
1717 < ( VECTOR2L( m_origin ) - aB.p ).SquaredEuclideanNorm();
1718 }
1719
1721};
1722
1723
1724int SHAPE_LINE_CHAIN::Intersect( const SEG& aSeg, INTERSECTIONS& aIp ) const
1725{
1726 const int segCount = SegmentCount();
1727 const int ptCount = static_cast<int>( m_points.size() );
1728
1729 const int segMinX = std::min( aSeg.A.x, aSeg.B.x );
1730 const int segMaxX = std::max( aSeg.A.x, aSeg.B.x );
1731 const int segMinY = std::min( aSeg.A.y, aSeg.B.y );
1732 const int segMaxY = std::max( aSeg.A.y, aSeg.B.y );
1733
1734 for( int s = 0; s < segCount; s++ )
1735 {
1736 const VECTOR2I& ptA = m_points[s];
1737 const VECTOR2I& ptB = m_points[s + 1 < ptCount ? s + 1 : 0];
1738
1739 if( std::max( ptA.x, ptB.x ) < segMinX || std::min( ptA.x, ptB.x ) > segMaxX
1740 || std::max( ptA.y, ptB.y ) < segMinY || std::min( ptA.y, ptB.y ) > segMaxY )
1741 {
1742 continue;
1743 }
1744
1745 OPT_VECTOR2I p = SEG( ptA, ptB, s ).Intersect( aSeg );
1746
1747 if( p )
1748 {
1749 INTERSECTION is;
1750 is.valid = true;
1751 is.index_our = s;
1752 is.index_their = -1;
1753 is.is_corner_our = is.is_corner_their = false;
1754 is.p = *p;
1755 aIp.push_back( is );
1756 }
1757 }
1758
1760 sort( aIp.begin(), aIp.end(), comp );
1761
1762 return aIp.size();
1763}
1764
1765
1766bool SHAPE_LINE_CHAIN::Intersects( const SEG& aSeg ) const
1767{
1768 const int segCount = SegmentCount();
1769 const int ptCount = static_cast<int>( m_points.size() );
1770
1771 const int segMinX = std::min( aSeg.A.x, aSeg.B.x );
1772 const int segMaxX = std::max( aSeg.A.x, aSeg.B.x );
1773 const int segMinY = std::min( aSeg.A.y, aSeg.B.y );
1774 const int segMaxY = std::max( aSeg.A.y, aSeg.B.y );
1775
1776 for( int s = 0; s < segCount; s++ )
1777 {
1778 const VECTOR2I& ptA = m_points[s];
1779 const VECTOR2I& ptB = m_points[s + 1 < ptCount ? s + 1 : 0];
1780
1781 if( std::max( ptA.x, ptB.x ) < segMinX || std::min( ptA.x, ptB.x ) > segMaxX
1782 || std::max( ptA.y, ptB.y ) < segMinY || std::min( ptA.y, ptB.y ) > segMaxY )
1783 {
1784 continue;
1785 }
1786
1787 if( SEG( ptA, ptB ).Intersects( aSeg ) )
1788 return true;
1789 }
1790
1791 return false;
1792}
1793
1794
1796 bool aExcludeColinearAndTouching, BOX2I* aChainBBox ) const
1797{
1798 const int ourSegCount = SegmentCount();
1799 const int theirSegCount = aChain.SegmentCount();
1800
1801 if( ourSegCount == 0 || theirSegCount == 0 )
1802 return 0;
1803
1804 const BOX2I_MINMAX bbOther( aChainBBox ? *aChainBBox : aChain.BBox() );
1805 const int ourPtCount = static_cast<int>( m_points.size() );
1806 const int theirPtCount = static_cast<int>( aChain.CPoints().size() );
1807 const std::vector<VECTOR2I>& theirPts = aChain.CPoints();
1808
1809 thread_local std::vector<SEG> theirSegs;
1810 thread_local std::vector<SEG_EXTENT> sorted;
1811
1812 theirSegs.resize( theirSegCount );
1813 sorted.resize( theirSegCount );
1814
1815 // Pre-build SEGs for the other chain so each is constructed exactly once
1816 for( int i = 0; i < theirSegCount; i++ )
1817 {
1818 const VECTOR2I& pa = theirPts[i];
1819 const VECTOR2I& pb = theirPts[i + 1 < theirPtCount ? i + 1 : 0];
1820 theirSegs[i] = SEG( pa, pb, i );
1821 }
1822
1823 // Compact extent array for cache-friendly scanning, sorted by minX
1824 for( int i = 0; i < theirSegCount; i++ )
1825 {
1826 const SEG& s = theirSegs[i];
1827 sorted[i] = { std::min( s.A.x, s.B.x ), std::max( s.A.x, s.B.x ),
1828 std::min( s.A.y, s.B.y ), std::max( s.A.y, s.B.y ), i };
1829 }
1830
1831 std::sort( sorted.begin(), sorted.end(),
1832 []( const SEG_EXTENT& a, const SEG_EXTENT& b ) { return a.minX < b.minX; } );
1833
1834 for( int s1 = 0; s1 < ourSegCount; s1++ )
1835 {
1836 const VECTOR2I& a1 = m_points[s1];
1837 const VECTOR2I& b1 = m_points[s1 + 1 < ourPtCount ? s1 + 1 : 0];
1838
1839 const int ourMinX = std::min( a1.x, b1.x );
1840 const int ourMaxX = std::max( a1.x, b1.x );
1841 const int ourMinY = std::min( a1.y, b1.y );
1842 const int ourMaxY = std::max( a1.y, b1.y );
1843
1844 if( ourMaxX < bbOther.m_Left || ourMinX > bbOther.m_Right
1845 || ourMaxY < bbOther.m_Top || ourMinY > bbOther.m_Bottom )
1846 {
1847 continue;
1848 }
1849
1850 const SEG a( a1, b1, s1 );
1851
1852 // Find the right boundary in the sorted extents: first entry where minX > ourMaxX.
1853 // Everything past this point is too far right to overlap.
1854 auto rightEnd = std::upper_bound( sorted.begin(), sorted.end(), ourMaxX,
1855 []( int val, const SEG_EXTENT& e )
1856 {
1857 return val < e.minX;
1858 } );
1859
1860 for( auto jt = sorted.begin(); jt != rightEnd; ++jt )
1861 {
1862 if( jt->maxX < ourMinX || jt->maxY < ourMinY || jt->minY > ourMaxY )
1863 continue;
1864
1865 const SEG& b = theirSegs[jt->segIdx];
1866
1867 INTERSECTION is;
1868 is.index_our = s1;
1869 is.index_their = jt->segIdx;
1870 is.is_corner_our = false;
1871 is.is_corner_their = false;
1872 is.valid = true;
1873
1874 OPT_VECTOR2I p = a.Intersect( b );
1875
1876 if( !aExcludeColinearAndTouching && a.Collinear( b ) )
1877 {
1878 if( a.Contains( b.A ) )
1879 {
1880 is.p = b.A;
1881 is.is_corner_their = true;
1882 aIp.push_back( is );
1883 }
1884
1885 if( a.Contains( b.B ) )
1886 {
1887 is.p = b.B;
1888 is.index_their++;
1889 is.is_corner_their = true;
1890 aIp.push_back( is );
1891 }
1892
1893 if( b.Contains( a.A ) )
1894 {
1895 is.p = a.A;
1896 is.is_corner_our = true;
1897 aIp.push_back( is );
1898 }
1899
1900 if( b.Contains( a.B ) )
1901 {
1902 is.p = a.B;
1903 is.index_our++;
1904 is.is_corner_our = true;
1905 aIp.push_back( is );
1906 }
1907 }
1908 else if( p )
1909 {
1910 is.p = *p;
1911 is.is_corner_our = false;
1912 is.is_corner_their = false;
1913
1914 if( p == a.A )
1915 {
1916 is.is_corner_our = true;
1917 }
1918
1919 if( p == a.B )
1920 {
1921 is.is_corner_our = true;
1922 is.index_our++;
1923 }
1924
1925 if( p == b.A )
1926 {
1927 is.is_corner_their = true;
1928 }
1929
1930 if( p == b.B )
1931 {
1932 is.is_corner_their = true;
1933 is.index_their++;
1934 }
1935
1936 aIp.push_back( is );
1937 }
1938 }
1939 }
1940
1941 return aIp.size();
1942}
1943
1944
1945int SHAPE_LINE_CHAIN::PathLength( const VECTOR2I& aP, int aIndex ) const
1946{
1947 int sum = 0;
1948
1949 for( int i = 0; i < SegmentCount(); i++ )
1950 {
1951 const SEG seg = CSegment( i );
1952 bool indexMatch = false;
1953
1954 if( aIndex >= 0 )
1955 {
1956 if( aIndex == SegmentCount() )
1957 {
1958 indexMatch = ( i == SegmentCount() - 1 );
1959 }
1960 else
1961 {
1962 indexMatch = ( i == aIndex );
1963 }
1964 }
1965
1966 if( indexMatch )
1967 {
1968 sum += ( aP - seg.A ).EuclideanNorm();
1969 return sum;
1970 }
1971 else
1972 {
1973 sum += seg.Length();
1974 }
1975 }
1976
1977 return -1;
1978}
1979
1980
1981bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy, bool aUseBBoxCache ) const
1982{
1983 if( aUseBBoxCache && GetCachedBBox() && !GetCachedBBox()->Contains( aPt ) )
1984 return false;
1985
1986 const int pointCount = static_cast<int>( m_points.size() );
1987
1988 if( !m_closed || pointCount < 3 )
1989 return false;
1990
1991 // Direct access to m_points avoids virtual GetPoint() dispatch and per-call bounds checking
1992 const VECTOR2I* pts = m_points.data();
1993 bool inside = false;
1994
1995 for( int i = 0; i < pointCount; )
1996 {
1997 const VECTOR2I& p1 = pts[i++];
1998 const VECTOR2I& p2 = pts[i == pointCount ? 0 : i];
1999 const VECTOR2I diff = p2 - p1;
2000
2001 if( diff.y == 0 )
2002 continue;
2003
2004 const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
2005
2006 if( ( ( p1.y >= aPt.y ) != ( p2.y >= aPt.y ) ) && ( aPt.x - p1.x < d ) )
2007 inside = !inside;
2008 }
2009
2010 if( aAccuracy <= 1 )
2011 return inside;
2012 else
2013 return inside || PointOnEdge( aPt, aAccuracy );
2014}
2015
2016
2017bool SHAPE_LINE_CHAIN_BASE::PointInside( const VECTOR2I& aPt, int aAccuracy,
2018 bool aUseBBoxCache ) const
2019{
2020 /*
2021 * Don't check the bounding box unless it's cached. Building it is about the same speed as
2022 * the rigorous test below and so just slows things down by doing potentially two tests.
2023 */
2024 if( aUseBBoxCache && GetCachedBBox() && !GetCachedBBox()->Contains( aPt ) )
2025 return false;
2026
2027 if( !IsClosed() || GetPointCount() < 3 )
2028 return false;
2029
2030 /*
2031 * To check for interior points, we draw a line in the positive x direction from
2032 * the point. If it intersects an even number of segments, the point is outside the
2033 * line chain (it had to first enter and then exit). Otherwise, it is inside the chain.
2034 *
2035 * Note: slope might be denormal here in the case of a horizontal line but we require our
2036 * y to move from above to below the point (or vice versa)
2037 *
2038 * Note: we open-code CPoint() here so that we don't end up calculating the size of the
2039 * vector number-of-points times. This has a non-trivial impact on zone fill times.
2040 */
2041 int pointCount = GetPointCount();
2042 bool inside = false;
2043
2044 for( int i = 0; i < pointCount; )
2045 {
2046 const VECTOR2I p1 = GetPoint( i++ );
2047 const VECTOR2I p2 = GetPoint( i == pointCount ? 0 : i );
2048 const VECTOR2I diff = p2 - p1;
2049
2050 if( diff.y == 0 )
2051 continue;
2052
2053 const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
2054
2055 if( ( ( p1.y >= aPt.y ) != ( p2.y >= aPt.y ) ) && ( aPt.x - p1.x < d ) )
2056 inside = !inside;
2057 }
2058
2059 // If accuracy is <= 1 (nm) then we skip the accuracy test for performance. Otherwise
2060 // we use "OnEdge(accuracy)" as a proxy for "Inside(accuracy)".
2061 if( aAccuracy <= 1 )
2062 return inside;
2063 else
2064 return inside || PointOnEdge( aPt, aAccuracy );
2065}
2066
2067
2068bool SHAPE_LINE_CHAIN_BASE::PointOnEdge( const VECTOR2I& aPt, int aAccuracy ) const
2069{
2070 return EdgeContainingPoint( aPt, aAccuracy ) >= 0;
2071}
2072
2073
2074int SHAPE_LINE_CHAIN_BASE::EdgeContainingPoint( const VECTOR2I& aPt, int aAccuracy ) const
2075{
2076 const int threshold = aAccuracy + 1;
2077 const int64_t thresholdSq = int64_t( threshold ) * threshold;
2078 const size_t pointCount = GetPointCount();
2079
2080 if( !pointCount )
2081 {
2082 return -1;
2083 }
2084 else if( pointCount == 1 )
2085 {
2086 SEG::ecoord distSq = GetPoint( 0 ).SquaredDistance( aPt );
2087 return distSq <= thresholdSq ? 0 : -1;
2088 }
2089
2090 const size_t segCount = GetSegmentCount();
2091
2092 for( size_t i = 0; i < segCount; i++ )
2093 {
2094 const SEG s = GetSegment( i );
2095
2096 if( s.A == aPt || s.B == aPt )
2097 return i;
2098
2099 if( s.SquaredDistance( aPt ) <= thresholdSq )
2100 return i;
2101 }
2102
2103 return -1;
2104}
2105
2106
2107bool SHAPE_LINE_CHAIN::CheckClearance( const VECTOR2I& aP, const int aDist ) const
2108{
2109 if( !PointCount() )
2110 return false;
2111 else if( PointCount() == 1 )
2112 return m_points[0].Distance( aP ) <= aDist;
2113
2114 for( int i = 0; i < SegmentCount(); i++ )
2115 {
2116 const SEG s = CSegment( i );
2117
2118 if( s.A == aP || s.B == aP )
2119 return true;
2120
2121 if( s.Distance( aP ) <= aDist )
2122 return true;
2123 }
2124
2125 return false;
2126}
2127
2128
2129const std::optional<SHAPE_LINE_CHAIN::INTERSECTION> SHAPE_LINE_CHAIN::SelfIntersecting() const
2130{
2131 const int segCount = SegmentCount();
2132 const int ptCount = static_cast<int>( m_points.size() );
2133 const bool closed = m_closed;
2134
2135 if( segCount < 2 )
2136 return std::optional<INTERSECTION>();
2137
2138 // Index of the second endpoint of segment i, handling the closed-chain wrap
2139 auto endIdx = [ptCount]( int i )
2140 {
2141 int next = i + 1;
2142 return next < ptCount ? next : 0;
2143 };
2144
2145 for( int s1 = 0; s1 < segCount; s1++ )
2146 {
2147 const VECTOR2I& a1 = m_points[s1];
2148 const VECTOR2I& b1 = m_points[endIdx( s1 )];
2149
2150 // Expand by 2 to account for Contains() tolerance (SquaredDistance <= 3)
2151 const int s1MinX = std::min( a1.x, b1.x ) - 2;
2152 const int s1MaxX = std::max( a1.x, b1.x ) + 2;
2153 const int s1MinY = std::min( a1.y, b1.y ) - 2;
2154 const int s1MaxY = std::max( a1.y, b1.y ) + 2;
2155
2156 for( int s2 = s1 + 1; s2 < segCount; s2++ )
2157 {
2158 const VECTOR2I& a2 = m_points[s2];
2159 const VECTOR2I& b2 = m_points[endIdx( s2 )];
2160
2161 const int s2MinX = std::min( a2.x, b2.x );
2162 const int s2MaxX = std::max( a2.x, b2.x );
2163
2164 if( s1MaxX < s2MinX || s2MaxX < s1MinX )
2165 continue;
2166
2167 const int s2MinY = std::min( a2.y, b2.y );
2168 const int s2MaxY = std::max( a2.y, b2.y );
2169
2170 if( s1MaxY < s2MinY || s2MaxY < s1MinY )
2171 continue;
2172
2173 const SEG seg1( a1, b1, s1 );
2174
2175 if( s1 + 1 != s2 && seg1.Contains( a2 ) )
2176 {
2177 INTERSECTION is;
2178 is.index_our = s1;
2179 is.index_their = s2;
2180 is.p = a2;
2181 return is;
2182 }
2183 else if( seg1.Contains( b2 ) &&
2184 // for closed polylines, the ending point of the
2185 // last segment == starting point of the first segment
2186 // this is a normal case, not self intersecting case
2187 !( closed && s1 == 0 && s2 == segCount - 1 ) )
2188 {
2189 INTERSECTION is;
2190 is.index_our = s1;
2191 is.index_their = s2;
2192 is.p = b2;
2193 return is;
2194 }
2195 else
2196 {
2197 OPT_VECTOR2I p = seg1.Intersect( SEG( a2, b2, s2 ), true );
2198
2199 if( p )
2200 {
2201 INTERSECTION is;
2202 is.index_our = s1;
2203 is.index_their = s2;
2204 is.p = *p;
2205 return is;
2206 }
2207 }
2208 }
2209 }
2210
2211 return std::optional<INTERSECTION>();
2212}
2213
2214
2216{
2217 SHAPE_KEY( int aFirstIdx, int aArcIdx, const BOX2I_MINMAX& aBBox ) :
2218 m_FirstIdx( aFirstIdx ), m_ArcIdx( aArcIdx ), m_BBox( aBBox )
2219 {
2220 }
2221
2225};
2226
2227
2228const std::optional<SHAPE_LINE_CHAIN::INTERSECTION>
2230{
2231 auto pointsClose = []( const VECTOR2I& aPt1, const VECTOR2I& aPt2 ) -> bool
2232 {
2233 return ( VECTOR2D( aPt1 ) - aPt2 ).SquaredEuclideanNorm() <= 2.0;
2234 };
2235
2236 auto collideArcSeg = [&pointsClose]( const SHAPE_ARC& aArc, const SEG& aSeg, int aClearance = 0,
2237 VECTOR2I* aLocation = nullptr )
2238 {
2239 VECTOR2I center = aArc.GetCenter();
2240 CIRCLE circle( center, aArc.GetRadius() );
2241
2242 std::vector<VECTOR2I> candidatePts = circle.Intersect( aSeg );
2243
2244 for( const VECTOR2I& candidate : candidatePts )
2245 {
2246 // Skip shared points
2247 if( aArc.GetP1() == aSeg.A && pointsClose( candidate, aSeg.A ) )
2248 continue;
2249
2250 if( aSeg.B == aArc.GetP0() && pointsClose( candidate, aSeg.B ) )
2251 continue;
2252
2253 bool collides = aArc.Collide( candidate, aClearance, nullptr, aLocation );
2254
2255 if( collides )
2256 return true;
2257 }
2258
2259 return false;
2260 };
2261
2262 auto collideArcArc = [&pointsClose]( const SHAPE_ARC& aArc1, const SHAPE_ARC& aArc2,
2263 VECTOR2I* aLocation = nullptr )
2264 {
2265 std::vector<VECTOR2I> candidatePts;
2266
2267 aArc1.Intersect( aArc2, &candidatePts );
2268
2269 for( const VECTOR2I& candidate : candidatePts )
2270 {
2271 // Skip shared points
2272 if( aArc1.GetP1() == aArc2.GetP0() && pointsClose( candidate, aArc1.GetP1() ) )
2273 continue;
2274
2275 if( aArc2.GetP1() == aArc1.GetP0() && pointsClose( candidate, aArc2.GetP1() ) )
2276 continue;
2277
2278 if( aLocation )
2279 *aLocation = candidate;
2280
2281 return true;
2282 }
2283
2284 return false;
2285 };
2286
2287 auto collideSegSeg = [this]( int s1, int s2, INTERSECTION& is )
2288 {
2289 SEG seg1 = CSegment( s1 );
2290 SEG seg2 = CSegment( s2 );
2291
2292 const VECTOR2I s2a = seg2.A, s2b = seg2.B;
2293
2294 if( s1 + 1 != s2 && seg1.Contains( s2a ) )
2295 {
2296 is.index_our = s1;
2297 is.index_their = s2;
2298 is.p = s2a;
2299 return true;
2300 }
2301 else if( seg1.Contains( s2b ) &&
2302 // for closed polylines, the ending point of the
2303 // last segment == starting point of the first segment
2304 // this is a normal case, not self intersecting case
2305 !( IsClosed() && s1 == 0 && s2 == SegmentCount() - 1 ) )
2306 {
2307 is.index_our = s1;
2308 is.index_their = s2;
2309 is.p = s2b;
2310 return true;
2311 }
2312 else
2313 {
2314 OPT_VECTOR2I p = seg1.Intersect( seg2, true );
2315
2316 if( p )
2317 {
2318 is.index_our = s1;
2319 is.index_their = s2;
2320 is.p = *p;
2321 return true;
2322 }
2323 }
2324
2325 return false;
2326 };
2327
2328 INTERSECTION is;
2329
2330 std::vector<SHAPE_KEY> shapeCache;
2331 for( int si = 0; si != -1; si = NextShape( si ) )
2332 {
2333 int arci = ArcIndex( si );
2334
2335 shapeCache.emplace_back( si, arci,
2336 arci == -1 ? BOX2I_MINMAX( CSegment( si ) )
2337 : BOX2I_MINMAX( Arc( arci ) ) );
2338 }
2339
2340 for( size_t sk1 = 0; sk1 < shapeCache.size(); sk1++ )
2341 {
2342 for( size_t sk2 = sk1 + 1; sk2 < shapeCache.size(); sk2++ )
2343 {
2344 VECTOR2I loc;
2345 const SHAPE_KEY& k1 = shapeCache[sk1];
2346 const SHAPE_KEY& k2 = shapeCache[sk2];
2347
2348 if( !k1.m_BBox.Intersects( k2.m_BBox ) )
2349 continue;
2350
2351 if( k1.m_ArcIdx == -1 && k2.m_ArcIdx == -1 )
2352 {
2353 if( collideSegSeg( k1.m_FirstIdx, k2.m_FirstIdx, is ) )
2354 {
2355 return is;
2356 }
2357 }
2358 else if( k1.m_ArcIdx != -1 && k2.m_ArcIdx == -1 )
2359 {
2360 if( collideArcSeg( Arc( k1.m_ArcIdx ), CSegment( k2.m_FirstIdx ), 0, &loc ) )
2361 {
2362 is.index_our = k1.m_FirstIdx;
2363 is.index_their = k2.m_FirstIdx;
2364 is.p = loc;
2365 return is;
2366 }
2367 }
2368 else if( k1.m_ArcIdx == -1 && k2.m_ArcIdx != -1 )
2369 {
2370 if( collideArcSeg( Arc( k2.m_ArcIdx ), CSegment( k1.m_FirstIdx ), 0, &loc ) )
2371 {
2372 is.index_our = k1.m_FirstIdx;
2373 is.index_their = k2.m_FirstIdx;
2374 is.p = loc;
2375 return is;
2376 }
2377 }
2378 else if( k1.m_ArcIdx != -1 && k2.m_ArcIdx != -1 )
2379 {
2380 if( collideArcArc( Arc( k1.m_ArcIdx ), Arc( k2.m_ArcIdx ), &loc ) )
2381 {
2382 is.index_our = k1.m_FirstIdx;
2383 is.index_their = k2.m_FirstIdx;
2384 is.p = loc;
2385 return is;
2386 }
2387 }
2388 }
2389 }
2390
2391 return std::optional<SHAPE_LINE_CHAIN::INTERSECTION>();
2392}
2393
2394
2396 bool aAllowInternalShapePoints ) const
2397{
2398 if( PointCount() == 0 )
2399 {
2400 // The only right answer here is "don't crash".
2401 return { 0, 0 };
2402 }
2403
2404 int min_d = std::numeric_limits<int>::max();
2405 int nearest = 0;
2406
2407 for( int i = 0; i < SegmentCount(); i++ )
2408 {
2409 int d = CSegment( i ).Distance( aP );
2410
2411 if( d < min_d )
2412 {
2413 min_d = d;
2414 nearest = i;
2415 }
2416 }
2417
2418 if( !aAllowInternalShapePoints )
2419 {
2420 //Snap to arc end points if the closest found segment is part of an arc segment
2421 if( nearest > 0 && nearest < PointCount() && IsArcSegment( nearest ) )
2422 {
2423 VECTOR2I ptToSegStart = CSegment( nearest ).A - aP;
2424 VECTOR2I ptToSegEnd = CSegment( nearest ).B - aP;
2425
2426 if( ptToSegStart.EuclideanNorm() > ptToSegEnd.EuclideanNorm() )
2427 nearest++;
2428
2429 // Is this the start or end of an arc? If so, return it directly
2430 if( IsArcStart( nearest ) || IsArcEnd( nearest ) )
2431 {
2432 return m_points[nearest];
2433 }
2434 else
2435 {
2436 const SHAPE_ARC& nearestArc = Arc( ArcIndex( nearest ) );
2437 VECTOR2I ptToArcStart = nearestArc.GetP0() - aP;
2438 VECTOR2I ptToArcEnd = nearestArc.GetP1() - aP;
2439
2440 if( ptToArcStart.EuclideanNorm() > ptToArcEnd.EuclideanNorm() )
2441 return nearestArc.GetP1();
2442 else
2443 return nearestArc.GetP0();
2444 }
2445
2446 }
2447 }
2448
2449 return CSegment( nearest ).NearestPoint( aP );
2450}
2451
2452
2453const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const SEG& aSeg, int& dist ) const
2454{
2455 if( PointCount() == 0 )
2456 {
2457 // The only right answer here is "don't crash".
2458 return { 0, 0 };
2459 }
2460
2461 int nearest = 0;
2462
2463 dist = std::numeric_limits<int>::max();
2464
2465 for( int i = 0; i < PointCount(); i++ )
2466 {
2467 int d = aSeg.LineDistance( CPoint( i ) );
2468
2469 if( d < dist )
2470 {
2471 dist = d;
2472 nearest = i;
2473 }
2474 }
2475
2476 return CPoint( nearest );
2477}
2478
2479
2481{
2482 int min_d = std::numeric_limits<int>::max();
2483 int nearest = 0;
2484
2485 for( int i = 0; i < SegmentCount(); i++ )
2486 {
2487 int d = CSegment( i ).Distance( aP );
2488
2489 if( d < min_d )
2490 {
2491 min_d = d;
2492 nearest = i;
2493 }
2494 }
2495
2496 return nearest;
2497}
2498
2499
2500const std::string SHAPE_LINE_CHAIN::Format( bool aCplusPlus ) const
2501{
2502 std::stringstream ss;
2503
2504 ss << "SHAPE_LINE_CHAIN( { ";
2505
2506 for( int i = 0; i < PointCount(); i++ )
2507 {
2508 ss << "VECTOR2I( " << m_points[i].x << ", " << m_points[i].y << ")";
2509
2510 if( i != PointCount() -1 )
2511 ss << ", ";
2512 }
2513
2514 ss << "}, " << ( m_closed ? "true" : "false" );
2515 ss << " );";
2516
2517 return ss.str();
2518
2519 /* fixme: arcs
2520 for( size_t i = 0; i < m_arcs.size(); i++ )
2521 ss << m_arcs[i].GetCenter().x << " " << m_arcs[i].GetCenter().y << " "
2522 << m_arcs[i].GetP0().x << " " << m_arcs[i].GetP0().y << " "
2523 << m_arcs[i].GetCentralAngle().AsDegrees();
2524
2525 return ss.str();*/
2526}
2527
2528
2530 bool aCyclicalCompare,
2531 int aEpsilon ) const
2532{
2533 SHAPE_LINE_CHAIN a( *this ), b( aOther );
2534 a.Simplify();
2535 b.Simplify();
2536
2537 if( a.m_points.size() != b.m_points.size() )
2538 return false;
2539
2540 if( aCyclicalCompare )
2541 {
2542 std::vector<VECTOR2I> aVerts = a.m_points;
2543 std::vector<VECTOR2I> bVerts = b.m_points;
2544
2545 auto centroid = []( const std::vector<VECTOR2I>& pts )
2546 {
2547 double sx = 0.0, sy = 0.0;
2548 for( const auto& p : pts )
2549 {
2550 sx += p.x;
2551 sy += p.y;
2552 }
2553 return std::pair<double, double>( sx / pts.size(), sy / pts.size() );
2554 };
2555
2556 auto aC = centroid( aVerts );
2557 auto bC = centroid( bVerts );
2558
2559 auto angleCmp =
2560 []( const std::pair<double, double>& c, const VECTOR2I& p1, const VECTOR2I& p2 )
2561 {
2562 double a1 = atan2( p1.y - c.second, p1.x - c.first );
2563 double a2 = atan2( p2.y - c.second, p2.x - c.first );
2564 return a1 < a2;
2565 };
2566
2567 // sort by angle around centroid so that cyclic vertex order doesn't matter
2568 std::sort( aVerts.begin(), aVerts.end(),
2569 [&]( const VECTOR2I& p1, const VECTOR2I& p2 )
2570 {
2571 return angleCmp( aC, p1, p2 );
2572 } );
2573
2574 std::sort( bVerts.begin(), bVerts.end(),
2575 [&]( const VECTOR2I& p1, const VECTOR2I& p2 )
2576 {
2577 return angleCmp( bC, p1, p2 );
2578 } );
2579
2580 for( size_t i = 0; i < aVerts.size(); i++ )
2581 {
2582 if( abs( aVerts[i].x - bVerts[i].x ) > aEpsilon
2583 || abs( aVerts[i].y - bVerts[i].y ) > aEpsilon )
2584 return false;
2585 }
2586
2587 }
2588 else
2589 {
2590 for( int i = 0; i < a.PointCount(); i++ )
2591 {
2592 if( abs( a.CPoint( i ).x - b.CPoint( i ).x ) > aEpsilon
2593 || abs( a.CPoint( i ).y - b.CPoint( i ).y ) > aEpsilon )
2594 return false;
2595 }
2596 }
2597
2598
2599
2600 return true;
2601}
2602
2603
2605{
2607 return Intersect( aChain, dummy ) != 0;
2608}
2609
2610
2612{
2613 return new SHAPE_LINE_CHAIN( *this );
2614}
2615
2616
2617bool SHAPE_LINE_CHAIN::Parse( std::stringstream& aStream )
2618{
2619 size_t n_pts;
2620 size_t n_arcs;
2621
2622 m_points.clear();
2623 aStream >> n_pts;
2624
2625 // Rough sanity check, just make sure the loop bounds aren't absolutely outlandish
2626 if( n_pts > aStream.str().size() )
2627 return false;
2628
2629 aStream >> m_closed;
2630 aStream >> n_arcs;
2631
2632 if( n_arcs > aStream.str().size() )
2633 return false;
2634
2635 for( size_t i = 0; i < n_pts; i++ )
2636 {
2637 int x, y;
2638 ssize_t ind;
2639 aStream >> x;
2640 aStream >> y;
2641 m_points.emplace_back( x, y );
2642
2643 aStream >> ind;
2644 m_shapes.emplace_back( std::make_pair( ind, SHAPE_IS_PT ) );
2645 }
2646
2647 for( size_t i = 0; i < n_arcs; i++ )
2648 {
2649 VECTOR2I p0, pc;
2650 double angle;
2651
2652 aStream >> pc.x;
2653 aStream >> pc.y;
2654 aStream >> p0.x;
2655 aStream >> p0.y;
2656 aStream >> angle;
2657
2658 m_arcs.emplace_back( pc, p0, EDA_ANGLE( angle, DEGREES_T ) );
2659 }
2660
2661 return true;
2662}
2663
2664
2665const VECTOR2I SHAPE_LINE_CHAIN::PointAlong( int aPathLength ) const
2666{
2667 int total = 0;
2668
2669 if( aPathLength == 0 )
2670 return CPoint( 0 );
2671
2672 for( int i = 0; i < SegmentCount(); i++ )
2673 {
2674 const SEG& s = CSegment( i );
2675 int l = s.Length();
2676
2677 if( total + l >= aPathLength )
2678 {
2679 VECTOR2I d( s.B - s.A );
2680 return s.A + d.Resize( aPathLength - total );
2681 }
2682
2683 total += l;
2684 }
2685
2686 return CLastPoint();
2687}
2688
2689
2690double SHAPE_LINE_CHAIN::Area( bool aAbsolute ) const
2691{
2692 // see https://www.mathopenref.com/coordpolygonarea2.html
2693
2694 if( !m_closed )
2695 return 0.0;
2696
2697 double area = 0.0;
2698 int size = m_points.size();
2699
2700 for( int i = 0, j = size - 1; i < size; ++i )
2701 {
2702 area += ( (double) m_points[j].x + m_points[i].x ) *
2703 ( (double) m_points[j].y - m_points[i].y );
2704 j = i;
2705 }
2706
2707 if( aAbsolute )
2708 return std::fabs( area * 0.5 ); // The result would be negative if points are anti-clockwise
2709 else
2710 return -area * 0.5; // The result would be negative if points are anti-clockwise
2711}
2712
2713
2715{
2716 std::vector<VECTOR2I> pts_unique;
2717 std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
2718
2719 // Always try to keep at least 2 points otherwise, we're not really a line
2720 if( PointCount() < 3 )
2721 {
2722 return;
2723 }
2724 else if( PointCount() == 3 )
2725 {
2726 if( m_points[0] == m_points[1] || m_points[1] == m_points[2] )
2727 Remove( 1 );
2728
2729 return;
2730 }
2731
2732 int i = 0;
2733
2734 while( i < PointCount() )
2735 {
2736 int j = i + 1;
2737
2738 // We can eliminate duplicate vertices as long as they are part of the same shape, OR if
2739 // one of them is part of a shape and one is not.
2740 while( j < PointCount() && m_points[i] == m_points[j] &&
2741 ( m_shapes[i] == m_shapes[j] ||
2742 m_shapes[i] == SHAPES_ARE_PT ||
2743 m_shapes[j] == SHAPES_ARE_PT ) )
2744 {
2745 j++;
2746 }
2747
2748 std::pair<ssize_t,ssize_t> shapeToKeep = m_shapes[i];
2749
2750 if( shapeToKeep == SHAPES_ARE_PT )
2751 shapeToKeep = m_shapes[j - 1];
2752
2753 assert( shapeToKeep.first < static_cast<int>( m_arcs.size() ) );
2754 assert( shapeToKeep.second < static_cast<int>( m_arcs.size() ) );
2755
2756 pts_unique.push_back( CPoint( i ) );
2757 shapes_unique.push_back( shapeToKeep );
2758
2759 i = j;
2760 }
2761
2762 m_points.clear();
2763 m_shapes.clear();
2764
2765 for( size_t ii = 0; ii < pts_unique.size(); ++ii )
2766 {
2767 const VECTOR2I p0 = pts_unique[ii];
2768
2769 m_points.push_back( p0 );
2770 m_shapes.push_back( shapes_unique[ii] );
2771 }
2772}
2773
2774
2775
2776void SHAPE_LINE_CHAIN::Simplify( int aTolerance )
2777{
2778 if( PointCount() < 3 )
2779 return;
2780
2781 std::vector<VECTOR2I> new_points;
2782 std::vector<std::pair<ssize_t, ssize_t>> new_shapes;
2783
2784 new_points.reserve( m_points.size() );
2785 new_shapes.reserve( m_shapes.size() );
2786
2787 for( size_t start_idx = 0; start_idx < m_points.size(); )
2788 {
2789 new_points.push_back( m_points[start_idx] );
2790 new_shapes.push_back( m_shapes[start_idx] );
2791
2792 // If the line is not closed, we need at least 3 points before simplifying
2793 if( !m_closed && start_idx == m_points.size() - 2 )
2794 break;
2795
2796 // Initialize the end index to be two points ahead of start
2797 size_t end_idx = ( start_idx + 2 ) % m_points.size();
2798 bool can_simplify = true;
2799
2800 while( can_simplify && end_idx != start_idx && ( end_idx > start_idx || m_closed ) )
2801 {
2802 // Test all points between start_idx and end_idx
2803 for( size_t test_idx = ( start_idx + 1 ) % m_points.size();
2804 test_idx != end_idx;
2805 test_idx = ( test_idx + 1 ) % m_points.size() )
2806 {
2807 // Only intermediate test points must be regular points (not arcs).
2808 // Allow start or end to be arc endpoints so collinear segments
2809 // adjacent to arcs can still be simplified while preserving arcs.
2810 if( m_shapes[test_idx].first != SHAPE_IS_PT )
2811 {
2812 can_simplify = false;
2813 break;
2814 }
2815
2816 // Test if the point is within the allowed error
2817 if( !TestSegmentHit( m_points[test_idx], m_points[start_idx], m_points[end_idx], aTolerance ) )
2818 {
2819 can_simplify = false;
2820 break;
2821 }
2822 }
2823
2824 if( can_simplify )
2825 {
2826 // If we can simplify, move end_idx one further
2827 end_idx = ( end_idx + 1 ) % m_points.size();
2828 }
2829 }
2830
2831 // If we couldn't simplify at all, move to the next point
2832 if( end_idx == ( start_idx + 2 ) % m_points.size() )
2833 {
2834 ++start_idx;
2835 }
2836 else
2837 {
2838 // Otherwise, jump to the last point we could include in the simplification
2839 size_t new_start_idx = ( end_idx + m_points.size() - 1 ) % m_points.size();
2840
2841 // If we looped all the way around, we're done
2842 if( new_start_idx <= start_idx )
2843 break;
2844
2845 start_idx = new_start_idx;
2846 }
2847 }
2848
2849 // If we have only one point, we need to add a second point to make a line
2850 if( new_points.size() == 1 )
2851 {
2852 new_points.push_back( m_points.back() );
2853 new_shapes.push_back( m_shapes.back() );
2854 }
2855
2856 // If we are not closed, then the start and end points of the original line need to
2857 // be the start and end points of the new line.
2858 if( !m_closed && m_points.back() != new_points.back() )
2859 {
2860 new_points.push_back( m_points.back() );
2861 new_shapes.push_back( m_shapes.back() );
2862 }
2863
2864 m_points.clear();
2865 m_shapes.clear();
2866 m_points = std::move( new_points );
2867 m_shapes = std::move( new_shapes );
2868}
2869
2870
2871void SHAPE_LINE_CHAIN::Split( const VECTOR2I& aStart, const VECTOR2I& aEnd, SHAPE_LINE_CHAIN& aPre,
2872 SHAPE_LINE_CHAIN& aMid, SHAPE_LINE_CHAIN& aPost ) const
2873{
2874 VECTOR2I cp( aEnd );
2875
2876 VECTOR2I n = NearestPoint( cp, false );
2877 VECTOR2I m = NearestPoint( aStart, false );
2878
2879 SHAPE_LINE_CHAIN l( *this );
2880 l.Split( n, true );
2881 l.Split( m, true );
2882
2883 int i_start = l.Find( m );
2884 int i_end = l.Find( n );
2885
2886 if( i_start > i_end )
2887 {
2888 l = l.Reverse();
2889 i_start = l.Find( m );
2890 i_end = l.Find( n );
2891 }
2892
2893 aPre = l.Slice( 0, i_start );
2894 aPost = l.Slice( i_end, -1 );
2895 aMid = l.Slice( i_start, i_end );
2896}
2897
2898
2899
2901{
2902 std::vector<VECTOR2I> pts_unique;
2903 std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
2904
2905 // Always try to keep at least 2 points otherwise, we're not really a line
2906 if( PointCount() < 3 )
2907 {
2908 return *this;
2909 }
2910 else if( PointCount() == 3 )
2911 {
2912 if( m_points[0] == m_points[1] || m_points[1] == m_points[2] )
2913 Remove( 1 );
2914
2915 return *this;
2916 }
2917
2918 int i = 0;
2919 int np = PointCount();
2920
2921 // stage 1: eliminate duplicate vertices
2922 while( i < np )
2923 {
2924 int j = i + 1;
2925
2926 // We can eliminate duplicate vertices as long as they are part of the same shape, OR if
2927 // one of them is part of a shape and one is not.
2928 while( j < np && m_points[i] == m_points[j] &&
2929 ( m_shapes[i] == m_shapes[j] ||
2930 m_shapes[i] == SHAPES_ARE_PT ||
2931 m_shapes[j] == SHAPES_ARE_PT ) )
2932 {
2933 j++;
2934 }
2935
2936 std::pair<ssize_t,ssize_t> shapeToKeep = m_shapes[i];
2937
2938 if( shapeToKeep == SHAPES_ARE_PT )
2939 shapeToKeep = m_shapes[j - 1];
2940
2941 assert( shapeToKeep.first < static_cast<int>( m_arcs.size() ) );
2942 assert( shapeToKeep.second < static_cast<int>( m_arcs.size() ) );
2943
2944 pts_unique.push_back( CPoint( i ) );
2945 shapes_unique.push_back( shapeToKeep );
2946
2947 i = j;
2948 }
2949
2950 m_points.clear();
2951 m_shapes.clear();
2952 np = pts_unique.size();
2953
2954 i = 0;
2955
2956 // stage 2: eliminate colinear segments
2957 while( i < np - 2 )
2958 {
2959 const VECTOR2I p0 = pts_unique[i];
2960 int n = i;
2961
2962 if( aRemoveColinear && shapes_unique[i] == SHAPES_ARE_PT
2963 && shapes_unique[i + 1] == SHAPES_ARE_PT )
2964 {
2965 while( n < np - 2
2966 && ( SEG( p0, pts_unique[n + 2] ).LineDistance( pts_unique[n + 1] ) <= 1
2967 || SEG( p0, pts_unique[n + 2] ).Collinear( SEG( p0, pts_unique[n + 1] ) ) ) )
2968 n++;
2969 }
2970
2971 m_points.push_back( p0 );
2972 m_shapes.push_back( shapes_unique[i] );
2973
2974 if( n > i )
2975 i = n;
2976
2977 if( n == np - 2 )
2978 {
2979 m_points.push_back( pts_unique[np - 1] );
2980 m_shapes.push_back( shapes_unique[np - 1] );
2981 return *this;
2982 }
2983
2984 i++;
2985 }
2986
2987 if( np > 1 )
2988 {
2989 m_points.push_back( pts_unique[np - 2] );
2990 m_shapes.push_back( shapes_unique[np - 2] );
2991 }
2992
2993 m_points.push_back( pts_unique[np - 1] );
2994 m_shapes.push_back( shapes_unique[np - 1] );
2995
2996 assert( m_points.size() == m_shapes.size() );
2997
2998 return *this;
2999}
3000
3001bool SHAPE_LINE_CHAIN::OffsetLine( int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError,
3002 SHAPE_LINE_CHAIN& aLeft, SHAPE_LINE_CHAIN& aRight,
3003 bool aSimplify ) const
3004{
3005 if( PointCount() < 2 )
3006 return false;
3007
3008 SHAPE_POLY_SET poly;
3009 poly.OffsetLineChain( *this, aAmount, aCornerStrategy, aMaxError, aSimplify );
3010
3011 if( poly.OutlineCount() != 1 )
3012 return false;
3013
3014 if( poly.COutline( 0 ).PointCount() < 3 )
3015 return false;
3016
3017 if( poly.HasHoles() )
3018 return false;
3019
3020 SHAPE_LINE_CHAIN outline = poly.COutline( 0 );
3021
3022 wxASSERT( outline.IsClosed() );
3023
3024 const VECTOR2I& start = CPoint( 0 );
3025 const VECTOR2I& end = CLastPoint();
3026
3027 outline.Split( start, true );
3028 outline.Split( end, true );
3029
3030 const int idA = outline.Find( start );
3031 const int idB = outline.Find( end );
3032
3033 if( idA == -1 || idB == -1 )
3034 return false;
3035
3036 aLeft.Clear();
3037 aRight.Clear();
3038
3039 for( int i = idA;; )
3040 {
3041 aLeft.Append( outline.CPoint( i ) );
3042
3043 i = ( i + 1 ) % outline.PointCount();
3044
3045 if( i == idB )
3046 {
3047 aLeft.Append( outline.CPoint( i ) );
3048 break;
3049 }
3050
3051 if( i == idA )
3052 return false;
3053 }
3054
3055 if( aLeft.PointCount() < 2 )
3056 return false;
3057
3058 for( int i = idB;; )
3059 {
3060 aRight.Append( outline.CPoint( i ) );
3061
3062 i = ( i + 1 ) % outline.PointCount();
3063
3064 if( i == idA )
3065 {
3066 aRight.Append( outline.CPoint( i ) );
3067 break;
3068 }
3069
3070 if( i == idB )
3071 return false;
3072 }
3073
3074 if( aRight.PointCount() < 2 )
3075 return false;
3076
3077 if( aLeft.CPoint( 0 ) != start )
3078 {
3079 aLeft = aLeft.Reverse();
3080 wxASSERT( aLeft.CPoint( 0 ) == start );
3081 }
3082
3083 if( aRight.CPoint( 0 ) != start )
3084 {
3085 aRight = aRight.Reverse();
3086 wxASSERT( aRight.CPoint( 0 ) == start );
3087 }
3088
3089 SEG base( CPoint( 0 ), CPoint( 1 ) );
3090 int sideLeft = base.Side( aLeft.CPoint( 1 ) );
3091 int sideRight = base.Side( aRight.CPoint( 1 ) );
3092
3093 if( sideLeft == 0 || sideRight == 0 )
3094 return false;
3095
3096 if( sideLeft == sideRight )
3097 return false;
3098
3099 if( sideLeft > 0 && sideRight < 0 )
3100 std::swap( aLeft, aRight );
3101
3102 if( aLeft.PointCount() < 4 )
3103 return false;
3104
3105 if( aRight.PointCount() < 4 )
3106 return false;
3107
3108 aLeft.Remove( 0 );
3109 aLeft.Remove( aLeft.PointCount() - 1 );
3110
3111 aRight.Remove( 0 );
3112 aRight.Remove( aRight.PointCount() - 1 );
3113
3114 return true;
3115}
3116
3117
3119 ERROR_LOC aErrorLoc ) const
3120{
3121 aBuffer.AddOutline( *this );
3122}
3123
3124
3126 m_point( aPoint ),
3127 m_finished( false ),
3128 m_state( 0 ),
3129 m_count( 0 )
3130{
3131}
3132
3133
3135 const VECTOR2I& ip, const VECTOR2I& ipNext )
3136{
3137 if( ipNext.y == m_point.y )
3138 {
3139 if( ( ipNext.x == m_point.x )
3140 || ( ip.y == m_point.y && ( ( ipNext.x > m_point.x ) == ( ip.x < m_point.x ) ) ) )
3141 {
3142 m_finished = true;
3143 m_state = -1;
3144 return false;
3145 }
3146 }
3147
3148 if( ( ip.y < m_point.y ) != ( ipNext.y < m_point.y ) )
3149 {
3150 if( ip.x >= m_point.x )
3151 {
3152 if( ipNext.x > m_point.x )
3153 {
3154 m_state = 1 - m_state;
3155 }
3156 else
3157 {
3158 double d = (double) ( ip.x - m_point.x ) * ( ipNext.y - m_point.y )
3159 - (double) ( ipNext.x - m_point.x ) * ( ip.y - m_point.y );
3160
3161 if( !d )
3162 {
3163 m_finished = true;
3164 m_state = -1;
3165 return false;
3166 }
3167
3168 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
3169 m_state = 1 - m_state;
3170 }
3171 }
3172 else
3173 {
3174 if( ipNext.x > m_point.x )
3175 {
3176 double d = (double) ( ip.x - m_point.x ) * ( ipNext.y - m_point.y )
3177 - (double) ( ipNext.x - m_point.x ) * ( ip.y - m_point.y );
3178
3179 if( !d )
3180 {
3181 m_finished = true;
3182 m_state = -1;
3183 return false;
3184 }
3185
3186 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
3187 m_state = 1 - m_state;
3188 }
3189 }
3190 }
3191
3192 return true;
3193}
3194
3195
3197{
3198 if( !m_count )
3199 {
3200 m_lastPoint = aPolyline.CPoint( 0 );
3201 m_firstPoint = aPolyline.CPoint( 0 );
3202 }
3203
3204 m_count += aPolyline.PointCount();
3205
3206 for( int i = 1; i < aPolyline.PointCount(); i++ )
3207 {
3208 auto p = aPolyline.CPoint( i );
3209
3210 if( !processVertex( m_lastPoint, p ) )
3211 return;
3212
3213 m_lastPoint = p;
3214 }
3215
3216}
3217
3218
3224
3225
3226bool SHAPE_LINE_CHAIN::IsSharedPt( size_t aIndex ) const
3227{
3228 return aIndex < m_shapes.size()
3229 && m_shapes[aIndex].first != SHAPE_IS_PT
3230 && m_shapes[aIndex].second != SHAPE_IS_PT;
3231}
3232
3233
3234bool SHAPE_LINE_CHAIN::IsPtOnArc( size_t aPtIndex ) const
3235{
3236 return aPtIndex < m_shapes.size() && m_shapes[aPtIndex] != SHAPES_ARE_PT;
3237}
3238
3239
3240bool SHAPE_LINE_CHAIN::IsArcSegment( size_t aSegment ) const
3241{
3242 /*
3243 * A segment is part of an arc except in the special case of two arcs next to each other
3244 * but without a shared vertex. Here there is a segment between the end of the first arc
3245 * and the start of the second arc.
3246 */
3247 size_t nextIdx = aSegment + 1;
3248
3249 if( nextIdx > m_shapes.size() - 1 )
3250 {
3251 if( nextIdx == m_shapes.size() && m_closed && IsSharedPt( 0 ) )
3252 nextIdx = 0; // segment between end point and first point
3253 else
3254 return false;
3255 }
3256
3257 return ( IsPtOnArc( aSegment )
3258 && ( ArcIndex( aSegment ) == m_shapes[nextIdx].first ) );
3259}
3260
3261
3262bool SHAPE_LINE_CHAIN::IsArcStart( size_t aIndex ) const
3263{
3264 if( !IsArcSegment( aIndex ) ) // also does bound checking
3265 return false;
3266
3267 if( IsSharedPt( aIndex ) )
3268 return true;
3269
3270 const SHAPE_ARC& arc = Arc( ArcIndex( aIndex ) );
3271
3272 return arc.GetP0() == m_points[aIndex];
3273}
3274
3275
3276bool SHAPE_LINE_CHAIN::IsArcEnd( size_t aIndex ) const
3277{
3278 size_t prevIndex = aIndex - 1;
3279
3280 if( aIndex == 0 )
3281 prevIndex = m_points.size() - 1;
3282 else if( aIndex > m_points.size() -1 )
3283 return false; // invalid index requested
3284
3285 if( !IsArcSegment( prevIndex ) )
3286 return false;
3287
3288 if( IsSharedPt( aIndex ) )
3289 return true;
3290
3291 const SHAPE_ARC& arc = Arc( ArcIndex( aIndex ) );
3292
3293 return arc.GetP1() == m_points[aIndex];
3294}
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
Represent basic circle geometry with utility geometry functions.
Definition circle.h:33
Definition seg.h:38
const VECTOR2I ReflectPoint(const VECTOR2I &aP) const
Reflect a point using this segment as axis.
Definition seg.cpp:630
VECTOR2I A
Definition seg.h:45
int LineDistance(const VECTOR2I &aP, bool aDetermineSide=false) const
Return the closest Euclidean distance between point aP and the line defined by the ends of segment (t...
Definition seg.cpp:710
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:35
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:599
int Length() const
Return the length (this).
Definition seg.h:340
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition seg.cpp:401
static SEG::ecoord Square(int a)
Definition seg.h:119
bool Collinear(const SEG &aSeg) const
Check if segment aSeg lies on the same line as (this).
Definition seg.h:283
bool NearestPoints(const SEG &aSeg, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this segment and aSeg.
Definition seg.cpp:117
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:668
bool Contains(const SEG &aSeg) const
Definition seg.h:321
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition seg.h:139
bool IsClockwise() const
Definition shape_arc.h:323
int GetWidth() const override
Definition shape_arc.h:215
const SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError=DefaultAccuracyForPCB(), int *aActualError=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
int Intersect(const CIRCLE &aArc, std::vector< VECTOR2I > *aIpsBuffer) const
Find intersection points between this arc and a CIRCLE.
SHAPE_ARC & ConstructFromStartEndCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aClockwise=false, double aWidth=0)
Constructs this arc from the given start, end and center.
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
bool Collide(const SEG &aSeg, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the segment aSeg than aClearance,...
static int DefaultAccuracyForPCB()
Definition shape_arc.h:283
double GetRadius() const
void Reverse()
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
void SetWidth(int aWidth) override
Definition shape_arc.h:210
const VECTOR2I & GetCenter() const
bool processVertex(const VECTOR2I &ip, const VECTOR2I &ipNext)
void AddPolyline(const SHAPE_LINE_CHAIN &aPolyline)
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if point aP lies closer to us than aClearance.
bool PointOnEdge(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of the line chain.
int EdgeContainingPoint(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of the line chain.
virtual size_t GetPointCount() const =0
SHAPE_LINE_CHAIN_BASE(SHAPE_TYPE aType)
Definition shape.h:318
virtual size_t GetSegmentCount() const =0
virtual const VECTOR2I GetPoint(int aIndex) const =0
virtual BOX2I * GetCachedBBox() const
Definition shape.h:382
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
virtual bool IsClosed() const =0
virtual const SEG GetSegment(int aIndex) const =0
SEG::ecoord SquaredDistance(const VECTOR2I &aP, bool aOutlineOnly=false) const override
std::vector< std::pair< ssize_t, ssize_t > > m_shapes
Array of indices that refer to the index of the shape if the point is part of a larger shape,...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
bool IsPtOnArc(size_t aPtIndex) const
const SHAPE_ARC & Arc(size_t aArc) const
void splitArc(ssize_t aPtIndex, bool aCoincident=false)
Splits an arc into two arcs at aPtIndex.
const std::optional< INTERSECTION > SelfIntersecting() const
Check if the line chain is self-intersecting.
bool Parse(std::stringstream &aStream) override
bool CheckClearance(const VECTOR2I &aP, const int aDist) const
Check if point aP is closer to (or on) an edge or vertex of the line chain.
std::vector< SHAPE_ARC > m_arcs
int Distance(const VECTOR2I &aP, bool aOutlineOnly) const
bool IsClosed() const override
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
const VECTOR2I PointAlong(int aPathLength) const
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.
void fixIndicesRotation()
Fix indices of this chain to ensure arcs are not split between the end and start indices.
std::vector< VECTOR2I > m_points
array of vertices
int FindSegment(const VECTOR2I &aP, int aThreshold=1) const
Search for segment containing point aP.
int ShapeCount() const
Return the number of shapes (line segments or arcs) in this line chain.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int Intersect(const SEG &aSeg, INTERSECTIONS &aIp) const
Find all intersection points between our line chain and the segment aSeg.
SHAPE_LINE_CHAIN()
Initialize an empty line chain.
int m_width
Width of the segments (for BBox calculations in RTree) TODO Adjust usage of SHAPE_LINE_CHAIN to accou...
int PointCount() const
Return the number of points (vertices) in this line chain.
bool IsArcEnd(size_t aIndex) const
void Replace(int aStartIndex, int aEndIndex, const VECTOR2I &aP)
Replace points with indices in range [start_index, end_index] with a single point aP.
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aError, ERROR_LOC aErrorLoc) const override
Fills a SHAPE_POLY_SET with a polygon representation of this shape.
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if point aP lies closer to us than aClearance.
void ClearArcs()
Remove all arc references in the line chain, resulting in a chain formed only of straight segments.
void mergeFirstLastPointIfNeeded()
Merge the first and last point if they are the same and this chain is closed.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
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.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
BOX2I m_bbox
cached bounding box
BOX2I * GetCachedBBox() const override
bool m_closed
is the line chain closed?
friend class SHAPE_POLY_SET
const std::vector< SHAPE_ARC > & CArcs() const
const std::optional< INTERSECTION > SelfIntersectingWithArcs() const
Check if the line chain is self-intersecting.
bool Intersects(const SEG &aSeg) const
int NearestSegment(const VECTOR2I &aP) const
Find the segment nearest the given point.
int NextShape(int aPointIndex) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
double Area(bool aAbsolute=true) const
Return the area of this chain.
SHAPE_LINE_CHAIN & Simplify2(bool aRemoveColinear=true)
void amendArc(size_t aArcIndex, const VECTOR2I &aNewStart, const VECTOR2I &aNewEnd)
bool ClosestPoints(const SHAPE_LINE_CHAIN &aOther, VECTOR2I &aPt0, VECTOR2I &aPt1) const
Finds closest points between this and the other line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
virtual const SEG GetSegment(int aIndex) const override
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const std::vector< std::pair< ssize_t, ssize_t > > & CShapes() const
bool CompareGeometry(const SHAPE_LINE_CHAIN &aOther, bool aCyclicalCompare=false, int aEpsilon=0) const
Compare this line chain with another one.
const VECTOR2I NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
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.
int PathLength(const VECTOR2I &aP, int aIndex=-1) const
Compute the walk path length from the beginning of the line chain and the point aP belonging to our l...
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the line points about y or x (or both).
bool ClosestSegmentsFast(const SHAPE_LINE_CHAIN &aOther, VECTOR2I &aPt0, VECTOR2I &aPt1) const
Finds closest points between segments of this and the other line chain.
const std::string Format(bool aCplusPlus=true) const override
static bool ClosestSegments(const VECTOR2I &aMyPrevPt, const point_citer &aMyStart, const point_citer &aMyEnd, const VECTOR2I &aOtherPrevPt, const point_citer &aOtherStart, const point_citer &aOtherEnd, VECTOR2I &aPt0, VECTOR2I &aPt1, int64_t &aDistSq)
Clipper2Lib::Path64 convertToClipper2(bool aRequiredOrientation, std::vector< CLIPPER_Z_VALUE > &aZValueBuffer, std::vector< SHAPE_ARC > &aArcBuffer) const
Create a new Clipper2 path from the SHAPE_LINE_CHAIN in a given orientation.
static const std::pair< ssize_t, ssize_t > SHAPES_ARE_PT
void convertArc(ssize_t aArcIndex)
Convert an arc to only a point chain by removing the arc and references.
virtual size_t GetSegmentCount() const override
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
void RemoveDuplicatePoints()
Remove the duplicate points 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
void RemoveShape(int aPointIndex)
Remove the shape at the given index from the line chain.
void Insert(size_t aVertex, const VECTOR2I &aP)
bool IsArcStart(size_t aIndex) const
bool IsSharedPt(size_t aIndex) const
Test if a point is shared between multiple shapes.
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
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
std::vector< VECTOR2I >::const_iterator point_citer
SHAPE * Clone() const override
Return a dynamically allocated copy of the shape.
static const ssize_t SHAPE_IS_PT
bool OffsetLine(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, SHAPE_LINE_CHAIN &aLeft, SHAPE_LINE_CHAIN &aRight, bool aSimplify=false) const
Creates line chains aLeft and aRight offset to this line chain.
bool HasHoles() const
Return true if the polygon set has any holes.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void OffsetLineChain(const SHAPE_LINE_CHAIN &aLine, int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify)
Perform offsetting of a line chain.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
An abstract shape on 2D plane.
Definition shape.h:124
VECTOR2I::extended_type ecoord
Definition shape.h:311
SHAPE(SHAPE_TYPE aType)
Create an empty shape of type aType.
Definition shape.h:134
virtual int GetWidth() const
Definition shape.h:299
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition vector2d.h:582
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
CORNER_STRATEGY
define how inflate transform build inflated polygon
@ DEGREES_T
Definition eda_angle.h:30
FLIP_DIRECTION
Definition mirror.h:23
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:24
bool pair_contains(const std::pair< _Type, _Type > __pair, _Value __value)
Returns true if either of the elements in an std::pair contains the given value.
Definition kicad_algo.h:109
void run_on_pair(std::pair< _Type, _Type > &__pair, _Function __f)
Apply a function to the first and second element of a std::pair.
Definition kicad_algo.h:41
CITER next(CITER it)
Definition ptree.cpp:120
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
@ SH_LINE_CHAIN
line chain (polyline)
Definition shape.h:45
int getArcPolygonizationMaxError()
std::vector< FAB_LAYER_COLOR > dummy
A min-max version of BOX2 for fast intersection checking.
Definition box2_minmax.h:33
bool Intersects(const BOX2I_MINMAX &aOther) const
Definition box2_minmax.h:69
Holds information on each point of a SHAPE_LINE_CHAIN that is retrievable after an operation with Cli...
SHAPE_KEY(int aFirstIdx, int aArcIdx, const BOX2I_MINMAX &aBBox)
BOX2I_MINMAX m_BBox
Represent an intersection between two line segments.
bool is_corner_their
When true, the corner [index_their] of the 'their' line lies exactly on 'our' line.
bool is_corner_our
When true, the corner [index_our] of the 'our' line lies exactly on 'their' line.
int index_our
Index of the intersecting corner/segment in the 'our' (== this) line.
VECTOR2I p
Point of intersection between our and their.
bool valid
Auxiliary flag to avoid copying intersection info to intersection refining code, used by the refining...
int index_their
index of the intersecting corner/segment in the 'their' (Intersect() method parameter) line.
bool operator()(const SHAPE_LINE_CHAIN::INTERSECTION &aA, const SHAPE_LINE_CHAIN::INTERSECTION &aB) const
compareOriginDistance(const VECTOR2I &aOrigin)
KIBIS_COMPONENT * comp
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
bool TestSegmentHit(const VECTOR2I &aRefPoint, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition trigo.cpp:173
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
Definition util.h:160
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:709