41#include <unordered_set>
47#include <clipper2/clipper.h>
67#if defined( __MINGW32__ )
68 #define TRIANGULATESIMPLIFICATIONLEVEL 50
69 #define ENABLECACHEFRIENDLYFRACTURE true
71 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
72 #define ENABLECACHEFRIENDLYFRACTURE ADVANCED_CFG::GetCfg().m_EnableCacheFriendlyFracture
113 m_triangulatedPolys.reserve( aOther.TriangulatedPolyCount() );
115 for( unsigned i = 0; i < aOther.TriangulatedPolyCount(); i++ )
117 const TRIANGULATED_POLYGON* poly = aOther.TriangulatedPolygon( i );
118 m_triangulatedPolys.push_back( std::make_unique<TRIANGULATED_POLYGON>( *poly ) );
129 m_triangulationValid =
false;
132 m_failedHash = aOther.m_failedHash;
133 m_failedHashValid.store( aOther.m_failedHashValid.load() );
168 unsigned int contourIdx = 0;
171 int currentGlobalIdx = 0;
173 for( polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
177 for( contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
180 int totalPoints = currentContour.
PointCount();
182 for( vertexIdx = 0; vertexIdx < totalPoints; vertexIdx++ )
185 if( currentGlobalIdx == aGlobalIdx )
187 aRelativeIndices->
m_polygon = polygonIdx;
188 aRelativeIndices->
m_contour = contourIdx;
189 aRelativeIndices->
m_vertex = vertexIdx;
205 int& aGlobalIdx )
const
207 int selectedVertex = aRelativeIndices.
m_vertex;
208 unsigned int selectedContour = aRelativeIndices.
m_contour;
209 unsigned int selectedPolygon = aRelativeIndices.
m_polygon;
212 if( selectedPolygon <
m_polys.size() && selectedContour <
m_polys[selectedPolygon].size()
213 && selectedVertex <
m_polys[selectedPolygon][selectedContour].PointCount() )
219 for(
unsigned int polygonIdx = 0; polygonIdx < selectedPolygon; polygonIdx++ )
221 currentPolygon =
Polygon( polygonIdx );
223 for(
unsigned int contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
224 aGlobalIdx += currentPolygon[contourIdx].PointCount();
227 currentPolygon =
Polygon( selectedPolygon );
229 for(
unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx++ )
230 aGlobalIdx += currentPolygon[contourIdx].PointCount();
232 aGlobalIdx += selectedVertex;
249 poly.push_back( empty_path );
250 m_polys.push_back( std::move( poly ) );
266 m_polys[aOutline].push_back( empty_path );
268 return m_polys.back().size() - 2;
286 assert( aOutline < (
int)
m_polys.size() );
287 assert( idx < (
int)
m_polys[aOutline].size() );
289 m_polys[aOutline][idx].Append( x, y, aAllowDuplication );
291 return m_polys[aOutline][idx].PointCount();
296 std::optional<int> aMaxError )
310 assert( aOutline < (
int)
m_polys.size() );
311 assert( idx < (
int)
m_polys[aOutline].size() );
313 if( aMaxError.has_value() )
314 m_polys[aOutline][idx].Append( aArc, aMaxError.value() );
316 m_polys[aOutline][idx].Append( aArc );
318 return m_polys[aOutline][idx].PointCount();
326 if( aGlobalIndex < 0 )
339 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
359 if( aOutline >= (
int)
m_polys.size() )
362 if( idx >= (
int)
m_polys[aOutline].size() )
365 return m_polys[aOutline][idx].PointCount();
380 for(
int idx = 0; idx <=
HoleCount( ii ); idx++ )
382 full_count +=
m_polys[ii][idx].PointCount();
392 assert( aFirstPolygon >= 0 && aLastPolygon <=
OutlineCount() );
415 assert( aOutline < (
int)
m_polys.size() );
416 assert( idx < (
int)
m_polys[aOutline].size() );
418 return m_polys[aOutline][idx].CPoint( aIndex );
428 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
453 if(
index.m_vertex == 0 )
455 index.m_vertex = lastpoint - 1;
458 else if(
index.m_vertex == lastpoint )
476 *aPrevious = previous;
492 std::vector<SEG> segments;
496 segments.emplace_back( *it );
498 std::sort( segments.begin(), segments.end(), [](
const SEG& a,
const SEG& b )
500 int min_a_x = std::min( a.A.x, a.B.x );
501 int min_b_x = std::min( b.A.x, b.B.x );
503 return min_a_x < min_b_x || ( min_a_x == min_b_x && std::min( a.A.y, a.B.y ) < std::min( b.A.y, b.B.y ) );
506 for(
auto it = segments.begin(); it != segments.end(); ++it )
508 SEG& firstSegment = *it;
511 auto innerIterator = it;
512 int max_x = std::max( firstSegment.
A.
x, firstSegment.
B.
x );
513 int max_y = std::max( firstSegment.
A.
y, firstSegment.
B.
y );
516 for( innerIterator++; innerIterator != segments.end(); innerIterator++ )
518 SEG& secondSegment = *innerIterator;
519 int min_x = std::min( secondSegment.
A.
x, secondSegment.
B.
x );
520 int min_y = std::min( secondSegment.
A.
y, secondSegment.
B.
y );
524 if( max_x < min_x || ( max_x == min_x && max_y < min_y ) )
528 bool adjacent = ( index_diff == 1) || (index_diff == ((
int)segments.size() - 1) );
531 if( !adjacent && firstSegment.
Collide( secondSegment, 0 ) )
542 for(
unsigned int polygon = 0; polygon <
m_polys.size(); polygon++ )
556 poly.push_back( aOutline );
561 wxCHECK2_MSG( aOutline.
IsClosed(), poly.back().SetClosed(
true ),
562 "Warning: non-closed outline added to SHAPE_POLY_SET" );
564 m_polys.push_back( std::move( poly ) );
566 return (
int)
m_polys.size() - 1;
575 aOutline += (int)
m_polys.size();
577 assert( aOutline < (
int)
m_polys.size() );
581 assert( poly.size() );
583 poly.push_back( aHole );
585 return (
int) poly.size() - 2;
605 for(
int j = 0; j <
HoleCount( i ); j++ )
619 for(
size_t i = 0; i < poly.size(); i++ )
631 for(
size_t i = 0; i < poly.size(); i++ )
634 aArcBuffer.push_back( arc );
644 for(
size_t i = 0; i < poly.size(); i++ )
652 std::vector<SHAPE_LINE_CHAIN> contours;
655 contours.insert( contours.end(), poly.begin(), poly.end() );
657 std::map<int, std::set<int>> parentToChildren;
658 std::map<int, std::set<int>> childToParents;
661 contour.GenerateBBoxCache();
663 for(
size_t i = 0; i < contours.size(); i++ )
667 for(
size_t j = 0; j < contours.size(); j++ )
677 parentToChildren[i].emplace( j );
678 childToParents[j].emplace( i );
683 std::set<int> topLevelParents;
685 for(
size_t i = 0; i < contours.size(); i++ )
687 if( childToParents[i].size() == 0 )
689 topLevelParents.emplace( i );
695 std::function<void(
int,
int, std::vector<int> )>
process;
698 [&](
int myId,
int parentOutlineId,
const std::vector<int>&
path )
700 std::set<int> relParents = childToParents[myId];
702 for(
int pathId :
path )
704 int erased = relParents.erase( pathId );
705 wxASSERT( erased > 0 );
708 wxASSERT( relParents.size() == 0 );
712 bool isOutline =
path.size() % 2 == 0;
716 int outlineId =
result.AddOutline( contours[myId] );
717 myOutline = outlineId;
721 wxASSERT( parentOutlineId != -1 );
722 result.AddHole( contours[myId], parentOutlineId );
725 auto it = parentToChildren.find( myId );
726 if( it != parentToChildren.end() )
728 std::vector<int> thisPath =
path;
729 thisPath.emplace_back( myId );
731 std::set<int> thisPathSet;
732 thisPathSet.insert( thisPath.begin(), thisPath.end() );
734 for(
int childId : it->second )
736 const std::set<int>& childPathSet = childToParents[childId];
738 if( thisPathSet != childPathSet )
741 process( childId, myOutline, thisPath );
746 for(
int topParentId : topLevelParents )
748 std::vector<int>
path;
752 *
this = std::move(
result );
768 wxFAIL_MSG( wxT(
"Boolean ops on curved polygons are not supported. You should call "
769 "ClearArcs() before carrying out the boolean operation." ) );
772 Clipper2Lib::Clipper64 c;
774 std::vector<CLIPPER_Z_VALUE> zValues;
775 std::vector<SHAPE_ARC> arcBuffer;
776 std::map<VECTOR2I, CLIPPER_Z_VALUE> newIntersectPoints;
778 Clipper2Lib::Paths64 paths;
779 Clipper2Lib::Paths64 clips;
783 for(
size_t i = 0; i < poly.size(); i++ )
785 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
791 for(
size_t i = 0; i < poly.size(); i++ )
793 clips.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
797 c.AddSubject( paths );
800 Clipper2Lib::PolyTree64 solution;
802 Clipper2Lib::ZCallback64 callback =
803 [&](
const Clipper2Lib::Point64 & e1bot,
const Clipper2Lib::Point64 & e1top,
804 const Clipper2Lib::Point64 & e2bot,
const Clipper2Lib::Point64 & e2top,
805 Clipper2Lib::Point64 & pt )
808 [&](
const ssize_t& aZvalue,
const ssize_t& aCompareVal = -1 ) -> ssize_t
812 retval = zValues.at( aZvalue ).m_SecondArcIdx;
814 if( retval == -1 || ( aCompareVal > 0 && retval != aCompareVal ) )
815 retval = zValues.at( aZvalue ).m_FirstArcIdx;
821 [&](
const ssize_t& aBottomZ,
const ssize_t aTopZ ) -> ssize_t
823 ssize_t retval = arcIndex( aBottomZ );
827 if( retval != arcIndex( aTopZ, retval ) )
834 ssize_t e1ArcSegmentIndex = arcSegment( e1bot.z, e1top.z );
835 ssize_t e2ArcSegmentIndex = arcSegment( e2bot.z, e2top.z );
839 if( e1ArcSegmentIndex != -1 )
850 size_t z_value_ptr = zValues.size();
851 zValues.push_back( newZval );
855 newIntersectPoints.insert( {
VECTOR2I( pt.x, pt.y ), newZval } );
861 c.SetZCallback( std::move( callback ) );
863 c.Execute( aType, Clipper2Lib::FillRule::NonZero, solution );
872 booleanOp( Clipper2Lib::ClipType::Union, b );
878 booleanOp( Clipper2Lib::ClipType::Difference, b );
884 booleanOp( Clipper2Lib::ClipType::Intersection, b );
890 booleanOp( Clipper2Lib::ClipType::Xor, b );
896 booleanOp( Clipper2Lib::ClipType::Union, a, b );
902 booleanOp( Clipper2Lib::ClipType::Difference, a, b );
908 booleanOp( Clipper2Lib::ClipType::Intersection, a, b );
914 booleanOp( Clipper2Lib::ClipType::Xor, a, b );
922 Inflate( aFactor, aCornerStrategy, aMaxError );
930 using namespace Clipper2Lib;
934 #define SEG_CNT_MAX 64
935 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
942 JoinType joinType = JoinType::Round;
943 double miterLimit = 2.0;
945 switch( aCornerStrategy )
948 joinType = JoinType::Miter;
953 joinType = JoinType::Miter;
957 joinType = JoinType::Miter;
961 joinType = JoinType::Square;
965 joinType = JoinType::Round;
969 std::vector<CLIPPER_Z_VALUE> zValues;
970 std::vector<SHAPE_ARC> arcBuffer;
976 for(
size_t i = 0; i < poly.size(); i++ )
977 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
979 c.AddPaths( paths, joinType, EndType::Polygon );
986 if( aCircleSegCount < 6 )
991 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
993 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
996 arc_tolerance_factor[aCircleSegCount] = coeff;
1000 coeff = arc_tolerance_factor[aCircleSegCount];
1003 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1004 c.MiterLimit( miterLimit );
1011 c.Execute( aAmount, paths );
1013 Clipper2Lib::SimplifyPaths( paths,
std::abs( aAmount ) * coeff,
true );
1016 c2.PreserveCollinear(
false );
1017 c2.ReverseSolution(
false );
1018 c2.AddSubject( paths );
1019 c2.Execute(ClipType::Union, FillRule::Positive, tree);
1023 c.Execute( aAmount, tree );
1034 using namespace Clipper2Lib;
1038 #define SEG_CNT_MAX 64
1039 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
1046 JoinType joinType = JoinType::Round;
1047 double miterLimit = 2.0;
1049 switch( aCornerStrategy )
1052 joinType = JoinType::Miter;
1057 joinType = JoinType::Miter;
1061 joinType = JoinType::Miter;
1065 joinType = JoinType::Square;
1069 joinType = JoinType::Round;
1073 std::vector<CLIPPER_Z_VALUE> zValues;
1074 std::vector<SHAPE_ARC> arcBuffer;
1077 c.AddPath(
path, joinType, EndType::Butt );
1083 if( aCircleSegCount < 6 )
1084 aCircleSegCount = 6;
1088 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
1090 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
1093 arc_tolerance_factor[aCircleSegCount] = coeff;
1097 coeff = arc_tolerance_factor[aCircleSegCount];
1100 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1101 c.MiterLimit( miterLimit );
1108 c.Execute( aAmount, paths2 );
1110 Clipper2Lib::SimplifyPaths( paths2,
std::abs( aAmount ) * coeff,
false );
1113 c2.PreserveCollinear(
false );
1114 c2.ReverseSolution(
false );
1115 c2.AddSubject( paths2 );
1116 c2.Execute( ClipType::Union, FillRule::Positive, tree );
1120 c.Execute( aAmount, tree );
1133 inflate2( aAmount, segCount, aCornerStrategy, aSimplify );
1142 inflateLine2( aLine, aAmount, segCount, aCornerStrategy, aSimplify );
1147 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1148 const std::vector<SHAPE_ARC>& aArcBuffer )
1150 if( !aPolyPath->IsHole() )
1153 paths.reserve( aPolyPath->Count() + 1 );
1154 paths.emplace_back( aPolyPath->Polygon(), aZValueBuffer, aArcBuffer );
1156 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& child : *aPolyPath )
1158 paths.emplace_back( child->Polygon(), aZValueBuffer, aArcBuffer );
1160 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& grandchild : *child )
1164 m_polys.emplace_back( std::move( paths ) );
1170 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1171 const std::vector<SHAPE_ARC>& aArcBuffer )
1175 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& n : tree )
1181 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1182 const std::vector<SHAPE_ARC>& aArcBuffer )
1187 for(
const Clipper2Lib::Path64& n : aPath )
1189 if( Clipper2Lib::Area( n ) > 0 )
1198 wxCHECK2_MSG( !
path.empty(),
continue, wxT(
"Cannot add a hole before an outline" ) );
1201 path.emplace_back( n, aZValueBuffer, aArcBuffer );
1240 int x = edge.
m_p1.
x;
1241 int y = edge.
m_p1.
y;
1242 int min_dist = std::numeric_limits<int>::max();
1261 x_intersect = std::max( e.
m_p1.
x, e.
m_p2.
x );
1269 int dist = ( x - x_intersect );
1271 if( dist >= 0 && dist < min_dist )
1274 x_nearest = x_intersect;
1287 edges[hole2outline_index] =
1290 edges[split_index] =
1295 e_nearest->
m_next = outline2hole_index;
1298 for( ; last->
m_next != edgeIndex; last = &edges[last->
m_next] )
1300 last->
m_next = hole2outline_index;
1310 bool outline =
true;
1312 if( paths.size() == 1 )
1315 size_t total_point_count = 0;
1319 total_point_count +=
path.PointCount();
1322 if( total_point_count > (
size_t) std::numeric_limits<FractureEdge::Index>::max() )
1324 wxLogWarning( wxT(
"Polygon has more points than int limit" ) );
1331 edges.reserve( total_point_count + paths.size() * 3 );
1337 int path_or_provoking_index;
1342 std::vector<PathInfo> sorted_paths;
1343 const int paths_count =
static_cast<int>( paths.size() );
1344 sorted_paths.reserve( paths_count );
1346 for(
int path_index = 0; path_index < paths_count; path_index++ )
1349 const std::vector<VECTOR2I>& points =
path.CPoints();
1350 const int point_count =
static_cast<int>( points.size() );
1351 int x_min = std::numeric_limits<int>::max();
1352 int y_min = std::numeric_limits<int>::max();
1355 for(
int point_index = 0; point_index < point_count; point_index++ )
1357 const VECTOR2I& point = points[point_index];
1358 if( point.
x < x_min )
1361 leftmost = point_index;
1363 if( point.
y < y_min )
1367 sorted_paths.emplace_back( PathInfo{ path_index, leftmost, x_min, y_min } );
1370 std::sort( sorted_paths.begin() + 1, sorted_paths.end(),
1371 [](
const PathInfo& a,
const PathInfo& b )
1374 return a.y_or_bridge < b.y_or_bridge;
1380 for( PathInfo& path_info : sorted_paths )
1383 const std::vector<VECTOR2I>& points =
path.CPoints();
1384 const size_t point_count = points.size();
1389 for(
size_t i = 0; i < point_count - 1; i++ )
1391 edges.emplace_back( points[i], points[i + 1], edge_index + 1 );
1396 edges.emplace_back( points[point_count - 1], points[0], provoking_edge );
1403 path_info.path_or_provoking_index = provoking_edge;
1404 path_info.y_or_bridge = edge_index;
1408 edges.resize( edge_index );
1414 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); it++ )
1416 auto edge =
processHole( edges, it->path_or_provoking_index,
1417 it->path_or_provoking_index + it->leftmost, it->y_or_bridge );
1422 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
1470 int x = edge->
m_p1.
x;
1471 int y = edge->
m_p1.
y;
1472 int min_dist = std::numeric_limits<int>::max();
1479 if( !e->matches( y ) )
1484 if( e->m_p1.y == e->m_p2.y )
1486 x_intersect = std::max( e->m_p1.x, e->m_p2.x );
1490 x_intersect = e->m_p1.x
1491 +
rescale( e->m_p2.x - e->m_p1.x, y - e->m_p1.y, e->m_p2.y - e->m_p1.y );
1494 int dist = ( x - x_intersect );
1496 if( dist >= 0 && dist < min_dist && e->m_connected )
1499 x_nearest = x_intersect;
1515 edges.push_back( split_2 );
1516 edges.push_back( lead1 );
1517 edges.push_back( lead2 );
1522 e_nearest->
m_next = lead1;
1527 for( last = edge; last->
m_next != edge; last = last->
m_next )
1553 if( paths.size() == 1 )
1556 int num_unconnected = 0;
1560 const std::vector<VECTOR2I>& points =
path.CPoints();
1561 int pointCount = points.size();
1565 int x_min = std::numeric_limits<int>::max();
1567 for(
int i = 0; i < pointCount; i++ )
1569 if( points[i].x < x_min )
1570 x_min = points[i].x;
1575 points[i + 1 == pointCount ? 0 : i + 1] );
1586 if( i == pointCount - 1 )
1590 edges.push_back( fe );
1594 if( fe->
m_p1.
x == x_min )
1595 border_edges.push_back( fe );
1606 while( num_unconnected > 0 )
1608 int x_min = std::numeric_limits<int>::max();
1609 auto it = border_edges.begin();
1614 for( ; it != border_edges.end(); ++it )
1617 int xt = border_edge->
m_p1.
x;
1619 if( ( xt <= x_min ) && !border_edge->
m_connected )
1622 smallestX = border_edge;
1626 int num_processed =
processEdge( edges, smallestX );
1629 if( !num_processed )
1631 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
1639 num_unconnected -= num_processed;
1657 paths.push_back( std::move( newPath ) );
1681 assert( aPoly.size() == 1 );
1687 bool m_duplicate =
false;
1694 bool compareSegs(
const SEG& s1,
const SEG& s2 )
const
1696 return (s1.
A == s2.
B && s1.
B == s2.
A);
1701 return compareSegs( m_poly->
CSegment( m_index ),
1702 aOther.m_poly->CSegment( aOther.m_index ) );
1707 return !compareSegs( m_poly->
CSegment( m_index ),
1708 aOther.m_poly->CSegment( aOther.m_index ) );
1713 std::size_t operator()(
const EDGE& aEdge )
const
1715 const SEG& a = aEdge.m_poly->CSegment( aEdge.m_index );
1716 std::size_t
seed = 0xa82de1c0;
1723 struct EDGE_LIST_ENTRY
1726 EDGE_LIST_ENTRY*
next;
1729 std::unordered_set<EDGE, EDGE::HASH> uniqueEdges;
1734 auto edgeList = std::make_unique<EDGE_LIST_ENTRY[]>( lc.
SegmentCount() );
1738 edgeList[i].index = i;
1739 edgeList[i].next = &edgeList[ (i != lc.
SegmentCount() - 1) ? i + 1 : 0 ];
1742 std::unordered_set<EDGE_LIST_ENTRY*> queue;
1747 uniqueEdges.insert( e );
1753 auto it = uniqueEdges.find( e );
1755 if( it != uniqueEdges.end() && it->m_index != i )
1757 int e1 = it->m_index;
1761 std::swap( e1, e2 );
1763 int e1_prev = e1 - 1;
1768 int e2_prev = e2 - 1;
1773 int e1_next = e1 + 1;
1778 int e2_next = e2 + 1;
1783 edgeList[e1_prev].next = &edgeList[ e2_next ];
1784 edgeList[e2_prev].next = &edgeList[ e1_next ];
1785 edgeList[i].next =
nullptr;
1786 edgeList[it->m_index].next =
nullptr;
1792 if( edgeList[i].
next )
1793 queue.insert( &edgeList[i] );
1796 auto edgeBuf = std::make_unique<EDGE_LIST_ENTRY* []>( lc.
SegmentCount() );
1802 double max_poly = 0.0;
1804 while( queue.size() )
1806 EDGE_LIST_ENTRY* e_first = *queue.begin();
1807 EDGE_LIST_ENTRY* e = e_first;
1814 }
while( e && e != e_first );
1818 for(
int i = 0; i < cnt; i++ )
1822 queue.erase( edgeBuf[i] );
1827 double area = std::fabs( outl.
Area() );
1829 if( area > max_poly )
1835 result.push_back( outl );
1842 aPoly = std::move(
result );
1852 if( paths.size() > 1 )
1876 std::array<VECTOR2I,4> pts = { aSegA.
A, aSegA.
B, aSegB.
A, aSegB.
B };
1878 std::sort( pts.begin(), pts.end(), [axis](
const VECTOR2I& p,
const VECTOR2I& q )
1881 return p.x < q.x || ( p.x == q.x && p.y < q.y );
1883 return p.y < q.y || ( p.y == q.y && p.x < q.x );
1904 wxLogTrace( wxT(
"collinear" ), wxT(
"Found exterior waist between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)" ),
1905 aSegA.
A.
x, aSegA.
A.
y, aSegA.
B.
x, aSegA.
B.
y,
1906 aSegB.
A.
x, aSegB.
A.
y, aSegB.
B.
x, aSegB.
B.
y );
1917 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
1919 bool changed =
true;
1930 for( intptr_t i = 0; i < count; ++i )
1934 intptr_t min[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
1935 intptr_t max[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
1936 rtree.
Insert( min, max, i );
1943 for( intptr_t i = 0; i < count && !found; ++i )
1948 intptr_t min[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
1949 intptr_t max[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
1952 [&](
const intptr_t& j ) ->
bool
1954 if( j == i || j == ( ( i + 1 ) % count ) || j == ( ( i + count - 1 ) % count ) )
1959 SEG other( oa, ob );
1963 if( oa == a && ob == b )
1966 if( oa == b && ob == a )
1980 rtree.
Search( min, max, visitor );
1987 int a1 = ( segA + 1 ) % outline.
PointCount();
1989 int b1 = ( segB + 1 ) % outline.
PointCount();
2015 m_polys[polyIdx][0] = std::move( lc1 );
2018 np.push_back( std::move( lc2 ) );
2019 m_polys.push_back( std::move( np ) );
2029 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
2031 bool changed =
true;
2043 int insertSegIdx = -1;
2044 int insertVertIdx = -1;
2047 constexpr int RTREE_THRESHOLD = 32;
2049 if( count < RTREE_THRESHOLD )
2051 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2054 const int prevSeg = ( vertIdx + count - 1 ) % count;
2056 for(
int segIdx = 0; segIdx < count; ++segIdx )
2059 if( segIdx == prevSeg || segIdx == vertIdx )
2071 insertSegIdx = segIdx;
2072 insertVertIdx = vertIdx;
2082 for(
int i = 0; i < count; ++i )
2086 int bmin[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
2087 int bmax[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
2088 rtree.
Insert( bmin, bmax, i );
2091 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2094 const int prevSeg = ( vertIdx + count - 1 ) % count;
2095 int bmin[2] = { pt.
x, pt.
y };
2096 int bmax[2] = { pt.
x, pt.
y };
2099 [&](
const intptr_t& segIdx ) ->
bool
2101 if( segIdx == prevSeg || segIdx == vertIdx )
2113 insertSegIdx = segIdx;
2114 insertVertIdx = vertIdx;
2121 rtree.
Search( bmin, bmax, pinchVisitor );
2125 if( insertSegIdx < 0 )
2132 const int splitStart1 = ( insertSegIdx + 1 ) % count;
2137 if( insertVertIdx >= splitStart1 )
2138 size1 = insertVertIdx - splitStart1 + 1;
2140 size1 = count - splitStart1 + insertVertIdx + 1;
2142 if( insertSegIdx >= insertVertIdx )
2143 size2 = insertSegIdx - insertVertIdx + 1;
2145 size2 = count - insertVertIdx + insertSegIdx + 1;
2147 if( size1 < 3 || size2 < 3 )
2155 int idx = splitStart1;
2157 for(
int i = 0; i < size1; ++i )
2160 idx = ( idx + 1 ) % count;
2165 idx = insertVertIdx;
2167 for(
int i = 0; i < size2; ++i )
2170 idx = ( idx + 1 ) % count;
2175 m_polys[polyIdx][0] = std::move( poly1 );
2178 np.push_back( std::move( poly2 ) );
2179 m_polys.push_back( std::move( np ) );
2203 path.Simplify( aTolerance );
2221 while( outline.size() > 1 )
2246 std::stringstream ss;
2248 ss <<
"SHAPE_LINE_CHAIN poly; \n";
2250 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2252 for(
unsigned j = 0; j <
m_polys[i].size(); j++ )
2255 ss <<
"{ auto tmp = " <<
m_polys[i][j].Format() <<
";\n";
2261 ss <<
" poly.AddOutline(tmp); } \n";
2265 ss <<
" poly.AddHole(tmp); } \n";
2281 if( tmp !=
"polyset" )
2286 int n_polys = atoi( tmp.c_str() );
2291 for(
int i = 0; i < n_polys; i++ )
2301 int n_outlines = atoi( tmp.c_str() );
2303 if( n_outlines < 0 )
2306 for(
int j = 0; j < n_outlines; j++ )
2313 int n_vertices = atoi( tmp.c_str() );
2315 for(
int v = 0; v < n_vertices; v++ )
2319 aStream >> tmp; p.
x = atoi( tmp.c_str() );
2320 aStream >> tmp; p.
y = atoi( tmp.c_str() );
2324 paths.push_back( std::move( outline ) );
2327 m_polys.push_back( std::move( paths ) );
2338 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2355 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2358 bb = *
m_polys[i][0].GetCachedBBox();
2375 if( lineChain.PointOnEdge( aP, aAccuracy ) )
2390 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2393 *aLocation = nearest;
2396 *aActual = sqrt( dist_sq );
2414 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2417 *aLocation = nearest;
2420 *aActual = sqrt( dist_sq );
2437 int extra = segment->
GetWidth() / 2;
2439 if(
Collide( segment->
GetSeg(), aClearance + extra, aActual, aLocation ) )
2442 *aActual = std::max( 0, *aActual - extra );
2453 int extra =
circle->GetRadius();
2455 if(
Collide(
circle->GetCenter(), aClearance + extra, aActual, aLocation ) )
2458 *aActual = std::max( 0, *aActual - extra );
2475 if( aActual || aLocation )
2480 if( aShape->
Collide( &tri, aClearance, &triActual, &triLocation ) )
2491 if( aShape->
Collide( &tri, aClearance ) )
2500 *aActual = std::max( 0,
actual );
2523 if( aPolygonIdx < 0 )
2524 aPolygonIdx +=
m_polys.size();
2526 m_polys[aPolygonIdx].erase(
m_polys[aPolygonIdx].begin() + aContourIdx );
2546 std::vector<VERTEX_INDEX> indices_to_remove;
2551 segmentStart = *iterator;
2557 segmentEnd = contourStart;
2565 contourStart = *iterator;
2573 wxCHECK_MSG( iterator, removed, wxT(
"Invalid polygon. Reached end without noticing. Please report this error" ) );
2575 segmentEnd = *iterator;
2579 if( segmentStart == segmentEnd )
2581 indices_to_remove.push_back( indexStart );
2588 for(
auto it = indices_to_remove.rbegin(); it != indices_to_remove.rend(); ++it )
2611 if( triangleSet->GetSourceOutlineIndex() == aIdx )
2613 else if( triangleSet->GetSourceOutlineIndex() > aIdx )
2614 triangleSet->SetSourceOutlineIndex( triangleSet->GetSourceOutlineIndex() - 1 );
2641 Append( aP.
x, aP.
y, aOutline, aHole );
2647 int aClearance )
const
2650 bool collision =
false;
2660 delta = *iterator - aPoint;
2663 distance_squared =
delta.SquaredEuclideanNorm();
2666 if( distance_squared <= clearance_squared )
2668 if( !aClosestVertex )
2674 clearance_squared = distance_squared;
2677 *aClosestVertex = iterator.GetIndex();
2687 int aClearance )
const
2690 bool collision =
false;
2695 const SEG currentSegment = *iterator;
2699 if( distance_squared <= clearance_squared )
2701 if( !aClosestVertex )
2707 clearance_squared = distance_squared;
2710 *aClosestVertex = iterator.GetIndex();
2720 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
2724 for(
int holeIdx = 0; holeIdx <
HoleCount( polygonIdx ); holeIdx++ )
2731 bool aUseBBoxCaches )
const
2737 if( aSubpolyIndex >= 0 )
2738 return containsSingle( aP, aSubpolyIndex, aAccuracy, aUseBBoxCaches );
2741 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
2743 if(
containsSingle( aP, polygonIdx, aAccuracy, aUseBBoxCaches ) )
2759 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
2776 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
2787 bool aUseBBoxCaches )
const
2793 for(
int holeIdx = 0; holeIdx <
HoleCount( aSubpolyIndex ); holeIdx++ )
2815 path.Move( aVector );
2819 tri->Move( aVector );
2831 path.Mirror( aRef, aFlipDirection );
2844 path.Rotate( aAngle, aCenter );
2860 c +=
path.PointCount();
2897 SEG::ecoord minDistance = (*iterator).SquaredDistance( aPoint );
2899 for( iterator++; iterator && minDistance > 0; iterator++ )
2901 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aPoint );
2903 if( currentDistance < minDistance )
2906 *aNearest = (*iterator).NearestPoint( aPoint );
2908 minDistance = currentDistance;
2924 *aNearest = ( aSegment.
A + aSegment.
B ) / 2;
2930 SEG::ecoord minDistance = (*iterator).SquaredDistance( aSegment );
2932 if( aNearest && minDistance == 0 )
2933 *aNearest = ( *iterator ).NearestPoint( aSegment );
2935 for( iterator++; iterator && minDistance > 0; iterator++ )
2937 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aSegment );
2939 if( currentDistance < minDistance )
2942 *aNearest = (*iterator).NearestPoint( aSegment );
2944 minDistance = currentDistance;
2949 return minDistance < 0 ? 0 : minDistance;
2956 wxASSERT_MSG( !aOutlineOnly, wxT(
"Warning: SHAPE_POLY_SET::SquaredDistance does not yet "
2957 "support aOutlineOnly==true" ) );
2964 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
2967 aNearest ? &nearest :
nullptr );
2969 if( currentDistance_sq < minDistance_sq )
2972 *aNearest = nearest;
2974 minDistance_sq = currentDistance_sq;
2978 return minDistance_sq;
2989 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
2992 aNearest ? &nearest :
nullptr );
2994 if( currentDistance_sq < minDistance_sq )
2997 *aNearest = nearest;
2999 minDistance_sq = currentDistance_sq;
3003 return minDistance_sq;
3016 return index.m_contour > 0;
3024 for(
unsigned int idx = 0; idx <
m_polys.size(); idx++ )
3035 for(
size_t idx = 0; idx <
m_polys.size(); idx++ )
3044 SHAPE::operator=( aOther );
3101 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* aHintData,
3123 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>& dest,
3124 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* hintData,
3127 bool triangulationValid =
false;
3131 if( hintData && hintData->size() != (
unsigned) polySet.
OutlineCount() )
3136 if( !dest.empty() && dest.back()->GetTriangleCount() == 0 )
3137 dest.erase( dest.end() - 1 );
3145 if( partitionLeaves > 1 )
3147 std::vector<SHAPE_LINE_CHAIN> partitions =
3150 if( partitions.size() > 1 )
3154 if( taskSubmitter && partitions.size() > 2 )
3156 size_t leafCount = partitions.size();
3158 struct WorkStealState
3160 std::unique_ptr<std::atomic<bool>[]> claimed;
3161 std::unique_ptr<std::atomic<bool>[]> done;
3162 std::unique_ptr<std::atomic<bool>[]> ok;
3164 explicit WorkStealState(
size_t n ) :
3165 claimed(
new std::atomic<bool>[n] ),
3166 done(
new std::atomic<bool>[n] ),
3167 ok(
new std::atomic<bool>[n] )
3169 for(
size_t i = 0; i < n; ++i )
3171 claimed[i].store(
false );
3172 done[i].store(
false );
3173 ok[i].store(
false );
3178 auto state = std::make_shared<WorkStealState>( leafCount );
3180 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>> results( leafCount );
3182 for(
size_t i = 0; i < leafCount; ++i )
3184 results[i] = std::make_unique<TRIANGULATED_POLYGON>( forOutline );
3187 for(
size_t i = 0; i < leafCount; ++i )
3189 auto* triPoly = results[i].get();
3190 auto* leaf = &partitions[i];
3192 taskSubmitter( [state, i, triPoly, leaf]()
3194 if( state->claimed[i].exchange(
true ) )
3199 state->done[i].store(
true, std::memory_order_release );
3203 for(
size_t i = 0; i < leafCount; ++i )
3205 if( state->claimed[i].exchange(
true ) )
3210 state->done[i].store(
true, std::memory_order_release );
3213 for(
size_t i = 0; i < leafCount; ++i )
3215 while( !state->done[i].load( std::memory_order_acquire ) )
3217 std::this_thread::yield();
3223 for(
size_t i = 0; i < leafCount; ++i )
3224 allOk = allOk && state->ok[i].load();
3228 for(
auto& r : results )
3230 if( r->GetTriangleCount() > 0 )
3231 dest.push_back( std::move( r ) );
3234 triangulationValid =
true;
3241 for(
auto it = partitions.rbegin(); it != partitions.rend(); ++it )
3250 dest.push_back( std::make_unique<TRIANGULATED_POLYGON>( forOutline ) );
3257 hintData ? hintData->at(
index ).get() :
nullptr ) )
3271 triangulationValid =
false;
3278 triangulationValid =
true;
3281 return triangulationValid;
3300 bool directOk =
true;
3302 for(
int ii = 0; ii < srcSet->
OutlineCount() && directOk; ++ii )
3307 if( poly.size() > 1 )
3320 for(
size_t jj = 1; jj < poly.size(); ++jj )
3360 double originalArea =
std::abs( poly.front().Area() );
3362 if( originalArea > 0.0 )
3364 double triArea = 0.0;
3369 double coverage = triArea / originalArea;
3371 if( coverage > 1.01 || coverage < 0.99 )
3384 bool splitOk =
true;
3386 for(
int jj = 0; jj < splitSet.
OutlineCount() && splitOk; ++jj )
3411 bool fallbackOk =
true;
3413 for(
int ii = 0; ii < srcSet->
OutlineCount() && fallbackOk; ++ii )
3418 for(
size_t jj = 1; jj < poly.size(); ++jj )
3475 hash.
add( outline.size() );
3479 hash.
add( lc.PointCount() );
3481 for(
int i = 0; i < lc.PointCount(); i++ )
3509 std::set<long long> ptHashes;
3513 for(
const VECTOR2I& pt : lc.CPoints() )
3515 const long long ptHash = (
long long) pt.x << 32 | pt.y;
3517 if( ptHashes.count( ptHash ) > 0 )
3520 ptHashes.insert( ptHash );
3539 n += t->GetTriangleCount();
3552 aSubshapes.push_back( &tri );
3563 if( aClearance != 0 )
3617 for(
int i = 0; i <
path.PointCount(); i++ )
3621 vec.
x = ( pt.
x - aCenter.
x ) * aScaleFactorX;
3622 vec.
y = ( pt.
y - aCenter.
y ) * aScaleFactorY;
3625 path.SetPoint( i, pt );
3639 Clipper2Lib::Clipper64 clipper;
3640 Clipper2Lib::PolyTree64 tree;
3641 Clipper2Lib::Paths64 paths;
3645 Clipper2Lib::Path64 lc;
3646 lc.reserve(
path.PointCount() );
3648 for(
int i = 0; i <
path.PointCount(); i++ )
3649 lc.emplace_back(
path.CPoint( i ).x,
path.CPoint( i ).y );
3651 paths.push_back( std::move( lc ) );
3654 clipper.AddSubject( paths );
3655 clipper.Execute( Clipper2Lib::ClipType::Union, aEvenOdd ? Clipper2Lib::FillRule::EvenOdd
3656 : Clipper2Lib::FillRule::NonZero, tree );
3658 std::vector<CLIPPER_Z_VALUE> zValues;
3659 std::vector<SHAPE_ARC> arcBuffer;
3679 int aSpacing,
int aLineLength )
const
3681 std::vector<SEG> hatchLines;
3694 if( iterator->x < min_x )
3695 min_x = iterator->x;
3697 if( iterator->x > max_x )
3698 max_x = iterator->x;
3700 if( iterator->y < min_y )
3701 min_y = iterator->y;
3703 if( iterator->y > max_y )
3704 max_y = iterator->y;
3707 auto sortEndsByDescendingX =
3710 return tst.x < ref.
x;
3713 for(
double slope : aSlopes )
3715 int64_t max_a, min_a;
3728 min_a = ( min_a / aSpacing ) * aSpacing;
3731 std::vector<VECTOR2I> pointbuffer;
3732 pointbuffer.reserve( 256 );
3734 for( int64_t a = min_a; a < max_a; a += aSpacing )
3736 pointbuffer.clear();
3741 const SEG seg = *iterator;
3747 if( pt.
x < min_x || pt.
x > max_x || pt.
y < min_y || pt.
y > max_y )
3758 if( pointbuffer.size() > 2 )
3759 sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX );
3762 for(
size_t ip = 0; ip + 1 < pointbuffer.size(); ip++ )
3764 const VECTOR2I& p1 = pointbuffer[ip];
3765 const VECTOR2I& p2 = pointbuffer[ip + 1];
3771 SEG candidate( p1, p2 );
3773 VECTOR2I mid( ( candidate.
A.
x + candidate.
B.
x ) / 2, ( candidate.
A.
y + candidate.
B.
y ) / 2 );
3778 int dx = p2.
x - p1.
x;
3782 if( aLineLength == -1 ||
std::abs( dx ) < 2 * aLineLength )
3784 hatchLines.emplace_back( candidate );
3788 double dy = p2.
y - p1.
y;
3798 int y1 =
KiROUND( p1.
y + dx * slope );
3799 int y2 =
KiROUND( p2.
y - dx * slope );
3801 hatchLines.emplace_back(
SEG( p1.
x, p1.
y, x1, y1 ) );
3803 hatchLines.emplace_back(
SEG( p2.
x, p2.
y, x2, y2 ) );
bool operator==(const wxAuiPaneInfo &aLhs, const wxAuiPaneInfo &aRhs)
bool operator!=(const BOM_FIELD &lhs, const BOM_FIELD &rhs)
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
constexpr coord_type GetLeft() const
constexpr coord_type GetRight() const
constexpr coord_type GetTop() const
constexpr coord_type GetBottom() const
Dynamic R*-tree with SoA node layout and stored insertion bounding boxes.
int Search(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], VISITOR &aVisitor) const
Search for items whose bounding boxes overlap the query rectangle.
void Insert(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], const DATATYPE &aData)
Insert an item with the given bounding box.
A streaming C++ equivalent for MurmurHash3_x64_128.
FORCE_INLINE void add(const std::string &input)
FORCE_INLINE HASH_128 digest()
bool TesselatePolygon(const SHAPE_POLY_SET::POLYGON &aPolygon, SHAPE_POLY_SET::TRIANGULATED_POLYGON *aHintData)
Triangulate a polygon with holes by bridging holes directly into the outer ring's VERTEX linked list,...
std::vector< SHAPE_LINE_CHAIN > partitionPolygonBalanced(const SHAPE_LINE_CHAIN &aPoly, size_t aTargetLeaves) const
size_t suggestedPartitionLeafCount(const SHAPE_LINE_CHAIN &aPoly) const
ecoord SquaredDistance(const SEG &aSeg) const
bool IntersectsLine(double aSlope, double aOffset, VECTOR2I &aIntersection) const
Check if this segment intersects a line defined by slope aSlope and offset aOffset.
VECTOR2I::extended_type ecoord
int Index() const
Return the index of this segment in its parent shape (applicable only to non-local segments).
static SEG::ecoord Square(int a)
bool Collide(const SEG &aSeg, int aClearance, int *aActual=nullptr) const
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
SHAPE_TYPE Type() const
Return the type of the shape.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
void GenerateBBoxCache() const
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void ReservePoints(size_t aSize)
Allocate a number of points all at once (for performance).
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.
double Area(bool aAbsolute=true) const
Return the area of this chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
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.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
bool IsEndContour() const
TRIANGULATED_POLYGON(int aSourceOutline)
std::deque< TRI > m_triangles
std::deque< VECTOR2I > m_vertices
void AddTriangle(int a, int b, int c)
TRIANGULATED_POLYGON & operator=(const TRIANGULATED_POLYGON &aOther)
std::mutex m_triangulationMutex
virtual bool HasIndexableSubshapes() const override
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
SHAPE_POLY_SET Chamfer(int aDistance)
Return a chamfered version of the polygon set.
void RemoveOutline(int aOutlineIdx)
Delete the aOutlineIdx-th outline of the set including its contours and holes.
void Scale(double aScaleFactorX, double aScaleFactorY, const VECTOR2I &aCenter)
bool CollideEdge(const VECTOR2I &aPoint, VERTEX_INDEX *aClosestVertex=nullptr, int aClearance=0) const
Check whether aPoint collides with any edge of any of the contours of the polygon.
virtual void GetIndexableSubshapes(std::vector< const SHAPE * > &aSubshapes) const override
void BooleanXor(const SHAPE_POLY_SET &b)
Perform boolean polyset exclusive or.
ITERATOR_TEMPLATE< VECTOR2I > ITERATOR
void fractureSingle(POLYGON &paths)
bool HasHoles() const
Return true if the polygon set has any holes.
CONST_ITERATOR CIterateWithHoles() const
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
ITERATOR IterateWithHoles()
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
bool IsTriangulationUpToDate() const
void importPaths(Clipper2Lib::Paths64 &paths, const std::vector< CLIPPER_Z_VALUE > &aZValueBuffer, const std::vector< SHAPE_ARC > &aArcBuffe)
void InsertVertex(int aGlobalIndex, const VECTOR2I &aNewVertex)
Adds a vertex in the globally indexed position aGlobalIndex.
std::atomic< bool > m_failedHashValid
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
int VertexCount(int aOutline=-1, int aHole=-1) const
Return the number of vertices in a given outline/hole.
void DeletePolygon(int aIdx)
Delete aIdx-th polygon from the set.
void cacheTriangulation(bool aSimplify, std::vector< std::unique_ptr< TRIANGULATED_POLYGON > > *aHintData, const TASK_SUBMITTER &aSubmitter={})
void splitCollinearOutlines()
double Area()
Return the area of this poly set.
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Accessor function to set the position of a specific point.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
void BuildPolysetFromOrientedPaths(const std::vector< SHAPE_LINE_CHAIN > &aPaths, bool aEvenOdd=false)
Build a SHAPE_POLY_SET from a bunch of outlines in provided in random order.
bool Parse(std::stringstream &aStream) override
int TotalVertices() const
Return total number of vertices stored in the set.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
int FullPointCount() const
Return the number of points in the shape poly set.
void GetArcs(std::vector< SHAPE_ARC > &aArcBuffer) const
Appends all the arcs in this polyset to aArcBuffer.
bool IsVertexInHole(int aGlobalIdx)
Check whether the aGlobalIndex-th vertex belongs to a hole.
int NormalizeAreaOutlines()
Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s).
void RemoveVertex(int aGlobalIndex)
Delete the aGlobalIndex-th vertex.
void unfractureSingle(POLYGON &path)
void inflateLine2(const SHAPE_LINE_CHAIN &aLine, int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy, bool aSimplify=false)
bool GetRelativeIndices(int aGlobalIdx, VERTEX_INDEX *aRelativeIndices) const
Convert a global vertex index —i.e., a number that globally identifies a vertex in a concatenated lis...
bool IsPolygonSelfIntersecting(int aPolygonIndex) const
Check whether the aPolygonIndex-th polygon in the set is self intersecting.
SHAPE_POLY_SET Subset(int aFirstPolygon, int aLastPolygon)
Return a subset of the polygons in this set, the ones between aFirstPolygon and aLastPolygon.
int RemoveNullSegments()
Look for null segments; ie, segments whose ends are exactly the same and deletes them.
HASH_128 checksum() const
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
virtual void CacheTriangulation(bool aSimplify=false, const TASK_SUBMITTER &aSubmitter={})
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
int AddPolygon(const POLYGON &apolygon)
Adds a polygon to the set.
const std::vector< SEG > GenerateHatchLines(const std::vector< double > &aSlopes, int aSpacing, int aLineLength) const
const std::string Format(bool aCplusPlus=true) const override
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
std::vector< std::unique_ptr< TRIANGULATED_POLYGON > > m_triangulatedPolys
ITERATOR_TEMPLATE< const VECTOR2I > CONST_ITERATOR
void inflate2(int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy, bool aSimplify=false)
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
SEG::ecoord SquaredDistance(const VECTOR2I &aPoint, bool aOutlineOnly, VECTOR2I *aNearest) const
Compute the minimum distance squared between aPoint and all the polygons in the set.
void RemoveContour(int aContourIdx, int aPolygonIdx=-1)
Delete the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
void Unfracture()
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
int ArcCount() const
Count the number of arc shapes present.
bool GetGlobalIndex(VERTEX_INDEX aRelativeIndices, int &aGlobalIdx) const
Compute the global index of a vertex from the relative indices of polygon, contour and vertex.
bool GetNeighbourIndexes(int aGlobalIndex, int *aPrevious, int *aNext) const
Return the global indexes of the previous and the next corner of the aGlobalIndex-th corner of a cont...
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void SimplifyOutlines(int aMaxError=0)
Simplifies the lines in the polyset.
void booleanOp(Clipper2Lib::ClipType aType, const SHAPE_POLY_SET &aOtherShape)
This is the engine to execute all polygon boolean transforms (AND, OR, ... and polygon simplification...
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
bool hasTouchingHoles(const POLYGON &aPoly) const
Return true if the polygon set has any holes that touch share a vertex.
bool PointOnEdge(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of some of the outlines or holes.
std::atomic< bool > m_hashValid
bool CollideVertex(const VECTOR2I &aPoint, VERTEX_INDEX *aClosestVertex=nullptr, int aClearance=0) const
Check whether aPoint collides with any vertex of any of the contours of the polygon.
void DeletePolygonAndTriangulationData(int aIdx, bool aUpdateHash=true)
Delete aIdx-th polygon and its triangulation data from the set.
unsigned int TriangulatedPolyCount() const
Return the number of triangulated polygons.
std::atomic< bool > m_triangulationValid
void UpdateTriangulationDataHash()
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int NewHole(int aOutline=-1)
Creates a new hole in a given outline.
SEG::ecoord SquaredDistanceToPolygon(VECTOR2I aPoint, int aIndex, VECTOR2I *aNearest) const
Compute the minimum distance between the aIndex-th polygon and aPoint.
CONST_SEGMENT_ITERATOR CIterateSegmentsWithHoles() const
Return an iterator object, for the aOutline-th outline in the set (with holes).
virtual size_t GetIndexableSubshapeCount() const override
SEG::ecoord SquaredDistanceToSeg(const SEG &aSegment, VECTOR2I *aNearest=nullptr) const
Compute the minimum distance squared between aSegment and all the polygons in the set.
void importPolyPath(const std::unique_ptr< Clipper2Lib::PolyPath64 > &aPolyPath, const std::vector< CLIPPER_Z_VALUE > &aZValueBuffer, const std::vector< SHAPE_ARC > &aArcBuffer)
void RebuildHolesFromContours()
Extract all contours from this polygon set, then recreate polygons with holes.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the line points about y or x (or both)
void OffsetLineChain(const SHAPE_LINE_CHAIN &aLine, int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify)
Perform offsetting of a line chain.
void BuildBBoxCaches() const
Construct BBoxCaches for Contains(), below.
std::vector< POLYGON > m_polys
void splitSelfTouchingOutlines()
Split outline segments at vertices that lie on them (self-touching polygons).
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
POLYGON FilletPolygon(unsigned int aRadius, int aErrorMax, int aIndex)
Return a filleted version of the aIndex-th polygon.
bool containsSingle(const VECTOR2I &aP, int aSubpolyIndex, int aAccuracy, bool aUseBBoxCaches=false) const
Check whether the point aP is inside the aSubpolyIndex-th polygon of the polyset.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
void InflateWithLinkedHoles(int aFactor, CORNER_STRATEGY aCornerStrategy, int aMaxError)
Perform outline inflation/deflation, using round corners.
POLYGON chamferFilletPolygon(CORNER_MODE aMode, unsigned int aDistance, int aIndex, int aErrorMax)
Return the chamfered or filleted version of the aIndex-th polygon in the set, depending on the aMode ...
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax)
Return a filleted version of the polygon set.
void Move(const VECTOR2I &aVector) override
bool HasTouchingHoles() const
Return true if the polygon set has any holes that share a vertex.
SHAPE * Clone() const override
Return a dynamically allocated copy of the shape.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
SHAPE_POLY_SET & operator=(const SHAPE_POLY_SET &aOther)
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
SHAPE_POLY_SET CloneDropTriangulation() const
bool isExteriorWaist(const SEG &aSegA, const SEG &aSegB) const
Check if two line segments are collinear and overlap.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const POLYGON & CPolygon(int aIndex) const
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
POLYGON ChamferPolygon(unsigned int aDistance, int aIndex)
Return a chamfered version of the aIndex-th polygon.
std::function< void(std::function< void()>)> TASK_SUBMITTER
Callback that submits a unit of work for asynchronous execution.
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
const BOX2I BBoxFromCaches() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
void importTree(Clipper2Lib::PolyTree64 &tree, const std::vector< CLIPPER_Z_VALUE > &aZValueBuffer, const std::vector< SHAPE_ARC > &aArcBuffe)
SEGMENT_ITERATOR_TEMPLATE< const SEG > CONST_SEGMENT_ITERATOR
bool IsSelfIntersecting() const
Check whether any of the polygons in the set is self intersecting.
const SEG & GetSeg() const
int GetWidth() const override
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
VECTOR2I::extended_type ecoord
SHAPE(SHAPE_TYPE aType)
Create an empty shape of type aType.
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).
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
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
@ ROUND_ACUTE_CORNERS
Acute angles are rounded.
@ CHAMFER_ACUTE_CORNERS
Acute angles are chamfered.
@ CHAMFER_ALL_CORNERS
All angles are chamfered.
@ ROUND_ALL_CORNERS
All angles are rounded.
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
static bool empty(const wxTextEntryBase *aCtrl)
static constexpr EDA_ANGLE FULL_CIRCLE
a few functions useful in geometry calculations.
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
static constexpr void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
static PGM_BASE * process
#define TRIANGULATE_TRACE
#define TRIANGULATESIMPLIFICATIONLEVEL
@ SH_POLY_SET
set of polygons (with holes, etc.)
static void fractureSingleCacheFriendly(SHAPE_POLY_SET::POLYGON &paths)
static void fractureSingleSlow(SHAPE_POLY_SET::POLYGON &paths)
static FractureEdge * processHole(FractureEdgeSet &edges, FractureEdge::Index provokingIndex, FractureEdge::Index edgeIndex, FractureEdge::Index bridgeIndex)
std::vector< FractureEdge > FractureEdgeSet
std::vector< FractureEdgeSlow * > FractureEdgeSetSlow
#define ENABLECACHEFRIENDLYFRACTURE
static int processEdge(FractureEdgeSetSlow &edges, FractureEdgeSlow *edge)
Holds information on each point of a SHAPE_LINE_CHAIN that is retrievable after an operation with Cli...
FractureEdgeSlow(int y=0)
FractureEdgeSlow * m_next
bool matches(int y) const
FractureEdgeSlow(bool connected, const VECTOR2I &p1, const VECTOR2I &p2)
FractureEdge(const VECTOR2I &p1, const VECTOR2I &p2, Index next)
bool matches(int y) const
A storage class for 128-bit hash value.
virtual const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
TRIANGULATED_POLYGON * parent
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D