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 = true;
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 sum += seg.Length();
1980 }
1981
1982 return -1;
1983}
1984
1985
1986bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy,
1987 bool aUseBBoxCache ) const
1988{
1989 if( aUseBBoxCache && GetCachedBBox() && !GetCachedBBox()->Contains( aPt ) )
1990 return false;
1991
1992 const int pointCount = static_cast<int>( m_points.size() );
1993
1994 if( !m_closed || pointCount < 3 )
1995 return false;
1996
1997 // Direct access to m_points avoids virtual GetPoint() dispatch and per-call bounds checking
1998 const VECTOR2I* pts = m_points.data();
1999 bool inside = false;
2000
2001 for( int i = 0; i < pointCount; )
2002 {
2003 const VECTOR2I& p1 = pts[i++];
2004 const VECTOR2I& p2 = pts[i == pointCount ? 0 : i];
2005 const VECTOR2I diff = p2 - p1;
2006
2007 if( diff.y == 0 )
2008 continue;
2009
2010 const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
2011
2012 if( ( ( p1.y >= aPt.y ) != ( p2.y >= aPt.y ) ) && ( aPt.x - p1.x < d ) )
2013 inside = !inside;
2014 }
2015
2016 if( aAccuracy <= 1 )
2017 return inside;
2018 else
2019 return inside || PointOnEdge( aPt, aAccuracy );
2020}
2021
2022
2023bool SHAPE_LINE_CHAIN_BASE::PointInside( const VECTOR2I& aPt, int aAccuracy,
2024 bool aUseBBoxCache ) const
2025{
2026 /*
2027 * Don't check the bounding box unless it's cached. Building it is about the same speed as
2028 * the rigorous test below and so just slows things down by doing potentially two tests.
2029 */
2030 if( aUseBBoxCache && GetCachedBBox() && !GetCachedBBox()->Contains( aPt ) )
2031 return false;
2032
2033 if( !IsClosed() || GetPointCount() < 3 )
2034 return false;
2035
2036 /*
2037 * To check for interior points, we draw a line in the positive x direction from
2038 * the point. If it intersects an even number of segments, the point is outside the
2039 * line chain (it had to first enter and then exit). Otherwise, it is inside the chain.
2040 *
2041 * Note: slope might be denormal here in the case of a horizontal line but we require our
2042 * y to move from above to below the point (or vice versa)
2043 *
2044 * Note: we open-code CPoint() here so that we don't end up calculating the size of the
2045 * vector number-of-points times. This has a non-trivial impact on zone fill times.
2046 */
2047 int pointCount = GetPointCount();
2048 bool inside = false;
2049
2050 for( int i = 0; i < pointCount; )
2051 {
2052 const VECTOR2I p1 = GetPoint( i++ );
2053 const VECTOR2I p2 = GetPoint( i == pointCount ? 0 : i );
2054 const VECTOR2I diff = p2 - p1;
2055
2056 if( diff.y == 0 )
2057 continue;
2058
2059 const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y );
2060
2061 if( ( ( p1.y >= aPt.y ) != ( p2.y >= aPt.y ) ) && ( aPt.x - p1.x < d ) )
2062 inside = !inside;
2063 }
2064
2065 // If accuracy is <= 1 (nm) then we skip the accuracy test for performance. Otherwise
2066 // we use "OnEdge(accuracy)" as a proxy for "Inside(accuracy)".
2067 if( aAccuracy <= 1 )
2068 return inside;
2069 else
2070 return inside || PointOnEdge( aPt, aAccuracy );
2071}
2072
2073
2074bool SHAPE_LINE_CHAIN_BASE::PointOnEdge( const VECTOR2I& aPt, int aAccuracy ) const
2075{
2076 return EdgeContainingPoint( aPt, aAccuracy ) >= 0;
2077}
2078
2079
2080int SHAPE_LINE_CHAIN_BASE::EdgeContainingPoint( const VECTOR2I& aPt, int aAccuracy ) const
2081{
2082 const int threshold = aAccuracy + 1;
2083 const int64_t thresholdSq = int64_t( threshold ) * threshold;
2084 const size_t pointCount = GetPointCount();
2085
2086 if( !pointCount )
2087 {
2088 return -1;
2089 }
2090 else if( pointCount == 1 )
2091 {
2092 SEG::ecoord distSq = GetPoint( 0 ).SquaredDistance( aPt );
2093 return distSq <= thresholdSq ? 0 : -1;
2094 }
2095
2096 const size_t segCount = GetSegmentCount();
2097
2098 for( size_t i = 0; i < segCount; i++ )
2099 {
2100 const SEG s = GetSegment( i );
2101
2102 if( s.A == aPt || s.B == aPt )
2103 return i;
2104
2105 if( s.SquaredDistance( aPt ) <= thresholdSq )
2106 return i;
2107 }
2108
2109 return -1;
2110}
2111
2112
2113bool SHAPE_LINE_CHAIN::CheckClearance( const VECTOR2I& aP, const int aDist ) const
2114{
2115 if( !PointCount() )
2116 return false;
2117 else if( PointCount() == 1 )
2118 return m_points[0] == aP;
2119
2120 for( int i = 0; i < SegmentCount(); i++ )
2121 {
2122 const SEG s = CSegment( i );
2123
2124 if( s.A == aP || s.B == aP )
2125 return true;
2126
2127 if( s.Distance( aP ) <= aDist )
2128 return true;
2129 }
2130
2131 return false;
2132}
2133
2134
2135const std::optional<SHAPE_LINE_CHAIN::INTERSECTION> SHAPE_LINE_CHAIN::SelfIntersecting() const
2136{
2137 const int segCount = SegmentCount();
2138 const int ptCount = static_cast<int>( m_points.size() );
2139 const bool closed = m_closed;
2140
2141 if( segCount < 2 )
2142 return std::optional<INTERSECTION>();
2143
2144 // Index of the second endpoint of segment i, handling the closed-chain wrap
2145 auto endIdx = [ptCount]( int i )
2146 {
2147 int next = i + 1;
2148 return next < ptCount ? next : 0;
2149 };
2150
2151 for( int s1 = 0; s1 < segCount; s1++ )
2152 {
2153 const VECTOR2I& a1 = m_points[s1];
2154 const VECTOR2I& b1 = m_points[endIdx( s1 )];
2155
2156 // Expand by 2 to account for Contains() tolerance (SquaredDistance <= 3)
2157 const int s1MinX = std::min( a1.x, b1.x ) - 2;
2158 const int s1MaxX = std::max( a1.x, b1.x ) + 2;
2159 const int s1MinY = std::min( a1.y, b1.y ) - 2;
2160 const int s1MaxY = std::max( a1.y, b1.y ) + 2;
2161
2162 for( int s2 = s1 + 1; s2 < segCount; s2++ )
2163 {
2164 const VECTOR2I& a2 = m_points[s2];
2165 const VECTOR2I& b2 = m_points[endIdx( s2 )];
2166
2167 const int s2MinX = std::min( a2.x, b2.x );
2168 const int s2MaxX = std::max( a2.x, b2.x );
2169
2170 if( s1MaxX < s2MinX || s2MaxX < s1MinX )
2171 continue;
2172
2173 const int s2MinY = std::min( a2.y, b2.y );
2174 const int s2MaxY = std::max( a2.y, b2.y );
2175
2176 if( s1MaxY < s2MinY || s2MaxY < s1MinY )
2177 continue;
2178
2179 const SEG seg1( a1, b1, s1 );
2180
2181 if( s1 + 1 != s2 && seg1.Contains( a2 ) )
2182 {
2183 INTERSECTION is;
2184 is.index_our = s1;
2185 is.index_their = s2;
2186 is.p = a2;
2187 return is;
2188 }
2189 else if( seg1.Contains( b2 ) &&
2190 // for closed polylines, the ending point of the
2191 // last segment == starting point of the first segment
2192 // this is a normal case, not self intersecting case
2193 !( closed && s1 == 0 && s2 == segCount - 1 ) )
2194 {
2195 INTERSECTION is;
2196 is.index_our = s1;
2197 is.index_their = s2;
2198 is.p = b2;
2199 return is;
2200 }
2201 else
2202 {
2203 OPT_VECTOR2I p = seg1.Intersect( SEG( a2, b2, s2 ), true );
2204
2205 if( p )
2206 {
2207 INTERSECTION is;
2208 is.index_our = s1;
2209 is.index_their = s2;
2210 is.p = *p;
2211 return is;
2212 }
2213 }
2214 }
2215 }
2216
2217 return std::optional<INTERSECTION>();
2218}
2219
2220
2222{
2223 SHAPE_KEY( int aFirstIdx, int aArcIdx, const BOX2I_MINMAX& aBBox ) :
2224 m_FirstIdx( aFirstIdx ), m_ArcIdx( aArcIdx ), m_BBox( aBBox )
2225 {
2226 }
2227
2231};
2232
2233
2234const std::optional<SHAPE_LINE_CHAIN::INTERSECTION>
2236{
2237 auto pointsClose = []( const VECTOR2I& aPt1, const VECTOR2I& aPt2 ) -> bool
2238 {
2239 return ( VECTOR2D( aPt1 ) - aPt2 ).SquaredEuclideanNorm() <= 2.0;
2240 };
2241
2242 auto collideArcSeg = [&pointsClose]( const SHAPE_ARC& aArc, const SEG& aSeg, int aClearance = 0,
2243 VECTOR2I* aLocation = nullptr )
2244 {
2245 VECTOR2I center = aArc.GetCenter();
2246 CIRCLE circle( center, aArc.GetRadius() );
2247
2248 std::vector<VECTOR2I> candidatePts = circle.Intersect( aSeg );
2249
2250 for( const VECTOR2I& candidate : candidatePts )
2251 {
2252 // Skip shared points
2253 if( aArc.GetP1() == aSeg.A && pointsClose( candidate, aSeg.A ) )
2254 continue;
2255
2256 if( aSeg.B == aArc.GetP0() && pointsClose( candidate, aSeg.B ) )
2257 continue;
2258
2259 bool collides = aArc.Collide( candidate, aClearance, nullptr, aLocation );
2260
2261 if( collides )
2262 return true;
2263 }
2264
2265 return false;
2266 };
2267
2268 auto collideArcArc = [&pointsClose]( const SHAPE_ARC& aArc1, const SHAPE_ARC& aArc2,
2269 VECTOR2I* aLocation = nullptr )
2270 {
2271 std::vector<VECTOR2I> candidatePts;
2272
2273 aArc1.Intersect( aArc2, &candidatePts );
2274
2275 for( const VECTOR2I& candidate : candidatePts )
2276 {
2277 // Skip shared points
2278 if( aArc1.GetP1() == aArc2.GetP0() && pointsClose( candidate, aArc1.GetP1() ) )
2279 continue;
2280
2281 if( aArc2.GetP1() == aArc1.GetP0() && pointsClose( candidate, aArc2.GetP1() ) )
2282 continue;
2283
2284 if( aLocation )
2285 *aLocation = candidate;
2286
2287 return true;
2288 }
2289
2290 return false;
2291 };
2292
2293 auto collideSegSeg = [this]( int s1, int s2, INTERSECTION& is )
2294 {
2295 SEG seg1 = CSegment( s1 );
2296 SEG seg2 = CSegment( s2 );
2297
2298 const VECTOR2I s2a = seg2.A, s2b = seg2.B;
2299
2300 if( s1 + 1 != s2 && seg1.Contains( s2a ) )
2301 {
2302 is.index_our = s1;
2303 is.index_their = s2;
2304 is.p = s2a;
2305 return true;
2306 }
2307 else if( seg1.Contains( s2b ) &&
2308 // for closed polylines, the ending point of the
2309 // last segment == starting point of the first segment
2310 // this is a normal case, not self intersecting case
2311 !( IsClosed() && s1 == 0 && s2 == SegmentCount() - 1 ) )
2312 {
2313 is.index_our = s1;
2314 is.index_their = s2;
2315 is.p = s2b;
2316 return true;
2317 }
2318 else
2319 {
2320 OPT_VECTOR2I p = seg1.Intersect( seg2, true );
2321
2322 if( p )
2323 {
2324 is.index_our = s1;
2325 is.index_their = s2;
2326 is.p = *p;
2327 return true;
2328 }
2329 }
2330
2331 return false;
2332 };
2333
2334 INTERSECTION is;
2335
2336 std::vector<SHAPE_KEY> shapeCache;
2337 for( int si = 0; si != -1; si = NextShape( si ) )
2338 {
2339 int arci = ArcIndex( si );
2340
2341 shapeCache.emplace_back( si, arci,
2342 arci == -1 ? BOX2I_MINMAX( CSegment( si ) )
2343 : BOX2I_MINMAX( Arc( arci ) ) );
2344 }
2345
2346 for( size_t sk1 = 0; sk1 < shapeCache.size(); sk1++ )
2347 {
2348 for( size_t sk2 = sk1 + 1; sk2 < shapeCache.size(); sk2++ )
2349 {
2350 VECTOR2I loc;
2351 const SHAPE_KEY& k1 = shapeCache[sk1];
2352 const SHAPE_KEY& k2 = shapeCache[sk2];
2353
2354 if( !k1.m_BBox.Intersects( k2.m_BBox ) )
2355 continue;
2356
2357 if( k1.m_ArcIdx == -1 && k2.m_ArcIdx == -1 )
2358 {
2359 if( collideSegSeg( k1.m_FirstIdx, k2.m_FirstIdx, is ) )
2360 {
2361 return is;
2362 }
2363 }
2364 else if( k1.m_ArcIdx != -1 && k2.m_ArcIdx == -1 )
2365 {
2366 if( collideArcSeg( Arc( k1.m_ArcIdx ), CSegment( k2.m_FirstIdx ), 0, &loc ) )
2367 {
2368 is.index_our = k1.m_FirstIdx;
2369 is.index_their = k2.m_FirstIdx;
2370 is.p = loc;
2371 return is;
2372 }
2373 }
2374 else if( k1.m_ArcIdx == -1 && k2.m_ArcIdx != -1 )
2375 {
2376 if( collideArcSeg( Arc( k2.m_ArcIdx ), CSegment( k1.m_FirstIdx ), 0, &loc ) )
2377 {
2378 is.index_our = k1.m_FirstIdx;
2379 is.index_their = k2.m_FirstIdx;
2380 is.p = loc;
2381 return is;
2382 }
2383 }
2384 else if( k1.m_ArcIdx != -1 && k2.m_ArcIdx != -1 )
2385 {
2386 if( collideArcArc( Arc( k1.m_ArcIdx ), Arc( k2.m_ArcIdx ), &loc ) )
2387 {
2388 is.index_our = k1.m_FirstIdx;
2389 is.index_their = k2.m_FirstIdx;
2390 is.p = loc;
2391 return is;
2392 }
2393 }
2394 }
2395 }
2396
2397 return std::optional<SHAPE_LINE_CHAIN::INTERSECTION>();
2398}
2399
2400
2402 bool aAllowInternalShapePoints ) const
2403{
2404 if( PointCount() == 0 )
2405 {
2406 // The only right answer here is "don't crash".
2407 return { 0, 0 };
2408 }
2409
2410 int min_d = std::numeric_limits<int>::max();
2411 int nearest = 0;
2412
2413 for( int i = 0; i < SegmentCount(); i++ )
2414 {
2415 int d = CSegment( i ).Distance( aP );
2416
2417 if( d < min_d )
2418 {
2419 min_d = d;
2420 nearest = i;
2421 }
2422 }
2423
2424 if( !aAllowInternalShapePoints )
2425 {
2426 //Snap to arc end points if the closest found segment is part of an arc segment
2427 if( nearest > 0 && nearest < PointCount() && IsArcSegment( nearest ) )
2428 {
2429 VECTOR2I ptToSegStart = CSegment( nearest ).A - aP;
2430 VECTOR2I ptToSegEnd = CSegment( nearest ).B - aP;
2431
2432 if( ptToSegStart.EuclideanNorm() > ptToSegEnd.EuclideanNorm() )
2433 nearest++;
2434
2435 // Is this the start or end of an arc? If so, return it directly
2436 if( IsArcStart( nearest ) || IsArcEnd( nearest ) )
2437 {
2438 return m_points[nearest];
2439 }
2440 else
2441 {
2442 const SHAPE_ARC& nearestArc = Arc( ArcIndex( nearest ) );
2443 VECTOR2I ptToArcStart = nearestArc.GetP0() - aP;
2444 VECTOR2I ptToArcEnd = nearestArc.GetP1() - aP;
2445
2446 if( ptToArcStart.EuclideanNorm() > ptToArcEnd.EuclideanNorm() )
2447 return nearestArc.GetP1();
2448 else
2449 return nearestArc.GetP0();
2450 }
2451
2452 }
2453 }
2454
2455 return CSegment( nearest ).NearestPoint( aP );
2456}
2457
2458
2459const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const SEG& aSeg, int& dist ) const
2460{
2461 if( PointCount() == 0 )
2462 {
2463 // The only right answer here is "don't crash".
2464 return { 0, 0 };
2465 }
2466
2467 int nearest = 0;
2468
2469 dist = std::numeric_limits<int>::max();
2470
2471 for( int i = 0; i < PointCount(); i++ )
2472 {
2473 int d = aSeg.LineDistance( CPoint( i ) );
2474
2475 if( d < dist )
2476 {
2477 dist = d;
2478 nearest = i;
2479 }
2480 }
2481
2482 return CPoint( nearest );
2483}
2484
2485
2487{
2488 int min_d = std::numeric_limits<int>::max();
2489 int nearest = 0;
2490
2491 for( int i = 0; i < SegmentCount(); i++ )
2492 {
2493 int d = CSegment( i ).Distance( aP );
2494
2495 if( d < min_d )
2496 {
2497 min_d = d;
2498 nearest = i;
2499 }
2500 }
2501
2502 return nearest;
2503}
2504
2505
2506const std::string SHAPE_LINE_CHAIN::Format( bool aCplusPlus ) const
2507{
2508 std::stringstream ss;
2509
2510 ss << "SHAPE_LINE_CHAIN( { ";
2511
2512 for( int i = 0; i < PointCount(); i++ )
2513 {
2514 ss << "VECTOR2I( " << m_points[i].x << ", " << m_points[i].y << ")";
2515
2516 if( i != PointCount() -1 )
2517 ss << ", ";
2518 }
2519
2520 ss << "}, " << ( m_closed ? "true" : "false" );
2521 ss << " );";
2522
2523 return ss.str();
2524
2525 /* fixme: arcs
2526 for( size_t i = 0; i < m_arcs.size(); i++ )
2527 ss << m_arcs[i].GetCenter().x << " " << m_arcs[i].GetCenter().y << " "
2528 << m_arcs[i].GetP0().x << " " << m_arcs[i].GetP0().y << " "
2529 << m_arcs[i].GetCentralAngle().AsDegrees();
2530
2531 return ss.str();*/
2532}
2533
2534
2536 bool aCyclicalCompare,
2537 int aEpsilon ) const
2538{
2539 SHAPE_LINE_CHAIN a( *this ), b( aOther );
2540 a.Simplify();
2541 b.Simplify();
2542
2543 if( a.m_points.size() != b.m_points.size() )
2544 return false;
2545
2546 if( aCyclicalCompare )
2547 {
2548 std::vector<VECTOR2I> aVerts = a.m_points;
2549 std::vector<VECTOR2I> bVerts = b.m_points;
2550
2551 auto centroid = []( const std::vector<VECTOR2I>& pts )
2552 {
2553 double sx = 0.0, sy = 0.0;
2554 for( const auto& p : pts )
2555 {
2556 sx += p.x;
2557 sy += p.y;
2558 }
2559 return std::pair<double, double>( sx / pts.size(), sy / pts.size() );
2560 };
2561
2562 auto aC = centroid( aVerts );
2563 auto bC = centroid( bVerts );
2564
2565 auto angleCmp =
2566 []( const std::pair<double, double>& c, const VECTOR2I& p1, const VECTOR2I& p2 )
2567 {
2568 double a1 = atan2( p1.y - c.second, p1.x - c.first );
2569 double a2 = atan2( p2.y - c.second, p2.x - c.first );
2570 return a1 < a2;
2571 };
2572
2573 // sort by angle around centroid so that cyclic vertex order doesn't matter
2574 std::sort( aVerts.begin(), aVerts.end(),
2575 [&]( const VECTOR2I& p1, const VECTOR2I& p2 )
2576 {
2577 return angleCmp( aC, p1, p2 );
2578 } );
2579
2580 std::sort( bVerts.begin(), bVerts.end(),
2581 [&]( const VECTOR2I& p1, const VECTOR2I& p2 )
2582 {
2583 return angleCmp( bC, p1, p2 );
2584 } );
2585
2586 for( size_t i = 0; i < aVerts.size(); i++ )
2587 {
2588 if( abs( aVerts[i].x - bVerts[i].x ) > aEpsilon
2589 || abs( aVerts[i].y - bVerts[i].y ) > aEpsilon )
2590 return false;
2591 }
2592
2593 }
2594 else
2595 {
2596 for( int i = 0; i < a.PointCount(); i++ )
2597 {
2598 if( abs( a.CPoint( i ).x - b.CPoint( i ).x ) > aEpsilon
2599 || abs( a.CPoint( i ).y - b.CPoint( i ).y ) > aEpsilon )
2600 return false;
2601 }
2602 }
2603
2604
2605
2606 return true;
2607}
2608
2609
2611{
2613 return Intersect( aChain, dummy ) != 0;
2614}
2615
2616
2618{
2619 return new SHAPE_LINE_CHAIN( *this );
2620}
2621
2622
2623bool SHAPE_LINE_CHAIN::Parse( std::stringstream& aStream )
2624{
2625 size_t n_pts;
2626 size_t n_arcs;
2627
2628 m_points.clear();
2629 aStream >> n_pts;
2630
2631 // Rough sanity check, just make sure the loop bounds aren't absolutely outlandish
2632 if( n_pts > aStream.str().size() )
2633 return false;
2634
2635 aStream >> m_closed;
2636 aStream >> n_arcs;
2637
2638 if( n_arcs > aStream.str().size() )
2639 return false;
2640
2641 for( size_t i = 0; i < n_pts; i++ )
2642 {
2643 int x, y;
2644 ssize_t ind;
2645 aStream >> x;
2646 aStream >> y;
2647 m_points.emplace_back( x, y );
2648
2649 aStream >> ind;
2650 m_shapes.emplace_back( std::make_pair( ind, SHAPE_IS_PT ) );
2651 }
2652
2653 for( size_t i = 0; i < n_arcs; i++ )
2654 {
2655 VECTOR2I p0, pc;
2656 double angle;
2657
2658 aStream >> pc.x;
2659 aStream >> pc.y;
2660 aStream >> p0.x;
2661 aStream >> p0.y;
2662 aStream >> angle;
2663
2664 m_arcs.emplace_back( pc, p0, EDA_ANGLE( angle, DEGREES_T ) );
2665 }
2666
2667 return true;
2668}
2669
2670
2671const VECTOR2I SHAPE_LINE_CHAIN::PointAlong( int aPathLength ) const
2672{
2673 int total = 0;
2674
2675 if( aPathLength == 0 )
2676 return CPoint( 0 );
2677
2678 for( int i = 0; i < SegmentCount(); i++ )
2679 {
2680 const SEG& s = CSegment( i );
2681 int l = s.Length();
2682
2683 if( total + l >= aPathLength )
2684 {
2685 VECTOR2I d( s.B - s.A );
2686 return s.A + d.Resize( aPathLength - total );
2687 }
2688
2689 total += l;
2690 }
2691
2692 return CLastPoint();
2693}
2694
2695
2696double SHAPE_LINE_CHAIN::Area( bool aAbsolute ) const
2697{
2698 // see https://www.mathopenref.com/coordpolygonarea2.html
2699
2700 if( !m_closed )
2701 return 0.0;
2702
2703 double area = 0.0;
2704 int size = m_points.size();
2705
2706 for( int i = 0, j = size - 1; i < size; ++i )
2707 {
2708 area += ( (double) m_points[j].x + m_points[i].x ) *
2709 ( (double) m_points[j].y - m_points[i].y );
2710 j = i;
2711 }
2712
2713 if( aAbsolute )
2714 return std::fabs( area * 0.5 ); // The result would be negative if points are anti-clockwise
2715 else
2716 return -area * 0.5; // The result would be negative if points are anti-clockwise
2717}
2718
2719
2721{
2722 std::vector<VECTOR2I> pts_unique;
2723 std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
2724
2725 // Always try to keep at least 2 points otherwise, we're not really a line
2726 if( PointCount() < 3 )
2727 {
2728 return;
2729 }
2730 else if( PointCount() == 3 )
2731 {
2732 if( m_points[0] == m_points[1] )
2733 Remove( 1 );
2734
2735 return;
2736 }
2737
2738 int i = 0;
2739
2740 while( i < PointCount() )
2741 {
2742 int j = i + 1;
2743
2744 // We can eliminate duplicate vertices as long as they are part of the same shape, OR if
2745 // one of them is part of a shape and one is not.
2746 while( j < PointCount() && m_points[i] == m_points[j] &&
2747 ( m_shapes[i] == m_shapes[j] ||
2748 m_shapes[i] == SHAPES_ARE_PT ||
2749 m_shapes[j] == SHAPES_ARE_PT ) )
2750 {
2751 j++;
2752 }
2753
2754 std::pair<ssize_t,ssize_t> shapeToKeep = m_shapes[i];
2755
2756 if( shapeToKeep == SHAPES_ARE_PT )
2757 shapeToKeep = m_shapes[j - 1];
2758
2759 assert( shapeToKeep.first < static_cast<int>( m_arcs.size() ) );
2760 assert( shapeToKeep.second < static_cast<int>( m_arcs.size() ) );
2761
2762 pts_unique.push_back( CPoint( i ) );
2763 shapes_unique.push_back( shapeToKeep );
2764
2765 i = j;
2766 }
2767
2768 m_points.clear();
2769 m_shapes.clear();
2770
2771 for( size_t ii = 0; ii < pts_unique.size(); ++ii )
2772 {
2773 const VECTOR2I p0 = pts_unique[ii];
2774
2775 m_points.push_back( p0 );
2776 m_shapes.push_back( shapes_unique[ii] );
2777 }
2778}
2779
2780
2781
2782void SHAPE_LINE_CHAIN::Simplify( int aTolerance )
2783{
2784 if( PointCount() < 3 )
2785 return;
2786
2787 std::vector<VECTOR2I> new_points;
2788 std::vector<std::pair<ssize_t, ssize_t>> new_shapes;
2789
2790 new_points.reserve( m_points.size() );
2791 new_shapes.reserve( m_shapes.size() );
2792
2793 for( size_t start_idx = 0; start_idx < m_points.size(); )
2794 {
2795 new_points.push_back( m_points[start_idx] );
2796 new_shapes.push_back( m_shapes[start_idx] );
2797
2798 // If the line is not closed, we need at least 3 points before simplifying
2799 if( !m_closed && start_idx == m_points.size() - 2 )
2800 break;
2801
2802 // Initialize the end index to be two points ahead of start
2803 size_t end_idx = ( start_idx + 2 ) % m_points.size();
2804 bool can_simplify = true;
2805
2806 while( can_simplify && end_idx != start_idx && ( end_idx > start_idx || m_closed ) )
2807 {
2808 // Test all points between start_idx and end_idx
2809 for( size_t test_idx = ( start_idx + 1 ) % m_points.size();
2810 test_idx != end_idx;
2811 test_idx = ( test_idx + 1 ) % m_points.size() )
2812 {
2813 // Only intermediate test points must be regular points (not arcs).
2814 // Allow start or end to be arc endpoints so collinear segments
2815 // adjacent to arcs can still be simplified while preserving arcs.
2816 if( m_shapes[test_idx].first != SHAPE_IS_PT )
2817 {
2818 can_simplify = false;
2819 break;
2820 }
2821
2822 // Test if the point is within the allowed error
2823 if( !TestSegmentHit( m_points[test_idx], m_points[start_idx], m_points[end_idx], aTolerance ) )
2824 {
2825 can_simplify = false;
2826 break;
2827 }
2828 }
2829
2830 if( can_simplify )
2831 {
2832 // If we can simplify, move end_idx one further
2833 end_idx = ( end_idx + 1 ) % m_points.size();
2834 }
2835 }
2836
2837 // If we couldn't simplify at all, move to the next point
2838 if( end_idx == ( start_idx + 2 ) % m_points.size() )
2839 {
2840 ++start_idx;
2841 }
2842 else
2843 {
2844 // Otherwise, jump to the last point we could include in the simplification
2845 size_t new_start_idx = ( end_idx + m_points.size() - 1 ) % m_points.size();
2846
2847 // If we looped all the way around, we're done
2848 if( new_start_idx <= start_idx )
2849 break;
2850
2851 start_idx = new_start_idx;
2852 }
2853 }
2854
2855 // If we have only one point, we need to add a second point to make a line
2856 if( new_points.size() == 1 )
2857 {
2858 new_points.push_back( m_points.back() );
2859 new_shapes.push_back( m_shapes.back() );
2860 }
2861
2862 // If we are not closed, then the start and end points of the original line need to
2863 // be the start and end points of the new line.
2864 if( !m_closed && m_points.back() != new_points.back() )
2865 {
2866 new_points.push_back( m_points.back() );
2867 new_shapes.push_back( m_shapes.back() );
2868 }
2869
2870 m_points.clear();
2871 m_shapes.clear();
2872 m_points = std::move( new_points );
2873 m_shapes = std::move( new_shapes );
2874}
2875
2876
2877void SHAPE_LINE_CHAIN::Split( const VECTOR2I& aStart, const VECTOR2I& aEnd, SHAPE_LINE_CHAIN& aPre,
2878 SHAPE_LINE_CHAIN& aMid, SHAPE_LINE_CHAIN& aPost ) const
2879{
2880 VECTOR2I cp( aEnd );
2881
2882 VECTOR2I n = NearestPoint( cp, false );
2883 VECTOR2I m = NearestPoint( aStart, false );
2884
2885 SHAPE_LINE_CHAIN l( *this );
2886 l.Split( n, true );
2887 l.Split( m, true );
2888
2889 int i_start = l.Find( m );
2890 int i_end = l.Find( n );
2891
2892 if( i_start > i_end )
2893 {
2894 l = l.Reverse();
2895 i_start = l.Find( m );
2896 i_end = l.Find( n );
2897 }
2898
2899 aPre = l.Slice( 0, i_start );
2900 aPost = l.Slice( i_end, -1 );
2901 aMid = l.Slice( i_start, i_end );
2902}
2903
2904
2905
2907{
2908 std::vector<VECTOR2I> pts_unique;
2909 std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
2910
2911 // Always try to keep at least 2 points otherwise, we're not really a line
2912 if( PointCount() < 3 )
2913 {
2914 return *this;
2915 }
2916 else if( PointCount() == 3 )
2917 {
2918 if( m_points[0] == m_points[1] )
2919 Remove( 1 );
2920
2921 return *this;
2922 }
2923
2924 int i = 0;
2925 int np = PointCount();
2926
2927 // stage 1: eliminate duplicate vertices
2928 while( i < np )
2929 {
2930 int j = i + 1;
2931
2932 // We can eliminate duplicate vertices as long as they are part of the same shape, OR if
2933 // one of them is part of a shape and one is not.
2934 while( j < np && m_points[i] == m_points[j] &&
2935 ( m_shapes[i] == m_shapes[j] ||
2936 m_shapes[i] == SHAPES_ARE_PT ||
2937 m_shapes[j] == SHAPES_ARE_PT ) )
2938 {
2939 j++;
2940 }
2941
2942 std::pair<ssize_t,ssize_t> shapeToKeep = m_shapes[i];
2943
2944 if( shapeToKeep == SHAPES_ARE_PT )
2945 shapeToKeep = m_shapes[j - 1];
2946
2947 assert( shapeToKeep.first < static_cast<int>( m_arcs.size() ) );
2948 assert( shapeToKeep.second < static_cast<int>( m_arcs.size() ) );
2949
2950 pts_unique.push_back( CPoint( i ) );
2951 shapes_unique.push_back( shapeToKeep );
2952
2953 i = j;
2954 }
2955
2956 m_points.clear();
2957 m_shapes.clear();
2958 np = pts_unique.size();
2959
2960 i = 0;
2961
2962 // stage 2: eliminate colinear segments
2963 while( i < np - 2 )
2964 {
2965 const VECTOR2I p0 = pts_unique[i];
2966 int n = i;
2967
2968 if( aRemoveColinear && shapes_unique[i] == SHAPES_ARE_PT
2969 && shapes_unique[i + 1] == SHAPES_ARE_PT )
2970 {
2971 while( n < np - 2
2972 && ( SEG( p0, pts_unique[n + 2] ).LineDistance( pts_unique[n + 1] ) <= 1
2973 || SEG( p0, pts_unique[n + 2] ).Collinear( SEG( p0, pts_unique[n + 1] ) ) ) )
2974 n++;
2975 }
2976
2977 m_points.push_back( p0 );
2978 m_shapes.push_back( shapes_unique[i] );
2979
2980 if( n > i )
2981 i = n;
2982
2983 if( n == np - 2 )
2984 {
2985 m_points.push_back( pts_unique[np - 1] );
2986 m_shapes.push_back( shapes_unique[np - 1] );
2987 return *this;
2988 }
2989
2990 i++;
2991 }
2992
2993 if( np > 1 )
2994 {
2995 m_points.push_back( pts_unique[np - 2] );
2996 m_shapes.push_back( shapes_unique[np - 2] );
2997 }
2998
2999 m_points.push_back( pts_unique[np - 1] );
3000 m_shapes.push_back( shapes_unique[np - 1] );
3001
3002 assert( m_points.size() == m_shapes.size() );
3003
3004 return *this;
3005}
3006
3007bool SHAPE_LINE_CHAIN::OffsetLine( int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError,
3008 SHAPE_LINE_CHAIN& aLeft, SHAPE_LINE_CHAIN& aRight,
3009 bool aSimplify ) const
3010{
3011 if( PointCount() < 2 )
3012 return false;
3013
3014 SHAPE_POLY_SET poly;
3015 poly.OffsetLineChain( *this, aAmount, aCornerStrategy, aMaxError, aSimplify );
3016
3017 if( poly.OutlineCount() != 1 )
3018 return false;
3019
3020 if( poly.COutline( 0 ).PointCount() < 3 )
3021 return false;
3022
3023 if( poly.HasHoles() )
3024 return false;
3025
3026 SHAPE_LINE_CHAIN outline = poly.COutline( 0 );
3027
3028 wxASSERT( outline.IsClosed() );
3029
3030 const VECTOR2I& start = CPoint( 0 );
3031 const VECTOR2I& end = CLastPoint();
3032
3033 outline.Split( start, true );
3034 outline.Split( end, true );
3035
3036 const int idA = outline.Find( start );
3037 const int idB = outline.Find( end );
3038
3039 if( idA == -1 || idB == -1 )
3040 return false;
3041
3042 aLeft.Clear();
3043 aRight.Clear();
3044
3045 for( int i = idA;; )
3046 {
3047 aLeft.Append( outline.CPoint( i ) );
3048
3049 i = ( i + 1 ) % outline.PointCount();
3050
3051 if( i == idB )
3052 {
3053 aLeft.Append( outline.CPoint( i ) );
3054 break;
3055 }
3056
3057 if( i == idA )
3058 return false;
3059 }
3060
3061 if( aLeft.PointCount() < 2 )
3062 return false;
3063
3064 for( int i = idB;; )
3065 {
3066 aRight.Append( outline.CPoint( i ) );
3067
3068 i = ( i + 1 ) % outline.PointCount();
3069
3070 if( i == idA )
3071 {
3072 aRight.Append( outline.CPoint( i ) );
3073 break;
3074 }
3075
3076 if( i == idB )
3077 return false;
3078 }
3079
3080 if( aRight.PointCount() < 2 )
3081 return false;
3082
3083 if( aLeft.CPoint( 0 ) != start )
3084 {
3085 aLeft = aLeft.Reverse();
3086 wxASSERT( aLeft.CPoint( 0 ) == start );
3087 }
3088
3089 if( aRight.CPoint( 0 ) != start )
3090 {
3091 aRight = aRight.Reverse();
3092 wxASSERT( aRight.CPoint( 0 ) == start );
3093 }
3094
3095 SEG base( CPoint( 0 ), CPoint( 1 ) );
3096 int sideLeft = base.Side( aLeft.CPoint( 1 ) );
3097 int sideRight = base.Side( aRight.CPoint( 1 ) );
3098
3099 if( sideLeft == 0 || sideRight == 0 )
3100 return false;
3101
3102 if( sideLeft == sideRight )
3103 return false;
3104
3105 if( sideLeft > 0 && sideRight < 0 )
3106 std::swap( aLeft, aRight );
3107
3108 if( aLeft.PointCount() < 4 )
3109 return false;
3110
3111 if( aRight.PointCount() < 4 )
3112 return false;
3113
3114 aLeft.Remove( 0 );
3115 aLeft.Remove( aLeft.PointCount() - 1 );
3116
3117 aRight.Remove( 0 );
3118 aRight.Remove( aRight.PointCount() - 1 );
3119
3120 return true;
3121}
3122
3123
3125 ERROR_LOC aErrorLoc ) const
3126{
3127 aBuffer.AddOutline( *this );
3128}
3129
3130
3132 m_point( aPoint ),
3133 m_finished( false ),
3134 m_state( 0 ),
3135 m_count( 0 )
3136{
3137}
3138
3139
3141 const VECTOR2I& ip, const VECTOR2I& ipNext )
3142{
3143 if( ipNext.y == m_point.y )
3144 {
3145 if( ( ipNext.x == m_point.x )
3146 || ( ip.y == m_point.y && ( ( ipNext.x > m_point.x ) == ( ip.x < m_point.x ) ) ) )
3147 {
3148 m_finished = true;
3149 m_state = -1;
3150 return false;
3151 }
3152 }
3153
3154 if( ( ip.y < m_point.y ) != ( ipNext.y < m_point.y ) )
3155 {
3156 if( ip.x >= m_point.x )
3157 {
3158 if( ipNext.x > m_point.x )
3159 {
3160 m_state = 1 - m_state;
3161 }
3162 else
3163 {
3164 double d = (double) ( ip.x - m_point.x ) * ( ipNext.y - m_point.y )
3165 - (double) ( ipNext.x - m_point.x ) * ( ip.y - m_point.y );
3166
3167 if( !d )
3168 {
3169 m_finished = true;
3170 m_state = -1;
3171 return false;
3172 }
3173
3174 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
3175 m_state = 1 - m_state;
3176 }
3177 }
3178 else
3179 {
3180 if( ipNext.x > m_point.x )
3181 {
3182 double d = (double) ( ip.x - m_point.x ) * ( ipNext.y - m_point.y )
3183 - (double) ( ipNext.x - m_point.x ) * ( ip.y - m_point.y );
3184
3185 if( !d )
3186 {
3187 m_finished = true;
3188 m_state = -1;
3189 return false;
3190 }
3191
3192 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
3193 m_state = 1 - m_state;
3194 }
3195 }
3196 }
3197
3198 return true;
3199}
3200
3201
3203{
3204 if( !m_count )
3205 {
3206 m_lastPoint = aPolyline.CPoint( 0 );
3207 m_firstPoint = aPolyline.CPoint( 0 );
3208 }
3209
3210 m_count += aPolyline.PointCount();
3211
3212 for( int i = 1; i < aPolyline.PointCount(); i++ )
3213 {
3214 auto p = aPolyline.CPoint( i );
3215
3216 if( !processVertex( m_lastPoint, p ) )
3217 return;
3218
3219 m_lastPoint = p;
3220 }
3221
3222}
3223
3224
3230
3231
3232bool SHAPE_LINE_CHAIN::IsSharedPt( size_t aIndex ) const
3233{
3234 return aIndex < m_shapes.size()
3235 && m_shapes[aIndex].first != SHAPE_IS_PT
3236 && m_shapes[aIndex].second != SHAPE_IS_PT;
3237}
3238
3239
3240bool SHAPE_LINE_CHAIN::IsPtOnArc( size_t aPtIndex ) const
3241{
3242 return aPtIndex < m_shapes.size() && m_shapes[aPtIndex] != SHAPES_ARE_PT;
3243}
3244
3245
3246bool SHAPE_LINE_CHAIN::IsArcSegment( size_t aSegment ) const
3247{
3248 /*
3249 * A segment is part of an arc except in the special case of two arcs next to each other
3250 * but without a shared vertex. Here there is a segment between the end of the first arc
3251 * and the start of the second arc.
3252 */
3253 size_t nextIdx = aSegment + 1;
3254
3255 if( nextIdx > m_shapes.size() - 1 )
3256 {
3257 if( nextIdx == m_shapes.size() && m_closed && IsSharedPt( 0 ) )
3258 nextIdx = 0; // segment between end point and first point
3259 else
3260 return false;
3261 }
3262
3263 return ( IsPtOnArc( aSegment )
3264 && ( ArcIndex( aSegment ) == m_shapes[nextIdx].first ) );
3265}
3266
3267
3268bool SHAPE_LINE_CHAIN::IsArcStart( size_t aIndex ) const
3269{
3270 if( !IsArcSegment( aIndex ) ) // also does bound checking
3271 return false;
3272
3273 if( IsSharedPt( aIndex ) )
3274 return true;
3275
3276 const SHAPE_ARC& arc = Arc( ArcIndex( aIndex ) );
3277
3278 return arc.GetP0() == m_points[aIndex];
3279}
3280
3281
3282bool SHAPE_LINE_CHAIN::IsArcEnd( size_t aIndex ) const
3283{
3284 size_t prevIndex = aIndex - 1;
3285
3286 if( aIndex == 0 )
3287 prevIndex = m_points.size() - 1;
3288 else if( aIndex > m_points.size() -1 )
3289 return false; // invalid index requested
3290
3291 if( !IsArcSegment( prevIndex ) )
3292 return false;
3293
3294 if( IsSharedPt( aIndex ) )
3295 return true;
3296
3297 const SHAPE_ARC& arc = Arc( ArcIndex( aIndex ) );
3298
3299 return arc.GetP1() == m_points[aIndex];
3300}
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:918
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
Represent basic circle geometry with utility geometry functions.
Definition circle.h:33
Definition seg.h:38
const VECTOR2I ReflectPoint(const VECTOR2I &aP) const
Reflect a point using this segment as axis.
Definition seg.cpp:660
VECTOR2I A
Definition seg.h:45
int LineDistance(const VECTOR2I &aP, bool aDetermineSide=false) const
Return the closest Euclidean distance between point aP and the line defined by the ends of segment (t...
Definition seg.cpp:742
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:76
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:629
int Length() const
Return the length (this).
Definition seg.h:339
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition seg.cpp:442
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:282
bool NearestPoints(const SEG &aSeg, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this segment and aSeg.
Definition seg.cpp:158
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:698
bool Contains(const SEG &aSeg) const
Definition seg.h:320
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:319
int GetWidth() const override
Definition shape_arc.h:211
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:279
double GetRadius() const
void Reverse()
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
void SetWidth(int aWidth) override
Definition shape_arc.h:206
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:306
virtual size_t GetSegmentCount() const =0
virtual const VECTOR2I GetPoint(int aIndex) const =0
virtual BOX2I * GetCachedBBox() const
Definition shape.h:366
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:299
SHAPE(SHAPE_TYPE aType)
Create an empty shape of type aType.
Definition shape.h:134
virtual int GetWidth() const
Definition shape.h:287
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition vector2d.h:557
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:279
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:381
CORNER_STRATEGY
define how inflate transform build inflated polygon
@ DEGREES_T
Definition eda_angle.h:31
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:171
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:225
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
Definition util.h:135
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:684