37#include <unordered_map>
38#include <unordered_set>
44#include <clipper2/clipper.h>
68#if defined( __MINGW32__ )
69 #define TRIANGULATESIMPLIFICATIONLEVEL 50
70 #define ENABLECACHEFRIENDLYFRACTURE true
71 #define ENABLEFRACTUREEDGEINDEX true
73 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
74 #define ENABLECACHEFRIENDLYFRACTURE ADVANCED_CFG::GetCfg().m_EnableCacheFriendlyFracture
75 #define ENABLEFRACTUREEDGEINDEX ADVANCED_CFG::GetCfg().m_EnableFractureEdgeIndex
120 m_triangulatedPolys.reserve( aOther.TriangulatedPolyCount() );
122 for( unsigned i = 0; i < aOther.TriangulatedPolyCount(); i++ )
124 const TRIANGULATED_POLYGON* poly = aOther.TriangulatedPolygon( i );
125 m_triangulatedPolys.push_back( std::make_unique<TRIANGULATED_POLYGON>( *poly ) );
136 m_triangulationValid =
false;
139 m_failedHash = aOther.m_failedHash;
140 m_failedHashValid.store( aOther.m_failedHashValid.load() );
175 unsigned int contourIdx = 0;
178 int currentGlobalIdx = 0;
180 for( polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
184 for( contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
187 int totalPoints = currentContour.
PointCount();
189 for( vertexIdx = 0; vertexIdx < totalPoints; vertexIdx++ )
192 if( currentGlobalIdx == aGlobalIdx )
194 aRelativeIndices->
m_polygon = polygonIdx;
195 aRelativeIndices->
m_contour = contourIdx;
196 aRelativeIndices->
m_vertex = vertexIdx;
212 int& aGlobalIdx )
const
214 int selectedVertex = aRelativeIndices.
m_vertex;
215 unsigned int selectedContour = aRelativeIndices.
m_contour;
216 unsigned int selectedPolygon = aRelativeIndices.
m_polygon;
219 if( selectedPolygon <
m_polys.size() && selectedContour <
m_polys[selectedPolygon].size()
220 && selectedVertex <
m_polys[selectedPolygon][selectedContour].PointCount() )
226 for(
unsigned int polygonIdx = 0; polygonIdx < selectedPolygon; polygonIdx++ )
228 currentPolygon =
Polygon( polygonIdx );
230 for(
unsigned int contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
231 aGlobalIdx += currentPolygon[contourIdx].PointCount();
234 currentPolygon =
Polygon( selectedPolygon );
236 for(
unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx++ )
237 aGlobalIdx += currentPolygon[contourIdx].PointCount();
239 aGlobalIdx += selectedVertex;
256 poly.push_back( empty_path );
257 m_polys.push_back( std::move( poly ) );
273 m_polys[aOutline].push_back( empty_path );
275 return m_polys.back().size() - 2;
293 assert( aOutline < (
int)
m_polys.size() );
294 assert( idx < (
int)
m_polys[aOutline].size() );
296 m_polys[aOutline][idx].Append( x, y, aAllowDuplication );
298 return m_polys[aOutline][idx].PointCount();
303 std::optional<int> aMaxError )
317 assert( aOutline < (
int)
m_polys.size() );
318 assert( idx < (
int)
m_polys[aOutline].size() );
320 if( aMaxError.has_value() )
321 m_polys[aOutline][idx].Append( aArc, aMaxError.value() );
323 m_polys[aOutline][idx].Append( aArc );
325 return m_polys[aOutline][idx].PointCount();
333 if( aGlobalIndex < 0 )
346 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
366 if( aOutline >= (
int)
m_polys.size() )
369 if( idx >= (
int)
m_polys[aOutline].size() )
372 return m_polys[aOutline][idx].PointCount();
387 for(
int idx = 0; idx <=
HoleCount( ii ); idx++ )
389 full_count +=
m_polys[ii][idx].PointCount();
399 assert( aFirstPolygon >= 0 && aLastPolygon <=
OutlineCount() );
422 assert( aOutline < (
int)
m_polys.size() );
423 assert( idx < (
int)
m_polys[aOutline].size() );
425 return m_polys[aOutline][idx].CPoint( aIndex );
435 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
460 if(
index.m_vertex == 0 )
462 index.m_vertex = lastpoint - 1;
465 else if(
index.m_vertex == lastpoint )
483 *aPrevious = previous;
499 std::vector<SEG> segments;
503 segments.emplace_back( *it );
505 std::sort( segments.begin(), segments.end(), [](
const SEG& a,
const SEG& b )
507 int min_a_x = std::min( a.A.x, a.B.x );
508 int min_b_x = std::min( b.A.x, b.B.x );
510 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 ) );
513 for(
auto it = segments.begin(); it != segments.end(); ++it )
515 SEG& firstSegment = *it;
518 auto innerIterator = it;
519 int max_x = std::max( firstSegment.
A.
x, firstSegment.
B.
x );
520 int max_y = std::max( firstSegment.
A.
y, firstSegment.
B.
y );
523 for( innerIterator++; innerIterator != segments.end(); innerIterator++ )
525 SEG& secondSegment = *innerIterator;
526 int min_x = std::min( secondSegment.
A.
x, secondSegment.
B.
x );
527 int min_y = std::min( secondSegment.
A.
y, secondSegment.
B.
y );
531 if( max_x < min_x || ( max_x == min_x && max_y < min_y ) )
535 bool adjacent = ( index_diff == 1) || (index_diff == ((
int)segments.size() - 1) );
538 if( !adjacent && firstSegment.
Collide( secondSegment, 0 ) )
549 for(
unsigned int polygon = 0; polygon <
m_polys.size(); polygon++ )
563 poly.push_back( aOutline );
568 wxCHECK2_MSG( aOutline.
IsClosed(), poly.back().SetClosed(
true ),
569 "Warning: non-closed outline added to SHAPE_POLY_SET" );
571 m_polys.push_back( std::move( poly ) );
573 return (
int)
m_polys.size() - 1;
582 aOutline += (int)
m_polys.size();
584 assert( aOutline < (
int)
m_polys.size() );
588 assert( poly.size() );
590 poly.push_back( aHole );
592 return (
int) poly.size() - 2;
612 for(
int j = 0; j <
HoleCount( i ); j++ )
626 for(
size_t i = 0; i < poly.size(); i++ )
638 for(
size_t i = 0; i < poly.size(); i++ )
641 aArcBuffer.push_back( arc );
651 for(
size_t i = 0; i < poly.size(); i++ )
659 std::vector<SHAPE_LINE_CHAIN> contours;
662 contours.insert( contours.end(), poly.begin(), poly.end() );
664 std::map<int, std::set<int>> parentToChildren;
665 std::map<int, std::set<int>> childToParents;
668 contour.GenerateBBoxCache();
670 for(
size_t i = 0; i < contours.size(); i++ )
674 for(
size_t j = 0; j < contours.size(); j++ )
684 parentToChildren[i].emplace( j );
685 childToParents[j].emplace( i );
690 std::set<int> topLevelParents;
692 for(
size_t i = 0; i < contours.size(); i++ )
694 if( childToParents[i].size() == 0 )
696 topLevelParents.emplace( i );
702 std::function<void(
int,
int, std::vector<int> )>
process;
705 [&](
int myId,
int parentOutlineId,
const std::vector<int>&
path )
707 std::set<int> relParents = childToParents[myId];
709 for(
int pathId :
path )
711 int erased = relParents.erase( pathId );
712 wxASSERT( erased > 0 );
715 wxASSERT( relParents.size() == 0 );
719 bool isOutline =
path.size() % 2 == 0;
723 int outlineId =
result.AddOutline( contours[myId] );
724 myOutline = outlineId;
728 wxASSERT( parentOutlineId != -1 );
729 result.AddHole( contours[myId], parentOutlineId );
732 auto it = parentToChildren.find( myId );
733 if( it != parentToChildren.end() )
735 std::vector<int> thisPath =
path;
736 thisPath.emplace_back( myId );
738 std::set<int> thisPathSet;
739 thisPathSet.insert( thisPath.begin(), thisPath.end() );
741 for(
int childId : it->second )
743 const std::set<int>& childPathSet = childToParents[childId];
745 if( thisPathSet != childPathSet )
748 process( childId, myOutline, thisPath );
753 for(
int topParentId : topLevelParents )
755 std::vector<int>
path;
759 *
this = std::move(
result );
782 bool operator==(
const DIRECTED_EDGE& aOther )
const
784 return from == aOther.from && to == aOther.to;
788 struct DIRECTED_EDGE_HASH
790 std::size_t operator()(
const DIRECTED_EDGE& aEdge )
const
792 std::size_t seed = 0x51ed27a3;
793 hash_combine( seed, aEdge.from.x, aEdge.from.y, aEdge.to.x, aEdge.to.y );
798 const std::vector<VECTOR2I>& pts = aChain.
CPoints();
799 const int count =
static_cast<int>( pts.size() );
802 std::unordered_map<DIRECTED_EDGE, std::vector<int>, DIRECTED_EDGE_HASH> unpaired;
803 std::vector<int>
partner( count, -1 );
804 bool hasBridge =
false;
806 unpaired.reserve( count );
808 for(
int ii = 0; ii < count; ++ii )
811 const VECTOR2I& b = pts[( ii + 1 ) % count];
816 auto twin = unpaired.find( { b, a } );
818 if( twin != unpaired.end() )
820 const int jj = twin->second.back();
822 twin->second.pop_back();
824 if( twin->second.empty() )
825 unpaired.erase( twin );
833 unpaired[{ a, b }].push_back( ii );
842 [&](
int aEdge ) ->
int
844 int next = ( aEdge + 1 ) % count;
857 std::vector<bool> visited( count,
false );
859 for(
int start = 0; start < count; ++start )
861 if( visited[start] ||
partner[start] >= 0 )
869 if( edge < 0 || visited[edge] )
875 visited[edge] =
true;
876 ring.
Append( pts[edge],
true );
877 edge = nextEdge( edge );
878 }
while( edge != start );
883 aRings.push_back( std::move( ring ) );
892 std::vector<CLIPPER_Z_VALUE>& aZValues,
893 std::vector<SHAPE_ARC>& aArcBuffer )
896 constexpr int kMinPoints = 1024;
901 std::vector<SHAPE_LINE_CHAIN> rings;
908 const bool flip = aChain.
Area(
false ) < 0;
911 aPaths.push_back( ring.convertToClipper2( ( ring.Area(
false ) >= 0 ) != flip, aZValues, aArcBuffer ) );
923 wxFAIL_MSG( wxT(
"Boolean ops on curved polygons are not supported. You should call "
924 "ClearArcs() before carrying out the boolean operation." ) );
927 Clipper2Lib::Clipper64 c;
929 std::vector<CLIPPER_Z_VALUE> zValues;
930 std::vector<SHAPE_ARC> arcBuffer;
931 std::map<VECTOR2I, CLIPPER_Z_VALUE> newIntersectPoints;
933 Clipper2Lib::Paths64 paths;
934 Clipper2Lib::Paths64 clips;
941 for(
size_t i = 0; i < poly.size(); i++ )
943 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
952 for(
size_t i = 0; i < poly.size(); i++ )
954 clips.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
958 c.AddSubject( paths );
961 Clipper2Lib::PolyTree64 solution;
963 Clipper2Lib::ZCallback64 callback =
964 [&](
const Clipper2Lib::Point64 & e1bot,
const Clipper2Lib::Point64 & e1top,
965 const Clipper2Lib::Point64 & e2bot,
const Clipper2Lib::Point64 & e2top,
966 Clipper2Lib::Point64 & pt )
969 [&](
const ssize_t& aZvalue,
const ssize_t& aCompareVal = -1 ) -> ssize_t
973 retval = zValues.at( aZvalue ).m_SecondArcIdx;
975 if( retval == -1 || ( aCompareVal > 0 && retval != aCompareVal ) )
976 retval = zValues.at( aZvalue ).m_FirstArcIdx;
982 [&](
const ssize_t& aBottomZ,
const ssize_t aTopZ ) -> ssize_t
984 ssize_t retval = arcIndex( aBottomZ );
988 if( retval != arcIndex( aTopZ, retval ) )
995 ssize_t e1ArcSegmentIndex = arcSegment( e1bot.z, e1top.z );
996 ssize_t e2ArcSegmentIndex = arcSegment( e2bot.z, e2top.z );
1000 if( e1ArcSegmentIndex != -1 )
1011 size_t z_value_ptr = zValues.size();
1012 zValues.push_back( newZval );
1016 newIntersectPoints.insert( {
VECTOR2I( pt.x, pt.y ), newZval } );
1022 c.SetZCallback( std::move( callback ) );
1024 c.Execute( aType, Clipper2Lib::FillRule::NonZero, solution );
1033 booleanOp( Clipper2Lib::ClipType::Union, b );
1039 booleanOp( Clipper2Lib::ClipType::Difference, b );
1045 booleanOp( Clipper2Lib::ClipType::Intersection, b );
1051 booleanOp( Clipper2Lib::ClipType::Xor, b );
1057 booleanOp( Clipper2Lib::ClipType::Union, a, b );
1063 booleanOp( Clipper2Lib::ClipType::Difference, a, b );
1069 booleanOp( Clipper2Lib::ClipType::Intersection, a, b );
1075 booleanOp( Clipper2Lib::ClipType::Xor, a, b );
1083 Inflate( aFactor, aCornerStrategy, aMaxError );
1091 using namespace Clipper2Lib;
1095 #define SEG_CNT_MAX 64
1096 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
1103 JoinType joinType = JoinType::Round;
1104 double miterLimit = 2.0;
1106 switch( aCornerStrategy )
1109 joinType = JoinType::Miter;
1114 joinType = JoinType::Miter;
1118 joinType = JoinType::Miter;
1122 joinType = JoinType::Square;
1126 joinType = JoinType::Round;
1130 std::vector<CLIPPER_Z_VALUE> zValues;
1131 std::vector<SHAPE_ARC> arcBuffer;
1137 for(
size_t i = 0; i < poly.size(); i++ )
1138 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
1140 c.AddPaths( paths, joinType, EndType::Polygon );
1147 if( aCircleSegCount < 6 )
1148 aCircleSegCount = 6;
1152 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
1154 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
1157 arc_tolerance_factor[aCircleSegCount] = coeff;
1161 coeff = arc_tolerance_factor[aCircleSegCount];
1164 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1165 c.MiterLimit( miterLimit );
1172 c.Execute( aAmount, paths );
1174 Clipper2Lib::SimplifyPaths( paths,
std::abs( aAmount ) * coeff,
true );
1177 c2.PreserveCollinear(
false );
1178 c2.ReverseSolution(
false );
1179 c2.AddSubject( paths );
1180 c2.Execute(ClipType::Union, FillRule::Positive, tree);
1184 c.Execute( aAmount, tree );
1195 using namespace Clipper2Lib;
1199 #define SEG_CNT_MAX 64
1200 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
1207 JoinType joinType = JoinType::Round;
1208 double miterLimit = 2.0;
1210 switch( aCornerStrategy )
1213 joinType = JoinType::Miter;
1218 joinType = JoinType::Miter;
1222 joinType = JoinType::Miter;
1226 joinType = JoinType::Square;
1230 joinType = JoinType::Round;
1234 std::vector<CLIPPER_Z_VALUE> zValues;
1235 std::vector<SHAPE_ARC> arcBuffer;
1238 c.AddPath(
path, joinType, EndType::Butt );
1244 if( aCircleSegCount < 6 )
1245 aCircleSegCount = 6;
1249 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
1251 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
1254 arc_tolerance_factor[aCircleSegCount] = coeff;
1258 coeff = arc_tolerance_factor[aCircleSegCount];
1261 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1262 c.MiterLimit( miterLimit );
1269 c.Execute( aAmount, paths2 );
1271 Clipper2Lib::SimplifyPaths( paths2,
std::abs( aAmount ) * coeff,
false );
1274 c2.PreserveCollinear(
false );
1275 c2.ReverseSolution(
false );
1276 c2.AddSubject( paths2 );
1277 c2.Execute( ClipType::Union, FillRule::Positive, tree );
1281 c.Execute( aAmount, tree );
1294 inflate2( aAmount, segCount, aCornerStrategy, aSimplify );
1303 inflateLine2( aLine, aAmount, segCount, aCornerStrategy, aSimplify );
1308 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1309 const std::vector<SHAPE_ARC>& aArcBuffer )
1311 if( !aPolyPath->IsHole() )
1314 paths.reserve( aPolyPath->Count() + 1 );
1315 paths.emplace_back( aPolyPath->Polygon(), aZValueBuffer, aArcBuffer );
1317 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& child : *aPolyPath )
1319 paths.emplace_back( child->Polygon(), aZValueBuffer, aArcBuffer );
1321 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& grandchild : *child )
1325 m_polys.emplace_back( std::move( paths ) );
1331 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1332 const std::vector<SHAPE_ARC>& aArcBuffer )
1336 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& n : tree )
1342 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1343 const std::vector<SHAPE_ARC>& aArcBuffer )
1348 for(
const Clipper2Lib::Path64& n : aPath )
1350 if( Clipper2Lib::Area( n ) > 0 )
1359 wxCHECK2_MSG( !
path.empty(),
continue, wxT(
"Cannot add a hole before an outline" ) );
1362 path.emplace_back( n, aZValueBuffer, aArcBuffer );
1408 template <
typename Originals>
1410 uint32_t aStripeCount,
size_t aHoleCount ) :
1415 m_maxNodes( (
KIGEOM::FRACTURE_INDEX::MAX_BUCKET_SPAN + 2 ) * aHoleCount ),
1419 size_t bucketIds = 0;
1423 [&]( uint32_t, uint32_t aFirst, uint32_t aLast )
1431 bucketIds += aLast - aFirst + 1;
1433 for( uint32_t stripe = aFirst; stripe <= aLast; ++stripe )
1446 for( uint32_t stripe = 0; stripe <
m_stripeCount; ++stripe )
1455 [&]( uint32_t aEdge, uint32_t aFirst, uint32_t aLast )
1463 for( uint32_t stripe = aFirst; stripe <= aLast; ++stripe )
1473 for( uint32_t stripe = 0; stripe <
m_stripeCount; ++stripe )
1479 for(
size_t pos = 1; pos <
m_longIds.size(); ++pos )
1484 size_t allocated_bytes = 0;
1489 &allocated_bytes ) )
1500 template <
typename Visitor>
1501 void Query(
int aY,
Index aProvokingIndex, Visitor&& aVisitor )
const
1503 const uint32_t stripe =
map( aY );
1510 if( edge >=
static_cast<uint32_t
>( aProvokingIndex ) )
1513 aVisitor(
static_cast<Index>( edge ) );
1518 if( edge >=
static_cast<uint32_t
>( aProvokingIndex ) )
1521 aVisitor(
static_cast<Index>( edge ) );
1530 for(
Index edge = aFirst; edge < aFirst + 3; ++edge )
1540 for( uint32_t stripe = first; stripe <= last; ++stripe )
1541 insert(
static_cast<uint32_t
>( edge ), stripe );
1547 static constexpr uint32_t
INVALID = std::numeric_limits<uint32_t>::max();
1556 template <
typename Visitor>
1564 assert( node.
edge <
static_cast<uint32_t
>( aProvokingIndex ) );
1566 if( node.
edge <
static_cast<uint32_t
>( aProvokingIndex ) )
1567 aVisitor(
static_cast<Index>( node.
edge ) );
1573 void insert( uint32_t aEdge, uint32_t aHead )
1580 static void logAccepted(
size_t aEdges,
size_t aHoles, uint32_t aStripes,
size_t aBytes )
1582 wxLogTrace( wxS(
"KICAD_FRACTURE_INDEX" ),
1583 wxS(
"Using fracture edge index: edges=%zu, holes=%zu, stripes=%u, allocated=%zu bytes" ),
1584 aEdges, aHoles, aStripes, aBytes );
1606 return std::max( aEdge.
m_p1.
x, aEdge.
m_p2.
x );
1618 int x = edge.
m_p1.
x;
1619 int y = edge.
m_p1.
y;
1620 int min_dist = std::numeric_limits<int>::max();
1640 int dist = x - x_intersect;
1643 && ( dist < min_dist
1644 || ( nearest_index >= 0 && dist == min_dist && aCandidateIndex < nearest_index ) ) )
1647 x_nearest = x_intersect;
1648 nearest_index = aCandidateIndex;
1652 aIndex->
Query( y, provokingIndex, consider );
1654 if( nearest_index >= 0 )
1655 e_nearest = &edges[nearest_index];
1667 int dist = x - x_intersect;
1669 if( dist >= 0 && dist < min_dist )
1672 x_nearest = x_intersect;
1673 e_nearest = &candidate;
1686 edges[hole2outline_index] =
1689 edges[split_index] =
1696 e_nearest->
m_next = outline2hole_index;
1699 for( ; last->
m_next != edgeIndex; last = &edges[last->
m_next] )
1701 last->
m_next = hole2outline_index;
1714 bool outline =
true;
1716 if( paths.size() == 1 )
1719 size_t total_point_count = 0;
1723 total_point_count +=
path.PointCount();
1726 if( total_point_count > (
size_t) std::numeric_limits<FractureEdge::Index>::max() )
1728 wxLogWarning( wxT(
"Polygon has more points than int limit" ) );
1735 edges.reserve( total_point_count + paths.size() * 3 );
1741 int path_or_provoking_index;
1746 std::vector<PathInfo> sorted_paths;
1747 const int paths_count =
static_cast<int>( paths.size() );
1748 sorted_paths.reserve( paths_count );
1750 for(
int path_index = 0; path_index < paths_count; path_index++ )
1753 const std::vector<VECTOR2I>& points =
path.CPoints();
1754 const int point_count =
static_cast<int>( points.size() );
1755 int x_min = std::numeric_limits<int>::max();
1756 int y_min = std::numeric_limits<int>::max();
1759 for(
int point_index = 0; point_index < point_count; point_index++ )
1761 const VECTOR2I& point = points[point_index];
1762 if( point.
x < x_min )
1765 leftmost = point_index;
1767 if( point.
y < y_min )
1771 sorted_paths.emplace_back( PathInfo{ path_index, leftmost, x_min, y_min } );
1774 std::sort( sorted_paths.begin() + 1, sorted_paths.end(),
1775 [](
const PathInfo& a,
const PathInfo& b )
1778 return a.y_or_bridge < b.y_or_bridge;
1784 for( PathInfo& path_info : sorted_paths )
1787 const std::vector<VECTOR2I>& points =
path.CPoints();
1788 const size_t point_count = points.size();
1793 for(
size_t i = 0; i < point_count - 1; i++ )
1795 edges.emplace_back( points[i], points[i + 1], edge_index + 1 );
1800 edges.emplace_back( points[point_count - 1], points[0], provoking_edge );
1807 path_info.path_or_provoking_index = provoking_edge;
1808 path_info.y_or_bridge = edge_index;
1812 edges.resize( edge_index );
1818 assert( edges.size() == total_point_count + 3 * ( paths.size() - 1 ) );
1822 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); ++it )
1824 assert( it->path_or_provoking_index == expected_edge );
1825 expected_edge = it->y_or_bridge + 3;
1831 uint64_t estimated_visits = 0;
1833 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); ++it )
1834 estimated_visits +=
static_cast<uint64_t
>( it->path_or_provoking_index );
1836 std::unique_ptr<FRACTURE_EDGE_INDEX>
index;
1840 int min_y = std::numeric_limits<int>::max();
1841 int max_y = std::numeric_limits<int>::min();
1848 min_y = std::min( min_y, point.y );
1849 max_y = std::max( max_y, point.y );
1853 auto visitOriginals = [&](
auto&& aVisitor )
1861 min_y, max_y, stripe_count );
1862 aVisitor(
static_cast<uint32_t
>( edge ), first, last );
1866 visitRange( 0, paths[0].PointCount() );
1868 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); ++it )
1869 visitRange( it->path_or_provoking_index, it->y_or_bridge );
1872 index = std::make_unique<FRACTURE_EDGE_INDEX>( edges, visitOriginals, min_y, max_y, stripe_count,
1875 if( !
index->IsValid() )
1879 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); it++ )
1881 auto edge =
processHole( edges, it->path_or_provoking_index, it->path_or_provoking_index + it->leftmost,
1882 it->y_or_bridge,
index.get() );
1887 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
1935 int x = edge->
m_p1.
x;
1936 int y = edge->
m_p1.
y;
1937 int min_dist = std::numeric_limits<int>::max();
1944 if( !e->matches( y ) )
1949 if( e->m_p1.y == e->m_p2.y )
1951 x_intersect = std::max( e->m_p1.x, e->m_p2.x );
1955 x_intersect = e->m_p1.x
1956 +
rescale( e->m_p2.x - e->m_p1.x, y - e->m_p1.y, e->m_p2.y - e->m_p1.y );
1959 int dist = ( x - x_intersect );
1961 if( dist >= 0 && dist < min_dist && e->m_connected )
1964 x_nearest = x_intersect;
1980 edges.push_back( split_2 );
1981 edges.push_back( lead1 );
1982 edges.push_back( lead2 );
1987 e_nearest->
m_next = lead1;
1992 for( last = edge; last->
m_next != edge; last = last->
m_next )
2018 if( paths.size() == 1 )
2021 int num_unconnected = 0;
2025 const std::vector<VECTOR2I>& points =
path.CPoints();
2026 int pointCount = points.size();
2030 int x_min = std::numeric_limits<int>::max();
2032 for(
int i = 0; i < pointCount; i++ )
2034 if( points[i].x < x_min )
2035 x_min = points[i].x;
2040 points[i + 1 == pointCount ? 0 : i + 1] );
2051 if( i == pointCount - 1 )
2055 edges.push_back( fe );
2059 if( fe->
m_p1.
x == x_min )
2060 border_edges.push_back( fe );
2071 while( num_unconnected > 0 )
2073 int x_min = std::numeric_limits<int>::max();
2074 auto it = border_edges.begin();
2079 for( ; it != border_edges.end(); ++it )
2082 int xt = border_edge->
m_p1.
x;
2084 if( ( xt <= x_min ) && !border_edge->
m_connected )
2087 smallestX = border_edge;
2091 int num_processed =
processEdge( edges, smallestX );
2094 if( !num_processed )
2096 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
2104 num_unconnected -= num_processed;
2122 paths.push_back( std::move( newPath ) );
2146 assert( aPoly.size() == 1 );
2154 std::vector<SHAPE_LINE_CHAIN> rings;
2158 aPoly[0] = std::move( lc );
2162 auto outline = std::max_element( rings.begin(), rings.end(),
2165 return std::fabs( aA.Area() ) < std::fabs( aB.Area() );
2168 std::iter_swap( rings.begin(), outline );
2169 aPoly = std::move( rings );
2179 if( paths.size() > 1 )
2203 std::array<VECTOR2I,4> pts = { aSegA.
A, aSegA.
B, aSegB.
A, aSegB.
B };
2205 std::sort( pts.begin(), pts.end(), [axis](
const VECTOR2I& p,
const VECTOR2I& q )
2208 return p.x < q.x || ( p.x == q.x && p.y < q.y );
2210 return p.y < q.y || ( p.y == q.y && p.x < q.x );
2231 wxLogTrace( wxT(
"collinear" ), wxT(
"Found exterior waist between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)" ),
2232 aSegA.
A.
x, aSegA.
A.
y, aSegA.
B.
x, aSegA.
B.
y,
2233 aSegB.
A.
x, aSegB.
A.
y, aSegB.
B.
x, aSegB.
B.
y );
2244 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
2246 bool changed =
true;
2255 std::vector<SEG> segs;
2256 segs.reserve( count );
2258 for( intptr_t i = 0; i < count; ++i )
2259 segs.emplace_back( outline.
CPoint( i ), outline.
CPoint( ( i + 1 ) % count ) );
2267 for( intptr_t i = 0; i < count && !found; ++i )
2274 [&](
int j ) ->
bool
2276 if( j == i || j == ( ( i + 1 ) % count ) || j == ( ( i + count - 1 ) % count ) )
2281 SEG other( oa, ob );
2285 if( oa == a && ob == b )
2288 if( oa == b && ob == a )
2302 index.VisitCandidates( seg, 0, visitor );
2309 int a1 = ( segA + 1 ) % outline.
PointCount();
2311 int b1 = ( segB + 1 ) % outline.
PointCount();
2337 m_polys[polyIdx][0] = std::move( lc1 );
2340 np.push_back( std::move( lc2 ) );
2341 m_polys.push_back( std::move( np ) );
2351 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
2353 bool changed =
true;
2367 std::vector<double> prefix;
2370 [&](
int aFrom,
int aTo ) ->
double
2372 if( prefix.empty() )
2374 prefix.resize( count + 1, 0.0 );
2376 for(
int ii = 0; ii < count; ++ii )
2381 prefix[ii + 1] = prefix[ii]
2382 + ( (double) a.
x + b.
x ) * ( (
double) a.
y - b.
y );
2386 double term = aFrom <= aTo ? prefix[aTo] - prefix[aFrom]
2387 : prefix[count] - prefix[aFrom] + prefix[aTo];
2392 return term + ( (double) last.
x + first.
x ) * ( (
double) last.
y - first.
y );
2396 [&](
int aFrom,
int aTo )
2398 return aFrom <= aTo ? aTo - aFrom + 1 : count - aFrom + aTo + 1;
2404 [&](
int aVertIdx,
int aSegIdx )
2406 const int start = ( aSegIdx + 1 ) % count;
2408 if( ringSize( start, aVertIdx ) < 3 || ringSize( aVertIdx, aSegIdx ) < 3 )
2411 const bool sign = ringTerm( 0, count - 1 ) > 0;
2412 const double term1 = ringTerm( start, aVertIdx );
2413 const double term2 = ringTerm( aVertIdx, aSegIdx );
2415 return term1 != 0.0 && term2 != 0.0 && ( term1 > 0 ) ==
sign
2416 && ( term2 > 0 ) ==
sign;
2419 int insertSegIdx = -1;
2420 int insertVertIdx = -1;
2424 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2427 const int prevSeg = ( vertIdx + count - 1 ) % count;
2429 for(
int segIdx = 0; segIdx < count; ++segIdx )
2432 if( segIdx == prevSeg || segIdx == vertIdx )
2443 && splittable( vertIdx, segIdx ) )
2445 insertSegIdx = segIdx;
2446 insertVertIdx = vertIdx;
2454 std::vector<SEG> segs;
2455 segs.reserve( count );
2457 for(
int i = 0; i < count; ++i )
2458 segs.emplace_back( outline.
CPoint( i ), outline.
CPoint( ( i + 1 ) % count ) );
2462 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2465 const int prevSeg = ( vertIdx + count - 1 ) % count;
2468 [&](
int segIdx ) ->
bool
2470 if( segIdx == prevSeg || segIdx == vertIdx )
2481 && splittable( vertIdx, segIdx ) )
2483 insertSegIdx = segIdx;
2484 insertVertIdx = vertIdx;
2491 index.VisitCandidates(
SEG( pt, pt ), 0, pinchVisitor );
2495 if( insertSegIdx < 0 )
2502 const int splitStart1 = ( insertSegIdx + 1 ) % count;
2503 const int size1 = ringSize( splitStart1, insertVertIdx );
2504 const int size2 = ringSize( insertVertIdx, insertSegIdx );
2511 int idx = splitStart1;
2513 for(
int i = 0; i < size1; ++i )
2516 idx = ( idx + 1 ) % count;
2521 idx = insertVertIdx;
2523 for(
int i = 0; i < size2; ++i )
2526 idx = ( idx + 1 ) % count;
2531 m_polys[polyIdx][0] = std::move( poly1 );
2534 np.push_back( std::move( poly2 ) );
2535 m_polys.push_back( std::move( np ) );
2559 path.Simplify( aTolerance );
2577 while( outline.size() > 1 )
2602 std::stringstream ss;
2604 ss <<
"SHAPE_LINE_CHAIN poly; \n";
2606 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2608 for(
unsigned j = 0; j <
m_polys[i].size(); j++ )
2611 ss <<
"{ auto tmp = " <<
m_polys[i][j].Format() <<
";\n";
2617 ss <<
" poly.AddOutline(tmp); } \n";
2621 ss <<
" poly.AddHole(tmp); } \n";
2637 if( tmp !=
"polyset" )
2642 int n_polys = atoi( tmp.c_str() );
2647 for(
int i = 0; i < n_polys; i++ )
2657 int n_outlines = atoi( tmp.c_str() );
2659 if( n_outlines < 0 )
2662 for(
int j = 0; j < n_outlines; j++ )
2669 int n_vertices = atoi( tmp.c_str() );
2671 for(
int v = 0; v < n_vertices; v++ )
2675 aStream >> tmp; p.
x = atoi( tmp.c_str() );
2676 aStream >> tmp; p.
y = atoi( tmp.c_str() );
2680 paths.push_back( std::move( outline ) );
2683 m_polys.push_back( std::move( paths ) );
2694 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2711 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2714 bb = *
m_polys[i][0].GetCachedBBox();
2731 if( lineChain.PointOnEdge( aP, aAccuracy ) )
2746 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2749 *aLocation = nearest;
2752 *aActual = sqrt( dist_sq );
2770 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2773 *aLocation = nearest;
2776 *aActual = sqrt( dist_sq );
2793 int extra = segment->
GetWidth() / 2;
2795 if(
Collide( segment->
GetSeg(), aClearance + extra, aActual, aLocation ) )
2798 *aActual = std::max( 0, *aActual - extra );
2809 int extra =
circle->GetRadius();
2811 if(
Collide(
circle->GetCenter(), aClearance + extra, aActual, aLocation ) )
2814 *aActual = std::max( 0, *aActual - extra );
2824 if( !aActual && !aLocation )
2834 *aActual = std::max( 0,
actual );
2856 if( aPolygonIdx < 0 )
2857 aPolygonIdx +=
m_polys.size();
2859 m_polys[aPolygonIdx].erase(
m_polys[aPolygonIdx].begin() + aContourIdx );
2879 std::vector<VERTEX_INDEX> indices_to_remove;
2884 segmentStart = *iterator;
2890 segmentEnd = contourStart;
2898 contourStart = *iterator;
2906 wxCHECK_MSG( iterator, removed, wxT(
"Invalid polygon. Reached end without noticing. Please report this error" ) );
2908 segmentEnd = *iterator;
2912 if( segmentStart == segmentEnd )
2914 indices_to_remove.push_back( indexStart );
2921 for(
auto it = indices_to_remove.rbegin(); it != indices_to_remove.rend(); ++it )
2946 if( triangleSet->GetSourceOutlineIndex() == aIdx )
2948 else if( triangleSet->GetSourceOutlineIndex() > aIdx )
2949 triangleSet->SetSourceOutlineIndex( triangleSet->GetSourceOutlineIndex() - 1 );
2976 Append( aP.
x, aP.
y, aOutline, aHole );
2982 int aClearance )
const
2985 bool collision =
false;
2995 delta = *iterator - aPoint;
2998 distance_squared =
delta.SquaredEuclideanNorm();
3001 if( distance_squared <= clearance_squared )
3003 if( !aClosestVertex )
3009 clearance_squared = distance_squared;
3012 *aClosestVertex = iterator.GetIndex();
3022 int aClearance )
const
3025 bool collision =
false;
3030 const SEG currentSegment = *iterator;
3034 if( distance_squared <= clearance_squared )
3036 if( !aClosestVertex )
3042 clearance_squared = distance_squared;
3045 *aClosestVertex = iterator.GetIndex();
3055 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
3059 for(
int holeIdx = 0; holeIdx <
HoleCount( polygonIdx ); holeIdx++ )
3066 bool aUseBBoxCaches )
const
3072 if( aSubpolyIndex >= 0 )
3073 return containsSingle( aP, aSubpolyIndex, aAccuracy, aUseBBoxCaches );
3076 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
3078 if(
containsSingle( aP, polygonIdx, aAccuracy, aUseBBoxCaches ) )
3094 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
3111 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
3122 bool aUseBBoxCaches )
const
3128 for(
int holeIdx = 0; holeIdx <
HoleCount( aSubpolyIndex ); holeIdx++ )
3152 path.Move( aVector );
3156 tri->Move( aVector );
3168 path.Mirror( aRef, aFlipDirection );
3181 path.Rotate( aAngle, aCenter );
3197 c +=
path.PointCount();
3234 SEG::ecoord minDistance = (*iterator).SquaredDistance( aPoint );
3237 *aNearest = ( *iterator ).NearestPoint( aPoint );
3239 for( iterator++; iterator && minDistance > 0; iterator++ )
3241 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aPoint );
3243 if( currentDistance < minDistance )
3246 *aNearest = (*iterator).NearestPoint( aPoint );
3248 minDistance = currentDistance;
3264 *aNearest = ( aSegment.
A + aSegment.
B ) / 2;
3270 SEG::ecoord minDistance = (*iterator).SquaredDistance( aSegment );
3273 *aNearest = ( *iterator ).NearestPoint( aSegment );
3275 for( iterator++; iterator && minDistance > 0; iterator++ )
3277 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aSegment );
3279 if( currentDistance < minDistance )
3282 *aNearest = (*iterator).NearestPoint( aSegment );
3284 minDistance = currentDistance;
3289 return minDistance < 0 ? 0 : minDistance;
3296 wxASSERT_MSG( !aOutlineOnly, wxT(
"Warning: SHAPE_POLY_SET::SquaredDistance does not yet "
3297 "support aOutlineOnly==true" ) );
3304 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
3307 aNearest ? &nearest :
nullptr );
3309 if( currentDistance_sq < minDistance_sq )
3312 *aNearest = nearest;
3314 minDistance_sq = currentDistance_sq;
3318 return minDistance_sq;
3329 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
3332 aNearest ? &nearest :
nullptr );
3334 if( currentDistance_sq < minDistance_sq )
3337 *aNearest = nearest;
3339 minDistance_sq = currentDistance_sq;
3343 return minDistance_sq;
3356 return index.m_contour > 0;
3364 for(
unsigned int idx = 0; idx <
m_polys.size(); idx++ )
3375 for(
size_t idx = 0; idx <
m_polys.size(); idx++ )
3384 if(
this != &aOther )
3386 SHAPE::operator=( aOther );
3388 m_polys = std::move( aOther.m_polys );
3406 SHAPE::operator=( aOther );
3465 auto buildIndexes = [
this]()
3473 triCount +=
static_cast<int>( tpoly->GetTriangleCount() );
3506 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>& dest,
3507 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* hintData,
3510 bool triangulationValid =
false;
3514 if( hintData && hintData->size() != (
unsigned) polySet.
OutlineCount() )
3519 if( !dest.empty() && dest.back()->GetTriangleCount() == 0 )
3520 dest.erase( dest.end() - 1 );
3528 if( partitionLeaves > 1 )
3530 std::vector<SHAPE_LINE_CHAIN> partitions =
3533 if( partitions.size() > 1 )
3537 if( taskSubmitter && partitions.size() > 2 )
3539 size_t leafCount = partitions.size();
3541 struct WorkStealState
3543 std::unique_ptr<std::atomic<bool>[]> claimed;
3544 std::unique_ptr<std::atomic<bool>[]> done;
3545 std::unique_ptr<std::atomic<bool>[]> ok;
3547 explicit WorkStealState(
size_t n ) :
3548 claimed(
new std::atomic<bool>[n] ),
3549 done(
new std::atomic<bool>[n] ),
3550 ok(
new std::atomic<bool>[n] )
3552 for(
size_t i = 0; i < n; ++i )
3554 claimed[i].store(
false );
3555 done[i].store(
false );
3556 ok[i].store(
false );
3561 auto state = std::make_shared<WorkStealState>( leafCount );
3563 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>> results( leafCount );
3565 for(
size_t i = 0; i < leafCount; ++i )
3567 results[i] = std::make_unique<TRIANGULATED_POLYGON>( forOutline );
3570 for(
size_t i = 0; i < leafCount; ++i )
3572 auto* triPoly = results[i].get();
3573 auto* leaf = &partitions[i];
3575 taskSubmitter( [state, i, triPoly, leaf]()
3577 if( state->claimed[i].exchange(
true ) )
3582 state->done[i].store(
true, std::memory_order_release );
3586 for(
size_t i = 0; i < leafCount; ++i )
3588 if( state->claimed[i].exchange(
true ) )
3593 state->done[i].store(
true, std::memory_order_release );
3596 for(
size_t i = 0; i < leafCount; ++i )
3598 while( !state->done[i].load( std::memory_order_acquire ) )
3600 std::this_thread::yield();
3606 for(
size_t i = 0; i < leafCount; ++i )
3607 allOk = allOk && state->ok[i].load();
3611 for(
auto& r : results )
3613 if( r->GetTriangleCount() > 0 )
3614 dest.push_back( std::move( r ) );
3617 triangulationValid =
true;
3624 for(
auto it = partitions.rbegin(); it != partitions.rend(); ++it )
3633 dest.push_back( std::make_unique<TRIANGULATED_POLYGON>( forOutline ) );
3640 hintData ? hintData->at(
index ).get() :
nullptr ) )
3654 triangulationValid =
false;
3661 triangulationValid =
true;
3664 return triangulationValid;
3683 bool directOk =
true;
3685 for(
int ii = 0; ii < srcSet->
OutlineCount() && directOk; ++ii )
3690 if( poly.size() > 1 )
3703 for(
size_t jj = 1; jj < poly.size(); ++jj )
3743 double originalArea =
std::abs( poly.front().Area() );
3745 if( originalArea > 0.0 )
3747 double triArea = 0.0;
3752 double coverage = triArea / originalArea;
3754 if( coverage > 1.01 || coverage < 0.99 )
3767 bool splitOk =
true;
3769 for(
int jj = 0; jj < splitSet.
OutlineCount() && splitOk; ++jj )
3794 bool fallbackOk =
true;
3796 for(
int ii = 0; ii < srcSet->
OutlineCount() && fallbackOk; ++ii )
3801 for(
size_t jj = 1; jj < poly.size(); ++jj )
3861 hash.
add( outline.size() );
3865 hash.
add( lc.PointCount() );
3867 for(
int i = 0; i < lc.PointCount(); i++ )
3895 std::set<long long> ptHashes;
3899 for(
const VECTOR2I& pt : lc.CPoints() )
3901 const long long ptHash = (
long long) pt.x << 32 | pt.y;
3903 if( ptHashes.count( ptHash ) > 0 )
3906 ptHashes.insert( ptHash );
3925 n += t->GetTriangleCount();
3938 aSubshapes.push_back( &tri );
3949 if( aClearance != 0 )
3991 if( triCount < 2 || triCount >
static_cast<size_t>( std::numeric_limits<int>::max() ) / 6 )
3994 const int halfEdgeCount =
static_cast<int>( 3 * triCount );
3997 std::vector<int> heVertex( halfEdgeCount );
3999 for(
size_t k = 0; k < triCount; k++ )
4006 auto nextInTriangle = [](
int aEdge ) {
return aEdge - aEdge % 3 + ( aEdge + 1 ) % 3; };
4010 std::vector<int> twin( halfEdgeCount, -1 );
4014 while( tableSize < halfEdgeCount * 2 )
4017 const uint32_t tableMask =
static_cast<uint32_t
>( tableSize - 1 );
4018 std::vector<int> slotEdge( tableSize, -1 );
4022 std::vector<char> retired( tableSize, 0 );
4023 std::vector<int> toVisit;
4024 std::vector<char> queued( halfEdgeCount, 0 );
4026 for(
int edge = 0; edge < halfEdgeCount; edge++ )
4028 int from = heVertex[edge];
4029 int to = heVertex[nextInTriangle( edge )];
4030 uint32_t keyLow =
static_cast<uint32_t
>( from < to ? from : to );
4031 uint32_t keyHigh =
static_cast<uint32_t
>( from < to ? to : from );
4032 uint32_t slot = ( ( keyLow * 0x9e3779b1u ) ^ ( keyHigh * 0x85ebca6bu ) ) & tableMask;
4033 bool handled =
false;
4035 while( slotEdge[slot] != -1 )
4037 int other = slotEdge[slot];
4038 int otherFrom = heVertex[other];
4039 int otherTo = heVertex[nextInTriangle( other )];
4040 uint32_t otherLow =
static_cast<uint32_t
>( otherFrom < otherTo ? otherFrom : otherTo );
4041 uint32_t otherHigh =
static_cast<uint32_t
>( otherFrom < otherTo ? otherTo : otherFrom );
4043 if( otherLow == keyLow && otherHigh == keyHigh )
4049 else if( twin[other] == -1 )
4054 toVisit.push_back( other );
4059 int mate = twin[other];
4069 slot = ( slot + 1 ) & tableMask;
4073 slotEdge[slot] = edge;
4076 while( !toVisit.empty() )
4078 int edge = toVisit.back();
4082 int twinEdge = twin[edge];
4084 if( twinEdge == -1 )
4087 int tri = edge - edge % 3;
4088 int twinTri = twinEdge - twinEdge % 3;
4090 int apexAEdge = tri + ( edge + 2 ) % 3;
4091 int sharedEndEdge = tri + ( edge + 1 ) % 3;
4092 int apexBEdge = twinTri + ( twinEdge + 2 ) % 3;
4093 int twinSharedEdge = twinTri + ( twinEdge + 1 ) % 3;
4095 int apexA = heVertex[apexAEdge];
4096 int sharedFrom = heVertex[edge];
4097 int sharedTo = heVertex[sharedEndEdge];
4098 int apexB = heVertex[apexBEdge];
4120 if( legal && curSlivers == 0 )
4126 bool doFlip = ( flipSlivers != curSlivers ) ? ( flipSlivers < curSlivers ) : !legal;
4131 heVertex[edge] = apexB;
4132 heVertex[twinEdge] = apexA;
4134 int apexBOuterTwin = twin[apexBEdge];
4135 int apexAOuterTwin = twin[apexAEdge];
4137 twin[edge] = apexBOuterTwin;
4139 if( apexBOuterTwin != -1 )
4140 twin[apexBOuterTwin] = edge;
4142 twin[twinEdge] = apexAOuterTwin;
4144 if( apexAOuterTwin != -1 )
4145 twin[apexAOuterTwin] = twinEdge;
4147 twin[apexAEdge] = apexBEdge;
4148 twin[apexBEdge] = apexAEdge;
4151 for(
int outer : { edge, twinEdge, sharedEndEdge, twinSharedEdge } )
4153 if( twin[outer] != -1 && !queued[outer] )
4156 toVisit.push_back( outer );
4161 std::deque<TRI> refined;
4163 for(
size_t i = 0; i < triCount; i++ )
4164 refined.emplace_back( heVertex[3 * i + 0], heVertex[3 * i + 1], heVertex[3 * i + 2],
this );
4187 for(
int i = 0; i <
path.PointCount(); i++ )
4191 vec.
x = ( pt.
x - aCenter.
x ) * aScaleFactorX;
4192 vec.
y = ( pt.
y - aCenter.
y ) * aScaleFactorY;
4195 path.SetPoint( i, pt );
4209 Clipper2Lib::Clipper64 clipper;
4210 Clipper2Lib::PolyTree64 tree;
4211 Clipper2Lib::Paths64 paths;
4215 Clipper2Lib::Path64 lc;
4216 lc.reserve(
path.PointCount() );
4218 for(
int i = 0; i <
path.PointCount(); i++ )
4219 lc.emplace_back(
path.CPoint( i ).x,
path.CPoint( i ).y );
4221 paths.push_back( std::move( lc ) );
4224 clipper.AddSubject( paths );
4225 clipper.Execute( Clipper2Lib::ClipType::Union, aEvenOdd ? Clipper2Lib::FillRule::EvenOdd
4226 : Clipper2Lib::FillRule::NonZero, tree );
4228 std::vector<CLIPPER_Z_VALUE> zValues;
4229 std::vector<SHAPE_ARC> arcBuffer;
4249 int aSpacing,
int aLineLength )
const
4251 std::vector<SEG> hatchLines;
4264 if( iterator->x < min_x )
4265 min_x = iterator->x;
4267 if( iterator->x > max_x )
4268 max_x = iterator->x;
4270 if( iterator->y < min_y )
4271 min_y = iterator->y;
4273 if( iterator->y > max_y )
4274 max_y = iterator->y;
4277 auto sortEndsByDescendingX =
4280 return tst.x < ref.
x;
4283 for(
double slope : aSlopes )
4285 int64_t max_a, min_a;
4298 min_a = ( min_a / aSpacing ) * aSpacing;
4301 std::vector<VECTOR2I> pointbuffer;
4302 pointbuffer.reserve( 256 );
4304 for( int64_t a = min_a; a < max_a; a += aSpacing )
4306 pointbuffer.clear();
4311 const SEG seg = *iterator;
4317 if( pt.
x < min_x || pt.
x > max_x || pt.
y < min_y || pt.
y > max_y )
4328 if( pointbuffer.size() > 2 )
4329 sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX );
4332 for(
size_t ip = 0; ip + 1 < pointbuffer.size(); ip++ )
4334 const VECTOR2I& p1 = pointbuffer[ip];
4335 const VECTOR2I& p2 = pointbuffer[ip + 1];
4341 SEG candidate( p1, p2 );
4343 VECTOR2I mid( ( candidate.
A.
x + candidate.
B.
x ) / 2, ( candidate.
A.
y + candidate.
B.
y ) / 2 );
4348 int dx = p2.
x - p1.
x;
4352 if( aLineLength == -1 ||
std::abs( dx ) < 2 * aLineLength )
4354 hatchLines.emplace_back( candidate );
4358 double dy = p2.
y - p1.
y;
4368 int y1 =
KiROUND( p1.
y + dx * slope );
4369 int y2 =
KiROUND( p2.
y - dx * slope );
4371 hatchLines.emplace_back(
SEG( p1.
x, p1.
y, x1, y1 ) );
4373 hatchLines.emplace_back(
SEG( p2.
x, p2.
y, x2, y2 ) );
bool operator==(const wxAuiPaneInfo &aLhs, const wxAuiPaneInfo &aRhs)
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
void InsertBridges(Index aFirst)
void insert(uint32_t aEdge, uint32_t aHead)
std::vector< NODE > m_nodes
std::vector< uint32_t > m_bucketIds
FractureEdge::Index Index
void Query(int aY, Index aProvokingIndex, Visitor &&aVisitor) const
std::vector< uint32_t > m_longIds
std::vector< uint32_t > m_offsets
static void logAccepted(size_t aEdges, size_t aHoles, uint32_t aStripes, size_t aBytes)
std::pair< uint32_t, uint32_t > span(const FractureEdge &aEdge) const
FRACTURE_EDGE_INDEX(FractureEdgeSet &aEdges, Originals &&aOriginals, int aMinY, int aMaxY, uint32_t aStripeCount, size_t aHoleCount)
uint32_t map(int aY) const
static constexpr uint32_t INVALID
void visitOverflow(uint32_t aNode, Index aProvokingIndex, Visitor &&aVisitor) const
FractureEdgeSet & m_edges
std::vector< uint32_t > m_heads
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
Immutable owning spatial snapshot of straight segments.
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.
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.
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
const std::vector< VECTOR2I > & CPoints() const
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.
void cacheTriangulation(bool aSimplify, std::vector< std::unique_ptr< TRIANGULATED_POLYGON > > *aHintData, const TASK_SUBMITTER &aSubmitter={}, bool aBuildIndexes=false)
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 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)
virtual void CacheTriangulation(bool aSimplify=false, const TASK_SUBMITTER &aSubmitter={}, bool aBuildIndexes=false)
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
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).
static bool appendBridgeFreePaths(const SHAPE_LINE_CHAIN &aChain, Clipper2Lib::Paths64 &aPaths, std::vector< CLIPPER_Z_VALUE > &aZValues, std::vector< SHAPE_ARC > &aArcBuffer)
Append the rings left after cutting the fracture bridges out of aChain.
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)
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...
std::unique_ptr< TRIANGULATION_INDEX > m_triIndex
One index over every triangle of every polygon above.
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
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.
std::pair< uint32_t, uint32_t > StripeSpan(int aY1, int aY2, int aMinY, int aMaxY, uint32_t aStripeCount)
bool ActualCapacityFits(size_t aBucketIds, size_t aLongIds, size_t aOffsets, size_t aScratch, size_t aHeads, size_t aNodes, size_t aNodeSize, size_t aBudget, size_t *aBytes=nullptr)
bool ShouldIndex(uint64_t aEstimatedVisits, size_t aHoleCount)
bool CapacityFits(size_t aBucketIds, size_t aLongIds, size_t aStripeCount, size_t aHoleCount, size_t aNodeSize, size_t aBudget)
uint32_t MapYToStripe(int aY, int aMinY, int aMaxY, uint32_t aStripeCount)
constexpr uint32_t MAX_BUCKET_SPAN
uint32_t StripeCountFor(size_t aEdgeCount)
Construction helpers for the interactive arc drawing modes.
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 constexpr int RTREE_THRESHOLD
static void fractureSingleSlow(SHAPE_POLY_SET::POLYGON &paths)
static int fractureIntersectX(const FractureEdge &aEdge, int aY)
std::vector< FractureEdge > FractureEdgeSet
std::vector< FractureEdgeSlow * > FractureEdgeSetSlow
#define ENABLEFRACTUREEDGEINDEX
static bool splitAtBridges(const SHAPE_LINE_CHAIN &aChain, std::vector< SHAPE_LINE_CHAIN > &aRings)
Split a closed ring into the rings left once pairs of coincident opposite edges are removed.
#define ENABLECACHEFRIENDLYFRACTURE
static FractureEdge * processHole(FractureEdgeSet &edges, FractureEdge::Index provokingIndex, FractureEdge::Index edgeIndex, FractureEdge::Index bridgeIndex, FRACTURE_EDGE_INDEX *aIndex)
static int processEdge(FractureEdgeSetSlow &edges, FractureEdgeSlow *edge)
bool AnyTriangleHit(const TRIANGULATION_INDEX *aIndex, const std::vector< std::unique_ptr< SHAPE_POLY_SET::TRIANGULATED_POLYGON > > &aPolys, const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr)
Return true if any candidate triangle collides with aShape within aClearance.
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