32#include <clipper2/clipper.h>
55 int minX, maxX, minY, maxY;
75 for(
size_t i = 0; i < aV.size(); i+= 2 )
100 if( aMaxError.has_value() )
101 Append( aArc, aMaxError.value() );
110 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
111 const std::vector<SHAPE_ARC>& aArcBuffer ) :
117 std::map<ssize_t, ssize_t> loadedArcs;
122 [&]( ssize_t aArcIndex ) -> ssize_t
124 if( aArcIndex == SHAPE_IS_PT )
128 else if( loadedArcs.count( aArcIndex ) == 0 )
130 loadedArcs.insert( { aArcIndex, m_arcs.size() } );
131 m_arcs.push_back( aArcBuffer.at( aArcIndex ) );
134 return loadedArcs.at( aArcIndex );
137 for(
size_t ii = 0; ii < aPath.size(); ++ii )
139 Append( aPath[ii].x, aPath[ii].y );
142 int idx_z = aPath[ii].z;
144 if( idx_z < 0 || idx_z >= (
int)aZValueBuffer.size() )
147 m_shapes[ii].first = loadArc( aZValueBuffer[idx_z].m_FirstArcIdx );
148 m_shapes[ii].second = loadArc( aZValueBuffer[idx_z].m_SecondArcIdx );
153 wxASSERT( m_shapes.size() == m_points.size() );
157 fixIndicesRotation();
162 std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
163 std::vector<SHAPE_ARC>& aArcBuffer )
const
165 Clipper2Lib::Path64 c_path;
167 bool orientation =
Area(
false ) >= 0;
168 ssize_t shape_offset = aArcBuffer.size();
170 if( orientation != aRequiredOrientation )
176 c_path.reserve( pointCount );
178 for(
int i = 0; i < pointCount; i++ )
183 size_t z_value_ptr = aZValueBuffer.size();
184 aZValueBuffer.push_back( z_value );
186 c_path.emplace_back( vertex.
x, vertex.
y, z_value_ptr );
189 aArcBuffer.insert( aArcBuffer.end(), input.
m_arcs.begin(), input.
m_arcs.end() );
202 size_t rotations = 0;
253 aArcIndex +=
m_arcs.size();
255 if( aArcIndex >=
static_cast<ssize_t
>(
m_arcs.size() ) )
262 [&]( ssize_t& aShapeIndex )
264 if( aShapeIndex == aArcIndex )
267 if( aShapeIndex > aArcIndex )
272 std::swap( sh.first, sh.second );
282 wxCHECK_MSG( aArcIndex <
m_arcs.size(), ,
283 wxT(
"Invalid arc index requested." ) );
292 m_arcs[aArcIndex] = newArc;
307 wxCHECK_MSG( aPtIndex <
static_cast<ssize_t
>(
m_shapes.size() ), ,
308 wxT(
"Invalid point index requested." ) );
312 if( aCoincident || aPtIndex == 0 )
315 ssize_t firstArcIndex =
m_shapes[aPtIndex].first;
319 amendArc( firstArcIndex, newStart, newEnd );
334 ssize_t currArcIdx =
ArcIndex( aPtIndex );
349 if( !aCoincident &&
ArcIndex( aPtIndex - 1 ) != currArcIdx )
352 m_arcs[currArcIdx] = newArc2;
356 m_arcs[currArcIdx] = newArc1;
357 m_arcs.insert(
m_arcs.begin() + currArcIdx + 1, newArc2 );
361 m_shapes[aPtIndex].second = currArcIdx + 1;
366 for(
int i = aPtIndex; i <
PointCount(); i++ )
399 SEG::ecoord dist_sq = ( pn - aP ).SquaredEuclideanNorm();
401 if( dist_sq < closest_dist_sq )
404 closest_dist_sq = dist_sq;
406 if( closest_dist_sq == 0 )
410 if( closest_dist_sq < clearance_sq && !aActual )
415 if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
418 *aLocation = nearest;
421 *aActual = sqrt( closest_dist_sq );
456 SEG::ecoord dist_sq = ( pn - aP ).SquaredEuclideanNorm();
458 if( dist_sq < closest_dist_sq )
461 closest_dist_sq = dist_sq;
463 if( closest_dist_sq == 0 )
467 if( closest_dist_sq < clearance_sq && !aActual )
472 if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
475 *aLocation = nearest;
478 *aActual = sqrt( closest_dist_sq );
484 for(
size_t i = 0; i <
ArcCount(); i++ )
489 wxASSERT_MSG( arc.
GetWidth() == 0, wxT(
"Invalid arc width - should be zero" ) );
491 if( arc.
Collide( aP, aClearance, aActual, aLocation ) )
505 arc.Rotate( aAngle, aCenter );
512 const std::vector<VECTOR2I>& myPts =
m_points;
513 const std::vector<VECTOR2I>& otherPts = aOther.
m_points;
515 const int c_maxBoxes = 100;
516 const int c_minPtsPerBox = 20;
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 );
521 int myNumBoxes = ( myPts.size() + myPointsPerBox - 1 ) / myPointsPerBox;
522 int otherNumBoxes = ( otherPts.size() + otherPointsPerBox - 1 ) / otherPointsPerBox;
532 std::vector<BOX> myBoxes( myNumBoxes );
533 std::vector<BOX> otherBoxes( otherNumBoxes );
536 for(
size_t i = 0; i < myPts.size(); i++ )
539 BOX& box = myBoxes[i / myPointsPerBox];
543 box.bbox.Merge( pt );
552 for(
size_t i = 0; i < otherPts.size(); i++ )
555 BOX& box = otherBoxes[i / otherPointsPerBox];
559 box.bbox.Merge( pt );
569 for( BOX& box : myBoxes )
571 box.center = box.bbox.GetCenter();
572 box.radius = int( box.bbox.GetDiameter() / 2 );
575 for( BOX& box : otherBoxes )
577 box.center = box.bbox.GetCenter();
578 box.radius = int( box.bbox.GetDiameter() / 2 );
584 DIST_PAIR( int64_t aDistSq,
size_t aIdA,
size_t aIdB ) :
585 dist( aDistSq ), idA( aIdA ), idB( aIdB )
594 std::vector<DIST_PAIR> pairsToTest;
596 for(
size_t ia = 0; ia < myBoxes.size(); ia++ )
598 for(
size_t ib = 0; ib < otherBoxes.size(); ib++ )
600 const BOX& ca = myBoxes[ia];
601 const BOX& cb = otherBoxes[ib];
603 if( !ca.valid || !cb.valid )
609 int64_t dist = ( pB - pA ).EuclideanNorm();
614 pairsToTest.emplace_back( dist, ia, ib );
618 std::sort( pairsToTest.begin(), pairsToTest.end(),
619 [](
const DIST_PAIR& a,
const DIST_PAIR& b )
621 return a.dist < b.dist;
624 const int c_polyPairsLimit = 5;
629 for(
size_t pairId = 0; pairId < pairsToTest.size() && pairId < c_polyPairsLimit; pairId++ )
631 const DIST_PAIR& pair = pairsToTest[pairId];
637 size_t myStartId = pair.idA * myPointsPerBox;
638 size_t myEndId = myStartId + myPointsPerBox;
640 if( myEndId > myPts.size() )
641 myEndId = myPts.size();
643 VECTOR2I myPrevPt = myPts[myStartId == 0 ? myPts.size() - 1 : myStartId - 1];
645 size_t otherStartId = pair.idB * otherPointsPerBox;
646 size_t otherEndId = otherStartId + otherPointsPerBox;
648 if( otherEndId > otherPts.size() )
649 otherEndId = otherPts.size();
651 VECTOR2I otherPrevPt = otherPts[otherStartId == 0 ? otherPts.size() - 1 : otherStartId - 1];
653 if(
ClosestSegments( myPrevPt, myPts.begin() + myStartId, myPts.begin() + myEndId,
654 otherPrevPt, otherPts.begin() + otherStartId,
655 otherPts.begin() + otherEndId, ptA, ptB, dist_sq ) )
657 if( dist_sq < total_closest_dist_sq )
659 total_closest_dist_sq = dist_sq;
676 if( aMyStart == aMyEnd )
679 if( aOtherStart == aOtherEnd )
685 for(
point_citer itA = aMyStart; itA != aMyEnd; itA++ )
690 for(
point_citer itB = aOtherStart; itB != aOtherEnd; itB++ )
694 SEG segA( lastPtA, ptA );
695 SEG segB( lastPtB, ptB );
701 if( segA.
NearestPoints( segB, nearestA, nearestB, dist_sq ) )
703 if( dist_sq < closest_dist_sq )
705 closest_dist_sq = dist_sq;
717 aDistSq = closest_dist_sq;
728 for(
point_citer itA = aMyStart; itA != aMyEnd; itA++ )
732 for(
point_citer itB = aOtherStart; itB != aOtherEnd; itB++ )
741 if( dist_sq < closest_dist_sq )
743 closest_dist_sq = dist_sq;
750 aDistSq = closest_dist_sq;
761 aOther.
m_points.cend(), aPt0, aPt1, dist_sq );
788 if( dist_sq < closest_dist_sq )
793 closest_dist_sq = dist_sq;
795 if( closest_dist_sq == 0)
799 if( closest_dist_sq < clearance_sq && !aActual )
804 if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
807 *aLocation = nearest;
810 *aActual = sqrt( closest_dist_sq );
846 if( dist_sq < closest_dist_sq )
851 closest_dist_sq = dist_sq;
853 if( closest_dist_sq == 0 )
857 if( closest_dist_sq < clearance_sq && !aActual )
862 if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
865 *aLocation = nearest;
868 *aActual = sqrt( closest_dist_sq );
873 int dist = std::numeric_limits<int>::max();
874 SEG::ecoord closest_dist = sqrt( closest_dist_sq );
877 for(
size_t i = 0; i <
ArcCount(); i++ )
883 wxASSERT_MSG( arc.
GetWidth() == 0, wxT(
"Invalid arc width - should be zero" ) );
885 if( arc.
Collide( aSeg, aClearance, aActual || aLocation ? &dist :
nullptr,
886 aLocation ? &pos :
nullptr ) )
891 if( dist < closest_dist )
899 if( closest_dist == 0 || closest_dist < aClearance )
902 *aLocation = nearest;
905 *aActual = closest_dist;
927 [&]( ssize_t& aShapeIndex )
930 aShapeIndex = a.
m_arcs.size() - aShapeIndex - 1;
939 std::swap( sh.first, sh.second );
955 for( ssize_t arcIndex =
m_arcs.size() - 1; arcIndex >= 0; --arcIndex )
971 for(
size_t i = 0; i <
ArcCount(); i++ )
972 l +=
CArcs()[i].GetLength();
983 pt.x = -pt.x + 2 * aRef.
x;
985 pt.y = -pt.y + 2 * aRef.
y;
989 arc.Mirror( aRef, aFlipDirection );
1005 Remove( aStartIndex, aEndIndex );
1006 Insert( aStartIndex, aP );
1016 if( aStartIndex < 0 )
1020 wxASSERT( aStartIndex <= aEndIndex );
1021 wxASSERT( aEndIndex <
static_cast<int>(
m_points.size() ) );
1028 Remove( aStartIndex, aEndIndex );
1041 Remove( aStartIndex, aEndIndex );
1052 Remove( aStartIndex, aEndIndex );
1059 size_t prev_arc_count =
m_arcs.size();
1060 std::vector<std::pair<ssize_t, ssize_t>> new_shapes = newLine.
m_shapes;
1062 for( std::pair<ssize_t, ssize_t>& shape_pair : new_shapes )
1065 [&]( ssize_t& aShape )
1068 aShape += prev_arc_count;
1072 m_shapes.insert(
m_shapes.begin() + aStartIndex, new_shapes.begin(), new_shapes.end() );
1093 if( aStartIndex < 0 )
1116 if( aStartIndex > aEndIndex )
1122 std::set<size_t> extra_arcs;
1123 auto logArcIdxRemoval = [&]( ssize_t& aShapeIndex )
1126 extra_arcs.insert( aShapeIndex );
1130 for(
int i = aStartIndex; i <= aEndIndex; i++ )
1134 if( i == aStartIndex )
1136 logArcIdxRemoval(
m_shapes[i].second );
1143 else if( i == aEndIndex )
1145 logArcIdxRemoval(
m_shapes[i].first );
1148 assert( i > aStartIndex || (
IsSharedPt( i - 1 )
1160 for(
auto arc : extra_arcs )
1190 int found_index =
Find( aP );
1192 if( found_index >= 0 && aExact )
1202 if( dist < min_dist && seg.
A != aP && seg.
B != aP )
1205 if( found_index < 0 )
1207 else if( s < found_index )
1221 size_t newIndex =
static_cast<size_t>( ii ) + 1;
1226 m_shapes.insert(
m_shapes.begin() + newIndex, { ArcIndex( ii ), SHAPE_IS_PT } );
1245 if( aThreshold == 0 )
1252 if( (
CPoint( s ) - aP).EuclideanNorm() <= aThreshold )
1275 wxCHECK2_MSG(
m_points.size() ==
m_shapes.size(),
return 0,
"Invalid chain!" );
1296 wxCHECK( aIndex < segCount && aIndex >= 0,
1308 if( aPointIndex < 0 )
1311 if( aPointIndex < 0 )
1317 if( aPointIndex >= lastIndex )
1322 if( aPointIndex == lastIndex - 1 )
1331 return aPointIndex + 1;
1335 int arcStart = aPointIndex;
1339 wxCHECK2_MSG(
m_shapes[aPointIndex].first !=
SHAPE_IS_PT,
return -1,
"malformed chain!" );
1341 ssize_t currentArcIdx =
ArcIndex( aPointIndex );
1344 while( aPointIndex < lastIndex &&
ArcIndex( aPointIndex ) == currentArcIdx )
1351 if( aPointIndex - arcStart > 1 && !indexStillOnArc )
1354 if( aPointIndex == lastIndex )
1376 [&]( ssize_t& aIdx )
1386 if( aPointIndex < 0 )
1389 if( aPointIndex >=
PointCount() || aPointIndex < 0 )
1398 int start = aPointIndex;
1399 int end = aPointIndex;
1400 int arcIdx =
ArcIndex( aPointIndex );
1405 while( start > 0 &&
ArcIndex(
static_cast<ssize_t
>( start ) - 1 ) == arcIdx )
1429 if( aStartIndex < 0 )
1439 int numPoints =
static_cast<int>(
m_points.size() );
1444 ssize_t arcToSplitIndex =
ArcIndex( aStartIndex );
1448 for(
size_t i = aStartIndex; i <
m_points.size() && arcToSplitIndex ==
ArcIndex( i ); i++ )
1465 rv.
m_arcs.push_back( newArc );
1470 for(
int i = aStartIndex; i <= aEndIndex && i < numPoints; i =
NextShape( i ) )
1476 bool isLastShape = nextShape < 0;
1480 if( ( isLastShape && aEndIndex != ( numPoints - 1 ) )
1481 || ( nextShape > aEndIndex ) )
1483 if( i == aEndIndex )
1495 for( ; i <= aEndIndex && i < numPoints; i++ )
1515 rv.
m_arcs.push_back( newArc );
1523 rv.
Append( currentArc, aMaxError );
1531 wxASSERT_MSG( !
IsArcSegment( i ), wxT(
"Still on an arc segment, we missed something..." ) );
1533 if( i == aStartIndex )
1536 bool nextPointIsArc = isLastShape ? false :
IsArcSegment( nextShape );
1538 if( !nextPointIsArc && i <
SegmentCount() && i < aEndIndex )
1559 size_t num_arcs =
m_arcs.size();
1562 auto fixShapeIndices =
1563 [&](
const std::pair<ssize_t, ssize_t>& aShapeIndices ) -> std::pair<ssize_t, ssize_t>
1565 std::pair<ssize_t, ssize_t> retval = aShapeIndices;
1570 aIndex = aIndex + num_arcs;
1593 for(
int i = 1; i < aOtherLine.
PointCount(); i++ )
1598 ssize_t arcIndex = aOtherLine.
ArcIndex( i );
1626 if(
chain.PointCount() > 2 )
1628 chain.m_arcs.push_back( aArc );
1629 chain.m_arcs.back().SetWidth( 0 );
1631 for(
auto& sh :
chain.m_shapes )
1649 wxCHECK( aVertex <
m_points.size(), );
1651 if( aVertex > 0 &&
IsPtOnArc( aVertex ) )
1670 wxCHECK( aVertex <
m_points.size(), );
1672 if( aVertex > 0 &&
IsPtOnArc( aVertex ) )
1676 ssize_t arc_pos =
m_arcs.size();
1678 for(
auto arc_it =
m_shapes.rbegin() ;
1679 arc_it !=
m_shapes.rend() + aVertex;
1684 arc_pos = std::max( ( *arc_it ).first, ( *arc_it ).second );
1693 [&]( ssize_t& aIndex )
1695 if( aIndex >= arc_pos )
1711 std::vector<std::pair<ssize_t, ssize_t>> new_points(
chain.PointCount(),
1712 { arc_pos, SHAPE_IS_PT } );
1714 m_shapes.insert(
m_shapes.begin() + aVertex, new_points.begin(), new_points.end() );
1738 const int ptCount =
static_cast<int>(
m_points.size() );
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 );
1745 for(
int s = 0; s < segCount; s++ )
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 )
1766 aIp.push_back( is );
1771 sort( aIp.begin(), aIp.end(),
comp );
1780 const int ptCount =
static_cast<int>(
m_points.size() );
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 );
1787 for(
int s = 0; s < segCount; s++ )
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 )
1807 bool aExcludeColinearAndTouching,
BOX2I* aChainBBox )
const
1812 if( ourSegCount == 0 || theirSegCount == 0 )
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();
1820 thread_local std::vector<SEG> theirSegs;
1821 thread_local std::vector<SEG_EXTENT> sorted;
1823 theirSegs.resize( theirSegCount );
1824 sorted.resize( theirSegCount );
1827 for(
int i = 0; i < theirSegCount; i++ )
1830 const VECTOR2I& pb = theirPts[i + 1 < theirPtCount ? i + 1 : 0];
1831 theirSegs[i] =
SEG( pa, pb, i );
1835 for(
int i = 0; i < theirSegCount; i++ )
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 };
1842 std::sort( sorted.begin(), sorted.end(),
1843 [](
const SEG_EXTENT& a,
const SEG_EXTENT& b ) { return a.minX < b.minX; } );
1845 for(
int s1 = 0; s1 < ourSegCount; s1++ )
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 );
1855 if( ourMaxX < bbOther.m_Left || ourMinX > bbOther.
m_Right
1856 || ourMaxY < bbOther.m_Top || ourMinY > bbOther.
m_Bottom )
1861 const SEG a( a1, b1, s1 );
1865 auto rightEnd = std::upper_bound( sorted.begin(), sorted.end(), ourMaxX,
1866 [](
int val,
const SEG_EXTENT& e )
1868 return val < e.minX;
1871 for(
auto jt = sorted.begin(); jt != rightEnd; ++jt )
1873 if( jt->maxX < ourMinX || jt->maxY < ourMinY || jt->minY > ourMaxY )
1876 const SEG& b = theirSegs[jt->segIdx];
1887 if( !aExcludeColinearAndTouching && a.
Collinear( b ) )
1893 aIp.push_back( is );
1901 aIp.push_back( is );
1908 aIp.push_back( is );
1916 aIp.push_back( is );
1947 aIp.push_back( is );
1963 bool indexMatch =
true;
1973 indexMatch = ( i == aIndex );
1979 sum += ( aP - seg.
A ).EuclideanNorm();
1991 bool aUseBBoxCache )
const
2015 bool inside =
false;
2017 for(
int i = 0; i < pointCount; )
2026 const int d =
rescale( diff.
x, ( aPt.
y - p1.
y ), diff.
y );
2028 if( ( ( p1.
y >= aPt.
y ) != ( p2.
y >= aPt.
y ) ) && ( aPt.
x - p1.
x < d ) )
2034 if( aAccuracy <= 1 )
2049 const int threshold = aAccuracy + 1;
2050 const int64_t thresholdSq = int64_t( threshold ) * threshold;
2057 else if( pointCount == 1 )
2060 return distSq <= thresholdSq ? 0 : -1;
2065 for(
size_t i = 0; i < segCount; i++ )
2069 if( s.
A == aPt || s.
B == aPt )
2091 if( s.
A == aP || s.
B == aP )
2105 const int ptCount =
static_cast<int>(
m_points.size() );
2109 return std::optional<INTERSECTION>();
2112 auto endIdx = [ptCount](
int i )
2118 for(
int s1 = 0; s1 < segCount; s1++ )
2124 const int s1MinX = std::min( a1.
x, b1.
x ) - 2;
2125 const int s1MaxX = std::max( a1.
x, b1.
x ) + 2;
2126 const int s1MinY = std::min( a1.
y, b1.
y ) - 2;
2127 const int s1MaxY = std::max( a1.
y, b1.
y ) + 2;
2129 for(
int s2 = s1 + 1; s2 < segCount; s2++ )
2134 const int s2MinX = std::min( a2.
x, b2.
x );
2135 const int s2MaxX = std::max( a2.
x, b2.
x );
2137 if( s1MaxX < s2MinX || s2MaxX < s1MinX )
2140 const int s2MinY = std::min( a2.
y, b2.
y );
2141 const int s2MaxY = std::max( a2.
y, b2.
y );
2143 if( s1MaxY < s2MinY || s2MaxY < s1MinY )
2146 const SEG seg1( a1, b1, s1 );
2148 if( s1 + 1 != s2 && seg1.
Contains( a2 ) )
2160 !( closed && s1 == 0 && s2 == segCount - 1 ) )
2184 return std::optional<INTERSECTION>();
2201const std::optional<SHAPE_LINE_CHAIN::INTERSECTION>
2204 auto pointsClose = [](
const VECTOR2I& aPt1,
const VECTOR2I& aPt2 ) ->
bool
2206 return (
VECTOR2D( aPt1 ) - aPt2 ).SquaredEuclideanNorm() <= 2.0;
2209 auto collideArcSeg = [&pointsClose](
const SHAPE_ARC& aArc,
const SEG& aSeg,
int aClearance = 0,
2215 std::vector<VECTOR2I> candidatePts =
circle.Intersect( aSeg );
2217 for(
const VECTOR2I& candidate : candidatePts )
2220 if( aArc.
GetP1() == aSeg.A && pointsClose( candidate, aSeg.A ) )
2223 if( aSeg.B == aArc.
GetP0() && pointsClose( candidate, aSeg.B ) )
2226 bool collides = aArc.
Collide( candidate, aClearance,
nullptr, aLocation );
2238 std::vector<VECTOR2I> candidatePts;
2240 aArc1.
Intersect( aArc2, &candidatePts );
2242 for(
const VECTOR2I& candidate : candidatePts )
2245 if( aArc1.
GetP1() == aArc2.GetP0() && pointsClose( candidate, aArc1.
GetP1() ) )
2248 if( aArc2.GetP1() == aArc1.
GetP0() && pointsClose( candidate, aArc2.GetP1() ) )
2252 *aLocation = candidate;
2260 auto collideSegSeg = [
this](
int s1,
int s2,
INTERSECTION& is )
2267 if( s1 + 1 != s2 && seg1.
Contains( s2a ) )
2270 is.index_their = s2;
2281 is.index_their = s2;
2292 is.index_their = s2;
2303 std::vector<SHAPE_KEY> shapeCache;
2304 for(
int si = 0; si != -1; si =
NextShape( si ) )
2308 shapeCache.emplace_back( si, arci,
2313 for(
size_t sk1 = 0; sk1 < shapeCache.size(); sk1++ )
2315 for(
size_t sk2 = sk1 + 1; sk2 < shapeCache.size(); sk2++ )
2364 return std::optional<SHAPE_LINE_CHAIN::INTERSECTION>();
2369 bool aAllowInternalShapePoints )
const
2377 int min_d = std::numeric_limits<int>::max();
2391 if( !aAllowInternalShapePoints )
2414 return nearestArc.
GetP1();
2416 return nearestArc.
GetP0();
2436 dist = std::numeric_limits<int>::max();
2449 return CPoint( nearest );
2455 int min_d = std::numeric_limits<int>::max();
2475 std::stringstream ss;
2477 ss <<
"SHAPE_LINE_CHAIN( { ";
2487 ss <<
"}, " << (
m_closed ?
"true" :
"false" );
2503 bool aCyclicalCompare,
2504 int aEpsilon )
const
2510 if( a.m_points.size() != b.
m_points.size() )
2513 if( aCyclicalCompare )
2515 std::vector<VECTOR2I> aVerts = a.m_points;
2516 std::vector<VECTOR2I> bVerts = b.
m_points;
2518 auto centroid = [](
const std::vector<VECTOR2I>& pts )
2520 double sx = 0.0, sy = 0.0;
2521 for(
const auto& p : pts )
2526 return std::pair<double, double>( sx / pts.size(), sy / pts.size() );
2529 auto aC = centroid( aVerts );
2530 auto bC = centroid( bVerts );
2533 [](
const std::pair<double, double>& c,
const VECTOR2I& p1,
const VECTOR2I& p2 )
2535 double a1 = atan2( p1.y - c.second, p1.x - c.first );
2536 double a2 = atan2( p2.y - c.second, p2.x - c.first );
2541 std::sort( aVerts.begin(), aVerts.end(),
2544 return angleCmp( aC, p1, p2 );
2547 std::sort( bVerts.begin(), bVerts.end(),
2550 return angleCmp( bC, p1, p2 );
2553 for(
size_t i = 0; i < aVerts.size(); i++ )
2555 if( abs( aVerts[i].x - bVerts[i].x ) > aEpsilon
2556 || abs( aVerts[i].y - bVerts[i].y ) > aEpsilon )
2563 for(
int i = 0; i < a.PointCount(); i++ )
2565 if( abs( a.CPoint( i ).x - b.
CPoint( i ).
x ) > aEpsilon
2566 || abs( a.CPoint( i ).y - b.
CPoint( i ).
y ) > aEpsilon )
2599 if( n_pts > aStream.str().size() )
2605 if( n_arcs > aStream.str().size() )
2608 for(
size_t i = 0; i < n_pts; i++ )
2620 for(
size_t i = 0; i < n_arcs; i++ )
2642 if( aPathLength == 0 )
2650 if( total + l >= aPathLength )
2653 return s.
A + d.
Resize( aPathLength - total );
2673 for(
int i = 0, j = size - 1; i < size; ++i )
2681 return std::fabs( area * 0.5 );
2689 std::vector<VECTOR2I> pts_unique;
2690 std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
2721 std::pair<ssize_t,ssize_t> shapeToKeep =
m_shapes[i];
2726 assert( shapeToKeep.first <
static_cast<int>(
m_arcs.size() ) );
2727 assert( shapeToKeep.second <
static_cast<int>(
m_arcs.size() ) );
2729 pts_unique.push_back(
CPoint( i ) );
2730 shapes_unique.push_back( shapeToKeep );
2738 for(
size_t ii = 0; ii < pts_unique.size(); ++ii )
2740 const VECTOR2I p0 = pts_unique[ii];
2743 m_shapes.push_back( shapes_unique[ii] );
2754 std::vector<VECTOR2I> new_points;
2755 std::vector<std::pair<ssize_t, ssize_t>> new_shapes;
2757 new_points.reserve(
m_points.size() );
2758 new_shapes.reserve(
m_shapes.size() );
2760 for(
size_t start_idx = 0; start_idx <
m_points.size(); )
2762 new_points.push_back(
m_points[start_idx] );
2763 new_shapes.push_back(
m_shapes[start_idx] );
2770 size_t end_idx = ( start_idx + 2 ) %
m_points.size();
2771 bool can_simplify =
true;
2773 while( can_simplify && end_idx != start_idx && ( end_idx > start_idx ||
m_closed ) )
2776 for(
size_t test_idx = ( start_idx + 1 ) %
m_points.size();
2777 test_idx != end_idx;
2778 test_idx = ( test_idx + 1 ) %
m_points.size() )
2785 can_simplify =
false;
2792 can_simplify =
false;
2800 end_idx = ( end_idx + 1 ) %
m_points.size();
2805 if( end_idx == ( start_idx + 2 ) %
m_points.size() )
2812 size_t new_start_idx = ( end_idx +
m_points.size() - 1 ) %
m_points.size();
2815 if( new_start_idx <= start_idx )
2818 start_idx = new_start_idx;
2823 if( new_points.size() == 1 )
2825 new_points.push_back(
m_points.back() );
2826 new_shapes.push_back(
m_shapes.back() );
2833 new_points.push_back(
m_points.back() );
2834 new_shapes.push_back(
m_shapes.back() );
2839 m_points = std::move( new_points );
2840 m_shapes = std::move( new_shapes );
2856 int i_start = l.
Find( m );
2857 int i_end = l.
Find( n );
2859 if( i_start > i_end )
2862 i_start = l.
Find( m );
2863 i_end = l.
Find( n );
2866 aPre = l.
Slice( 0, i_start );
2867 aPost = l.
Slice( i_end, -1 );
2868 aMid = l.
Slice( i_start, i_end );
2875 std::vector<VECTOR2I> pts_unique;
2876 std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
2909 std::pair<ssize_t,ssize_t> shapeToKeep =
m_shapes[i];
2914 assert( shapeToKeep.first <
static_cast<int>(
m_arcs.size() ) );
2915 assert( shapeToKeep.second <
static_cast<int>(
m_arcs.size() ) );
2917 pts_unique.push_back(
CPoint( i ) );
2918 shapes_unique.push_back( shapeToKeep );
2925 np = pts_unique.size();
2939 && (
SEG( p0, pts_unique[n + 2] ).LineDistance( pts_unique[n + 1] ) <= 1
2940 ||
SEG( p0, pts_unique[n + 2] ).Collinear(
SEG( p0, pts_unique[n + 1] ) ) ) )
2945 m_shapes.push_back( shapes_unique[i] );
2952 m_points.push_back( pts_unique[np - 1] );
2953 m_shapes.push_back( shapes_unique[np - 1] );
2962 m_points.push_back( pts_unique[np - 2] );
2963 m_shapes.push_back( shapes_unique[np - 2] );
2966 m_points.push_back( pts_unique[np - 1] );
2967 m_shapes.push_back( shapes_unique[np - 1] );
2976 bool aSimplify )
const
2982 poly.
OffsetLineChain( *
this, aAmount, aCornerStrategy, aMaxError, aSimplify );
3000 outline.
Split( start,
true );
3003 const int idA = outline.
Find( start );
3004 const int idB = outline.
Find(
end );
3006 if( idA == -1 || idB == -1 )
3012 for(
int i = idA;; )
3031 for(
int i = idB;; )
3050 if( aLeft.
CPoint( 0 ) != start )
3053 wxASSERT( aLeft.
CPoint( 0 ) == start );
3056 if( aRight.
CPoint( 0 ) != start )
3059 wxASSERT( aRight.
CPoint( 0 ) == start );
3063 int sideLeft = base.
Side( aLeft.
CPoint( 1 ) );
3064 int sideRight = base.
Side( aRight.
CPoint( 1 ) );
3066 if( sideLeft == 0 || sideRight == 0 )
3069 if( sideLeft == sideRight )
3072 if( sideLeft > 0 && sideRight < 0 )
3073 std::swap( aLeft, aRight );
3141 if( ( d > 0 ) == ( ipNext.
y > ip.
y ) )
3159 if( ( d > 0 ) == ( ipNext.
y > ip.
y ) )
3179 for(
int i = 1; i < aPolyline.
PointCount(); i++ )
3181 auto p = aPolyline.
CPoint( i );
3220 size_t nextIdx = aSegment + 1;
3222 if( nextIdx >
m_shapes.size() - 1 )
3251 size_t prevIndex = aIndex - 1;
3255 else if( aIndex >
m_points.size() -1 )
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Represent basic circle geometry with utility geometry functions.
const VECTOR2I ReflectPoint(const VECTOR2I &aP) const
Reflect a point using this segment as axis.
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...
ecoord SquaredDistance(const SEG &aSeg) const
VECTOR2I::extended_type ecoord
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
int Length() const
Return the length (this).
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
static SEG::ecoord Square(int a)
bool Collinear(const SEG &aSeg) const
Check if segment aSeg lies on the same line as (this).
bool NearestPoints(const SEG &aSeg, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this segment and aSeg.
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
bool Contains(const SEG &aSeg) const
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
int GetWidth() const override
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
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()
const VECTOR2I & GetP0() const
void SetWidth(int aWidth) override
const VECTOR2I & GetCenter() const
POINT_INSIDE_TRACKER(const VECTOR2I &aPoint)
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)
virtual size_t GetSegmentCount() const =0
virtual const VECTOR2I GetPoint(int aIndex) const =0
virtual BOX2I * GetCachedBBox() const
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
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.
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 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.
VECTOR2I::extended_type ecoord
SHAPE(SHAPE_TYPE aType)
Create an empty shape of type aType.
virtual int GetWidth() const
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
static constexpr extended_type ECOORD_MAX
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
CORNER_STRATEGY
define how inflate transform build inflated polygon
@ LEFT_RIGHT
Flip left to right (around the Y axis)
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.
void run_on_pair(std::pair< _Type, _Type > &__pair, _Function __f)
Apply a function to the first and second element of a std::pair.
std::optional< VECTOR2I > OPT_VECTOR2I
@ SH_LINE_CHAIN
line chain (polyline)
int getArcPolygonizationMaxError()
std::vector< FAB_LAYER_COLOR > dummy
A min-max version of BOX2 for fast intersection checking.
bool Intersects(const BOX2I_MINMAX &aOther) const
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)
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)
const SHAPE_LINE_CHAIN chain
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.
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.
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D
VECTOR2< int64_t > VECTOR2L