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;
165 unsigned int contourIdx = 0;
168 int currentGlobalIdx = 0;
170 for( polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
174 for( contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
177 int totalPoints = currentContour.
PointCount();
179 for( vertexIdx = 0; vertexIdx < totalPoints; vertexIdx++ )
182 if( currentGlobalIdx == aGlobalIdx )
184 aRelativeIndices->
m_polygon = polygonIdx;
185 aRelativeIndices->
m_contour = contourIdx;
186 aRelativeIndices->
m_vertex = vertexIdx;
202 int& aGlobalIdx )
const
204 int selectedVertex = aRelativeIndices.
m_vertex;
205 unsigned int selectedContour = aRelativeIndices.
m_contour;
206 unsigned int selectedPolygon = aRelativeIndices.
m_polygon;
209 if( selectedPolygon <
m_polys.size() && selectedContour <
m_polys[selectedPolygon].size()
210 && selectedVertex <
m_polys[selectedPolygon][selectedContour].PointCount() )
216 for(
unsigned int polygonIdx = 0; polygonIdx < selectedPolygon; polygonIdx++ )
218 currentPolygon =
Polygon( polygonIdx );
220 for(
unsigned int contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
221 aGlobalIdx += currentPolygon[contourIdx].PointCount();
224 currentPolygon =
Polygon( selectedPolygon );
226 for(
unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx++ )
227 aGlobalIdx += currentPolygon[contourIdx].PointCount();
229 aGlobalIdx += selectedVertex;
246 poly.push_back( empty_path );
247 m_polys.push_back( std::move( poly ) );
263 m_polys[aOutline].push_back( empty_path );
265 return m_polys.back().size() - 2;
283 assert( aOutline < (
int)
m_polys.size() );
284 assert( idx < (
int)
m_polys[aOutline].size() );
286 m_polys[aOutline][idx].Append( x, y, aAllowDuplication );
288 return m_polys[aOutline][idx].PointCount();
293 std::optional<int> aMaxError )
307 assert( aOutline < (
int)
m_polys.size() );
308 assert( idx < (
int)
m_polys[aOutline].size() );
310 if( aMaxError.has_value() )
311 m_polys[aOutline][idx].Append( aArc, aMaxError.value() );
313 m_polys[aOutline][idx].Append( aArc );
315 return m_polys[aOutline][idx].PointCount();
323 if( aGlobalIndex < 0 )
336 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
356 if( aOutline >= (
int)
m_polys.size() )
359 if( idx >= (
int)
m_polys[aOutline].size() )
362 return m_polys[aOutline][idx].PointCount();
377 for(
int idx = 0; idx <=
HoleCount( ii ); idx++ )
379 full_count +=
m_polys[ii][idx].PointCount();
389 assert( aFirstPolygon >= 0 && aLastPolygon <=
OutlineCount() );
412 assert( aOutline < (
int)
m_polys.size() );
413 assert( idx < (
int)
m_polys[aOutline].size() );
415 return m_polys[aOutline][idx].CPoint( aIndex );
425 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
450 if(
index.m_vertex == 0 )
452 index.m_vertex = lastpoint - 1;
455 else if(
index.m_vertex == lastpoint )
473 *aPrevious = previous;
489 std::vector<SEG> segments;
493 segments.emplace_back( *it );
495 std::sort( segments.begin(), segments.end(), [](
const SEG& a,
const SEG& b )
497 int min_a_x = std::min( a.A.x, a.B.x );
498 int min_b_x = std::min( b.A.x, b.B.x );
500 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 ) );
503 for(
auto it = segments.begin(); it != segments.end(); ++it )
505 SEG& firstSegment = *it;
508 auto innerIterator = it;
509 int max_x = std::max( firstSegment.
A.
x, firstSegment.
B.
x );
510 int max_y = std::max( firstSegment.
A.
y, firstSegment.
B.
y );
513 for( innerIterator++; innerIterator != segments.end(); innerIterator++ )
515 SEG& secondSegment = *innerIterator;
516 int min_x = std::min( secondSegment.
A.
x, secondSegment.
B.
x );
517 int min_y = std::min( secondSegment.
A.
y, secondSegment.
B.
y );
521 if( max_x < min_x || ( max_x == min_x && max_y < min_y ) )
525 bool adjacent = ( index_diff == 1) || (index_diff == ((
int)segments.size() - 1) );
528 if( !adjacent && firstSegment.
Collide( secondSegment, 0 ) )
539 for(
unsigned int polygon = 0; polygon <
m_polys.size(); polygon++ )
553 poly.push_back( aOutline );
558 wxCHECK2_MSG( aOutline.
IsClosed(), poly.back().SetClosed(
true ),
559 "Warning: non-closed outline added to SHAPE_POLY_SET" );
561 m_polys.push_back( std::move( poly ) );
563 return (
int)
m_polys.size() - 1;
572 aOutline += (int)
m_polys.size();
574 assert( aOutline < (
int)
m_polys.size() );
578 assert( poly.size() );
580 poly.push_back( aHole );
582 return (
int) poly.size() - 2;
602 for(
int j = 0; j <
HoleCount( i ); j++ )
616 for(
size_t i = 0; i < poly.size(); i++ )
628 for(
size_t i = 0; i < poly.size(); i++ )
631 aArcBuffer.push_back( arc );
641 for(
size_t i = 0; i < poly.size(); i++ )
649 std::vector<SHAPE_LINE_CHAIN> contours;
652 contours.insert( contours.end(), poly.begin(), poly.end() );
654 std::map<int, std::set<int>> parentToChildren;
655 std::map<int, std::set<int>> childToParents;
658 contour.GenerateBBoxCache();
660 for(
size_t i = 0; i < contours.size(); i++ )
664 for(
size_t j = 0; j < contours.size(); j++ )
674 parentToChildren[i].emplace( j );
675 childToParents[j].emplace( i );
680 std::set<int> topLevelParents;
682 for(
size_t i = 0; i < contours.size(); i++ )
684 if( childToParents[i].size() == 0 )
686 topLevelParents.emplace( i );
692 std::function<void(
int,
int, std::vector<int> )>
process;
695 [&](
int myId,
int parentOutlineId,
const std::vector<int>&
path )
697 std::set<int> relParents = childToParents[myId];
699 for(
int pathId :
path )
701 int erased = relParents.erase( pathId );
702 wxASSERT( erased > 0 );
705 wxASSERT( relParents.size() == 0 );
709 bool isOutline =
path.size() % 2 == 0;
713 int outlineId =
result.AddOutline( contours[myId] );
714 myOutline = outlineId;
718 wxASSERT( parentOutlineId != -1 );
719 result.AddHole( contours[myId], parentOutlineId );
722 auto it = parentToChildren.find( myId );
723 if( it != parentToChildren.end() )
725 std::vector<int> thisPath =
path;
726 thisPath.emplace_back( myId );
728 std::set<int> thisPathSet;
729 thisPathSet.insert( thisPath.begin(), thisPath.end() );
731 for(
int childId : it->second )
733 const std::set<int>& childPathSet = childToParents[childId];
735 if( thisPathSet != childPathSet )
738 process( childId, myOutline, thisPath );
743 for(
int topParentId : topLevelParents )
745 std::vector<int>
path;
749 *
this = std::move(
result );
765 wxFAIL_MSG( wxT(
"Boolean ops on curved polygons are not supported. You should call "
766 "ClearArcs() before carrying out the boolean operation." ) );
769 Clipper2Lib::Clipper64 c;
771 std::vector<CLIPPER_Z_VALUE> zValues;
772 std::vector<SHAPE_ARC> arcBuffer;
773 std::map<VECTOR2I, CLIPPER_Z_VALUE> newIntersectPoints;
775 Clipper2Lib::Paths64 paths;
776 Clipper2Lib::Paths64 clips;
780 for(
size_t i = 0; i < poly.size(); i++ )
782 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
788 for(
size_t i = 0; i < poly.size(); i++ )
790 clips.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
794 c.AddSubject( paths );
797 Clipper2Lib::PolyTree64 solution;
799 Clipper2Lib::ZCallback64 callback =
800 [&](
const Clipper2Lib::Point64 & e1bot,
const Clipper2Lib::Point64 & e1top,
801 const Clipper2Lib::Point64 & e2bot,
const Clipper2Lib::Point64 & e2top,
802 Clipper2Lib::Point64 & pt )
805 [&](
const ssize_t& aZvalue,
const ssize_t& aCompareVal = -1 ) -> ssize_t
809 retval = zValues.at( aZvalue ).m_SecondArcIdx;
811 if( retval == -1 || ( aCompareVal > 0 && retval != aCompareVal ) )
812 retval = zValues.at( aZvalue ).m_FirstArcIdx;
818 [&](
const ssize_t& aBottomZ,
const ssize_t aTopZ ) -> ssize_t
820 ssize_t retval = arcIndex( aBottomZ );
824 if( retval != arcIndex( aTopZ, retval ) )
831 ssize_t e1ArcSegmentIndex = arcSegment( e1bot.z, e1top.z );
832 ssize_t e2ArcSegmentIndex = arcSegment( e2bot.z, e2top.z );
836 if( e1ArcSegmentIndex != -1 )
847 size_t z_value_ptr = zValues.size();
848 zValues.push_back( newZval );
852 newIntersectPoints.insert( {
VECTOR2I( pt.x, pt.y ), newZval } );
858 c.SetZCallback( std::move( callback ) );
860 c.Execute( aType, Clipper2Lib::FillRule::NonZero, solution );
869 booleanOp( Clipper2Lib::ClipType::Union, b );
875 booleanOp( Clipper2Lib::ClipType::Difference, b );
881 booleanOp( Clipper2Lib::ClipType::Intersection, b );
887 booleanOp( Clipper2Lib::ClipType::Xor, b );
893 booleanOp( Clipper2Lib::ClipType::Union, a, b );
899 booleanOp( Clipper2Lib::ClipType::Difference, a, b );
905 booleanOp( Clipper2Lib::ClipType::Intersection, a, b );
911 booleanOp( Clipper2Lib::ClipType::Xor, a, b );
919 Inflate( aFactor, aCornerStrategy, aMaxError );
927 using namespace Clipper2Lib;
931 #define SEG_CNT_MAX 64
932 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
939 JoinType joinType = JoinType::Round;
940 double miterLimit = 2.0;
942 switch( aCornerStrategy )
945 joinType = JoinType::Miter;
950 joinType = JoinType::Miter;
954 joinType = JoinType::Miter;
958 joinType = JoinType::Square;
962 joinType = JoinType::Round;
966 std::vector<CLIPPER_Z_VALUE> zValues;
967 std::vector<SHAPE_ARC> arcBuffer;
973 for(
size_t i = 0; i < poly.size(); i++ )
974 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
976 c.AddPaths( paths, joinType, EndType::Polygon );
983 if( aCircleSegCount < 6 )
988 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
990 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
993 arc_tolerance_factor[aCircleSegCount] = coeff;
997 coeff = arc_tolerance_factor[aCircleSegCount];
1000 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1001 c.MiterLimit( miterLimit );
1008 c.Execute( aAmount, paths );
1010 Clipper2Lib::SimplifyPaths( paths,
std::abs( aAmount ) * coeff,
true );
1013 c2.PreserveCollinear(
false );
1014 c2.ReverseSolution(
false );
1015 c2.AddSubject( paths );
1016 c2.Execute(ClipType::Union, FillRule::Positive, tree);
1020 c.Execute( aAmount, tree );
1031 using namespace Clipper2Lib;
1035 #define SEG_CNT_MAX 64
1036 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
1043 JoinType joinType = JoinType::Round;
1044 double miterLimit = 2.0;
1046 switch( aCornerStrategy )
1049 joinType = JoinType::Miter;
1054 joinType = JoinType::Miter;
1058 joinType = JoinType::Miter;
1062 joinType = JoinType::Square;
1066 joinType = JoinType::Round;
1070 std::vector<CLIPPER_Z_VALUE> zValues;
1071 std::vector<SHAPE_ARC> arcBuffer;
1074 c.AddPath(
path, joinType, EndType::Butt );
1080 if( aCircleSegCount < 6 )
1081 aCircleSegCount = 6;
1085 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
1087 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
1090 arc_tolerance_factor[aCircleSegCount] = coeff;
1094 coeff = arc_tolerance_factor[aCircleSegCount];
1097 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1098 c.MiterLimit( miterLimit );
1105 c.Execute( aAmount, paths2 );
1107 Clipper2Lib::SimplifyPaths( paths2,
std::abs( aAmount ) * coeff,
false );
1110 c2.PreserveCollinear(
false );
1111 c2.ReverseSolution(
false );
1112 c2.AddSubject( paths2 );
1113 c2.Execute( ClipType::Union, FillRule::Positive, tree );
1117 c.Execute( aAmount, tree );
1130 inflate2( aAmount, segCount, aCornerStrategy, aSimplify );
1139 inflateLine2( aLine, aAmount, segCount, aCornerStrategy, aSimplify );
1144 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1145 const std::vector<SHAPE_ARC>& aArcBuffer )
1147 if( !aPolyPath->IsHole() )
1150 paths.reserve( aPolyPath->Count() + 1 );
1151 paths.emplace_back( aPolyPath->Polygon(), aZValueBuffer, aArcBuffer );
1153 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& child : *aPolyPath )
1155 paths.emplace_back( child->Polygon(), aZValueBuffer, aArcBuffer );
1157 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& grandchild : *child )
1161 m_polys.emplace_back( std::move( paths ) );
1167 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1168 const std::vector<SHAPE_ARC>& aArcBuffer )
1172 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& n : tree )
1178 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1179 const std::vector<SHAPE_ARC>& aArcBuffer )
1184 for(
const Clipper2Lib::Path64& n : aPath )
1186 if( Clipper2Lib::Area( n ) > 0 )
1195 wxCHECK2_MSG( !
path.empty(),
continue, wxT(
"Cannot add a hole before an outline" ) );
1198 path.emplace_back( n, aZValueBuffer, aArcBuffer );
1237 int x = edge.
m_p1.
x;
1238 int y = edge.
m_p1.
y;
1239 int min_dist = std::numeric_limits<int>::max();
1258 x_intersect = std::max( e.
m_p1.
x, e.
m_p2.
x );
1266 int dist = ( x - x_intersect );
1268 if( dist >= 0 && dist < min_dist )
1271 x_nearest = x_intersect;
1284 edges[hole2outline_index] =
1287 edges[split_index] =
1292 e_nearest->
m_next = outline2hole_index;
1295 for( ; last->
m_next != edgeIndex; last = &edges[last->
m_next] )
1297 last->
m_next = hole2outline_index;
1307 bool outline =
true;
1309 if( paths.size() == 1 )
1312 size_t total_point_count = 0;
1316 total_point_count +=
path.PointCount();
1319 if( total_point_count > (
size_t) std::numeric_limits<FractureEdge::Index>::max() )
1321 wxLogWarning( wxT(
"Polygon has more points than int limit" ) );
1328 edges.reserve( total_point_count + paths.size() * 3 );
1334 int path_or_provoking_index;
1339 std::vector<PathInfo> sorted_paths;
1340 const int paths_count =
static_cast<int>( paths.size() );
1341 sorted_paths.reserve( paths_count );
1343 for(
int path_index = 0; path_index < paths_count; path_index++ )
1346 const std::vector<VECTOR2I>& points =
path.CPoints();
1347 const int point_count =
static_cast<int>( points.size() );
1348 int x_min = std::numeric_limits<int>::max();
1349 int y_min = std::numeric_limits<int>::max();
1352 for(
int point_index = 0; point_index < point_count; point_index++ )
1354 const VECTOR2I& point = points[point_index];
1355 if( point.
x < x_min )
1358 leftmost = point_index;
1360 if( point.
y < y_min )
1364 sorted_paths.emplace_back( PathInfo{ path_index, leftmost, x_min, y_min } );
1367 std::sort( sorted_paths.begin() + 1, sorted_paths.end(),
1368 [](
const PathInfo& a,
const PathInfo& b )
1371 return a.y_or_bridge < b.y_or_bridge;
1377 for( PathInfo& path_info : sorted_paths )
1380 const std::vector<VECTOR2I>& points =
path.CPoints();
1381 const size_t point_count = points.size();
1386 for(
size_t i = 0; i < point_count - 1; i++ )
1388 edges.emplace_back( points[i], points[i + 1], edge_index + 1 );
1393 edges.emplace_back( points[point_count - 1], points[0], provoking_edge );
1400 path_info.path_or_provoking_index = provoking_edge;
1401 path_info.y_or_bridge = edge_index;
1405 edges.resize( edge_index );
1411 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); it++ )
1413 auto edge =
processHole( edges, it->path_or_provoking_index,
1414 it->path_or_provoking_index + it->leftmost, it->y_or_bridge );
1419 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
1467 int x = edge->
m_p1.
x;
1468 int y = edge->
m_p1.
y;
1469 int min_dist = std::numeric_limits<int>::max();
1476 if( !e->matches( y ) )
1481 if( e->m_p1.y == e->m_p2.y )
1483 x_intersect = std::max( e->m_p1.x, e->m_p2.x );
1487 x_intersect = e->m_p1.x
1488 +
rescale( e->m_p2.x - e->m_p1.x, y - e->m_p1.y, e->m_p2.y - e->m_p1.y );
1491 int dist = ( x - x_intersect );
1493 if( dist >= 0 && dist < min_dist && e->m_connected )
1496 x_nearest = x_intersect;
1512 edges.push_back( split_2 );
1513 edges.push_back( lead1 );
1514 edges.push_back( lead2 );
1519 e_nearest->
m_next = lead1;
1524 for( last = edge; last->
m_next != edge; last = last->
m_next )
1550 if( paths.size() == 1 )
1553 int num_unconnected = 0;
1557 const std::vector<VECTOR2I>& points =
path.CPoints();
1558 int pointCount = points.size();
1562 int x_min = std::numeric_limits<int>::max();
1564 for(
int i = 0; i < pointCount; i++ )
1566 if( points[i].x < x_min )
1567 x_min = points[i].x;
1572 points[i + 1 == pointCount ? 0 : i + 1] );
1583 if( i == pointCount - 1 )
1587 edges.push_back( fe );
1591 if( fe->
m_p1.
x == x_min )
1592 border_edges.push_back( fe );
1603 while( num_unconnected > 0 )
1605 int x_min = std::numeric_limits<int>::max();
1606 auto it = border_edges.begin();
1611 for( ; it != border_edges.end(); ++it )
1614 int xt = border_edge->
m_p1.
x;
1616 if( ( xt <= x_min ) && !border_edge->
m_connected )
1619 smallestX = border_edge;
1623 int num_processed =
processEdge( edges, smallestX );
1626 if( !num_processed )
1628 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
1636 num_unconnected -= num_processed;
1654 paths.push_back( std::move( newPath ) );
1678 assert( aPoly.size() == 1 );
1684 bool m_duplicate =
false;
1691 bool compareSegs(
const SEG& s1,
const SEG& s2 )
const
1693 return (s1.
A == s2.
B && s1.
B == s2.
A);
1698 return compareSegs( m_poly->
CSegment( m_index ),
1699 aOther.m_poly->CSegment( aOther.m_index ) );
1704 return !compareSegs( m_poly->
CSegment( m_index ),
1705 aOther.m_poly->CSegment( aOther.m_index ) );
1710 std::size_t operator()(
const EDGE& aEdge )
const
1712 const SEG& a = aEdge.m_poly->CSegment( aEdge.m_index );
1713 std::size_t
seed = 0xa82de1c0;
1720 struct EDGE_LIST_ENTRY
1723 EDGE_LIST_ENTRY*
next;
1726 std::unordered_set<EDGE, EDGE::HASH> uniqueEdges;
1731 auto edgeList = std::make_unique<EDGE_LIST_ENTRY[]>( lc.
SegmentCount() );
1735 edgeList[i].index = i;
1736 edgeList[i].next = &edgeList[ (i != lc.
SegmentCount() - 1) ? i + 1 : 0 ];
1739 std::unordered_set<EDGE_LIST_ENTRY*> queue;
1744 uniqueEdges.insert( e );
1750 auto it = uniqueEdges.find( e );
1752 if( it != uniqueEdges.end() && it->m_index != i )
1754 int e1 = it->m_index;
1758 std::swap( e1, e2 );
1760 int e1_prev = e1 - 1;
1765 int e2_prev = e2 - 1;
1770 int e1_next = e1 + 1;
1775 int e2_next = e2 + 1;
1780 edgeList[e1_prev].next = &edgeList[ e2_next ];
1781 edgeList[e2_prev].next = &edgeList[ e1_next ];
1782 edgeList[i].next =
nullptr;
1783 edgeList[it->m_index].next =
nullptr;
1789 if( edgeList[i].
next )
1790 queue.insert( &edgeList[i] );
1793 auto edgeBuf = std::make_unique<EDGE_LIST_ENTRY* []>( lc.
SegmentCount() );
1799 double max_poly = 0.0;
1801 while( queue.size() )
1803 EDGE_LIST_ENTRY* e_first = *queue.begin();
1804 EDGE_LIST_ENTRY* e = e_first;
1811 }
while( e && e != e_first );
1815 for(
int i = 0; i < cnt; i++ )
1819 queue.erase( edgeBuf[i] );
1824 double area = std::fabs( outl.
Area() );
1826 if( area > max_poly )
1832 result.push_back( outl );
1839 aPoly = std::move(
result );
1849 if( paths.size() > 1 )
1873 std::array<VECTOR2I,4> pts = { aSegA.
A, aSegA.
B, aSegB.
A, aSegB.
B };
1875 std::sort( pts.begin(), pts.end(), [axis](
const VECTOR2I& p,
const VECTOR2I& q )
1878 return p.x < q.x || ( p.x == q.x && p.y < q.y );
1880 return p.y < q.y || ( p.y == q.y && p.x < q.x );
1901 wxLogTrace( wxT(
"collinear" ), wxT(
"Found exterior waist between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)" ),
1902 aSegA.
A.
x, aSegA.
A.
y, aSegA.
B.
x, aSegA.
B.
y,
1903 aSegB.
A.
x, aSegB.
A.
y, aSegB.
B.
x, aSegB.
B.
y );
1914 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
1916 bool changed =
true;
1927 for( intptr_t i = 0; i < count; ++i )
1931 intptr_t min[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
1932 intptr_t max[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
1933 rtree.
Insert( min, max, i );
1940 for( intptr_t i = 0; i < count && !found; ++i )
1945 intptr_t min[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
1946 intptr_t max[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
1949 [&](
const intptr_t& j ) ->
bool
1951 if( j == i || j == ( ( i + 1 ) % count ) || j == ( ( i + count - 1 ) % count ) )
1956 SEG other( oa, ob );
1960 if( oa == a && ob == b )
1963 if( oa == b && ob == a )
1977 rtree.
Search( min, max, visitor );
1984 int a1 = ( segA + 1 ) % outline.
PointCount();
1986 int b1 = ( segB + 1 ) % outline.
PointCount();
2012 m_polys[polyIdx][0] = std::move( lc1 );
2015 np.push_back( std::move( lc2 ) );
2016 m_polys.push_back( std::move( np ) );
2026 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
2028 bool changed =
true;
2040 int insertSegIdx = -1;
2041 int insertVertIdx = -1;
2044 constexpr int RTREE_THRESHOLD = 32;
2046 if( count < RTREE_THRESHOLD )
2048 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2051 const int prevSeg = ( vertIdx + count - 1 ) % count;
2053 for(
int segIdx = 0; segIdx < count; ++segIdx )
2056 if( segIdx == prevSeg || segIdx == vertIdx )
2068 insertSegIdx = segIdx;
2069 insertVertIdx = vertIdx;
2079 for(
int i = 0; i < count; ++i )
2083 int bmin[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
2084 int bmax[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
2085 rtree.
Insert( bmin, bmax, i );
2088 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2091 const int prevSeg = ( vertIdx + count - 1 ) % count;
2092 int bmin[2] = { pt.
x, pt.
y };
2093 int bmax[2] = { pt.
x, pt.
y };
2096 [&](
const intptr_t& segIdx ) ->
bool
2098 if( segIdx == prevSeg || segIdx == vertIdx )
2110 insertSegIdx = segIdx;
2111 insertVertIdx = vertIdx;
2118 rtree.
Search( bmin, bmax, pinchVisitor );
2122 if( insertSegIdx < 0 )
2129 const int splitStart1 = ( insertSegIdx + 1 ) % count;
2134 if( insertVertIdx >= splitStart1 )
2135 size1 = insertVertIdx - splitStart1 + 1;
2137 size1 = count - splitStart1 + insertVertIdx + 1;
2139 if( insertSegIdx >= insertVertIdx )
2140 size2 = insertSegIdx - insertVertIdx + 1;
2142 size2 = count - insertVertIdx + insertSegIdx + 1;
2144 if( size1 < 3 || size2 < 3 )
2152 int idx = splitStart1;
2154 for(
int i = 0; i < size1; ++i )
2157 idx = ( idx + 1 ) % count;
2162 idx = insertVertIdx;
2164 for(
int i = 0; i < size2; ++i )
2167 idx = ( idx + 1 ) % count;
2172 m_polys[polyIdx][0] = std::move( poly1 );
2175 np.push_back( std::move( poly2 ) );
2176 m_polys.push_back( std::move( np ) );
2200 path.Simplify( aTolerance );
2218 while( outline.size() > 1 )
2243 std::stringstream ss;
2245 ss <<
"SHAPE_LINE_CHAIN poly; \n";
2247 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2249 for(
unsigned j = 0; j <
m_polys[i].size(); j++ )
2252 ss <<
"{ auto tmp = " <<
m_polys[i][j].Format() <<
";\n";
2258 ss <<
" poly.AddOutline(tmp); } \n";
2262 ss <<
" poly.AddHole(tmp); } \n";
2278 if( tmp !=
"polyset" )
2283 int n_polys = atoi( tmp.c_str() );
2288 for(
int i = 0; i < n_polys; i++ )
2298 int n_outlines = atoi( tmp.c_str() );
2300 if( n_outlines < 0 )
2303 for(
int j = 0; j < n_outlines; j++ )
2310 int n_vertices = atoi( tmp.c_str() );
2312 for(
int v = 0; v < n_vertices; v++ )
2316 aStream >> tmp; p.
x = atoi( tmp.c_str() );
2317 aStream >> tmp; p.
y = atoi( tmp.c_str() );
2321 paths.push_back( std::move( outline ) );
2324 m_polys.push_back( std::move( paths ) );
2335 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2352 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2355 bb = *
m_polys[i][0].GetCachedBBox();
2372 if( lineChain.PointOnEdge( aP, aAccuracy ) )
2387 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2390 *aLocation = nearest;
2393 *aActual = sqrt( dist_sq );
2411 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2414 *aLocation = nearest;
2417 *aActual = sqrt( dist_sq );
2434 int extra = segment->
GetWidth() / 2;
2436 if(
Collide( segment->
GetSeg(), aClearance + extra, aActual, aLocation ) )
2439 *aActual = std::max( 0, *aActual - extra );
2450 int extra =
circle->GetRadius();
2452 if(
Collide(
circle->GetCenter(), aClearance + extra, aActual, aLocation ) )
2455 *aActual = std::max( 0, *aActual - extra );
2472 if( aActual || aLocation )
2477 if( aShape->
Collide( &tri, aClearance, &triActual, &triLocation ) )
2488 if( aShape->
Collide( &tri, aClearance ) )
2497 *aActual = std::max( 0,
actual );
2520 if( aPolygonIdx < 0 )
2521 aPolygonIdx +=
m_polys.size();
2523 m_polys[aPolygonIdx].erase(
m_polys[aPolygonIdx].begin() + aContourIdx );
2543 std::vector<VERTEX_INDEX> indices_to_remove;
2548 segmentStart = *iterator;
2554 segmentEnd = contourStart;
2562 contourStart = *iterator;
2570 wxCHECK_MSG( iterator, removed, wxT(
"Invalid polygon. Reached end without noticing. Please report this error" ) );
2572 segmentEnd = *iterator;
2576 if( segmentStart == segmentEnd )
2578 indices_to_remove.push_back( indexStart );
2585 for(
auto it = indices_to_remove.rbegin(); it != indices_to_remove.rend(); ++it )
2608 if( triangleSet->GetSourceOutlineIndex() == aIdx )
2610 else if( triangleSet->GetSourceOutlineIndex() > aIdx )
2611 triangleSet->SetSourceOutlineIndex( triangleSet->GetSourceOutlineIndex() - 1 );
2638 Append( aP.
x, aP.
y, aOutline, aHole );
2644 int aClearance )
const
2647 bool collision =
false;
2657 delta = *iterator - aPoint;
2660 distance_squared =
delta.SquaredEuclideanNorm();
2663 if( distance_squared <= clearance_squared )
2665 if( !aClosestVertex )
2671 clearance_squared = distance_squared;
2674 *aClosestVertex = iterator.GetIndex();
2684 int aClearance )
const
2687 bool collision =
false;
2692 const SEG currentSegment = *iterator;
2696 if( distance_squared <= clearance_squared )
2698 if( !aClosestVertex )
2704 clearance_squared = distance_squared;
2707 *aClosestVertex = iterator.GetIndex();
2717 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
2721 for(
int holeIdx = 0; holeIdx <
HoleCount( polygonIdx ); holeIdx++ )
2728 bool aUseBBoxCaches )
const
2734 if( aSubpolyIndex >= 0 )
2735 return containsSingle( aP, aSubpolyIndex, aAccuracy, aUseBBoxCaches );
2738 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
2740 if(
containsSingle( aP, polygonIdx, aAccuracy, aUseBBoxCaches ) )
2756 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
2773 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
2784 bool aUseBBoxCaches )
const
2790 for(
int holeIdx = 0; holeIdx <
HoleCount( aSubpolyIndex ); holeIdx++ )
2812 path.Move( aVector );
2816 tri->Move( aVector );
2828 path.Mirror( aRef, aFlipDirection );
2841 path.Rotate( aAngle, aCenter );
2857 c +=
path.PointCount();
2894 SEG::ecoord minDistance = (*iterator).SquaredDistance( aPoint );
2896 for( iterator++; iterator && minDistance > 0; iterator++ )
2898 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aPoint );
2900 if( currentDistance < minDistance )
2903 *aNearest = (*iterator).NearestPoint( aPoint );
2905 minDistance = currentDistance;
2921 *aNearest = ( aSegment.
A + aSegment.
B ) / 2;
2927 SEG::ecoord minDistance = (*iterator).SquaredDistance( aSegment );
2929 if( aNearest && minDistance == 0 )
2930 *aNearest = ( *iterator ).NearestPoint( aSegment );
2932 for( iterator++; iterator && minDistance > 0; iterator++ )
2934 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aSegment );
2936 if( currentDistance < minDistance )
2939 *aNearest = (*iterator).NearestPoint( aSegment );
2941 minDistance = currentDistance;
2946 return minDistance < 0 ? 0 : minDistance;
2953 wxASSERT_MSG( !aOutlineOnly, wxT(
"Warning: SHAPE_POLY_SET::SquaredDistance does not yet "
2954 "support aOutlineOnly==true" ) );
2961 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
2964 aNearest ? &nearest :
nullptr );
2966 if( currentDistance_sq < minDistance_sq )
2969 *aNearest = nearest;
2971 minDistance_sq = currentDistance_sq;
2975 return minDistance_sq;
2986 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
2989 aNearest ? &nearest :
nullptr );
2991 if( currentDistance_sq < minDistance_sq )
2994 *aNearest = nearest;
2996 minDistance_sq = currentDistance_sq;
3000 return minDistance_sq;
3013 return index.m_contour > 0;
3021 for(
unsigned int idx = 0; idx <
m_polys.size(); idx++ )
3032 for(
size_t idx = 0; idx <
m_polys.size(); idx++ )
3041 SHAPE::operator=( aOther );
3095 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* aHintData,
3112 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>& dest,
3113 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* hintData,
3116 bool triangulationValid =
false;
3120 if( hintData && hintData->size() != (
unsigned) polySet.
OutlineCount() )
3125 if( !dest.empty() && dest.back()->GetTriangleCount() == 0 )
3126 dest.erase( dest.end() - 1 );
3134 if( partitionLeaves > 1 )
3136 std::vector<SHAPE_LINE_CHAIN> partitions =
3139 if( partitions.size() > 1 )
3143 if( taskSubmitter && partitions.size() > 2 )
3145 size_t leafCount = partitions.size();
3147 struct WorkStealState
3149 std::unique_ptr<std::atomic<bool>[]> claimed;
3150 std::unique_ptr<std::atomic<bool>[]> done;
3151 std::unique_ptr<std::atomic<bool>[]> ok;
3153 explicit WorkStealState(
size_t n ) :
3154 claimed(
new std::atomic<bool>[n] ),
3155 done(
new std::atomic<bool>[n] ),
3156 ok(
new std::atomic<bool>[n] )
3158 for(
size_t i = 0; i < n; ++i )
3160 claimed[i].store(
false );
3161 done[i].store(
false );
3162 ok[i].store(
false );
3167 auto state = std::make_shared<WorkStealState>( leafCount );
3169 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>> results( leafCount );
3171 for(
size_t i = 0; i < leafCount; ++i )
3173 results[i] = std::make_unique<TRIANGULATED_POLYGON>( forOutline );
3176 for(
size_t i = 0; i < leafCount; ++i )
3178 auto* triPoly = results[i].get();
3179 auto* leaf = &partitions[i];
3181 taskSubmitter( [state, i, triPoly, leaf]()
3183 if( state->claimed[i].exchange(
true ) )
3188 state->done[i].store(
true, std::memory_order_release );
3192 for(
size_t i = 0; i < leafCount; ++i )
3194 if( state->claimed[i].exchange(
true ) )
3199 state->done[i].store(
true, std::memory_order_release );
3202 for(
size_t i = 0; i < leafCount; ++i )
3204 while( !state->done[i].load( std::memory_order_acquire ) )
3206 std::this_thread::yield();
3212 for(
size_t i = 0; i < leafCount; ++i )
3213 allOk = allOk && state->ok[i].load();
3217 for(
auto& r : results )
3219 if( r->GetTriangleCount() > 0 )
3220 dest.push_back( std::move( r ) );
3223 triangulationValid =
true;
3230 for(
auto it = partitions.rbegin(); it != partitions.rend(); ++it )
3239 dest.push_back( std::make_unique<TRIANGULATED_POLYGON>( forOutline ) );
3246 hintData ? hintData->at(
index ).get() :
nullptr ) )
3260 triangulationValid =
false;
3267 triangulationValid =
true;
3270 return triangulationValid;
3289 bool directOk =
true;
3291 for(
int ii = 0; ii < srcSet->
OutlineCount() && directOk; ++ii )
3296 if( poly.size() > 1 )
3309 for(
size_t jj = 1; jj < poly.size(); ++jj )
3349 double originalArea =
std::abs( poly.front().Area() );
3351 if( originalArea > 0.0 )
3353 double triArea = 0.0;
3358 double coverage = triArea / originalArea;
3360 if( coverage > 1.01 || coverage < 0.99 )
3373 bool splitOk =
true;
3375 for(
int jj = 0; jj < splitSet.
OutlineCount() && splitOk; ++jj )
3400 bool fallbackOk =
true;
3402 for(
int ii = 0; ii < srcSet->
OutlineCount() && fallbackOk; ++ii )
3407 for(
size_t jj = 1; jj < poly.size(); ++jj )
3457 hash.
add( outline.size() );
3461 hash.
add( lc.PointCount() );
3463 for(
int i = 0; i < lc.PointCount(); i++ )
3491 std::set<long long> ptHashes;
3495 for(
const VECTOR2I& pt : lc.CPoints() )
3497 const long long ptHash = (
long long) pt.x << 32 | pt.y;
3499 if( ptHashes.count( ptHash ) > 0 )
3502 ptHashes.insert( ptHash );
3521 n += t->GetTriangleCount();
3534 aSubshapes.push_back( &tri );
3545 if( aClearance != 0 )
3599 for(
int i = 0; i <
path.PointCount(); i++ )
3603 vec.
x = ( pt.
x - aCenter.
x ) * aScaleFactorX;
3604 vec.
y = ( pt.
y - aCenter.
y ) * aScaleFactorY;
3607 path.SetPoint( i, pt );
3621 Clipper2Lib::Clipper64 clipper;
3622 Clipper2Lib::PolyTree64 tree;
3623 Clipper2Lib::Paths64 paths;
3627 Clipper2Lib::Path64 lc;
3628 lc.reserve(
path.PointCount() );
3630 for(
int i = 0; i <
path.PointCount(); i++ )
3631 lc.emplace_back(
path.CPoint( i ).x,
path.CPoint( i ).y );
3633 paths.push_back( std::move( lc ) );
3636 clipper.AddSubject( paths );
3637 clipper.Execute( Clipper2Lib::ClipType::Union, aEvenOdd ? Clipper2Lib::FillRule::EvenOdd
3638 : Clipper2Lib::FillRule::NonZero, tree );
3640 std::vector<CLIPPER_Z_VALUE> zValues;
3641 std::vector<SHAPE_ARC> arcBuffer;
3661 int aSpacing,
int aLineLength )
const
3663 std::vector<SEG> hatchLines;
3676 if( iterator->x < min_x )
3677 min_x = iterator->x;
3679 if( iterator->x > max_x )
3680 max_x = iterator->x;
3682 if( iterator->y < min_y )
3683 min_y = iterator->y;
3685 if( iterator->y > max_y )
3686 max_y = iterator->y;
3689 auto sortEndsByDescendingX =
3692 return tst.x < ref.
x;
3695 for(
double slope : aSlopes )
3697 int64_t max_a, min_a;
3710 min_a = ( min_a / aSpacing ) * aSpacing;
3713 std::vector<VECTOR2I> pointbuffer;
3714 pointbuffer.reserve( 256 );
3716 for( int64_t a = min_a; a < max_a; a += aSpacing )
3718 pointbuffer.clear();
3723 const SEG seg = *iterator;
3729 if( pt.
x < min_x || pt.
x > max_x || pt.
y < min_y || pt.
y > max_y )
3740 if( pointbuffer.size() > 2 )
3741 sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX );
3744 for(
size_t ip = 0; ip + 1 < pointbuffer.size(); ip++ )
3746 const VECTOR2I& p1 = pointbuffer[ip];
3747 const VECTOR2I& p2 = pointbuffer[ip + 1];
3753 SEG candidate( p1, p2 );
3755 VECTOR2I mid( ( candidate.
A.
x + candidate.
B.
x ) / 2, ( candidate.
A.
y + candidate.
B.
y ) / 2 );
3760 int dx = p2.
x - p1.
x;
3764 if( aLineLength == -1 ||
std::abs( dx ) < 2 * aLineLength )
3766 hatchLines.emplace_back( candidate );
3770 double dy = p2.
y - p1.
y;
3780 int y1 =
KiROUND( p1.
y + dx * slope );
3781 int y2 =
KiROUND( p2.
y - dx * slope );
3783 hatchLines.emplace_back(
SEG( p1.
x, p1.
y, x1, y1 ) );
3785 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.
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