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>( m_points.size() );
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( size_t i = aStartIndex; i < m_points.size() && 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
1454 VECTOR2I newArcStart = m_points[aStartIndex];
1455
1456 newArc.ConstructFromStartEndCenter( newArcStart, arcToSplit.GetP1(),
1457 arcToSplit.GetCenter(),
1458 arcToSplit.IsClockwise() );
1459
1460
1461 rv.m_arcs.push_back( newArc );
1462
1463 aStartIndex += rv.PointCount();
1464 }
1465
1466 for( int i = aStartIndex; i <= aEndIndex && i < numPoints; i = NextShape( i ) )
1467 {
1468 if( i == -1 )
1469 return rv; // NextShape reached the end
1470
1471 int nextShape = NextShape( i );
1472 bool isLastShape = nextShape < 0;
1473
1474 if( IsArcStart( i ) )
1475 {
1476 if( ( isLastShape && aEndIndex != ( numPoints - 1 ) )
1477 || ( nextShape > aEndIndex ) )
1478 {
1479 if( i == aEndIndex )
1480 {
1481 // Single point on an arc, just append the single point
1482 rv.Append( m_points[i] );
1483 return rv;
1484 }
1485
1486 // Cutting in middle of an arc, lets split it
1487 ssize_t arcIndex = ArcIndex( i );
1488 const SHAPE_ARC& currentArc = Arc( arcIndex );
1489
1490 // Copy the points as arc points
1491 for( ; i <= aEndIndex && i < numPoints; i++ )
1492 {
1493 if( arcIndex != ArcIndex( i ) )
1494 break;
1495
1496 rv.m_points.push_back( m_points[i] );
1497 rv.m_shapes.push_back( { rv.m_arcs.size(), SHAPE_IS_PT } );
1498 rv.m_bbox.Merge( m_points[i] );
1499 }
1500
1501 // Create a new arc from the existing one, with different end point.
1502 SHAPE_ARC newArc;
1503
1504 VECTOR2I newArcEnd = m_points[aEndIndex];
1505
1506 newArc.ConstructFromStartEndCenter( currentArc.GetP0(), newArcEnd,
1507 currentArc.GetCenter(),
1508 currentArc.IsClockwise() );
1509
1510
1511 rv.m_arcs.push_back( newArc );
1512
1513 return rv;
1514 }
1515 else
1516 {
1517 // append the whole arc
1518 const SHAPE_ARC& currentArc = Arc( ArcIndex( i ) );
1519 rv.Append( currentArc, aMaxError );
1520 }
1521
1522 if( isLastShape )
1523 return rv;
1524 }
1525 else
1526 {
1527 wxASSERT_MSG( !IsArcSegment( i ), wxT( "Still on an arc segment, we missed something..." ) );
1528
1529 if( i == aStartIndex )
1530 rv.Append( m_points[i] );
1531
1532 bool nextPointIsArc = isLastShape ? false : IsArcSegment( nextShape );
1533
1534 if( !nextPointIsArc && i < SegmentCount() && i < aEndIndex )
1535 rv.Append( GetSegment( i ).B );
1536 }
1537
1538 }
1539
1540 wxASSERT( rv.m_points.size() == rv.m_shapes.size() );
1541
1542 return rv;
1543}
1544
1545
1547{
1548 assert( m_shapes.size() == m_points.size() );
1549
1550 if( aOtherLine.PointCount() == 0 )
1551 {
1552 return;
1553 }
1554
1555 size_t num_arcs = m_arcs.size();
1556 m_arcs.insert( m_arcs.end(), aOtherLine.m_arcs.begin(), aOtherLine.m_arcs.end() );
1557
1558 auto fixShapeIndices =
1559 [&]( const std::pair<ssize_t, ssize_t>& aShapeIndices ) -> std::pair<ssize_t, ssize_t>
1560 {
1561 std::pair<ssize_t, ssize_t> retval = aShapeIndices;
1562
1563 alg::run_on_pair( retval, [&]( ssize_t& aIndex )
1564 {
1565 if( aIndex != SHAPE_IS_PT )
1566 aIndex = aIndex + num_arcs;
1567 } );
1568
1569 return retval;
1570 };
1571
1572 if( PointCount() == 0 || aOtherLine.CPoint( 0 ) != CLastPoint() )
1573 {
1574 const VECTOR2I p = aOtherLine.CPoint( 0 );
1575 m_points.push_back( p );
1576 m_shapes.push_back( fixShapeIndices( aOtherLine.CShapes()[0] ) );
1577 m_bbox.Merge( p );
1578 }
1579 else if( aOtherLine.IsArcSegment( 0 ) )
1580 {
1581 // Associate the new arc shape with the last point of this chain
1582 if( m_shapes.back() == SHAPES_ARE_PT )
1583 m_shapes.back().first = aOtherLine.CShapes()[0].first + num_arcs;
1584 else
1585 m_shapes.back().second = aOtherLine.CShapes()[0].first + num_arcs;
1586 }
1587
1588
1589 for( int i = 1; i < aOtherLine.PointCount(); i++ )
1590 {
1591 const VECTOR2I p = aOtherLine.CPoint( i );
1592 m_points.push_back( p );
1593
1594 ssize_t arcIndex = aOtherLine.ArcIndex( i );
1595
1596 if( arcIndex != ssize_t( SHAPE_IS_PT ) )
1597 {
1598 m_shapes.push_back( fixShapeIndices( aOtherLine.m_shapes[i] ) );
1599 }
1600 else
1601 m_shapes.push_back( SHAPES_ARE_PT );
1602
1603 m_bbox.Merge( p );
1604 }
1605
1607
1608 assert( m_shapes.size() == m_points.size() );
1609}
1610
1611
1613{
1615}
1616
1617
1618void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc, int aMaxError )
1619{
1620 SHAPE_LINE_CHAIN chain = aArc.ConvertToPolyline( aMaxError );
1621
1622 if( chain.PointCount() > 2 )
1623 {
1624 chain.m_arcs.push_back( aArc );
1625 chain.m_arcs.back().SetWidth( 0 );
1626
1627 for( auto& sh : chain.m_shapes )
1628 sh.first = 0;
1629 }
1630
1631 Append( chain );
1632
1633 assert( m_shapes.size() == m_points.size() );
1634}
1635
1636
1637void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const VECTOR2I& aP )
1638{
1639 if( aVertex == m_points.size() )
1640 {
1641 Append( aP );
1642 return;
1643 }
1644
1645 wxCHECK( aVertex < m_points.size(), /* void */ );
1646
1647 if( aVertex > 0 && IsPtOnArc( aVertex ) )
1648 splitArc( aVertex );
1649
1650 //@todo need to check we aren't creating duplicate points
1651 m_points.insert( m_points.begin() + aVertex, aP );
1652 m_shapes.insert( m_shapes.begin() + aVertex, SHAPES_ARE_PT );
1653
1654 assert( m_shapes.size() == m_points.size() );
1655}
1656
1657
1658void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc )
1659{
1660 Insert( aVertex, aArc, getArcPolygonizationMaxError() );
1661}
1662
1663
1664void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc, int aMaxError )
1665{
1666 wxCHECK( aVertex < m_points.size(), /* void */ );
1667
1668 if( aVertex > 0 && IsPtOnArc( aVertex ) )
1669 splitArc( aVertex );
1670
1672 ssize_t arc_pos = m_arcs.size();
1673
1674 for( auto arc_it = m_shapes.rbegin() ;
1675 arc_it != m_shapes.rend() + aVertex;
1676 arc_it++ )
1677 {
1678 if( *arc_it != SHAPES_ARE_PT )
1679 {
1680 arc_pos = std::max( ( *arc_it ).first, ( *arc_it ).second );
1681 arc_pos++;
1682 }
1683 }
1684
1685 //Increment all arc indices before inserting the new arc
1686 for( auto& sh : m_shapes )
1687 {
1688 alg::run_on_pair( sh,
1689 [&]( ssize_t& aIndex )
1690 {
1691 if( aIndex >= arc_pos )
1692 aIndex++;
1693 } );
1694 }
1695
1696 SHAPE_ARC arcCopy( aArc );
1697 arcCopy.SetWidth( 0 );
1698 m_arcs.insert( m_arcs.begin() + arc_pos, arcCopy );
1699
1701 //@todo need to check we aren't creating duplicate points at start or end
1702 auto& chain = aArc.ConvertToPolyline( aMaxError );
1703 m_points.insert( m_points.begin() + aVertex, chain.CPoints().begin(), chain.CPoints().end() );
1704
1706 //@todo need to check we aren't creating duplicate points at start or end
1707 std::vector<std::pair<ssize_t, ssize_t>> new_points( chain.PointCount(),
1708 { arc_pos, SHAPE_IS_PT } );
1709
1710 m_shapes.insert( m_shapes.begin() + aVertex, new_points.begin(), new_points.end() );
1711 assert( m_shapes.size() == m_points.size() );
1712}
1713
1714
1716{
1718 m_origin( aOrigin ) {};
1719
1721 const SHAPE_LINE_CHAIN::INTERSECTION& aB ) const
1722 {
1723 return ( VECTOR2L( m_origin ) - aA.p ).SquaredEuclideanNorm()
1724 < ( VECTOR2L( m_origin ) - aB.p ).SquaredEuclideanNorm();
1725 }
1726
1728};
1729
1730
1731int SHAPE_LINE_CHAIN::Intersect( const SEG& aSeg, INTERSECTIONS& aIp ) const
1732{
1733 const int segCount = SegmentCount();
1734 const int ptCount = static_cast<int>( m_points.size() );
1735
1736 const int segMinX = std::min( aSeg.A.x, aSeg.B.x );
1737 const int segMaxX = std::max( aSeg.A.x, aSeg.B.x );
1738 const int segMinY = std::min( aSeg.A.y, aSeg.B.y );
1739 const int segMaxY = std::max( aSeg.A.y, aSeg.B.y );
1740
1741 for( int s = 0; s < segCount; s++ )
1742 {
1743 const VECTOR2I& ptA = m_points[s];
1744 const VECTOR2I& ptB = m_points[s + 1 < ptCount ? s + 1 : 0];
1745
1746 if( std::max( ptA.x, ptB.x ) < segMinX || std::min( ptA.x, ptB.x ) > segMaxX
1747 || std::max( ptA.y, ptB.y ) < segMinY || std::min( ptA.y, ptB.y ) > segMaxY )
1748 {
1749 continue;
1750 }
1751
1752 OPT_VECTOR2I p = SEG( ptA, ptB, s ).Intersect( aSeg );
1753
1754 if( p )
1755 {
1756 INTERSECTION is;
1757 is.valid = true;
1758 is.index_our = s;
1759 is.index_their = -1;
1760 is.is_corner_our = is.is_corner_their = false;
1761 is.p = *p;
1762 aIp.push_back( is );
1763 }
1764 }
1765
1767 sort( aIp.begin(), aIp.end(), comp );
1768
1769 return aIp.size();
1770}
1771
1772
1773bool SHAPE_LINE_CHAIN::Intersects( const SEG& aSeg ) const
1774{
1775 const int segCount = SegmentCount();
1776 const int ptCount = static_cast<int>( m_points.size() );
1777
1778 const int segMinX = std::min( aSeg.A.x, aSeg.B.x );
1779 const int segMaxX = std::max( aSeg.A.x, aSeg.B.x );
1780 const int segMinY = std::min( aSeg.A.y, aSeg.B.y );
1781 const int segMaxY = std::max( aSeg.A.y, aSeg.B.y );
1782
1783 for( int s = 0; s < segCount; s++ )
1784 {
1785 const VECTOR2I& ptA = m_points[s];
1786 const VECTOR2I& ptB = m_points[s + 1 < ptCount ? s + 1 : 0];
1787
1788 if( std::max( ptA.x, ptB.x ) < segMinX || std::min( ptA.x, ptB.x ) > segMaxX
1789 || std::max( ptA.y, ptB.y ) < segMinY || std::min( ptA.y, ptB.y ) > segMaxY )
1790 {
1791 continue;
1792 }
1793
1794 if( SEG( ptA, ptB ).Intersects( aSeg ) )
1795 return true;
1796 }
1797
1798 return false;
1799}
1800
1801
1803 bool aExcludeColinearAndTouching, BOX2I* aChainBBox ) const
1804{
1805 const int ourSegCount = SegmentCount();
1806 const int theirSegCount = aChain.SegmentCount();
1807
1808 if( ourSegCount == 0 || theirSegCount == 0 )
1809 return 0;
1810
1811 const BOX2I_MINMAX bbOther( aChainBBox ? *aChainBBox : aChain.BBox() );
1812 const int ourPtCount = static_cast<int>( m_points.size() );
1813 const int theirPtCount = static_cast<int>( aChain.CPoints().size() );
1814 const std::vector<VECTOR2I>& theirPts = aChain.CPoints();
1815
1816 thread_local std::vector<SEG> theirSegs;
1817 thread_local std::vector<SEG_EXTENT> sorted;
1818
1819 theirSegs.resize( theirSegCount );
1820 sorted.resize( theirSegCount );
1821
1822 // Pre-build SEGs for the other chain so each is constructed exactly once
1823 for( int i = 0; i < theirSegCount; i++ )
1824 {
1825 const VECTOR2I& pa = theirPts[i];
1826 const VECTOR2I& pb = theirPts[i + 1 < theirPtCount ? i + 1 : 0];
1827 theirSegs[i] = SEG( pa, pb, i );
1828 }
1829
1830 // Compact extent array for cache-friendly scanning, sorted by minX
1831 for( int i = 0; i < theirSegCount; i++ )
1832 {
1833 const SEG& s = theirSegs[i];
1834 sorted[i] = { std::min( s.A.x, s.B.x ), std::max( s.A.x, s.B.x ),
1835 std::min( s.A.y, s.B.y ), std::max( s.A.y, s.B.y ), i };
1836 }
1837
1838 std::sort( sorted.begin(), sorted.end(),
1839 []( const SEG_EXTENT& a, const SEG_EXTENT& b ) { return a.minX < b.minX; } );
1840
1841 for( int s1 = 0; s1 < ourSegCount; s1++ )
1842 {
1843 const VECTOR2I& a1 = m_points[s1];
1844 const VECTOR2I& b1 = m_points[s1 + 1 < ourPtCount ? s1 + 1 : 0];
1845
1846 const int ourMinX = std::min( a1.x, b1.x );
1847 const int ourMaxX = std::max( a1.x, b1.x );
1848 const int ourMinY = std::min( a1.y, b1.y );
1849 const int ourMaxY = std::max( a1.y, b1.y );
1850
1851 if( ourMaxX < bbOther.m_Left || ourMinX > bbOther.m_Right
1852 || ourMaxY < bbOther.m_Top || ourMinY > bbOther.m_Bottom )
1853 {
1854 continue;
1855 }
1856
1857 const SEG a( a1, b1, s1 );
1858
1859 // Find the right boundary in the sorted extents: first entry where minX > ourMaxX.
1860 // Everything past this point is too far right to overlap.
1861 auto rightEnd = std::upper_bound( sorted.begin(), sorted.end(), ourMaxX,
1862 []( int val, const SEG_EXTENT& e )
1863 {
1864 return val < e.minX;
1865 } );
1866
1867 for( auto jt = sorted.begin(); jt != rightEnd; ++jt )
1868 {
1869 if( jt->maxX < ourMinX || jt->maxY < ourMinY || jt->minY > ourMaxY )
1870 continue;
1871
1872 const SEG& b = theirSegs[jt->segIdx];
1873
1874 INTERSECTION is;
1875 is.index_our = s1;
1876 is.index_their = jt->segIdx;
1877 is.is_corner_our = false;
1878 is.is_corner_their = false;
1879 is.valid = true;
1880
1881 OPT_VECTOR2I p = a.Intersect( b );
1882
1883 if( !aExcludeColinearAndTouching && a.Collinear( b ) )
1884 {
1885 if( a.Contains( b.A ) )
1886 {
1887 is.p = b.A;
1888 is.is_corner_their = true;
1889 aIp.push_back( is );
1890 }
1891
1892 if( a.Contains( b.B ) )
1893 {
1894 is.p = b.B;
1895 is.index_their++;
1896 is.is_corner_their = true;
1897 aIp.push_back( is );
1898 }
1899
1900 if( b.Contains( a.A ) )
1901 {
1902 is.p = a.A;
1903 is.is_corner_our = true;
1904 aIp.push_back( is );
1905 }
1906
1907 if( b.Contains( a.B ) )
1908 {
1909 is.p = a.B;
1910 is.index_our++;
1911 is.is_corner_our = true;
1912 aIp.push_back( is );
1913 }
1914 }
1915 else if( p )
1916 {
1917 is.p = *p;
1918 is.is_corner_our = false;
1919 is.is_corner_their = false;
1920
1921 if( p == a.A )
1922 {
1923 is.is_corner_our = true;
1924 }
1925
1926 if( p == a.B )
1927 {
1928 is.is_corner_our = true;
1929 is.index_our++;
1930 }
1931
1932 if( p == b.A )
1933 {
1934 is.is_corner_their = true;
1935 }
1936
1937 if( p == b.B )
1938 {
1939 is.is_corner_their = true;
1940 is.index_their++;
1941 }
1942
1943 aIp.push_back( is );
1944 }
1945 }
1946 }
1947
1948 return aIp.size();
1949}
1950
1951
1952int SHAPE_LINE_CHAIN::PathLength( const VECTOR2I& aP, int aIndex ) const
1953{
1954 int sum = 0;
1955
1956 for( int i = 0; i < SegmentCount(); i++ )
1957 {
1958 const SEG seg = CSegment( i );
1959 bool indexMatch = false;
1960
1961 if( aIndex >= 0 )
1962 {
1963 if( aIndex == SegmentCount() )
1964 {
1965 indexMatch = ( i == SegmentCount() - 1 );
1966 }
1967 else
1968 {
1969 indexMatch = ( i == aIndex );
1970 }
1971 }
1972
1973 if( indexMatch )
1974 {
1975 sum += ( aP - seg.A ).EuclideanNorm();
1976 return sum;
1977 }
1978 else
1979 {
1980 sum += seg.Length();
1981 }
1982 }
1983
1984 return -1;
1985}
1986
1987
1988bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy, bool aUseBBoxCache ) const
1989{
1990 if( aUseBBoxCache && GetCachedBBox() && !GetCachedBBox()->Contains( aPt ) )
1991 return false;
1992
1993 const int pointCount = static_cast<int>( m_points.size() );
1994
1995 if( !m_closed || pointCount < 3 )
1996 return false;
1997
1998 // Direct access to m_points avoids virtual GetPoint() dispatch and per-call bounds checking
1999 const VECTOR2I* pts = m_points.data();
2000 bool inside = false;
2001
2002 for( int i = 0; i < pointCount; )
2003 {
2004 const VECTOR2I& p1 = pts[i++];
2005 const VECTOR2I& p2 = pts[i == pointCount ? 0 : i];
2006 const VECTOR2I diff = p2 - p1;
2007
2008 if( diff.y == 0 )
2009 continue;
2010
2011 const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
2012
2013 if( ( ( p1.y >= aPt.y ) != ( p2.y >= aPt.y ) ) && ( aPt.x - p1.x < d ) )
2014 inside = !inside;
2015 }
2016
2017 if( aAccuracy <= 1 )
2018 return inside;
2019 else
2020 return inside || PointOnEdge( aPt, aAccuracy );
2021}
2022
2023
2024bool SHAPE_LINE_CHAIN_BASE::PointInside( const VECTOR2I& aPt, int aAccuracy,
2025 bool aUseBBoxCache ) const
2026{
2027 /*
2028 * Don't check the bounding box unless it's cached. Building it is about the same speed as
2029 * the rigorous test below and so just slows things down by doing potentially two tests.
2030 */
2031 if( aUseBBoxCache && GetCachedBBox() && !GetCachedBBox()->Contains( aPt ) )
2032 return false;
2033
2034 if( !IsClosed() || GetPointCount() < 3 )
2035 return false;
2036
2037 /*
2038 * To check for interior points, we draw a line in the positive x direction from
2039 * the point. If it intersects an even number of segments, the point is outside the
2040 * line chain (it had to first enter and then exit). Otherwise, it is inside the chain.
2041 *
2042 * Note: slope might be denormal here in the case of a horizontal line but we require our
2043 * y to move from above to below the point (or vice versa)
2044 *
2045 * Note: we open-code CPoint() here so that we don't end up calculating the size of the
2046 * vector number-of-points times. This has a non-trivial impact on zone fill times.
2047 */
2048 int pointCount = GetPointCount();
2049 bool inside = false;
2050
2051 for( int i = 0; i < pointCount; )
2052 {
2053 const VECTOR2I p1 = GetPoint( i++ );
2054 const VECTOR2I p2 = GetPoint( i == pointCount ? 0 : i );
2055 const VECTOR2I diff = p2 - p1;
2056
2057 if( diff.y == 0 )
2058 continue;
2059
2060 const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
2061
2062 if( ( ( p1.y >= aPt.y ) != ( p2.y >= aPt.y ) ) && ( aPt.x - p1.x < d ) )
2063 inside = !inside;
2064 }
2065
2066 // If accuracy is <= 1 (nm) then we skip the accuracy test for performance. Otherwise
2067 // we use "OnEdge(accuracy)" as a proxy for "Inside(accuracy)".
2068 if( aAccuracy <= 1 )
2069 return inside;
2070 else
2071 return inside || PointOnEdge( aPt, aAccuracy );
2072}
2073
2074
2075bool SHAPE_LINE_CHAIN_BASE::PointOnEdge( const VECTOR2I& aPt, int aAccuracy ) const
2076{
2077 return EdgeContainingPoint( aPt, aAccuracy ) >= 0;
2078}
2079
2080
2081int SHAPE_LINE_CHAIN_BASE::EdgeContainingPoint( const VECTOR2I& aPt, int aAccuracy ) const
2082{
2083 const int threshold = aAccuracy + 1;
2084 const int64_t thresholdSq = int64_t( threshold ) * threshold;
2085 const size_t pointCount = GetPointCount();
2086
2087 if( !pointCount )
2088 {
2089 return -1;
2090 }
2091 else if( pointCount == 1 )
2092 {
2093 SEG::ecoord distSq = GetPoint( 0 ).SquaredDistance( aPt );
2094 return distSq <= thresholdSq ? 0 : -1;
2095 }
2096
2097 const size_t segCount = GetSegmentCount();
2098
2099 for( size_t i = 0; i < segCount; i++ )
2100 {
2101 const SEG s = GetSegment( i );
2102
2103 if( s.A == aPt || s.B == aPt )
2104 return i;
2105
2106 if( s.SquaredDistance( aPt ) <= thresholdSq )
2107 return i;
2108 }
2109
2110 return -1;
2111}
2112
2113
2114bool SHAPE_LINE_CHAIN::CheckClearance( const VECTOR2I& aP, const int aDist ) const
2115{
2116 if( !PointCount() )
2117 return false;
2118 else if( PointCount() == 1 )
2119 return m_points[0].Distance( aP ) <= aDist;
2120
2121 for( int i = 0; i < SegmentCount(); i++ )
2122 {
2123 const SEG s = CSegment( i );
2124
2125 if( s.A == aP || s.B == aP )
2126 return true;
2127
2128 if( s.Distance( aP ) <= aDist )
2129 return true;
2130 }
2131
2132 return false;
2133}
2134
2135
2136const std::optional<SHAPE_LINE_CHAIN::INTERSECTION> SHAPE_LINE_CHAIN::SelfIntersecting() const
2137{
2138 const int segCount = SegmentCount();
2139 const int ptCount = static_cast<int>( m_points.size() );
2140 const bool closed = m_closed;
2141
2142 if( segCount < 2 )
2143 return std::optional<INTERSECTION>();
2144
2145 // Index of the second endpoint of segment i, handling the closed-chain wrap
2146 auto endIdx = [ptCount]( int i )
2147 {
2148 int next = i + 1;
2149 return next < ptCount ? next : 0;
2150 };
2151
2152 for( int s1 = 0; s1 < segCount; s1++ )
2153 {
2154 const VECTOR2I& a1 = m_points[s1];
2155 const VECTOR2I& b1 = m_points[endIdx( s1 )];
2156
2157 // Expand by 2 to account for Contains() tolerance (SquaredDistance <= 3)
2158 const int s1MinX = std::min( a1.x, b1.x ) - 2;
2159 const int s1MaxX = std::max( a1.x, b1.x ) + 2;
2160 const int s1MinY = std::min( a1.y, b1.y ) - 2;
2161 const int s1MaxY = std::max( a1.y, b1.y ) + 2;
2162
2163 for( int s2 = s1 + 1; s2 < segCount; s2++ )
2164 {
2165 const VECTOR2I& a2 = m_points[s2];
2166 const VECTOR2I& b2 = m_points[endIdx( s2 )];
2167
2168 const int s2MinX = std::min( a2.x, b2.x );
2169 const int s2MaxX = std::max( a2.x, b2.x );
2170
2171 if( s1MaxX < s2MinX || s2MaxX < s1MinX )
2172 continue;
2173
2174 const int s2MinY = std::min( a2.y, b2.y );
2175 const int s2MaxY = std::max( a2.y, b2.y );
2176
2177 if( s1MaxY < s2MinY || s2MaxY < s1MinY )
2178 continue;
2179
2180 const SEG seg1( a1, b1, s1 );
2181
2182 if( s1 + 1 != s2 && seg1.Contains( a2 ) )
2183 {
2184 INTERSECTION is;
2185 is.index_our = s1;
2186 is.index_their = s2;
2187 is.p = a2;
2188 return is;
2189 }
2190 else if( seg1.Contains( b2 ) &&
2191 // for closed polylines, the ending point of the
2192 // last segment == starting point of the first segment
2193 // this is a normal case, not self intersecting case
2194 !( closed && s1 == 0 && s2 == segCount - 1 ) )
2195 {
2196 INTERSECTION is;
2197 is.index_our = s1;
2198 is.index_their = s2;
2199 is.p = b2;
2200 return is;
2201 }
2202 else
2203 {
2204 OPT_VECTOR2I p = seg1.Intersect( SEG( a2, b2, s2 ), true );
2205
2206 if( p )
2207 {
2208 INTERSECTION is;
2209 is.index_our = s1;
2210 is.index_their = s2;
2211 is.p = *p;
2212 return is;
2213 }
2214 }
2215 }
2216 }
2217
2218 return std::optional<INTERSECTION>();
2219}
2220
2221
2223{
2224 SHAPE_KEY( int aFirstIdx, int aArcIdx, const BOX2I_MINMAX& aBBox ) :
2225 m_FirstIdx( aFirstIdx ), m_ArcIdx( aArcIdx ), m_BBox( aBBox )
2226 {
2227 }
2228
2232};
2233
2234
2235const std::optional<SHAPE_LINE_CHAIN::INTERSECTION>
2237{
2238 auto pointsClose = []( const VECTOR2I& aPt1, const VECTOR2I& aPt2 ) -> bool
2239 {
2240 return ( VECTOR2D( aPt1 ) - aPt2 ).SquaredEuclideanNorm() <= 2.0;
2241 };
2242
2243 auto collideArcSeg = [&pointsClose]( const SHAPE_ARC& aArc, const SEG& aSeg, int aClearance = 0,
2244 VECTOR2I* aLocation = nullptr )
2245 {
2246 VECTOR2I center = aArc.GetCenter();
2247 CIRCLE circle( center, aArc.GetRadius() );
2248
2249 std::vector<VECTOR2I> candidatePts = circle.Intersect( aSeg );
2250
2251 for( const VECTOR2I& candidate : candidatePts )
2252 {
2253 // Skip shared points
2254 if( aArc.GetP1() == aSeg.A && pointsClose( candidate, aSeg.A ) )
2255 continue;
2256
2257 if( aSeg.B == aArc.GetP0() && pointsClose( candidate, aSeg.B ) )
2258 continue;
2259
2260 bool collides = aArc.Collide( candidate, aClearance, nullptr, aLocation );
2261
2262 if( collides )
2263 return true;
2264 }
2265
2266 return false;
2267 };
2268
2269 auto collideArcArc = [&pointsClose]( const SHAPE_ARC& aArc1, const SHAPE_ARC& aArc2,
2270 VECTOR2I* aLocation = nullptr )
2271 {
2272 std::vector<VECTOR2I> candidatePts;
2273
2274 aArc1.Intersect( aArc2, &candidatePts );
2275
2276 for( const VECTOR2I& candidate : candidatePts )
2277 {
2278 // Skip shared points
2279 if( aArc1.GetP1() == aArc2.GetP0() && pointsClose( candidate, aArc1.GetP1() ) )
2280 continue;
2281
2282 if( aArc2.GetP1() == aArc1.GetP0() && pointsClose( candidate, aArc2.GetP1() ) )
2283 continue;
2284
2285 if( aLocation )
2286 *aLocation = candidate;
2287
2288 return true;
2289 }
2290
2291 return false;
2292 };
2293
2294 auto collideSegSeg = [this]( int s1, int s2, INTERSECTION& is )
2295 {
2296 SEG seg1 = CSegment( s1 );
2297 SEG seg2 = CSegment( s2 );
2298
2299 const VECTOR2I s2a = seg2.A, s2b = seg2.B;
2300
2301 if( s1 + 1 != s2 && seg1.Contains( s2a ) )
2302 {
2303 is.index_our = s1;
2304 is.index_their = s2;
2305 is.p = s2a;
2306 return true;
2307 }
2308 else if( seg1.Contains( s2b ) &&
2309 // for closed polylines, the ending point of the
2310 // last segment == starting point of the first segment
2311 // this is a normal case, not self intersecting case
2312 !( IsClosed() && s1 == 0 && s2 == SegmentCount() - 1 ) )
2313 {
2314 is.index_our = s1;
2315 is.index_their = s2;
2316 is.p = s2b;
2317 return true;
2318 }
2319 else
2320 {
2321 OPT_VECTOR2I p = seg1.Intersect( seg2, true );
2322
2323 if( p )
2324 {
2325 is.index_our = s1;
2326 is.index_their = s2;
2327 is.p = *p;
2328 return true;
2329 }
2330 }
2331
2332 return false;
2333 };
2334
2335 INTERSECTION is;
2336
2337 std::vector<SHAPE_KEY> shapeCache;
2338 for( int si = 0; si != -1; si = NextShape( si ) )
2339 {
2340 int arci = ArcIndex( si );
2341
2342 shapeCache.emplace_back( si, arci,
2343 arci == -1 ? BOX2I_MINMAX( CSegment( si ) )
2344 : BOX2I_MINMAX( Arc( arci ) ) );
2345 }
2346
2347 for( size_t sk1 = 0; sk1 < shapeCache.size(); sk1++ )
2348 {
2349 for( size_t sk2 = sk1 + 1; sk2 < shapeCache.size(); sk2++ )
2350 {
2351 VECTOR2I loc;
2352 const SHAPE_KEY& k1 = shapeCache[sk1];
2353 const SHAPE_KEY& k2 = shapeCache[sk2];
2354
2355 if( !k1.m_BBox.Intersects( k2.m_BBox ) )
2356 continue;
2357
2358 if( k1.m_ArcIdx == -1 && k2.m_ArcIdx == -1 )
2359 {
2360 if( collideSegSeg( k1.m_FirstIdx, k2.m_FirstIdx, is ) )
2361 {
2362 return is;
2363 }
2364 }
2365 else if( k1.m_ArcIdx != -1 && k2.m_ArcIdx == -1 )
2366 {
2367 if( collideArcSeg( Arc( k1.m_ArcIdx ), CSegment( k2.m_FirstIdx ), 0, &loc ) )
2368 {
2369 is.index_our = k1.m_FirstIdx;
2370 is.index_their = k2.m_FirstIdx;
2371 is.p = loc;
2372 return is;
2373 }
2374 }
2375 else if( k1.m_ArcIdx == -1 && k2.m_ArcIdx != -1 )
2376 {
2377 if( collideArcSeg( Arc( k2.m_ArcIdx ), CSegment( k1.m_FirstIdx ), 0, &loc ) )
2378 {
2379 is.index_our = k1.m_FirstIdx;
2380 is.index_their = k2.m_FirstIdx;
2381 is.p = loc;
2382 return is;
2383 }
2384 }
2385 else if( k1.m_ArcIdx != -1 && k2.m_ArcIdx != -1 )
2386 {
2387 if( collideArcArc( Arc( k1.m_ArcIdx ), Arc( k2.m_ArcIdx ), &loc ) )
2388 {
2389 is.index_our = k1.m_FirstIdx;
2390 is.index_their = k2.m_FirstIdx;
2391 is.p = loc;
2392 return is;
2393 }
2394 }
2395 }
2396 }
2397
2398 return std::optional<SHAPE_LINE_CHAIN::INTERSECTION>();
2399}
2400
2401
2403 bool aAllowInternalShapePoints ) const
2404{
2405 if( PointCount() == 0 )
2406 {
2407 // The only right answer here is "don't crash".
2408 return { 0, 0 };
2409 }
2410
2411 int min_d = std::numeric_limits<int>::max();
2412 int nearest = 0;
2413
2414 for( int i = 0; i < SegmentCount(); i++ )
2415 {
2416 int d = CSegment( i ).Distance( aP );
2417
2418 if( d < min_d )
2419 {
2420 min_d = d;
2421 nearest = i;
2422 }
2423 }
2424
2425 if( !aAllowInternalShapePoints )
2426 {
2427 //Snap to arc end points if the closest found segment is part of an arc segment
2428 if( nearest > 0 && nearest < PointCount() && IsArcSegment( nearest ) )
2429 {
2430 VECTOR2I ptToSegStart = CSegment( nearest ).A - aP;
2431 VECTOR2I ptToSegEnd = CSegment( nearest ).B - aP;
2432
2433 if( ptToSegStart.EuclideanNorm() > ptToSegEnd.EuclideanNorm() )
2434 nearest++;
2435
2436 // Is this the start or end of an arc? If so, return it directly
2437 if( IsArcStart( nearest ) || IsArcEnd( nearest ) )
2438 {
2439 return m_points[nearest];
2440 }
2441 else
2442 {
2443 const SHAPE_ARC& nearestArc = Arc( ArcIndex( nearest ) );
2444 VECTOR2I ptToArcStart = nearestArc.GetP0() - aP;
2445 VECTOR2I ptToArcEnd = nearestArc.GetP1() - aP;
2446
2447 if( ptToArcStart.EuclideanNorm() > ptToArcEnd.EuclideanNorm() )
2448 return nearestArc.GetP1();
2449 else
2450 return nearestArc.GetP0();
2451 }
2452
2453 }
2454 }
2455
2456 return CSegment( nearest ).NearestPoint( aP );
2457}
2458
2459
2460const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const SEG& aSeg, int& dist ) const
2461{
2462 if( PointCount() == 0 )
2463 {
2464 // The only right answer here is "don't crash".
2465 return { 0, 0 };
2466 }
2467
2468 int nearest = 0;
2469
2470 dist = std::numeric_limits<int>::max();
2471
2472 for( int i = 0; i < PointCount(); i++ )
2473 {
2474 int d = aSeg.LineDistance( CPoint( i ) );
2475
2476 if( d < dist )
2477 {
2478 dist = d;
2479 nearest = i;
2480 }
2481 }
2482
2483 return CPoint( nearest );
2484}
2485
2486
2488{
2489 int min_d = std::numeric_limits<int>::max();
2490 int nearest = 0;
2491
2492 for( int i = 0; i < SegmentCount(); i++ )
2493 {
2494 int d = CSegment( i ).Distance( aP );
2495
2496 if( d < min_d )
2497 {
2498 min_d = d;
2499 nearest = i;
2500 }
2501 }
2502
2503 return nearest;
2504}
2505
2506
2507const std::string SHAPE_LINE_CHAIN::Format( bool aCplusPlus ) const
2508{
2509 std::stringstream ss;
2510
2511 ss << "SHAPE_LINE_CHAIN( { ";
2512
2513 for( int i = 0; i < PointCount(); i++ )
2514 {
2515 ss << "VECTOR2I( " << m_points[i].x << ", " << m_points[i].y << ")";
2516
2517 if( i != PointCount() -1 )
2518 ss << ", ";
2519 }
2520
2521 ss << "}, " << ( m_closed ? "true" : "false" );
2522 ss << " );";
2523
2524 return ss.str();
2525
2526 /* fixme: arcs
2527 for( size_t i = 0; i < m_arcs.size(); i++ )
2528 ss << m_arcs[i].GetCenter().x << " " << m_arcs[i].GetCenter().y << " "
2529 << m_arcs[i].GetP0().x << " " << m_arcs[i].GetP0().y << " "
2530 << m_arcs[i].GetCentralAngle().AsDegrees();
2531
2532 return ss.str();*/
2533}
2534
2535
2537 bool aCyclicalCompare,
2538 int aEpsilon ) const
2539{
2540 SHAPE_LINE_CHAIN a( *this ), b( aOther );
2541 a.Simplify();
2542 b.Simplify();
2543
2544 if( a.m_points.size() != b.m_points.size() )
2545 return false;
2546
2547 if( aCyclicalCompare )
2548 {
2549 std::vector<VECTOR2I> aVerts = a.m_points;
2550 std::vector<VECTOR2I> bVerts = b.m_points;
2551
2552 auto centroid = []( const std::vector<VECTOR2I>& pts )
2553 {
2554 double sx = 0.0, sy = 0.0;
2555 for( const auto& p : pts )
2556 {
2557 sx += p.x;
2558 sy += p.y;
2559 }
2560 return std::pair<double, double>( sx / pts.size(), sy / pts.size() );
2561 };
2562
2563 auto aC = centroid( aVerts );
2564 auto bC = centroid( bVerts );
2565
2566 auto angleCmp =
2567 []( const std::pair<double, double>& c, const VECTOR2I& p1, const VECTOR2I& p2 )
2568 {
2569 double a1 = atan2( p1.y - c.second, p1.x - c.first );
2570 double a2 = atan2( p2.y - c.second, p2.x - c.first );
2571 return a1 < a2;
2572 };
2573
2574 // sort by angle around centroid so that cyclic vertex order doesn't matter
2575 std::sort( aVerts.begin(), aVerts.end(),
2576 [&]( const VECTOR2I& p1, const VECTOR2I& p2 )
2577 {
2578 return angleCmp( aC, p1, p2 );
2579 } );
2580
2581 std::sort( bVerts.begin(), bVerts.end(),
2582 [&]( const VECTOR2I& p1, const VECTOR2I& p2 )
2583 {
2584 return angleCmp( bC, p1, p2 );
2585 } );
2586
2587 for( size_t i = 0; i < aVerts.size(); i++ )
2588 {
2589 if( abs( aVerts[i].x - bVerts[i].x ) > aEpsilon
2590 || abs( aVerts[i].y - bVerts[i].y ) > aEpsilon )
2591 return false;
2592 }
2593
2594 }
2595 else
2596 {
2597 for( int i = 0; i < a.PointCount(); i++ )
2598 {
2599 if( abs( a.CPoint( i ).x - b.CPoint( i ).x ) > aEpsilon
2600 || abs( a.CPoint( i ).y - b.CPoint( i ).y ) > aEpsilon )
2601 return false;
2602 }
2603 }
2604
2605
2606
2607 return true;
2608}
2609
2610
2612{
2614 return Intersect( aChain, dummy ) != 0;
2615}
2616
2617
2619{
2620 return new SHAPE_LINE_CHAIN( *this );
2621}
2622
2623
2624bool SHAPE_LINE_CHAIN::Parse( std::stringstream& aStream )
2625{
2626 size_t n_pts;
2627 size_t n_arcs;
2628
2629 m_points.clear();
2630 aStream >> n_pts;
2631
2632 // Rough sanity check, just make sure the loop bounds aren't absolutely outlandish
2633 if( n_pts > aStream.str().size() )
2634 return false;
2635
2636 aStream >> m_closed;
2637 aStream >> n_arcs;
2638
2639 if( n_arcs > aStream.str().size() )
2640 return false;
2641
2642 for( size_t i = 0; i < n_pts; i++ )
2643 {
2644 int x, y;
2645 ssize_t ind;
2646 aStream >> x;
2647 aStream >> y;
2648 m_points.emplace_back( x, y );
2649
2650 aStream >> ind;
2651 m_shapes.emplace_back( std::make_pair( ind, SHAPE_IS_PT ) );
2652 }
2653
2654 for( size_t i = 0; i < n_arcs; i++ )
2655 {
2656 VECTOR2I p0, pc;
2657 double angle;
2658
2659 aStream >> pc.x;
2660 aStream >> pc.y;
2661 aStream >> p0.x;
2662 aStream >> p0.y;
2663 aStream >> angle;
2664
2665 m_arcs.emplace_back( pc, p0, EDA_ANGLE( angle, DEGREES_T ) );
2666 }
2667
2668 return true;
2669}
2670
2671
2672const VECTOR2I SHAPE_LINE_CHAIN::PointAlong( int aPathLength ) const
2673{
2674 int total = 0;
2675
2676 if( aPathLength == 0 )
2677 return CPoint( 0 );
2678
2679 for( int i = 0; i < SegmentCount(); i++ )
2680 {
2681 const SEG& s = CSegment( i );
2682 int l = s.Length();
2683
2684 if( total + l >= aPathLength )
2685 {
2686 VECTOR2I d( s.B - s.A );
2687 return s.A + d.Resize( aPathLength - total );
2688 }
2689
2690 total += l;
2691 }
2692
2693 return CLastPoint();
2694}
2695
2696
2697double SHAPE_LINE_CHAIN::Area( bool aAbsolute ) const
2698{
2699 // see https://www.mathopenref.com/coordpolygonarea2.html
2700
2701 if( !m_closed )
2702 return 0.0;
2703
2704 double area = 0.0;
2705 int size = m_points.size();
2706
2707 for( int i = 0, j = size - 1; i < size; ++i )
2708 {
2709 area += ( (double) m_points[j].x + m_points[i].x ) *
2710 ( (double) m_points[j].y - m_points[i].y );
2711 j = i;
2712 }
2713
2714 if( aAbsolute )
2715 return std::fabs( area * 0.5 ); // The result would be negative if points are anti-clockwise
2716 else
2717 return -area * 0.5; // The result would be negative if points are anti-clockwise
2718}
2719
2720
2722{
2723 std::vector<VECTOR2I> pts_unique;
2724 std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
2725
2726 // Always try to keep at least 2 points otherwise, we're not really a line
2727 if( PointCount() < 3 )
2728 {
2729 return;
2730 }
2731 else if( PointCount() == 3 )
2732 {
2733 if( m_points[0] == m_points[1] || m_points[1] == m_points[2] )
2734 Remove( 1 );
2735
2736 return;
2737 }
2738
2739 int i = 0;
2740
2741 while( i < PointCount() )
2742 {
2743 int j = i + 1;
2744
2745 // We can eliminate duplicate vertices as long as they are part of the same shape, OR if
2746 // one of them is part of a shape and one is not.
2747 while( j < PointCount() && m_points[i] == m_points[j] &&
2748 ( m_shapes[i] == m_shapes[j] ||
2749 m_shapes[i] == SHAPES_ARE_PT ||
2750 m_shapes[j] == SHAPES_ARE_PT ) )
2751 {
2752 j++;
2753 }
2754
2755 std::pair<ssize_t,ssize_t> shapeToKeep = m_shapes[i];
2756
2757 if( shapeToKeep == SHAPES_ARE_PT )
2758 shapeToKeep = m_shapes[j - 1];
2759
2760 assert( shapeToKeep.first < static_cast<int>( m_arcs.size() ) );
2761 assert( shapeToKeep.second < static_cast<int>( m_arcs.size() ) );
2762
2763 pts_unique.push_back( CPoint( i ) );
2764 shapes_unique.push_back( shapeToKeep );
2765
2766 i = j;
2767 }
2768
2769 m_points.clear();
2770 m_shapes.clear();
2771
2772 for( size_t ii = 0; ii < pts_unique.size(); ++ii )
2773 {
2774 const VECTOR2I p0 = pts_unique[ii];
2775
2776 m_points.push_back( p0 );
2777 m_shapes.push_back( shapes_unique[ii] );
2778 }
2779}
2780
2781
2782
2783void SHAPE_LINE_CHAIN::Simplify( int aTolerance )
2784{
2785 if( PointCount() < 3 )
2786 return;
2787
2788 std::vector<VECTOR2I> new_points;
2789 std::vector<std::pair<ssize_t, ssize_t>> new_shapes;
2790
2791 new_points.reserve( m_points.size() );
2792 new_shapes.reserve( m_shapes.size() );
2793
2794 for( size_t start_idx = 0; start_idx < m_points.size(); )
2795 {
2796 new_points.push_back( m_points[start_idx] );
2797 new_shapes.push_back( m_shapes[start_idx] );
2798
2799 // If the line is not closed, we need at least 3 points before simplifying
2800 if( !m_closed && start_idx == m_points.size() - 2 )
2801 break;
2802
2803 // Initialize the end index to be two points ahead of start
2804 size_t end_idx = ( start_idx + 2 ) % m_points.size();
2805 bool can_simplify = true;
2806
2807 while( can_simplify && end_idx != start_idx && ( end_idx > start_idx || m_closed ) )
2808 {
2809 // Test all points between start_idx and end_idx
2810 for( size_t test_idx = ( start_idx + 1 ) % m_points.size();
2811 test_idx != end_idx;
2812 test_idx = ( test_idx + 1 ) % m_points.size() )
2813 {
2814 // Only intermediate test points must be regular points (not arcs).
2815 // Allow start or end to be arc endpoints so collinear segments
2816 // adjacent to arcs can still be simplified while preserving arcs.
2817 if( m_shapes[test_idx].first != SHAPE_IS_PT )
2818 {
2819 can_simplify = false;
2820 break;
2821 }
2822
2823 // Test if the point is within the allowed error
2824 if( !TestSegmentHit( m_points[test_idx], m_points[start_idx], m_points[end_idx], aTolerance ) )
2825 {
2826 can_simplify = false;
2827 break;
2828 }
2829 }
2830
2831 if( can_simplify )
2832 {
2833 // If we can simplify, move end_idx one further
2834 end_idx = ( end_idx + 1 ) % m_points.size();
2835 }
2836 }
2837
2838 // If we couldn't simplify at all, move to the next point
2839 if( end_idx == ( start_idx + 2 ) % m_points.size() )
2840 {
2841 ++start_idx;
2842 }
2843 else
2844 {
2845 // Otherwise, jump to the last point we could include in the simplification
2846 size_t new_start_idx = ( end_idx + m_points.size() - 1 ) % m_points.size();
2847
2848 // If we looped all the way around, we're done
2849 if( new_start_idx <= start_idx )
2850 break;
2851
2852 start_idx = new_start_idx;
2853 }
2854 }
2855
2856 // If we have only one point, we need to add a second point to make a line
2857 if( new_points.size() == 1 )
2858 {
2859 new_points.push_back( m_points.back() );
2860 new_shapes.push_back( m_shapes.back() );
2861 }
2862
2863 // If we are not closed, then the start and end points of the original line need to
2864 // be the start and end points of the new line.
2865 if( !m_closed && m_points.back() != new_points.back() )
2866 {
2867 new_points.push_back( m_points.back() );
2868 new_shapes.push_back( m_shapes.back() );
2869 }
2870
2871 m_points.clear();
2872 m_shapes.clear();
2873 m_points = std::move( new_points );
2874 m_shapes = std::move( new_shapes );
2875}
2876
2877
2878void SHAPE_LINE_CHAIN::Split( const VECTOR2I& aStart, const VECTOR2I& aEnd, SHAPE_LINE_CHAIN& aPre,
2879 SHAPE_LINE_CHAIN& aMid, SHAPE_LINE_CHAIN& aPost ) const
2880{
2881 VECTOR2I cp( aEnd );
2882
2883 VECTOR2I n = NearestPoint( cp, false );
2884 VECTOR2I m = NearestPoint( aStart, false );
2885
2886 SHAPE_LINE_CHAIN l( *this );
2887 l.Split( n, true );
2888 l.Split( m, true );
2889
2890 int i_start = l.Find( m );
2891 int i_end = l.Find( n );
2892
2893 if( i_start > i_end )
2894 {
2895 l = l.Reverse();
2896 i_start = l.Find( m );
2897 i_end = l.Find( n );
2898 }
2899
2900 aPre = l.Slice( 0, i_start );
2901 aPost = l.Slice( i_end, -1 );
2902 aMid = l.Slice( i_start, i_end );
2903}
2904
2905
2906
2908{
2909 std::vector<VECTOR2I> pts_unique;
2910 std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
2911
2912 // Always try to keep at least 2 points otherwise, we're not really a line
2913 if( PointCount() < 3 )
2914 {
2915 return *this;
2916 }
2917 else if( PointCount() == 3 )
2918 {
2919 if( m_points[0] == m_points[1] || m_points[1] == m_points[2] )
2920 Remove( 1 );
2921
2922 return *this;
2923 }
2924
2925 int i = 0;
2926 int np = PointCount();
2927
2928 // stage 1: eliminate duplicate vertices
2929 while( i < np )
2930 {
2931 int j = i + 1;
2932
2933 // We can eliminate duplicate vertices as long as they are part of the same shape, OR if
2934 // one of them is part of a shape and one is not.
2935 while( j < np && m_points[i] == m_points[j] &&
2936 ( m_shapes[i] == m_shapes[j] ||
2937 m_shapes[i] == SHAPES_ARE_PT ||
2938 m_shapes[j] == SHAPES_ARE_PT ) )
2939 {
2940 j++;
2941 }
2942
2943 std::pair<ssize_t,ssize_t> shapeToKeep = m_shapes[i];
2944
2945 if( shapeToKeep == SHAPES_ARE_PT )
2946 shapeToKeep = m_shapes[j - 1];
2947
2948 assert( shapeToKeep.first < static_cast<int>( m_arcs.size() ) );
2949 assert( shapeToKeep.second < static_cast<int>( m_arcs.size() ) );
2950
2951 pts_unique.push_back( CPoint( i ) );
2952 shapes_unique.push_back( shapeToKeep );
2953
2954 i = j;
2955 }
2956
2957 m_points.clear();
2958 m_shapes.clear();
2959 np = pts_unique.size();
2960
2961 i = 0;
2962
2963 // stage 2: eliminate colinear segments
2964 while( i < np - 2 )
2965 {
2966 const VECTOR2I p0 = pts_unique[i];
2967 int n = i;
2968
2969 if( aRemoveColinear && shapes_unique[i] == SHAPES_ARE_PT
2970 && shapes_unique[i + 1] == SHAPES_ARE_PT )
2971 {
2972 while( n < np - 2
2973 && ( SEG( p0, pts_unique[n + 2] ).LineDistance( pts_unique[n + 1] ) <= 1
2974 || SEG( p0, pts_unique[n + 2] ).Collinear( SEG( p0, pts_unique[n + 1] ) ) ) )
2975 n++;
2976 }
2977
2978 m_points.push_back( p0 );
2979 m_shapes.push_back( shapes_unique[i] );
2980
2981 if( n > i )
2982 i = n;
2983
2984 if( n == np - 2 )
2985 {
2986 m_points.push_back( pts_unique[np - 1] );
2987 m_shapes.push_back( shapes_unique[np - 1] );
2988 return *this;
2989 }
2990
2991 i++;
2992 }
2993
2994 if( np > 1 )
2995 {
2996 m_points.push_back( pts_unique[np - 2] );
2997 m_shapes.push_back( shapes_unique[np - 2] );
2998 }
2999
3000 m_points.push_back( pts_unique[np - 1] );
3001 m_shapes.push_back( shapes_unique[np - 1] );
3002
3003 assert( m_points.size() == m_shapes.size() );
3004
3005 return *this;
3006}
3007
3008bool SHAPE_LINE_CHAIN::OffsetLine( int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError,
3009 SHAPE_LINE_CHAIN& aLeft, SHAPE_LINE_CHAIN& aRight,
3010 bool aSimplify ) const
3011{
3012 if( PointCount() < 2 )
3013 return false;
3014
3015 SHAPE_POLY_SET poly;
3016 poly.OffsetLineChain( *this, aAmount, aCornerStrategy, aMaxError, aSimplify );
3017
3018 if( poly.OutlineCount() != 1 )
3019 return false;
3020
3021 if( poly.COutline( 0 ).PointCount() < 3 )
3022 return false;
3023
3024 if( poly.HasHoles() )
3025 return false;
3026
3027 SHAPE_LINE_CHAIN outline = poly.COutline( 0 );
3028
3029 wxASSERT( outline.IsClosed() );
3030
3031 const VECTOR2I& start = CPoint( 0 );
3032 const VECTOR2I& end = CLastPoint();
3033
3034 outline.Split( start, true );
3035 outline.Split( end, true );
3036
3037 const int idA = outline.Find( start );
3038 const int idB = outline.Find( end );
3039
3040 if( idA == -1 || idB == -1 )
3041 return false;
3042
3043 aLeft.Clear();
3044 aRight.Clear();
3045
3046 for( int i = idA;; )
3047 {
3048 aLeft.Append( outline.CPoint( i ) );
3049
3050 i = ( i + 1 ) % outline.PointCount();
3051
3052 if( i == idB )
3053 {
3054 aLeft.Append( outline.CPoint( i ) );
3055 break;
3056 }
3057
3058 if( i == idA )
3059 return false;
3060 }
3061
3062 if( aLeft.PointCount() < 2 )
3063 return false;
3064
3065 for( int i = idB;; )
3066 {
3067 aRight.Append( outline.CPoint( i ) );
3068
3069 i = ( i + 1 ) % outline.PointCount();
3070
3071 if( i == idA )
3072 {
3073 aRight.Append( outline.CPoint( i ) );
3074 break;
3075 }
3076
3077 if( i == idB )
3078 return false;
3079 }
3080
3081 if( aRight.PointCount() < 2 )
3082 return false;
3083
3084 if( aLeft.CPoint( 0 ) != start )
3085 {
3086 aLeft = aLeft.Reverse();
3087 wxASSERT( aLeft.CPoint( 0 ) == start );
3088 }
3089
3090 if( aRight.CPoint( 0 ) != start )
3091 {
3092 aRight = aRight.Reverse();
3093 wxASSERT( aRight.CPoint( 0 ) == start );
3094 }
3095
3096 SEG base( CPoint( 0 ), CPoint( 1 ) );
3097 int sideLeft = base.Side( aLeft.CPoint( 1 ) );
3098 int sideRight = base.Side( aRight.CPoint( 1 ) );
3099
3100 if( sideLeft == 0 || sideRight == 0 )
3101 return false;
3102
3103 if( sideLeft == sideRight )
3104 return false;
3105
3106 if( sideLeft > 0 && sideRight < 0 )
3107 std::swap( aLeft, aRight );
3108
3109 if( aLeft.PointCount() < 4 )
3110 return false;
3111
3112 if( aRight.PointCount() < 4 )
3113 return false;
3114
3115 aLeft.Remove( 0 );
3116 aLeft.Remove( aLeft.PointCount() - 1 );
3117
3118 aRight.Remove( 0 );
3119 aRight.Remove( aRight.PointCount() - 1 );
3120
3121 return true;
3122}
3123
3124
3126 ERROR_LOC aErrorLoc ) const
3127{
3128 aBuffer.AddOutline( *this );
3129}
3130
3131
3133 m_point( aPoint ),
3134 m_finished( false ),
3135 m_state( 0 ),
3136 m_count( 0 )
3137{
3138}
3139
3140
3142 const VECTOR2I& ip, const VECTOR2I& ipNext )
3143{
3144 if( ipNext.y == m_point.y )
3145 {
3146 if( ( ipNext.x == m_point.x )
3147 || ( ip.y == m_point.y && ( ( ipNext.x > m_point.x ) == ( ip.x < m_point.x ) ) ) )
3148 {
3149 m_finished = true;
3150 m_state = -1;
3151 return false;
3152 }
3153 }
3154
3155 if( ( ip.y < m_point.y ) != ( ipNext.y < m_point.y ) )
3156 {
3157 if( ip.x >= m_point.x )
3158 {
3159 if( ipNext.x > m_point.x )
3160 {
3161 m_state = 1 - m_state;
3162 }
3163 else
3164 {
3165 double d = (double) ( ip.x - m_point.x ) * ( ipNext.y - m_point.y )
3166 - (double) ( ipNext.x - m_point.x ) * ( ip.y - m_point.y );
3167
3168 if( !d )
3169 {
3170 m_finished = true;
3171 m_state = -1;
3172 return false;
3173 }
3174
3175 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
3176 m_state = 1 - m_state;
3177 }
3178 }
3179 else
3180 {
3181 if( ipNext.x > m_point.x )
3182 {
3183 double d = (double) ( ip.x - m_point.x ) * ( ipNext.y - m_point.y )
3184 - (double) ( ipNext.x - m_point.x ) * ( ip.y - m_point.y );
3185
3186 if( !d )
3187 {
3188 m_finished = true;
3189 m_state = -1;
3190 return false;
3191 }
3192
3193 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
3194 m_state = 1 - m_state;
3195 }
3196 }
3197 }
3198
3199 return true;
3200}
3201
3202
3204{
3205 if( !m_count )
3206 {
3207 m_lastPoint = aPolyline.CPoint( 0 );
3208 m_firstPoint = aPolyline.CPoint( 0 );
3209 }
3210
3211 m_count += aPolyline.PointCount();
3212
3213 for( int i = 1; i < aPolyline.PointCount(); i++ )
3214 {
3215 auto p = aPolyline.CPoint( i );
3216
3217 if( !processVertex( m_lastPoint, p ) )
3218 return;
3219
3220 m_lastPoint = p;
3221 }
3222
3223}
3224
3225
3231
3232
3233bool SHAPE_LINE_CHAIN::IsSharedPt( size_t aIndex ) const
3234{
3235 return aIndex < m_shapes.size()
3236 && m_shapes[aIndex].first != SHAPE_IS_PT
3237 && m_shapes[aIndex].second != SHAPE_IS_PT;
3238}
3239
3240
3241bool SHAPE_LINE_CHAIN::IsPtOnArc( size_t aPtIndex ) const
3242{
3243 return aPtIndex < m_shapes.size() && m_shapes[aPtIndex] != SHAPES_ARE_PT;
3244}
3245
3246
3247bool SHAPE_LINE_CHAIN::IsArcSegment( size_t aSegment ) const
3248{
3249 /*
3250 * A segment is part of an arc except in the special case of two arcs next to each other
3251 * but without a shared vertex. Here there is a segment between the end of the first arc
3252 * and the start of the second arc.
3253 */
3254 size_t nextIdx = aSegment + 1;
3255
3256 if( nextIdx > m_shapes.size() - 1 )
3257 {
3258 if( nextIdx == m_shapes.size() && m_closed && IsSharedPt( 0 ) )
3259 nextIdx = 0; // segment between end point and first point
3260 else
3261 return false;
3262 }
3263
3264 return ( IsPtOnArc( aSegment )
3265 && ( ArcIndex( aSegment ) == m_shapes[nextIdx].first ) );
3266}
3267
3268
3269bool SHAPE_LINE_CHAIN::IsArcStart( size_t aIndex ) const
3270{
3271 if( !IsArcSegment( aIndex ) ) // also does bound checking
3272 return false;
3273
3274 if( IsSharedPt( aIndex ) )
3275 return true;
3276
3277 const SHAPE_ARC& arc = Arc( ArcIndex( aIndex ) );
3278
3279 return arc.GetP0() == m_points[aIndex];
3280}
3281
3282
3283bool SHAPE_LINE_CHAIN::IsArcEnd( size_t aIndex ) const
3284{
3285 size_t prevIndex = aIndex - 1;
3286
3287 if( aIndex == 0 )
3288 prevIndex = m_points.size() - 1;
3289 else if( aIndex > m_points.size() -1 )
3290 return false; // invalid index requested
3291
3292 if( !IsArcSegment( prevIndex ) )
3293 return false;
3294
3295 if( IsSharedPt( aIndex ) )
3296 return true;
3297
3298 const SHAPE_ARC& arc = Arc( ArcIndex( aIndex ) );
3299
3300 return arc.GetP1() == m_points[aIndex];
3301}
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