37#include <unordered_set>
43#include <clipper2/clipper.h>
64#if defined( __MINGW32__ )
65 #define TRIANGULATESIMPLIFICATIONLEVEL 50
66 #define ENABLECACHEFRIENDLYFRACTURE true
68 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
69 #define ENABLECACHEFRIENDLYFRACTURE ADVANCED_CFG::GetCfg().m_EnableCacheFriendlyFracture
110 m_triangulatedPolys.reserve( aOther.TriangulatedPolyCount() );
112 for( unsigned i = 0; i < aOther.TriangulatedPolyCount(); i++ )
114 const TRIANGULATED_POLYGON* poly = aOther.TriangulatedPolygon( i );
115 m_triangulatedPolys.push_back( std::make_unique<TRIANGULATED_POLYGON>( *poly ) );
126 m_triangulationValid =
false;
129 m_failedHash = aOther.m_failedHash;
130 m_failedHashValid.store( aOther.m_failedHashValid.load() );
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;
2042 std::vector<double> prefix;
2045 [&](
int aFrom,
int aTo ) ->
double
2047 if( prefix.empty() )
2049 prefix.resize( count + 1, 0.0 );
2051 for(
int ii = 0; ii < count; ++ii )
2056 prefix[ii + 1] = prefix[ii]
2057 + ( (double) a.
x + b.
x ) * ( (
double) a.
y - b.
y );
2061 double term = aFrom <= aTo ? prefix[aTo] - prefix[aFrom]
2062 : prefix[count] - prefix[aFrom] + prefix[aTo];
2067 return term + ( (double) last.
x + first.
x ) * ( (
double) last.
y - first.
y );
2071 [&](
int aFrom,
int aTo )
2073 return aFrom <= aTo ? aTo - aFrom + 1 : count - aFrom + aTo + 1;
2079 [&](
int aVertIdx,
int aSegIdx )
2081 const int start = ( aSegIdx + 1 ) % count;
2083 if( ringSize( start, aVertIdx ) < 3 || ringSize( aVertIdx, aSegIdx ) < 3 )
2086 const bool sign = ringTerm( 0, count - 1 ) > 0;
2087 const double term1 = ringTerm( start, aVertIdx );
2088 const double term2 = ringTerm( aVertIdx, aSegIdx );
2090 return term1 != 0.0 && term2 != 0.0 && ( term1 > 0 ) ==
sign
2091 && ( term2 > 0 ) ==
sign;
2094 int insertSegIdx = -1;
2095 int insertVertIdx = -1;
2098 constexpr int RTREE_THRESHOLD = 32;
2100 if( count < RTREE_THRESHOLD )
2102 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2105 const int prevSeg = ( vertIdx + count - 1 ) % count;
2107 for(
int segIdx = 0; segIdx < count; ++segIdx )
2110 if( segIdx == prevSeg || segIdx == vertIdx )
2121 && splittable( vertIdx, segIdx ) )
2123 insertSegIdx = segIdx;
2124 insertVertIdx = vertIdx;
2134 for(
int i = 0; i < count; ++i )
2138 int bmin[2] = { std::min( a.
x, b.
x ), std::min( a.
y, b.
y ) };
2139 int bmax[2] = { std::max( a.
x, b.
x ), std::max( a.
y, b.
y ) };
2140 rtree.
Insert( bmin, bmax, i );
2143 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2146 const int prevSeg = ( vertIdx + count - 1 ) % count;
2147 int bmin[2] = { pt.
x, pt.
y };
2148 int bmax[2] = { pt.
x, pt.
y };
2151 [&](
const intptr_t& segIdx ) ->
bool
2153 if( segIdx == prevSeg || segIdx == vertIdx )
2164 && splittable( vertIdx,
static_cast<int>( segIdx ) ) )
2166 insertSegIdx = segIdx;
2167 insertVertIdx = vertIdx;
2174 rtree.
Search( bmin, bmax, pinchVisitor );
2178 if( insertSegIdx < 0 )
2185 const int splitStart1 = ( insertSegIdx + 1 ) % count;
2186 const int size1 = ringSize( splitStart1, insertVertIdx );
2187 const int size2 = ringSize( insertVertIdx, insertSegIdx );
2194 int idx = splitStart1;
2196 for(
int i = 0; i < size1; ++i )
2199 idx = ( idx + 1 ) % count;
2204 idx = insertVertIdx;
2206 for(
int i = 0; i < size2; ++i )
2209 idx = ( idx + 1 ) % count;
2214 m_polys[polyIdx][0] = std::move( poly1 );
2217 np.push_back( std::move( poly2 ) );
2218 m_polys.push_back( std::move( np ) );
2242 path.Simplify( aTolerance );
2260 while( outline.size() > 1 )
2285 std::stringstream ss;
2287 ss <<
"SHAPE_LINE_CHAIN poly; \n";
2289 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2291 for(
unsigned j = 0; j <
m_polys[i].size(); j++ )
2294 ss <<
"{ auto tmp = " <<
m_polys[i][j].Format() <<
";\n";
2300 ss <<
" poly.AddOutline(tmp); } \n";
2304 ss <<
" poly.AddHole(tmp); } \n";
2320 if( tmp !=
"polyset" )
2325 int n_polys = atoi( tmp.c_str() );
2330 for(
int i = 0; i < n_polys; i++ )
2340 int n_outlines = atoi( tmp.c_str() );
2342 if( n_outlines < 0 )
2345 for(
int j = 0; j < n_outlines; j++ )
2352 int n_vertices = atoi( tmp.c_str() );
2354 for(
int v = 0; v < n_vertices; v++ )
2358 aStream >> tmp; p.
x = atoi( tmp.c_str() );
2359 aStream >> tmp; p.
y = atoi( tmp.c_str() );
2363 paths.push_back( std::move( outline ) );
2366 m_polys.push_back( std::move( paths ) );
2377 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2394 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2397 bb = *
m_polys[i][0].GetCachedBBox();
2414 if( lineChain.PointOnEdge( aP, aAccuracy ) )
2429 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2432 *aLocation = nearest;
2435 *aActual = sqrt( dist_sq );
2453 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2456 *aLocation = nearest;
2459 *aActual = sqrt( dist_sq );
2476 int extra = segment->
GetWidth() / 2;
2478 if(
Collide( segment->
GetSeg(), aClearance + extra, aActual, aLocation ) )
2481 *aActual = std::max( 0, *aActual - extra );
2492 int extra =
circle->GetRadius();
2494 if(
Collide(
circle->GetCenter(), aClearance + extra, aActual, aLocation ) )
2497 *aActual = std::max( 0, *aActual - extra );
2514 if( aActual || aLocation )
2519 if( aShape->
Collide( &tri, aClearance, &triActual, &triLocation ) )
2530 if( aShape->
Collide( &tri, aClearance ) )
2539 *aActual = std::max( 0,
actual );
2562 if( aPolygonIdx < 0 )
2563 aPolygonIdx +=
m_polys.size();
2565 m_polys[aPolygonIdx].erase(
m_polys[aPolygonIdx].begin() + aContourIdx );
2585 std::vector<VERTEX_INDEX> indices_to_remove;
2590 segmentStart = *iterator;
2596 segmentEnd = contourStart;
2604 contourStart = *iterator;
2612 wxCHECK_MSG( iterator, removed, wxT(
"Invalid polygon. Reached end without noticing. Please report this error" ) );
2614 segmentEnd = *iterator;
2618 if( segmentStart == segmentEnd )
2620 indices_to_remove.push_back( indexStart );
2627 for(
auto it = indices_to_remove.rbegin(); it != indices_to_remove.rend(); ++it )
2650 if( triangleSet->GetSourceOutlineIndex() == aIdx )
2652 else if( triangleSet->GetSourceOutlineIndex() > aIdx )
2653 triangleSet->SetSourceOutlineIndex( triangleSet->GetSourceOutlineIndex() - 1 );
2680 Append( aP.
x, aP.
y, aOutline, aHole );
2686 int aClearance )
const
2689 bool collision =
false;
2699 delta = *iterator - aPoint;
2702 distance_squared =
delta.SquaredEuclideanNorm();
2705 if( distance_squared <= clearance_squared )
2707 if( !aClosestVertex )
2713 clearance_squared = distance_squared;
2716 *aClosestVertex = iterator.GetIndex();
2726 int aClearance )
const
2729 bool collision =
false;
2734 const SEG currentSegment = *iterator;
2738 if( distance_squared <= clearance_squared )
2740 if( !aClosestVertex )
2746 clearance_squared = distance_squared;
2749 *aClosestVertex = iterator.GetIndex();
2759 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
2763 for(
int holeIdx = 0; holeIdx <
HoleCount( polygonIdx ); holeIdx++ )
2770 bool aUseBBoxCaches )
const
2776 if( aSubpolyIndex >= 0 )
2777 return containsSingle( aP, aSubpolyIndex, aAccuracy, aUseBBoxCaches );
2780 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
2782 if(
containsSingle( aP, polygonIdx, aAccuracy, aUseBBoxCaches ) )
2798 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
2815 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
2826 bool aUseBBoxCaches )
const
2832 for(
int holeIdx = 0; holeIdx <
HoleCount( aSubpolyIndex ); holeIdx++ )
2854 path.Move( aVector );
2858 tri->Move( aVector );
2870 path.Mirror( aRef, aFlipDirection );
2883 path.Rotate( aAngle, aCenter );
2899 c +=
path.PointCount();
2936 SEG::ecoord minDistance = (*iterator).SquaredDistance( aPoint );
2939 *aNearest = ( *iterator ).NearestPoint( aPoint );
2941 for( iterator++; iterator && minDistance > 0; iterator++ )
2943 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aPoint );
2945 if( currentDistance < minDistance )
2948 *aNearest = (*iterator).NearestPoint( aPoint );
2950 minDistance = currentDistance;
2966 *aNearest = ( aSegment.
A + aSegment.
B ) / 2;
2972 SEG::ecoord minDistance = (*iterator).SquaredDistance( aSegment );
2975 *aNearest = ( *iterator ).NearestPoint( aSegment );
2977 for( iterator++; iterator && minDistance > 0; iterator++ )
2979 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aSegment );
2981 if( currentDistance < minDistance )
2984 *aNearest = (*iterator).NearestPoint( aSegment );
2986 minDistance = currentDistance;
2991 return minDistance < 0 ? 0 : minDistance;
2998 wxASSERT_MSG( !aOutlineOnly, wxT(
"Warning: SHAPE_POLY_SET::SquaredDistance does not yet "
2999 "support aOutlineOnly==true" ) );
3006 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
3009 aNearest ? &nearest :
nullptr );
3011 if( currentDistance_sq < minDistance_sq )
3014 *aNearest = nearest;
3016 minDistance_sq = currentDistance_sq;
3020 return minDistance_sq;
3031 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
3034 aNearest ? &nearest :
nullptr );
3036 if( currentDistance_sq < minDistance_sq )
3039 *aNearest = nearest;
3041 minDistance_sq = currentDistance_sq;
3045 return minDistance_sq;
3058 return index.m_contour > 0;
3066 for(
unsigned int idx = 0; idx <
m_polys.size(); idx++ )
3077 for(
size_t idx = 0; idx <
m_polys.size(); idx++ )
3086 SHAPE::operator=( aOther );
3143 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* aHintData,
3165 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>& dest,
3166 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* hintData,
3169 bool triangulationValid =
false;
3173 if( hintData && hintData->size() != (
unsigned) polySet.
OutlineCount() )
3178 if( !dest.empty() && dest.back()->GetTriangleCount() == 0 )
3179 dest.erase( dest.end() - 1 );
3187 if( partitionLeaves > 1 )
3189 std::vector<SHAPE_LINE_CHAIN> partitions =
3192 if( partitions.size() > 1 )
3196 if( taskSubmitter && partitions.size() > 2 )
3198 size_t leafCount = partitions.size();
3200 struct WorkStealState
3202 std::unique_ptr<std::atomic<bool>[]> claimed;
3203 std::unique_ptr<std::atomic<bool>[]> done;
3204 std::unique_ptr<std::atomic<bool>[]> ok;
3206 explicit WorkStealState(
size_t n ) :
3207 claimed(
new std::atomic<bool>[n] ),
3208 done(
new std::atomic<bool>[n] ),
3209 ok(
new std::atomic<bool>[n] )
3211 for(
size_t i = 0; i < n; ++i )
3213 claimed[i].store(
false );
3214 done[i].store(
false );
3215 ok[i].store(
false );
3220 auto state = std::make_shared<WorkStealState>( leafCount );
3222 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>> results( leafCount );
3224 for(
size_t i = 0; i < leafCount; ++i )
3226 results[i] = std::make_unique<TRIANGULATED_POLYGON>( forOutline );
3229 for(
size_t i = 0; i < leafCount; ++i )
3231 auto* triPoly = results[i].get();
3232 auto* leaf = &partitions[i];
3234 taskSubmitter( [state, i, triPoly, leaf]()
3236 if( state->claimed[i].exchange(
true ) )
3241 state->done[i].store(
true, std::memory_order_release );
3245 for(
size_t i = 0; i < leafCount; ++i )
3247 if( state->claimed[i].exchange(
true ) )
3252 state->done[i].store(
true, std::memory_order_release );
3255 for(
size_t i = 0; i < leafCount; ++i )
3257 while( !state->done[i].load( std::memory_order_acquire ) )
3259 std::this_thread::yield();
3265 for(
size_t i = 0; i < leafCount; ++i )
3266 allOk = allOk && state->ok[i].load();
3270 for(
auto& r : results )
3272 if( r->GetTriangleCount() > 0 )
3273 dest.push_back( std::move( r ) );
3276 triangulationValid =
true;
3283 for(
auto it = partitions.rbegin(); it != partitions.rend(); ++it )
3292 dest.push_back( std::make_unique<TRIANGULATED_POLYGON>( forOutline ) );
3299 hintData ? hintData->at(
index ).get() :
nullptr ) )
3313 triangulationValid =
false;
3320 triangulationValid =
true;
3323 return triangulationValid;
3342 bool directOk =
true;
3344 for(
int ii = 0; ii < srcSet->
OutlineCount() && directOk; ++ii )
3349 if( poly.size() > 1 )
3362 for(
size_t jj = 1; jj < poly.size(); ++jj )
3402 double originalArea =
std::abs( poly.front().Area() );
3404 if( originalArea > 0.0 )
3406 double triArea = 0.0;
3411 double coverage = triArea / originalArea;
3413 if( coverage > 1.01 || coverage < 0.99 )
3426 bool splitOk =
true;
3428 for(
int jj = 0; jj < splitSet.
OutlineCount() && splitOk; ++jj )
3453 bool fallbackOk =
true;
3455 for(
int ii = 0; ii < srcSet->
OutlineCount() && fallbackOk; ++ii )
3460 for(
size_t jj = 1; jj < poly.size(); ++jj )
3517 hash.
add( outline.size() );
3521 hash.
add( lc.PointCount() );
3523 for(
int i = 0; i < lc.PointCount(); i++ )
3551 std::set<long long> ptHashes;
3555 for(
const VECTOR2I& pt : lc.CPoints() )
3557 const long long ptHash = (
long long) pt.x << 32 | pt.y;
3559 if( ptHashes.count( ptHash ) > 0 )
3562 ptHashes.insert( ptHash );
3581 n += t->GetTriangleCount();
3594 aSubshapes.push_back( &tri );
3605 if( aClearance != 0 )
3647 if( triCount < 2 || triCount >
static_cast<size_t>( std::numeric_limits<int>::max() ) / 6 )
3650 const int halfEdgeCount =
static_cast<int>( 3 * triCount );
3653 std::vector<int> heVertex( halfEdgeCount );
3655 for(
size_t k = 0; k < triCount; k++ )
3662 auto nextInTriangle = [](
int aEdge ) {
return aEdge - aEdge % 3 + ( aEdge + 1 ) % 3; };
3666 std::vector<int> twin( halfEdgeCount, -1 );
3670 while( tableSize < halfEdgeCount * 2 )
3673 const uint32_t tableMask =
static_cast<uint32_t
>( tableSize - 1 );
3674 std::vector<int> slotEdge( tableSize, -1 );
3678 std::vector<char> retired( tableSize, 0 );
3679 std::vector<int> toVisit;
3680 std::vector<char> queued( halfEdgeCount, 0 );
3682 for(
int edge = 0; edge < halfEdgeCount; edge++ )
3684 int from = heVertex[edge];
3685 int to = heVertex[nextInTriangle( edge )];
3686 uint32_t keyLow =
static_cast<uint32_t
>( from < to ? from : to );
3687 uint32_t keyHigh =
static_cast<uint32_t
>( from < to ? to : from );
3688 uint32_t slot = ( ( keyLow * 0x9e3779b1u ) ^ ( keyHigh * 0x85ebca6bu ) ) & tableMask;
3689 bool handled =
false;
3691 while( slotEdge[slot] != -1 )
3693 int other = slotEdge[slot];
3694 int otherFrom = heVertex[other];
3695 int otherTo = heVertex[nextInTriangle( other )];
3696 uint32_t otherLow =
static_cast<uint32_t
>( otherFrom < otherTo ? otherFrom : otherTo );
3697 uint32_t otherHigh =
static_cast<uint32_t
>( otherFrom < otherTo ? otherTo : otherFrom );
3699 if( otherLow == keyLow && otherHigh == keyHigh )
3705 else if( twin[other] == -1 )
3710 toVisit.push_back( other );
3715 int mate = twin[other];
3725 slot = ( slot + 1 ) & tableMask;
3729 slotEdge[slot] = edge;
3732 while( !toVisit.empty() )
3734 int edge = toVisit.back();
3738 int twinEdge = twin[edge];
3740 if( twinEdge == -1 )
3743 int tri = edge - edge % 3;
3744 int twinTri = twinEdge - twinEdge % 3;
3746 int apexAEdge = tri + ( edge + 2 ) % 3;
3747 int sharedEndEdge = tri + ( edge + 1 ) % 3;
3748 int apexBEdge = twinTri + ( twinEdge + 2 ) % 3;
3749 int twinSharedEdge = twinTri + ( twinEdge + 1 ) % 3;
3751 int apexA = heVertex[apexAEdge];
3752 int sharedFrom = heVertex[edge];
3753 int sharedTo = heVertex[sharedEndEdge];
3754 int apexB = heVertex[apexBEdge];
3776 if( legal && curSlivers == 0 )
3782 bool doFlip = ( flipSlivers != curSlivers ) ? ( flipSlivers < curSlivers ) : !legal;
3787 heVertex[edge] = apexB;
3788 heVertex[twinEdge] = apexA;
3790 int apexBOuterTwin = twin[apexBEdge];
3791 int apexAOuterTwin = twin[apexAEdge];
3793 twin[edge] = apexBOuterTwin;
3795 if( apexBOuterTwin != -1 )
3796 twin[apexBOuterTwin] = edge;
3798 twin[twinEdge] = apexAOuterTwin;
3800 if( apexAOuterTwin != -1 )
3801 twin[apexAOuterTwin] = twinEdge;
3803 twin[apexAEdge] = apexBEdge;
3804 twin[apexBEdge] = apexAEdge;
3807 for(
int outer : { edge, twinEdge, sharedEndEdge, twinSharedEdge } )
3809 if( twin[outer] != -1 && !queued[outer] )
3812 toVisit.push_back( outer );
3817 std::deque<TRI> refined;
3819 for(
size_t i = 0; i < triCount; i++ )
3820 refined.emplace_back( heVertex[3 * i + 0], heVertex[3 * i + 1], heVertex[3 * i + 2],
this );
3843 for(
int i = 0; i <
path.PointCount(); i++ )
3847 vec.
x = ( pt.
x - aCenter.
x ) * aScaleFactorX;
3848 vec.
y = ( pt.
y - aCenter.
y ) * aScaleFactorY;
3851 path.SetPoint( i, pt );
3865 Clipper2Lib::Clipper64 clipper;
3866 Clipper2Lib::PolyTree64 tree;
3867 Clipper2Lib::Paths64 paths;
3871 Clipper2Lib::Path64 lc;
3872 lc.reserve(
path.PointCount() );
3874 for(
int i = 0; i <
path.PointCount(); i++ )
3875 lc.emplace_back(
path.CPoint( i ).x,
path.CPoint( i ).y );
3877 paths.push_back( std::move( lc ) );
3880 clipper.AddSubject( paths );
3881 clipper.Execute( Clipper2Lib::ClipType::Union, aEvenOdd ? Clipper2Lib::FillRule::EvenOdd
3882 : Clipper2Lib::FillRule::NonZero, tree );
3884 std::vector<CLIPPER_Z_VALUE> zValues;
3885 std::vector<SHAPE_ARC> arcBuffer;
3905 int aSpacing,
int aLineLength )
const
3907 std::vector<SEG> hatchLines;
3920 if( iterator->x < min_x )
3921 min_x = iterator->x;
3923 if( iterator->x > max_x )
3924 max_x = iterator->x;
3926 if( iterator->y < min_y )
3927 min_y = iterator->y;
3929 if( iterator->y > max_y )
3930 max_y = iterator->y;
3933 auto sortEndsByDescendingX =
3936 return tst.x < ref.
x;
3939 for(
double slope : aSlopes )
3941 int64_t max_a, min_a;
3954 min_a = ( min_a / aSpacing ) * aSpacing;
3957 std::vector<VECTOR2I> pointbuffer;
3958 pointbuffer.reserve( 256 );
3960 for( int64_t a = min_a; a < max_a; a += aSpacing )
3962 pointbuffer.clear();
3967 const SEG seg = *iterator;
3973 if( pt.
x < min_x || pt.
x > max_x || pt.
y < min_y || pt.
y > max_y )
3984 if( pointbuffer.size() > 2 )
3985 sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX );
3988 for(
size_t ip = 0; ip + 1 < pointbuffer.size(); ip++ )
3990 const VECTOR2I& p1 = pointbuffer[ip];
3991 const VECTOR2I& p2 = pointbuffer[ip + 1];
3997 SEG candidate( p1, p2 );
3999 VECTOR2I mid( ( candidate.
A.
x + candidate.
B.
x ) / 2, ( candidate.
A.
y + candidate.
B.
y ) / 2 );
4004 int dx = p2.
x - p1.
x;
4008 if( aLineLength == -1 ||
std::abs( dx ) < 2 * aLineLength )
4010 hatchLines.emplace_back( candidate );
4014 double dy = p2.
y - p1.
y;
4024 int y1 =
KiROUND( p1.
y + dx * slope );
4025 int y2 =
KiROUND( p2.
y - dx * slope );
4027 hatchLines.emplace_back(
SEG( p1.
x, p1.
y, x1, y1 ) );
4029 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
void Refine()
Refine this triangulation in place with boundary-preserving Lawson edge flips, minimizing the number ...
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
Exact orientation and in-circle predicates over integer coordinates.
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.
int OrientationSign(const VECTOR2I &a, const VECTOR2I &b, const VECTOR2I &c)
Orientation of triangle (a, b, c): +1 counter-clockwise, -1 clockwise, 0 collinear.
bool InCircleDelaunayLegal(const VECTOR2I &a, const VECTOR2I &b, const VECTOR2I &c, const VECTOR2I &p)
True when p is outside the circumcircle of CCW triangle (a, b, c): the shared edge is already Delauna...
bool IsSliverTriangle(const VECTOR2I &a, const VECTOR2I &b, const VECTOR2I &c)
A triangle is a sliver when its longest edge exceeds ten times its shortest.
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.
constexpr int sign(T val)
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D