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