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