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