37#include <unordered_set>
43#include <clipper2/clipper.h>
63#if defined( __MINGW32__ )
64 #define TRIANGULATESIMPLIFICATIONLEVEL 50
65 #define ENABLECACHEFRIENDLYFRACTURE true
67 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
68 #define ENABLECACHEFRIENDLYFRACTURE ADVANCED_CFG::GetCfg().m_EnableCacheFriendlyFracture
109 m_triangulatedPolys.reserve( aOther.TriangulatedPolyCount() );
111 for( unsigned i = 0; i < aOther.TriangulatedPolyCount(); i++ )
113 const TRIANGULATED_POLYGON* poly = aOther.TriangulatedPolygon( i );
114 m_triangulatedPolys.push_back( std::make_unique<TRIANGULATED_POLYGON>( *poly ) );
125 m_triangulationValid =
false;
128 m_failedHash = aOther.m_failedHash;
129 m_failedHashValid.store( aOther.m_failedHashValid.load() );
164 unsigned int contourIdx = 0;
167 int currentGlobalIdx = 0;
169 for( polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
173 for( contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
176 int totalPoints = currentContour.
PointCount();
178 for( vertexIdx = 0; vertexIdx < totalPoints; vertexIdx++ )
181 if( currentGlobalIdx == aGlobalIdx )
183 aRelativeIndices->
m_polygon = polygonIdx;
184 aRelativeIndices->
m_contour = contourIdx;
185 aRelativeIndices->
m_vertex = vertexIdx;
201 int& aGlobalIdx )
const
203 int selectedVertex = aRelativeIndices.
m_vertex;
204 unsigned int selectedContour = aRelativeIndices.
m_contour;
205 unsigned int selectedPolygon = aRelativeIndices.
m_polygon;
208 if( selectedPolygon <
m_polys.size() && selectedContour <
m_polys[selectedPolygon].size()
209 && selectedVertex <
m_polys[selectedPolygon][selectedContour].PointCount() )
215 for(
unsigned int polygonIdx = 0; polygonIdx < selectedPolygon; polygonIdx++ )
217 currentPolygon =
Polygon( polygonIdx );
219 for(
unsigned int contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
220 aGlobalIdx += currentPolygon[contourIdx].PointCount();
223 currentPolygon =
Polygon( selectedPolygon );
225 for(
unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx++ )
226 aGlobalIdx += currentPolygon[contourIdx].PointCount();
228 aGlobalIdx += selectedVertex;
245 poly.push_back( empty_path );
246 m_polys.push_back( std::move( poly ) );
262 m_polys[aOutline].push_back( empty_path );
264 return m_polys.back().size() - 2;
282 assert( aOutline < (
int)
m_polys.size() );
283 assert( idx < (
int)
m_polys[aOutline].size() );
285 m_polys[aOutline][idx].Append( x, y, aAllowDuplication );
287 return m_polys[aOutline][idx].PointCount();
292 std::optional<int> aMaxError )
306 assert( aOutline < (
int)
m_polys.size() );
307 assert( idx < (
int)
m_polys[aOutline].size() );
309 if( aMaxError.has_value() )
310 m_polys[aOutline][idx].Append( aArc, aMaxError.value() );
312 m_polys[aOutline][idx].Append( aArc );
314 return m_polys[aOutline][idx].PointCount();
322 if( aGlobalIndex < 0 )
335 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
355 if( aOutline >= (
int)
m_polys.size() )
358 if( idx >= (
int)
m_polys[aOutline].size() )
361 return m_polys[aOutline][idx].PointCount();
376 for(
int idx = 0; idx <=
HoleCount( ii ); idx++ )
378 full_count +=
m_polys[ii][idx].PointCount();
388 assert( aFirstPolygon >= 0 && aLastPolygon <=
OutlineCount() );
411 assert( aOutline < (
int)
m_polys.size() );
412 assert( idx < (
int)
m_polys[aOutline].size() );
414 return m_polys[aOutline][idx].CPoint( aIndex );
424 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
449 if(
index.m_vertex == 0 )
451 index.m_vertex = lastpoint - 1;
454 else if(
index.m_vertex == lastpoint )
472 *aPrevious = previous;
488 std::vector<SEG> segments;
492 segments.emplace_back( *it );
494 std::sort( segments.begin(), segments.end(), [](
const SEG& a,
const SEG& b )
496 int min_a_x = std::min( a.A.x, a.B.x );
497 int min_b_x = std::min( b.A.x, b.B.x );
499 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 ) );
502 for(
auto it = segments.begin(); it != segments.end(); ++it )
504 SEG& firstSegment = *it;
507 auto innerIterator = it;
508 int max_x = std::max( firstSegment.
A.
x, firstSegment.
B.
x );
509 int max_y = std::max( firstSegment.
A.
y, firstSegment.
B.
y );
512 for( innerIterator++; innerIterator != segments.end(); innerIterator++ )
514 SEG& secondSegment = *innerIterator;
515 int min_x = std::min( secondSegment.
A.
x, secondSegment.
B.
x );
516 int min_y = std::min( secondSegment.
A.
y, secondSegment.
B.
y );
520 if( max_x < min_x || ( max_x == min_x && max_y < min_y ) )
524 bool adjacent = ( index_diff == 1) || (index_diff == ((
int)segments.size() - 1) );
527 if( !adjacent && firstSegment.
Collide( secondSegment, 0 ) )
538 for(
unsigned int polygon = 0; polygon <
m_polys.size(); polygon++ )
552 poly.push_back( aOutline );
557 wxCHECK2_MSG( aOutline.
IsClosed(), poly.back().SetClosed(
true ),
558 "Warning: non-closed outline added to SHAPE_POLY_SET" );
560 m_polys.push_back( std::move( poly ) );
562 return (
int)
m_polys.size() - 1;
571 aOutline += (int)
m_polys.size();
573 assert( aOutline < (
int)
m_polys.size() );
577 assert( poly.size() );
579 poly.push_back( aHole );
581 return (
int) poly.size() - 2;
601 for(
int j = 0; j <
HoleCount( i ); j++ )
615 for(
size_t i = 0; i < poly.size(); i++ )
627 for(
size_t i = 0; i < poly.size(); i++ )
630 aArcBuffer.push_back( arc );
640 for(
size_t i = 0; i < poly.size(); i++ )
648 std::vector<SHAPE_LINE_CHAIN> contours;
651 contours.insert( contours.end(), poly.begin(), poly.end() );
653 std::map<int, std::set<int>> parentToChildren;
654 std::map<int, std::set<int>> childToParents;
657 contour.GenerateBBoxCache();
659 for(
size_t i = 0; i < contours.size(); i++ )
663 for(
size_t j = 0; j < contours.size(); j++ )
673 parentToChildren[i].emplace( j );
674 childToParents[j].emplace( i );
679 std::set<int> topLevelParents;
681 for(
size_t i = 0; i < contours.size(); i++ )
683 if( childToParents[i].size() == 0 )
685 topLevelParents.emplace( i );
691 std::function<void(
int,
int, std::vector<int> )>
process;
694 [&](
int myId,
int parentOutlineId,
const std::vector<int>&
path )
696 std::set<int> relParents = childToParents[myId];
698 for(
int pathId :
path )
700 int erased = relParents.erase( pathId );
701 wxASSERT( erased > 0 );
704 wxASSERT( relParents.size() == 0 );
708 bool isOutline =
path.size() % 2 == 0;
712 int outlineId =
result.AddOutline( contours[myId] );
713 myOutline = outlineId;
717 wxASSERT( parentOutlineId != -1 );
718 result.AddHole( contours[myId], parentOutlineId );
721 auto it = parentToChildren.find( myId );
722 if( it != parentToChildren.end() )
724 std::vector<int> thisPath =
path;
725 thisPath.emplace_back( myId );
727 std::set<int> thisPathSet;
728 thisPathSet.insert( thisPath.begin(), thisPath.end() );
730 for(
int childId : it->second )
732 const std::set<int>& childPathSet = childToParents[childId];
734 if( thisPathSet != childPathSet )
737 process( childId, myOutline, thisPath );
742 for(
int topParentId : topLevelParents )
744 std::vector<int>
path;
748 *
this = std::move(
result );
764 wxFAIL_MSG( wxT(
"Boolean ops on curved polygons are not supported. You should call "
765 "ClearArcs() before carrying out the boolean operation." ) );
768 Clipper2Lib::Clipper64 c;
770 std::vector<CLIPPER_Z_VALUE> zValues;
771 std::vector<SHAPE_ARC> arcBuffer;
772 std::map<VECTOR2I, CLIPPER_Z_VALUE> newIntersectPoints;
774 Clipper2Lib::Paths64 paths;
775 Clipper2Lib::Paths64 clips;
779 for(
size_t i = 0; i < poly.size(); i++ )
781 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
787 for(
size_t i = 0; i < poly.size(); i++ )
789 clips.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
793 c.AddSubject( paths );
796 Clipper2Lib::PolyTree64 solution;
798 Clipper2Lib::ZCallback64 callback =
799 [&](
const Clipper2Lib::Point64 & e1bot,
const Clipper2Lib::Point64 & e1top,
800 const Clipper2Lib::Point64 & e2bot,
const Clipper2Lib::Point64 & e2top,
801 Clipper2Lib::Point64 & pt )
804 [&](
const ssize_t& aZvalue,
const ssize_t& aCompareVal = -1 ) -> ssize_t
808 retval = zValues.at( aZvalue ).m_SecondArcIdx;
810 if( retval == -1 || ( aCompareVal > 0 && retval != aCompareVal ) )
811 retval = zValues.at( aZvalue ).m_FirstArcIdx;
817 [&](
const ssize_t& aBottomZ,
const ssize_t aTopZ ) -> ssize_t
819 ssize_t retval = arcIndex( aBottomZ );
823 if( retval != arcIndex( aTopZ, retval ) )
830 ssize_t e1ArcSegmentIndex = arcSegment( e1bot.z, e1top.z );
831 ssize_t e2ArcSegmentIndex = arcSegment( e2bot.z, e2top.z );
835 if( e1ArcSegmentIndex != -1 )
846 size_t z_value_ptr = zValues.size();
847 zValues.push_back( newZval );
851 newIntersectPoints.insert( {
VECTOR2I( pt.x, pt.y ), newZval } );
857 c.SetZCallback( std::move( callback ) );
859 c.Execute( aType, Clipper2Lib::FillRule::NonZero, solution );
868 booleanOp( Clipper2Lib::ClipType::Union, b );
874 booleanOp( Clipper2Lib::ClipType::Difference, b );
880 booleanOp( Clipper2Lib::ClipType::Intersection, b );
886 booleanOp( Clipper2Lib::ClipType::Xor, b );
892 booleanOp( Clipper2Lib::ClipType::Union, a, b );
898 booleanOp( Clipper2Lib::ClipType::Difference, a, b );
904 booleanOp( Clipper2Lib::ClipType::Intersection, a, b );
910 booleanOp( Clipper2Lib::ClipType::Xor, a, b );
918 Inflate( aFactor, aCornerStrategy, aMaxError );
926 using namespace Clipper2Lib;
930 #define SEG_CNT_MAX 64
931 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
938 JoinType joinType = JoinType::Round;
939 double miterLimit = 2.0;
941 switch( aCornerStrategy )
944 joinType = JoinType::Miter;
949 joinType = JoinType::Miter;
953 joinType = JoinType::Miter;
957 joinType = JoinType::Square;
961 joinType = JoinType::Round;
965 std::vector<CLIPPER_Z_VALUE> zValues;
966 std::vector<SHAPE_ARC> arcBuffer;
972 for(
size_t i = 0; i < poly.size(); i++ )
973 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
975 c.AddPaths( paths, joinType, EndType::Polygon );
982 if( aCircleSegCount < 6 )
987 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
989 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
992 arc_tolerance_factor[aCircleSegCount] = coeff;
996 coeff = arc_tolerance_factor[aCircleSegCount];
999 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1000 c.MiterLimit( miterLimit );
1007 c.Execute( aAmount, paths );
1009 Clipper2Lib::SimplifyPaths( paths,
std::abs( aAmount ) * coeff,
true );
1012 c2.PreserveCollinear(
false );
1013 c2.ReverseSolution(
false );
1014 c2.AddSubject( paths );
1015 c2.Execute(ClipType::Union, FillRule::Positive, tree);
1019 c.Execute( aAmount, tree );
1030 using namespace Clipper2Lib;
1034 #define SEG_CNT_MAX 64
1035 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
1042 JoinType joinType = JoinType::Round;
1043 double miterLimit = 2.0;
1045 switch( aCornerStrategy )
1048 joinType = JoinType::Miter;
1053 joinType = JoinType::Miter;
1057 joinType = JoinType::Miter;
1061 joinType = JoinType::Square;
1065 joinType = JoinType::Round;
1069 std::vector<CLIPPER_Z_VALUE> zValues;
1070 std::vector<SHAPE_ARC> arcBuffer;
1073 c.AddPath(
path, joinType, EndType::Butt );
1079 if( aCircleSegCount < 6 )
1080 aCircleSegCount = 6;
1084 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
1086 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
1089 arc_tolerance_factor[aCircleSegCount] = coeff;
1093 coeff = arc_tolerance_factor[aCircleSegCount];
1096 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1097 c.MiterLimit( miterLimit );
1104 c.Execute( aAmount, paths2 );
1106 Clipper2Lib::SimplifyPaths( paths2,
std::abs( aAmount ) * coeff,
false );
1109 c2.PreserveCollinear(
false );
1110 c2.ReverseSolution(
false );
1111 c2.AddSubject( paths2 );
1112 c2.Execute( ClipType::Union, FillRule::Positive, tree );
1116 c.Execute( aAmount, tree );
1129 inflate2( aAmount, segCount, aCornerStrategy, aSimplify );
1138 inflateLine2( aLine, aAmount, segCount, aCornerStrategy, aSimplify );
1143 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1144 const std::vector<SHAPE_ARC>& aArcBuffer )
1146 if( !aPolyPath->IsHole() )
1149 paths.reserve( aPolyPath->Count() + 1 );
1150 paths.emplace_back( aPolyPath->Polygon(), aZValueBuffer, aArcBuffer );
1152 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& child : *aPolyPath )
1154 paths.emplace_back( child->Polygon(), aZValueBuffer, aArcBuffer );
1156 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& grandchild : *child )
1160 m_polys.emplace_back( std::move( paths ) );
1166 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1167 const std::vector<SHAPE_ARC>& aArcBuffer )
1171 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& n : tree )
1177 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1178 const std::vector<SHAPE_ARC>& aArcBuffer )
1183 for(
const Clipper2Lib::Path64& n : aPath )
1185 if( Clipper2Lib::Area( n ) > 0 )
1194 wxCHECK2_MSG( !
path.empty(),
continue, wxT(
"Cannot add a hole before an outline" ) );
1197 path.emplace_back( n, aZValueBuffer, aArcBuffer );
1236 int x = edge.
m_p1.
x;
1237 int y = edge.
m_p1.
y;
1238 int min_dist = std::numeric_limits<int>::max();
1257 x_intersect = std::max( e.
m_p1.
x, e.
m_p2.
x );
1265 int dist = ( x - x_intersect );
1267 if( dist >= 0 && dist < min_dist )
1270 x_nearest = x_intersect;
1283 edges[hole2outline_index] =
1286 edges[split_index] =
1291 e_nearest->
m_next = outline2hole_index;
1294 for( ; last->
m_next != edgeIndex; last = &edges[last->
m_next] )
1296 last->
m_next = hole2outline_index;
1306 bool outline =
true;
1308 if( paths.size() == 1 )
1311 size_t total_point_count = 0;
1315 total_point_count +=
path.PointCount();
1318 if( total_point_count > (
size_t) std::numeric_limits<FractureEdge::Index>::max() )
1320 wxLogWarning( wxT(
"Polygon has more points than int limit" ) );
1327 edges.reserve( total_point_count + paths.size() * 3 );
1333 int path_or_provoking_index;
1338 std::vector<PathInfo> sorted_paths;
1339 const int paths_count =
static_cast<int>( paths.size() );
1340 sorted_paths.reserve( paths_count );
1342 for(
int path_index = 0; path_index < paths_count; path_index++ )
1345 const std::vector<VECTOR2I>& points =
path.CPoints();
1346 const int point_count =
static_cast<int>( points.size() );
1347 int x_min = std::numeric_limits<int>::max();
1348 int y_min = std::numeric_limits<int>::max();
1351 for(
int point_index = 0; point_index < point_count; point_index++ )
1353 const VECTOR2I& point = points[point_index];
1354 if( point.
x < x_min )
1357 leftmost = point_index;
1359 if( point.
y < y_min )
1363 sorted_paths.emplace_back( PathInfo{ path_index, leftmost, x_min, y_min } );
1366 std::sort( sorted_paths.begin() + 1, sorted_paths.end(),
1367 [](
const PathInfo& a,
const PathInfo& b )
1370 return a.y_or_bridge < b.y_or_bridge;
1376 for( PathInfo& path_info : sorted_paths )
1379 const std::vector<VECTOR2I>& points =
path.CPoints();
1380 const size_t point_count = points.size();
1385 for(
size_t i = 0; i < point_count - 1; i++ )
1387 edges.emplace_back( points[i], points[i + 1], edge_index + 1 );
1392 edges.emplace_back( points[point_count - 1], points[0], provoking_edge );
1399 path_info.path_or_provoking_index = provoking_edge;
1400 path_info.y_or_bridge = edge_index;
1404 edges.resize( edge_index );
1410 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); it++ )
1412 auto edge =
processHole( edges, it->path_or_provoking_index,
1413 it->path_or_provoking_index + it->leftmost, it->y_or_bridge );
1418 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
1466 int x = edge->
m_p1.
x;
1467 int y = edge->
m_p1.
y;
1468 int min_dist = std::numeric_limits<int>::max();
1475 if( !e->matches( y ) )
1480 if( e->m_p1.y == e->m_p2.y )
1482 x_intersect = std::max( e->m_p1.x, e->m_p2.x );
1486 x_intersect = e->m_p1.x
1487 +
rescale( e->m_p2.x - e->m_p1.x, y - e->m_p1.y, e->m_p2.y - e->m_p1.y );
1490 int dist = ( x - x_intersect );
1492 if( dist >= 0 && dist < min_dist && e->m_connected )
1495 x_nearest = x_intersect;
1511 edges.push_back( split_2 );
1512 edges.push_back( lead1 );
1513 edges.push_back( lead2 );
1518 e_nearest->
m_next = lead1;
1523 for( last = edge; last->
m_next != edge; last = last->
m_next )
1549 if( paths.size() == 1 )
1552 int num_unconnected = 0;
1556 const std::vector<VECTOR2I>& points =
path.CPoints();
1557 int pointCount = points.size();
1561 int x_min = std::numeric_limits<int>::max();
1563 for(
int i = 0; i < pointCount; i++ )
1565 if( points[i].x < x_min )
1566 x_min = points[i].x;
1571 points[i + 1 == pointCount ? 0 : i + 1] );
1582 if( i == pointCount - 1 )
1586 edges.push_back( fe );
1590 if( fe->
m_p1.
x == x_min )
1591 border_edges.push_back( fe );
1602 while( num_unconnected > 0 )
1604 int x_min = std::numeric_limits<int>::max();
1605 auto it = border_edges.begin();
1610 for( ; it != border_edges.end(); ++it )
1613 int xt = border_edge->
m_p1.
x;
1615 if( ( xt <= x_min ) && !border_edge->
m_connected )
1618 smallestX = border_edge;
1622 int num_processed =
processEdge( edges, smallestX );
1625 if( !num_processed )
1627 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
1635 num_unconnected -= num_processed;
1653 paths.push_back( std::move( newPath ) );
1677 assert( aPoly.size() == 1 );
1683 bool m_duplicate =
false;
1690 bool compareSegs(
const SEG& s1,
const SEG& s2 )
const
1692 return (s1.
A == s2.
B && s1.
B == s2.
A);
1697 return compareSegs( m_poly->
CSegment( m_index ),
1698 aOther.m_poly->CSegment( aOther.m_index ) );
1703 return !compareSegs( m_poly->
CSegment( m_index ),
1704 aOther.m_poly->CSegment( aOther.m_index ) );
1709 std::size_t operator()(
const EDGE& aEdge )
const
1711 const SEG& a = aEdge.m_poly->CSegment( aEdge.m_index );
1712 std::size_t
seed = 0xa82de1c0;
1719 struct EDGE_LIST_ENTRY
1722 EDGE_LIST_ENTRY*
next;
1725 std::unordered_set<EDGE, EDGE::HASH> uniqueEdges;
1730 auto edgeList = std::make_unique<EDGE_LIST_ENTRY[]>( lc.
SegmentCount() );
1734 edgeList[i].index = i;
1735 edgeList[i].next = &edgeList[ (i != lc.
SegmentCount() - 1) ? i + 1 : 0 ];
1738 std::unordered_set<EDGE_LIST_ENTRY*> queue;
1743 uniqueEdges.insert( e );
1749 auto it = uniqueEdges.find( e );
1751 if( it != uniqueEdges.end() && it->m_index != i )
1753 int e1 = it->m_index;
1757 std::swap( e1, e2 );
1759 int e1_prev = e1 - 1;
1764 int e2_prev = e2 - 1;
1769 int e1_next = e1 + 1;
1774 int e2_next = e2 + 1;
1779 edgeList[e1_prev].next = &edgeList[ e2_next ];
1780 edgeList[e2_prev].next = &edgeList[ e1_next ];
1781 edgeList[i].next =
nullptr;
1782 edgeList[it->m_index].next =
nullptr;
1788 if( edgeList[i].
next )
1789 queue.insert( &edgeList[i] );
1792 auto edgeBuf = std::make_unique<EDGE_LIST_ENTRY* []>( lc.
SegmentCount() );
1798 double max_poly = 0.0;
1800 while( queue.size() )
1802 EDGE_LIST_ENTRY* e_first = *queue.begin();
1803 EDGE_LIST_ENTRY* e = e_first;
1810 }
while( e && e != e_first );
1814 for(
int i = 0; i < cnt; i++ )
1818 queue.erase( edgeBuf[i] );
1823 double area = std::fabs( outl.
Area() );
1825 if( area > max_poly )
1831 result.push_back( outl );
1838 aPoly = std::move(
result );
1848 if( paths.size() > 1 )
1872 std::array<VECTOR2I,4> pts = { aSegA.
A, aSegA.
B, aSegB.
A, aSegB.
B };
1874 std::sort( pts.begin(), pts.end(), [axis](
const VECTOR2I& p,
const VECTOR2I& q )
1877 return p.x < q.x || ( p.x == q.x && p.y < q.y );
1879 return p.y < q.y || ( p.y == q.y && p.x < q.x );
1900 wxLogTrace( wxT(
"collinear" ), wxT(
"Found exterior waist between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)" ),
1901 aSegA.
A.
x, aSegA.
A.
y, aSegA.
B.
x, aSegA.
B.
y,
1902 aSegB.
A.
x, aSegB.
A.
y, aSegB.
B.
x, aSegB.
B.
y );
1913 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
1915 bool changed =
true;
1926 for( intptr_t i = 0; i < count; ++i )
1930 intptr_t min[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
1931 intptr_t max[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
1932 rtree.
Insert( min, max, i );
1939 for( intptr_t i = 0; i < count && !found; ++i )
1944 intptr_t min[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
1945 intptr_t max[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
1948 [&](
const intptr_t& j ) ->
bool
1950 if( j == i || j == ( ( i + 1 ) % count ) || j == ( ( i + count - 1 ) % count ) )
1955 SEG other( oa, ob );
1959 if( oa == a && ob == b )
1962 if( oa == b && ob == a )
1976 rtree.
Search( min, max, visitor );
1983 int a1 = ( segA + 1 ) % outline.
PointCount();
1985 int b1 = ( segB + 1 ) % outline.
PointCount();
2011 m_polys[polyIdx][0] = std::move( lc1 );
2014 np.push_back( std::move( lc2 ) );
2015 m_polys.push_back( std::move( np ) );
2025 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
2027 bool changed =
true;
2039 int insertSegIdx = -1;
2040 int insertVertIdx = -1;
2043 constexpr int RTREE_THRESHOLD = 32;
2045 if( count < RTREE_THRESHOLD )
2047 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2050 const int prevSeg = ( vertIdx + count - 1 ) % count;
2052 for(
int segIdx = 0; segIdx < count; ++segIdx )
2055 if( segIdx == prevSeg || segIdx == vertIdx )
2067 insertSegIdx = segIdx;
2068 insertVertIdx = vertIdx;
2078 for(
int i = 0; i < count; ++i )
2082 int bmin[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
2083 int bmax[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
2084 rtree.
Insert( bmin, bmax, i );
2087 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2090 const int prevSeg = ( vertIdx + count - 1 ) % count;
2091 int bmin[2] = { pt.
x, pt.
y };
2092 int bmax[2] = { pt.
x, pt.
y };
2095 [&](
const intptr_t& segIdx ) ->
bool
2097 if( segIdx == prevSeg || segIdx == vertIdx )
2109 insertSegIdx = segIdx;
2110 insertVertIdx = vertIdx;
2117 rtree.
Search( bmin, bmax, pinchVisitor );
2121 if( insertSegIdx < 0 )
2128 const int splitStart1 = ( insertSegIdx + 1 ) % count;
2133 if( insertVertIdx >= splitStart1 )
2134 size1 = insertVertIdx - splitStart1 + 1;
2136 size1 = count - splitStart1 + insertVertIdx + 1;
2138 if( insertSegIdx >= insertVertIdx )
2139 size2 = insertSegIdx - insertVertIdx + 1;
2141 size2 = count - insertVertIdx + insertSegIdx + 1;
2143 if( size1 < 3 || size2 < 3 )
2151 int idx = splitStart1;
2153 for(
int i = 0; i < size1; ++i )
2156 idx = ( idx + 1 ) % count;
2161 idx = insertVertIdx;
2163 for(
int i = 0; i < size2; ++i )
2166 idx = ( idx + 1 ) % count;
2171 m_polys[polyIdx][0] = std::move( poly1 );
2174 np.push_back( std::move( poly2 ) );
2175 m_polys.push_back( std::move( np ) );
2199 path.Simplify( aTolerance );
2217 while( outline.size() > 1 )
2242 std::stringstream ss;
2244 ss <<
"SHAPE_LINE_CHAIN poly; \n";
2246 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2248 for(
unsigned j = 0; j <
m_polys[i].size(); j++ )
2251 ss <<
"{ auto tmp = " <<
m_polys[i][j].Format() <<
";\n";
2257 ss <<
" poly.AddOutline(tmp); } \n";
2261 ss <<
" poly.AddHole(tmp); } \n";
2277 if( tmp !=
"polyset" )
2282 int n_polys = atoi( tmp.c_str() );
2287 for(
int i = 0; i < n_polys; i++ )
2297 int n_outlines = atoi( tmp.c_str() );
2299 if( n_outlines < 0 )
2302 for(
int j = 0; j < n_outlines; j++ )
2309 int n_vertices = atoi( tmp.c_str() );
2311 for(
int v = 0; v < n_vertices; v++ )
2315 aStream >> tmp; p.
x = atoi( tmp.c_str() );
2316 aStream >> tmp; p.
y = atoi( tmp.c_str() );
2320 paths.push_back( std::move( outline ) );
2323 m_polys.push_back( std::move( paths ) );
2334 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2351 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2354 bb = *
m_polys[i][0].GetCachedBBox();
2371 if( lineChain.PointOnEdge( aP, aAccuracy ) )
2386 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2389 *aLocation = nearest;
2392 *aActual = sqrt( dist_sq );
2410 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2413 *aLocation = nearest;
2416 *aActual = sqrt( dist_sq );
2433 int extra = segment->
GetWidth() / 2;
2435 if(
Collide( segment->
GetSeg(), aClearance + extra, aActual, aLocation ) )
2438 *aActual = std::max( 0, *aActual - extra );
2449 int extra =
circle->GetRadius();
2451 if(
Collide(
circle->GetCenter(), aClearance + extra, aActual, aLocation ) )
2454 *aActual = std::max( 0, *aActual - extra );
2471 if( aActual || aLocation )
2476 if( aShape->
Collide( &tri, aClearance, &triActual, &triLocation ) )
2487 if( aShape->
Collide( &tri, aClearance ) )
2496 *aActual = std::max( 0,
actual );
2519 if( aPolygonIdx < 0 )
2520 aPolygonIdx +=
m_polys.size();
2522 m_polys[aPolygonIdx].erase(
m_polys[aPolygonIdx].begin() + aContourIdx );
2542 std::vector<VERTEX_INDEX> indices_to_remove;
2547 segmentStart = *iterator;
2553 segmentEnd = contourStart;
2561 contourStart = *iterator;
2569 wxCHECK_MSG( iterator, removed, wxT(
"Invalid polygon. Reached end without noticing. Please report this error" ) );
2571 segmentEnd = *iterator;
2575 if( segmentStart == segmentEnd )
2577 indices_to_remove.push_back( indexStart );
2584 for(
auto it = indices_to_remove.rbegin(); it != indices_to_remove.rend(); ++it )
2607 if( triangleSet->GetSourceOutlineIndex() == aIdx )
2609 else if( triangleSet->GetSourceOutlineIndex() > aIdx )
2610 triangleSet->SetSourceOutlineIndex( triangleSet->GetSourceOutlineIndex() - 1 );
2637 Append( aP.
x, aP.
y, aOutline, aHole );
2643 int aClearance )
const
2646 bool collision =
false;
2656 delta = *iterator - aPoint;
2659 distance_squared =
delta.SquaredEuclideanNorm();
2662 if( distance_squared <= clearance_squared )
2664 if( !aClosestVertex )
2670 clearance_squared = distance_squared;
2673 *aClosestVertex = iterator.GetIndex();
2683 int aClearance )
const
2686 bool collision =
false;
2691 const SEG currentSegment = *iterator;
2695 if( distance_squared <= clearance_squared )
2697 if( !aClosestVertex )
2703 clearance_squared = distance_squared;
2706 *aClosestVertex = iterator.GetIndex();
2716 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
2720 for(
int holeIdx = 0; holeIdx <
HoleCount( polygonIdx ); holeIdx++ )
2727 bool aUseBBoxCaches )
const
2733 if( aSubpolyIndex >= 0 )
2734 return containsSingle( aP, aSubpolyIndex, aAccuracy, aUseBBoxCaches );
2737 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
2739 if(
containsSingle( aP, polygonIdx, aAccuracy, aUseBBoxCaches ) )
2755 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
2772 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
2783 bool aUseBBoxCaches )
const
2789 for(
int holeIdx = 0; holeIdx <
HoleCount( aSubpolyIndex ); holeIdx++ )
2811 path.Move( aVector );
2815 tri->Move( aVector );
2827 path.Mirror( aRef, aFlipDirection );
2840 path.Rotate( aAngle, aCenter );
2856 c +=
path.PointCount();
2893 SEG::ecoord minDistance = (*iterator).SquaredDistance( aPoint );
2896 *aNearest = ( *iterator ).NearestPoint( aPoint );
2898 for( iterator++; iterator && minDistance > 0; iterator++ )
2900 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aPoint );
2902 if( currentDistance < minDistance )
2905 *aNearest = (*iterator).NearestPoint( aPoint );
2907 minDistance = currentDistance;
2923 *aNearest = ( aSegment.
A + aSegment.
B ) / 2;
2929 SEG::ecoord minDistance = (*iterator).SquaredDistance( aSegment );
2932 *aNearest = ( *iterator ).NearestPoint( aSegment );
2934 for( iterator++; iterator && minDistance > 0; iterator++ )
2936 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aSegment );
2938 if( currentDistance < minDistance )
2941 *aNearest = (*iterator).NearestPoint( aSegment );
2943 minDistance = currentDistance;
2948 return minDistance < 0 ? 0 : minDistance;
2955 wxASSERT_MSG( !aOutlineOnly, wxT(
"Warning: SHAPE_POLY_SET::SquaredDistance does not yet "
2956 "support aOutlineOnly==true" ) );
2963 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
2966 aNearest ? &nearest :
nullptr );
2968 if( currentDistance_sq < minDistance_sq )
2971 *aNearest = nearest;
2973 minDistance_sq = currentDistance_sq;
2977 return minDistance_sq;
2988 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
2991 aNearest ? &nearest :
nullptr );
2993 if( currentDistance_sq < minDistance_sq )
2996 *aNearest = nearest;
2998 minDistance_sq = currentDistance_sq;
3002 return minDistance_sq;
3015 return index.m_contour > 0;
3023 for(
unsigned int idx = 0; idx <
m_polys.size(); idx++ )
3034 for(
size_t idx = 0; idx <
m_polys.size(); idx++ )
3043 SHAPE::operator=( aOther );
3100 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* aHintData,
3122 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>& dest,
3123 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* hintData,
3126 bool triangulationValid =
false;
3130 if( hintData && hintData->size() != (
unsigned) polySet.
OutlineCount() )
3135 if( !dest.empty() && dest.back()->GetTriangleCount() == 0 )
3136 dest.erase( dest.end() - 1 );
3144 if( partitionLeaves > 1 )
3146 std::vector<SHAPE_LINE_CHAIN> partitions =
3149 if( partitions.size() > 1 )
3153 if( taskSubmitter && partitions.size() > 2 )
3155 size_t leafCount = partitions.size();
3157 struct WorkStealState
3159 std::unique_ptr<std::atomic<bool>[]> claimed;
3160 std::unique_ptr<std::atomic<bool>[]> done;
3161 std::unique_ptr<std::atomic<bool>[]> ok;
3163 explicit WorkStealState(
size_t n ) :
3164 claimed(
new std::atomic<bool>[n] ),
3165 done(
new std::atomic<bool>[n] ),
3166 ok(
new std::atomic<bool>[n] )
3168 for(
size_t i = 0; i < n; ++i )
3170 claimed[i].store(
false );
3171 done[i].store(
false );
3172 ok[i].store(
false );
3177 auto state = std::make_shared<WorkStealState>( leafCount );
3179 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>> results( leafCount );
3181 for(
size_t i = 0; i < leafCount; ++i )
3183 results[i] = std::make_unique<TRIANGULATED_POLYGON>( forOutline );
3186 for(
size_t i = 0; i < leafCount; ++i )
3188 auto* triPoly = results[i].get();
3189 auto* leaf = &partitions[i];
3191 taskSubmitter( [state, i, triPoly, leaf]()
3193 if( state->claimed[i].exchange(
true ) )
3198 state->done[i].store(
true, std::memory_order_release );
3202 for(
size_t i = 0; i < leafCount; ++i )
3204 if( state->claimed[i].exchange(
true ) )
3209 state->done[i].store(
true, std::memory_order_release );
3212 for(
size_t i = 0; i < leafCount; ++i )
3214 while( !state->done[i].load( std::memory_order_acquire ) )
3216 std::this_thread::yield();
3222 for(
size_t i = 0; i < leafCount; ++i )
3223 allOk = allOk && state->ok[i].load();
3227 for(
auto& r : results )
3229 if( r->GetTriangleCount() > 0 )
3230 dest.push_back( std::move( r ) );
3233 triangulationValid =
true;
3240 for(
auto it = partitions.rbegin(); it != partitions.rend(); ++it )
3249 dest.push_back( std::make_unique<TRIANGULATED_POLYGON>( forOutline ) );
3256 hintData ? hintData->at(
index ).get() :
nullptr ) )
3270 triangulationValid =
false;
3277 triangulationValid =
true;
3280 return triangulationValid;
3299 bool directOk =
true;
3301 for(
int ii = 0; ii < srcSet->
OutlineCount() && directOk; ++ii )
3306 if( poly.size() > 1 )
3319 for(
size_t jj = 1; jj < poly.size(); ++jj )
3359 double originalArea =
std::abs( poly.front().Area() );
3361 if( originalArea > 0.0 )
3363 double triArea = 0.0;
3368 double coverage = triArea / originalArea;
3370 if( coverage > 1.01 || coverage < 0.99 )
3383 bool splitOk =
true;
3385 for(
int jj = 0; jj < splitSet.
OutlineCount() && splitOk; ++jj )
3410 bool fallbackOk =
true;
3412 for(
int ii = 0; ii < srcSet->
OutlineCount() && fallbackOk; ++ii )
3417 for(
size_t jj = 1; jj < poly.size(); ++jj )
3474 hash.
add( outline.size() );
3478 hash.
add( lc.PointCount() );
3480 for(
int i = 0; i < lc.PointCount(); i++ )
3508 std::set<long long> ptHashes;
3512 for(
const VECTOR2I& pt : lc.CPoints() )
3514 const long long ptHash = (
long long) pt.x << 32 | pt.y;
3516 if( ptHashes.count( ptHash ) > 0 )
3519 ptHashes.insert( ptHash );
3538 n += t->GetTriangleCount();
3551 aSubshapes.push_back( &tri );
3562 if( aClearance != 0 )
3616 for(
int i = 0; i <
path.PointCount(); i++ )
3620 vec.
x = ( pt.
x - aCenter.
x ) * aScaleFactorX;
3621 vec.
y = ( pt.
y - aCenter.
y ) * aScaleFactorY;
3624 path.SetPoint( i, pt );
3638 Clipper2Lib::Clipper64 clipper;
3639 Clipper2Lib::PolyTree64 tree;
3640 Clipper2Lib::Paths64 paths;
3644 Clipper2Lib::Path64 lc;
3645 lc.reserve(
path.PointCount() );
3647 for(
int i = 0; i <
path.PointCount(); i++ )
3648 lc.emplace_back(
path.CPoint( i ).x,
path.CPoint( i ).y );
3650 paths.push_back( std::move( lc ) );
3653 clipper.AddSubject( paths );
3654 clipper.Execute( Clipper2Lib::ClipType::Union, aEvenOdd ? Clipper2Lib::FillRule::EvenOdd
3655 : Clipper2Lib::FillRule::NonZero, tree );
3657 std::vector<CLIPPER_Z_VALUE> zValues;
3658 std::vector<SHAPE_ARC> arcBuffer;
3678 int aSpacing,
int aLineLength )
const
3680 std::vector<SEG> hatchLines;
3693 if( iterator->x < min_x )
3694 min_x = iterator->x;
3696 if( iterator->x > max_x )
3697 max_x = iterator->x;
3699 if( iterator->y < min_y )
3700 min_y = iterator->y;
3702 if( iterator->y > max_y )
3703 max_y = iterator->y;
3706 auto sortEndsByDescendingX =
3709 return tst.x < ref.
x;
3712 for(
double slope : aSlopes )
3714 int64_t max_a, min_a;
3727 min_a = ( min_a / aSpacing ) * aSpacing;
3730 std::vector<VECTOR2I> pointbuffer;
3731 pointbuffer.reserve( 256 );
3733 for( int64_t a = min_a; a < max_a; a += aSpacing )
3735 pointbuffer.clear();
3740 const SEG seg = *iterator;
3746 if( pt.
x < min_x || pt.
x > max_x || pt.
y < min_y || pt.
y > max_y )
3757 if( pointbuffer.size() > 2 )
3758 sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX );
3761 for(
size_t ip = 0; ip + 1 < pointbuffer.size(); ip++ )
3763 const VECTOR2I& p1 = pointbuffer[ip];
3764 const VECTOR2I& p2 = pointbuffer[ip + 1];
3770 SEG candidate( p1, p2 );
3772 VECTOR2I mid( ( candidate.
A.
x + candidate.
B.
x ) / 2, ( candidate.
A.
y + candidate.
B.
y ) / 2 );
3777 int dx = p2.
x - p1.
x;
3781 if( aLineLength == -1 ||
std::abs( dx ) < 2 * aLineLength )
3783 hatchLines.emplace_back( candidate );
3787 double dy = p2.
y - p1.
y;
3797 int y1 =
KiROUND( p1.
y + dx * slope );
3798 int y2 =
KiROUND( p2.
y - dx * slope );
3800 hatchLines.emplace_back(
SEG( p1.
x, p1.
y, x1, y1 ) );
3802 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