37#include <unordered_map>
38#include <unordered_set>
44#include <clipper2/clipper.h>
66#if defined( __MINGW32__ )
67 #define TRIANGULATESIMPLIFICATIONLEVEL 50
68 #define ENABLECACHEFRIENDLYFRACTURE true
69 #define ENABLEFRACTUREEDGEINDEX true
71 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
72 #define ENABLECACHEFRIENDLYFRACTURE ADVANCED_CFG::GetCfg().m_EnableCacheFriendlyFracture
73 #define ENABLEFRACTUREEDGEINDEX ADVANCED_CFG::GetCfg().m_EnableFractureEdgeIndex
114 m_triangulatedPolys.reserve( aOther.TriangulatedPolyCount() );
116 for( unsigned i = 0; i < aOther.TriangulatedPolyCount(); i++ )
118 const TRIANGULATED_POLYGON* poly = aOther.TriangulatedPolygon( i );
119 m_triangulatedPolys.push_back( std::make_unique<TRIANGULATED_POLYGON>( *poly ) );
130 m_triangulationValid =
false;
133 m_failedHash = aOther.m_failedHash;
134 m_failedHashValid.store( aOther.m_failedHashValid.load() );
169 unsigned int contourIdx = 0;
172 int currentGlobalIdx = 0;
174 for( polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
178 for( contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
181 int totalPoints = currentContour.
PointCount();
183 for( vertexIdx = 0; vertexIdx < totalPoints; vertexIdx++ )
186 if( currentGlobalIdx == aGlobalIdx )
188 aRelativeIndices->
m_polygon = polygonIdx;
189 aRelativeIndices->
m_contour = contourIdx;
190 aRelativeIndices->
m_vertex = vertexIdx;
206 int& aGlobalIdx )
const
208 int selectedVertex = aRelativeIndices.
m_vertex;
209 unsigned int selectedContour = aRelativeIndices.
m_contour;
210 unsigned int selectedPolygon = aRelativeIndices.
m_polygon;
213 if( selectedPolygon <
m_polys.size() && selectedContour <
m_polys[selectedPolygon].size()
214 && selectedVertex <
m_polys[selectedPolygon][selectedContour].PointCount() )
220 for(
unsigned int polygonIdx = 0; polygonIdx < selectedPolygon; polygonIdx++ )
222 currentPolygon =
Polygon( polygonIdx );
224 for(
unsigned int contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
225 aGlobalIdx += currentPolygon[contourIdx].PointCount();
228 currentPolygon =
Polygon( selectedPolygon );
230 for(
unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx++ )
231 aGlobalIdx += currentPolygon[contourIdx].PointCount();
233 aGlobalIdx += selectedVertex;
250 poly.push_back( empty_path );
251 m_polys.push_back( std::move( poly ) );
267 m_polys[aOutline].push_back( empty_path );
269 return m_polys.back().size() - 2;
287 assert( aOutline < (
int)
m_polys.size() );
288 assert( idx < (
int)
m_polys[aOutline].size() );
290 m_polys[aOutline][idx].Append( x, y, aAllowDuplication );
292 return m_polys[aOutline][idx].PointCount();
297 std::optional<int> aMaxError )
311 assert( aOutline < (
int)
m_polys.size() );
312 assert( idx < (
int)
m_polys[aOutline].size() );
314 if( aMaxError.has_value() )
315 m_polys[aOutline][idx].Append( aArc, aMaxError.value() );
317 m_polys[aOutline][idx].Append( aArc );
319 return m_polys[aOutline][idx].PointCount();
327 if( aGlobalIndex < 0 )
340 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
360 if( aOutline >= (
int)
m_polys.size() )
363 if( idx >= (
int)
m_polys[aOutline].size() )
366 return m_polys[aOutline][idx].PointCount();
381 for(
int idx = 0; idx <=
HoleCount( ii ); idx++ )
383 full_count +=
m_polys[ii][idx].PointCount();
393 assert( aFirstPolygon >= 0 && aLastPolygon <=
OutlineCount() );
416 assert( aOutline < (
int)
m_polys.size() );
417 assert( idx < (
int)
m_polys[aOutline].size() );
419 return m_polys[aOutline][idx].CPoint( aIndex );
429 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
454 if(
index.m_vertex == 0 )
456 index.m_vertex = lastpoint - 1;
459 else if(
index.m_vertex == lastpoint )
477 *aPrevious = previous;
493 std::vector<SEG> segments;
497 segments.emplace_back( *it );
499 std::sort( segments.begin(), segments.end(), [](
const SEG& a,
const SEG& b )
501 int min_a_x = std::min( a.A.x, a.B.x );
502 int min_b_x = std::min( b.A.x, b.B.x );
504 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 ) );
507 for(
auto it = segments.begin(); it != segments.end(); ++it )
509 SEG& firstSegment = *it;
512 auto innerIterator = it;
513 int max_x = std::max( firstSegment.
A.
x, firstSegment.
B.
x );
514 int max_y = std::max( firstSegment.
A.
y, firstSegment.
B.
y );
517 for( innerIterator++; innerIterator != segments.end(); innerIterator++ )
519 SEG& secondSegment = *innerIterator;
520 int min_x = std::min( secondSegment.
A.
x, secondSegment.
B.
x );
521 int min_y = std::min( secondSegment.
A.
y, secondSegment.
B.
y );
525 if( max_x < min_x || ( max_x == min_x && max_y < min_y ) )
529 bool adjacent = ( index_diff == 1) || (index_diff == ((
int)segments.size() - 1) );
532 if( !adjacent && firstSegment.
Collide( secondSegment, 0 ) )
543 for(
unsigned int polygon = 0; polygon <
m_polys.size(); polygon++ )
557 poly.push_back( aOutline );
562 wxCHECK2_MSG( aOutline.
IsClosed(), poly.back().SetClosed(
true ),
563 "Warning: non-closed outline added to SHAPE_POLY_SET" );
565 m_polys.push_back( std::move( poly ) );
567 return (
int)
m_polys.size() - 1;
576 aOutline += (int)
m_polys.size();
578 assert( aOutline < (
int)
m_polys.size() );
582 assert( poly.size() );
584 poly.push_back( aHole );
586 return (
int) poly.size() - 2;
606 for(
int j = 0; j <
HoleCount( i ); j++ )
620 for(
size_t i = 0; i < poly.size(); i++ )
632 for(
size_t i = 0; i < poly.size(); i++ )
635 aArcBuffer.push_back( arc );
645 for(
size_t i = 0; i < poly.size(); i++ )
653 std::vector<SHAPE_LINE_CHAIN> contours;
656 contours.insert( contours.end(), poly.begin(), poly.end() );
658 std::map<int, std::set<int>> parentToChildren;
659 std::map<int, std::set<int>> childToParents;
662 contour.GenerateBBoxCache();
664 for(
size_t i = 0; i < contours.size(); i++ )
668 for(
size_t j = 0; j < contours.size(); j++ )
678 parentToChildren[i].emplace( j );
679 childToParents[j].emplace( i );
684 std::set<int> topLevelParents;
686 for(
size_t i = 0; i < contours.size(); i++ )
688 if( childToParents[i].size() == 0 )
690 topLevelParents.emplace( i );
696 std::function<void(
int,
int, std::vector<int> )>
process;
699 [&](
int myId,
int parentOutlineId,
const std::vector<int>&
path )
701 std::set<int> relParents = childToParents[myId];
703 for(
int pathId :
path )
705 int erased = relParents.erase( pathId );
706 wxASSERT( erased > 0 );
709 wxASSERT( relParents.size() == 0 );
713 bool isOutline =
path.size() % 2 == 0;
717 int outlineId =
result.AddOutline( contours[myId] );
718 myOutline = outlineId;
722 wxASSERT( parentOutlineId != -1 );
723 result.AddHole( contours[myId], parentOutlineId );
726 auto it = parentToChildren.find( myId );
727 if( it != parentToChildren.end() )
729 std::vector<int> thisPath =
path;
730 thisPath.emplace_back( myId );
732 std::set<int> thisPathSet;
733 thisPathSet.insert( thisPath.begin(), thisPath.end() );
735 for(
int childId : it->second )
737 const std::set<int>& childPathSet = childToParents[childId];
739 if( thisPathSet != childPathSet )
742 process( childId, myOutline, thisPath );
747 for(
int topParentId : topLevelParents )
749 std::vector<int>
path;
753 *
this = std::move(
result );
776 bool operator==(
const DIRECTED_EDGE& aOther )
const
778 return from == aOther.from && to == aOther.to;
782 struct DIRECTED_EDGE_HASH
784 std::size_t operator()(
const DIRECTED_EDGE& aEdge )
const
786 std::size_t seed = 0x51ed27a3;
787 hash_combine( seed, aEdge.from.x, aEdge.from.y, aEdge.to.x, aEdge.to.y );
792 const std::vector<VECTOR2I>& pts = aChain.
CPoints();
793 const int count =
static_cast<int>( pts.size() );
796 std::unordered_map<DIRECTED_EDGE, std::vector<int>, DIRECTED_EDGE_HASH> unpaired;
797 std::vector<int>
partner( count, -1 );
798 bool hasBridge =
false;
800 unpaired.reserve( count );
802 for(
int ii = 0; ii < count; ++ii )
805 const VECTOR2I& b = pts[( ii + 1 ) % count];
810 auto twin = unpaired.find( { b, a } );
812 if( twin != unpaired.end() )
814 const int jj = twin->second.back();
816 twin->second.pop_back();
818 if( twin->second.empty() )
819 unpaired.erase( twin );
827 unpaired[{ a, b }].push_back( ii );
836 [&](
int aEdge ) ->
int
838 int next = ( aEdge + 1 ) % count;
851 std::vector<bool> visited( count,
false );
853 for(
int start = 0; start < count; ++start )
855 if( visited[start] ||
partner[start] >= 0 )
863 if( edge < 0 || visited[edge] )
869 visited[edge] =
true;
870 ring.
Append( pts[edge],
true );
871 edge = nextEdge( edge );
872 }
while( edge != start );
877 aRings.push_back( std::move( ring ) );
886 std::vector<CLIPPER_Z_VALUE>& aZValues,
887 std::vector<SHAPE_ARC>& aArcBuffer )
890 constexpr int kMinPoints = 1024;
895 std::vector<SHAPE_LINE_CHAIN> rings;
902 const bool flip = aChain.
Area(
false ) < 0;
905 aPaths.push_back( ring.convertToClipper2( ( ring.Area(
false ) >= 0 ) != flip, aZValues, aArcBuffer ) );
917 wxFAIL_MSG( wxT(
"Boolean ops on curved polygons are not supported. You should call "
918 "ClearArcs() before carrying out the boolean operation." ) );
921 Clipper2Lib::Clipper64 c;
923 std::vector<CLIPPER_Z_VALUE> zValues;
924 std::vector<SHAPE_ARC> arcBuffer;
925 std::map<VECTOR2I, CLIPPER_Z_VALUE> newIntersectPoints;
927 Clipper2Lib::Paths64 paths;
928 Clipper2Lib::Paths64 clips;
935 for(
size_t i = 0; i < poly.size(); i++ )
937 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
946 for(
size_t i = 0; i < poly.size(); i++ )
948 clips.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
952 c.AddSubject( paths );
955 Clipper2Lib::PolyTree64 solution;
957 Clipper2Lib::ZCallback64 callback =
958 [&](
const Clipper2Lib::Point64 & e1bot,
const Clipper2Lib::Point64 & e1top,
959 const Clipper2Lib::Point64 & e2bot,
const Clipper2Lib::Point64 & e2top,
960 Clipper2Lib::Point64 & pt )
963 [&](
const ssize_t& aZvalue,
const ssize_t& aCompareVal = -1 ) -> ssize_t
967 retval = zValues.at( aZvalue ).m_SecondArcIdx;
969 if( retval == -1 || ( aCompareVal > 0 && retval != aCompareVal ) )
970 retval = zValues.at( aZvalue ).m_FirstArcIdx;
976 [&](
const ssize_t& aBottomZ,
const ssize_t aTopZ ) -> ssize_t
978 ssize_t retval = arcIndex( aBottomZ );
982 if( retval != arcIndex( aTopZ, retval ) )
989 ssize_t e1ArcSegmentIndex = arcSegment( e1bot.z, e1top.z );
990 ssize_t e2ArcSegmentIndex = arcSegment( e2bot.z, e2top.z );
994 if( e1ArcSegmentIndex != -1 )
1005 size_t z_value_ptr = zValues.size();
1006 zValues.push_back( newZval );
1010 newIntersectPoints.insert( {
VECTOR2I( pt.x, pt.y ), newZval } );
1016 c.SetZCallback( std::move( callback ) );
1018 c.Execute( aType, Clipper2Lib::FillRule::NonZero, solution );
1027 booleanOp( Clipper2Lib::ClipType::Union, b );
1033 booleanOp( Clipper2Lib::ClipType::Difference, b );
1039 booleanOp( Clipper2Lib::ClipType::Intersection, b );
1045 booleanOp( Clipper2Lib::ClipType::Xor, b );
1051 booleanOp( Clipper2Lib::ClipType::Union, a, b );
1057 booleanOp( Clipper2Lib::ClipType::Difference, a, b );
1063 booleanOp( Clipper2Lib::ClipType::Intersection, a, b );
1069 booleanOp( Clipper2Lib::ClipType::Xor, a, b );
1077 Inflate( aFactor, aCornerStrategy, aMaxError );
1085 using namespace Clipper2Lib;
1089 #define SEG_CNT_MAX 64
1090 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
1097 JoinType joinType = JoinType::Round;
1098 double miterLimit = 2.0;
1100 switch( aCornerStrategy )
1103 joinType = JoinType::Miter;
1108 joinType = JoinType::Miter;
1112 joinType = JoinType::Miter;
1116 joinType = JoinType::Square;
1120 joinType = JoinType::Round;
1124 std::vector<CLIPPER_Z_VALUE> zValues;
1125 std::vector<SHAPE_ARC> arcBuffer;
1131 for(
size_t i = 0; i < poly.size(); i++ )
1132 paths.push_back( poly[i].convertToClipper2( i == 0, zValues, arcBuffer ) );
1134 c.AddPaths( paths, joinType, EndType::Polygon );
1141 if( aCircleSegCount < 6 )
1142 aCircleSegCount = 6;
1146 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
1148 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
1151 arc_tolerance_factor[aCircleSegCount] = coeff;
1155 coeff = arc_tolerance_factor[aCircleSegCount];
1158 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1159 c.MiterLimit( miterLimit );
1166 c.Execute( aAmount, paths );
1168 Clipper2Lib::SimplifyPaths( paths,
std::abs( aAmount ) * coeff,
true );
1171 c2.PreserveCollinear(
false );
1172 c2.ReverseSolution(
false );
1173 c2.AddSubject( paths );
1174 c2.Execute(ClipType::Union, FillRule::Positive, tree);
1178 c.Execute( aAmount, tree );
1189 using namespace Clipper2Lib;
1193 #define SEG_CNT_MAX 64
1194 static thread_local double arc_tolerance_factor[
SEG_CNT_MAX + 1];
1201 JoinType joinType = JoinType::Round;
1202 double miterLimit = 2.0;
1204 switch( aCornerStrategy )
1207 joinType = JoinType::Miter;
1212 joinType = JoinType::Miter;
1216 joinType = JoinType::Miter;
1220 joinType = JoinType::Square;
1224 joinType = JoinType::Round;
1228 std::vector<CLIPPER_Z_VALUE> zValues;
1229 std::vector<SHAPE_ARC> arcBuffer;
1232 c.AddPath(
path, joinType, EndType::Butt );
1238 if( aCircleSegCount < 6 )
1239 aCircleSegCount = 6;
1243 if( aCircleSegCount >
SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 )
1245 coeff = 1.0 - cos(
M_PI / aCircleSegCount );
1248 arc_tolerance_factor[aCircleSegCount] = coeff;
1252 coeff = arc_tolerance_factor[aCircleSegCount];
1255 c.ArcTolerance(
std::abs( aAmount ) * coeff );
1256 c.MiterLimit( miterLimit );
1263 c.Execute( aAmount, paths2 );
1265 Clipper2Lib::SimplifyPaths( paths2,
std::abs( aAmount ) * coeff,
false );
1268 c2.PreserveCollinear(
false );
1269 c2.ReverseSolution(
false );
1270 c2.AddSubject( paths2 );
1271 c2.Execute( ClipType::Union, FillRule::Positive, tree );
1275 c.Execute( aAmount, tree );
1288 inflate2( aAmount, segCount, aCornerStrategy, aSimplify );
1297 inflateLine2( aLine, aAmount, segCount, aCornerStrategy, aSimplify );
1302 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1303 const std::vector<SHAPE_ARC>& aArcBuffer )
1305 if( !aPolyPath->IsHole() )
1308 paths.reserve( aPolyPath->Count() + 1 );
1309 paths.emplace_back( aPolyPath->Polygon(), aZValueBuffer, aArcBuffer );
1311 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& child : *aPolyPath )
1313 paths.emplace_back( child->Polygon(), aZValueBuffer, aArcBuffer );
1315 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& grandchild : *child )
1319 m_polys.emplace_back( std::move( paths ) );
1325 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1326 const std::vector<SHAPE_ARC>& aArcBuffer )
1330 for(
const std::unique_ptr<Clipper2Lib::PolyPath64>& n : tree )
1336 const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
1337 const std::vector<SHAPE_ARC>& aArcBuffer )
1342 for(
const Clipper2Lib::Path64& n : aPath )
1344 if( Clipper2Lib::Area( n ) > 0 )
1353 wxCHECK2_MSG( !
path.empty(),
continue, wxT(
"Cannot add a hole before an outline" ) );
1356 path.emplace_back( n, aZValueBuffer, aArcBuffer );
1402 template <
typename Originals>
1404 uint32_t aStripeCount,
size_t aHoleCount ) :
1409 m_maxNodes( (
KIGEOM::FRACTURE_INDEX::MAX_BUCKET_SPAN + 2 ) * aHoleCount ),
1413 size_t bucketIds = 0;
1417 [&]( uint32_t, uint32_t aFirst, uint32_t aLast )
1425 bucketIds += aLast - aFirst + 1;
1427 for( uint32_t stripe = aFirst; stripe <= aLast; ++stripe )
1440 for( uint32_t stripe = 0; stripe <
m_stripeCount; ++stripe )
1449 [&]( uint32_t aEdge, uint32_t aFirst, uint32_t aLast )
1457 for( uint32_t stripe = aFirst; stripe <= aLast; ++stripe )
1467 for( uint32_t stripe = 0; stripe <
m_stripeCount; ++stripe )
1473 for(
size_t pos = 1; pos <
m_longIds.size(); ++pos )
1478 size_t allocated_bytes = 0;
1483 &allocated_bytes ) )
1494 template <
typename Visitor>
1495 void Query(
int aY,
Index aProvokingIndex, Visitor&& aVisitor )
const
1497 const uint32_t stripe =
map( aY );
1504 if( edge >=
static_cast<uint32_t
>( aProvokingIndex ) )
1507 aVisitor(
static_cast<Index>( edge ) );
1512 if( edge >=
static_cast<uint32_t
>( aProvokingIndex ) )
1515 aVisitor(
static_cast<Index>( edge ) );
1524 for(
Index edge = aFirst; edge < aFirst + 3; ++edge )
1534 for( uint32_t stripe = first; stripe <= last; ++stripe )
1535 insert(
static_cast<uint32_t
>( edge ), stripe );
1541 static constexpr uint32_t
INVALID = std::numeric_limits<uint32_t>::max();
1550 template <
typename Visitor>
1558 assert( node.
edge <
static_cast<uint32_t
>( aProvokingIndex ) );
1560 if( node.
edge <
static_cast<uint32_t
>( aProvokingIndex ) )
1561 aVisitor(
static_cast<Index>( node.
edge ) );
1567 void insert( uint32_t aEdge, uint32_t aHead )
1574 static void logAccepted(
size_t aEdges,
size_t aHoles, uint32_t aStripes,
size_t aBytes )
1576 wxLogTrace( wxS(
"KICAD_FRACTURE_INDEX" ),
1577 wxS(
"Using fracture edge index: edges=%zu, holes=%zu, stripes=%u, allocated=%zu bytes" ),
1578 aEdges, aHoles, aStripes, aBytes );
1600 return std::max( aEdge.
m_p1.
x, aEdge.
m_p2.
x );
1612 int x = edge.
m_p1.
x;
1613 int y = edge.
m_p1.
y;
1614 int min_dist = std::numeric_limits<int>::max();
1634 int dist = x - x_intersect;
1637 && ( dist < min_dist
1638 || ( nearest_index >= 0 && dist == min_dist && aCandidateIndex < nearest_index ) ) )
1641 x_nearest = x_intersect;
1642 nearest_index = aCandidateIndex;
1646 aIndex->
Query( y, provokingIndex, consider );
1648 if( nearest_index >= 0 )
1649 e_nearest = &edges[nearest_index];
1661 int dist = x - x_intersect;
1663 if( dist >= 0 && dist < min_dist )
1666 x_nearest = x_intersect;
1667 e_nearest = &candidate;
1680 edges[hole2outline_index] =
1683 edges[split_index] =
1690 e_nearest->
m_next = outline2hole_index;
1693 for( ; last->
m_next != edgeIndex; last = &edges[last->
m_next] )
1695 last->
m_next = hole2outline_index;
1708 bool outline =
true;
1710 if( paths.size() == 1 )
1713 size_t total_point_count = 0;
1717 total_point_count +=
path.PointCount();
1720 if( total_point_count > (
size_t) std::numeric_limits<FractureEdge::Index>::max() )
1722 wxLogWarning( wxT(
"Polygon has more points than int limit" ) );
1729 edges.reserve( total_point_count + paths.size() * 3 );
1735 int path_or_provoking_index;
1740 std::vector<PathInfo> sorted_paths;
1741 const int paths_count =
static_cast<int>( paths.size() );
1742 sorted_paths.reserve( paths_count );
1744 for(
int path_index = 0; path_index < paths_count; path_index++ )
1747 const std::vector<VECTOR2I>& points =
path.CPoints();
1748 const int point_count =
static_cast<int>( points.size() );
1749 int x_min = std::numeric_limits<int>::max();
1750 int y_min = std::numeric_limits<int>::max();
1753 for(
int point_index = 0; point_index < point_count; point_index++ )
1755 const VECTOR2I& point = points[point_index];
1756 if( point.
x < x_min )
1759 leftmost = point_index;
1761 if( point.
y < y_min )
1765 sorted_paths.emplace_back( PathInfo{ path_index, leftmost, x_min, y_min } );
1768 std::sort( sorted_paths.begin() + 1, sorted_paths.end(),
1769 [](
const PathInfo& a,
const PathInfo& b )
1772 return a.y_or_bridge < b.y_or_bridge;
1778 for( PathInfo& path_info : sorted_paths )
1781 const std::vector<VECTOR2I>& points =
path.CPoints();
1782 const size_t point_count = points.size();
1787 for(
size_t i = 0; i < point_count - 1; i++ )
1789 edges.emplace_back( points[i], points[i + 1], edge_index + 1 );
1794 edges.emplace_back( points[point_count - 1], points[0], provoking_edge );
1801 path_info.path_or_provoking_index = provoking_edge;
1802 path_info.y_or_bridge = edge_index;
1806 edges.resize( edge_index );
1812 assert( edges.size() == total_point_count + 3 * ( paths.size() - 1 ) );
1816 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); ++it )
1818 assert( it->path_or_provoking_index == expected_edge );
1819 expected_edge = it->y_or_bridge + 3;
1825 uint64_t estimated_visits = 0;
1827 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); ++it )
1828 estimated_visits +=
static_cast<uint64_t
>( it->path_or_provoking_index );
1830 std::unique_ptr<FRACTURE_EDGE_INDEX>
index;
1834 int min_y = std::numeric_limits<int>::max();
1835 int max_y = std::numeric_limits<int>::min();
1842 min_y = std::min( min_y, point.y );
1843 max_y = std::max( max_y, point.y );
1847 auto visitOriginals = [&](
auto&& aVisitor )
1855 min_y, max_y, stripe_count );
1856 aVisitor(
static_cast<uint32_t
>( edge ), first, last );
1860 visitRange( 0, paths[0].PointCount() );
1862 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); ++it )
1863 visitRange( it->path_or_provoking_index, it->y_or_bridge );
1866 index = std::make_unique<FRACTURE_EDGE_INDEX>( edges, visitOriginals, min_y, max_y, stripe_count,
1869 if( !
index->IsValid() )
1873 for(
auto it = sorted_paths.begin() + 1; it != sorted_paths.end(); it++ )
1875 auto edge =
processHole( edges, it->path_or_provoking_index, it->path_or_provoking_index + it->leftmost,
1876 it->y_or_bridge,
index.get() );
1881 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
1929 int x = edge->
m_p1.
x;
1930 int y = edge->
m_p1.
y;
1931 int min_dist = std::numeric_limits<int>::max();
1938 if( !e->matches( y ) )
1943 if( e->m_p1.y == e->m_p2.y )
1945 x_intersect = std::max( e->m_p1.x, e->m_p2.x );
1949 x_intersect = e->m_p1.x
1950 +
rescale( e->m_p2.x - e->m_p1.x, y - e->m_p1.y, e->m_p2.y - e->m_p1.y );
1953 int dist = ( x - x_intersect );
1955 if( dist >= 0 && dist < min_dist && e->m_connected )
1958 x_nearest = x_intersect;
1974 edges.push_back( split_2 );
1975 edges.push_back( lead1 );
1976 edges.push_back( lead2 );
1981 e_nearest->
m_next = lead1;
1986 for( last = edge; last->
m_next != edge; last = last->
m_next )
2012 if( paths.size() == 1 )
2015 int num_unconnected = 0;
2019 const std::vector<VECTOR2I>& points =
path.CPoints();
2020 int pointCount = points.size();
2024 int x_min = std::numeric_limits<int>::max();
2026 for(
int i = 0; i < pointCount; i++ )
2028 if( points[i].x < x_min )
2029 x_min = points[i].x;
2034 points[i + 1 == pointCount ? 0 : i + 1] );
2045 if( i == pointCount - 1 )
2049 edges.push_back( fe );
2053 if( fe->
m_p1.
x == x_min )
2054 border_edges.push_back( fe );
2065 while( num_unconnected > 0 )
2067 int x_min = std::numeric_limits<int>::max();
2068 auto it = border_edges.begin();
2073 for( ; it != border_edges.end(); ++it )
2076 int xt = border_edge->
m_p1.
x;
2078 if( ( xt <= x_min ) && !border_edge->
m_connected )
2081 smallestX = border_edge;
2085 int num_processed =
processEdge( edges, smallestX );
2088 if( !num_processed )
2090 wxLogWarning( wxT(
"Broken polygon, dropping path" ) );
2098 num_unconnected -= num_processed;
2116 paths.push_back( std::move( newPath ) );
2140 assert( aPoly.size() == 1 );
2148 std::vector<SHAPE_LINE_CHAIN> rings;
2152 aPoly[0] = std::move( lc );
2156 auto outline = std::max_element( rings.begin(), rings.end(),
2159 return std::fabs( aA.Area() ) < std::fabs( aB.Area() );
2162 std::iter_swap( rings.begin(), outline );
2163 aPoly = std::move( rings );
2173 if( paths.size() > 1 )
2197 std::array<VECTOR2I,4> pts = { aSegA.
A, aSegA.
B, aSegB.
A, aSegB.
B };
2199 std::sort( pts.begin(), pts.end(), [axis](
const VECTOR2I& p,
const VECTOR2I& q )
2202 return p.x < q.x || ( p.x == q.x && p.y < q.y );
2204 return p.y < q.y || ( p.y == q.y && p.x < q.x );
2225 wxLogTrace( wxT(
"collinear" ), wxT(
"Found exterior waist between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)" ),
2226 aSegA.
A.
x, aSegA.
A.
y, aSegA.
B.
x, aSegA.
B.
y,
2227 aSegB.
A.
x, aSegB.
A.
y, aSegB.
B.
x, aSegB.
B.
y );
2238 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
2240 bool changed =
true;
2249 std::vector<SEG> segs;
2250 segs.reserve( count );
2252 for( intptr_t i = 0; i < count; ++i )
2253 segs.emplace_back( outline.
CPoint( i ), outline.
CPoint( ( i + 1 ) % count ) );
2261 for( intptr_t i = 0; i < count && !found; ++i )
2268 [&](
int j ) ->
bool
2270 if( j == i || j == ( ( i + 1 ) % count ) || j == ( ( i + count - 1 ) % count ) )
2275 SEG other( oa, ob );
2279 if( oa == a && ob == b )
2282 if( oa == b && ob == a )
2296 index.VisitCandidates( seg, 0, visitor );
2303 int a1 = ( segA + 1 ) % outline.
PointCount();
2305 int b1 = ( segB + 1 ) % outline.
PointCount();
2331 m_polys[polyIdx][0] = std::move( lc1 );
2334 np.push_back( std::move( lc2 ) );
2335 m_polys.push_back( std::move( np ) );
2345 for(
size_t polyIdx = 0; polyIdx <
m_polys.size(); ++polyIdx )
2347 bool changed =
true;
2361 std::vector<double> prefix;
2364 [&](
int aFrom,
int aTo ) ->
double
2366 if( prefix.empty() )
2368 prefix.resize( count + 1, 0.0 );
2370 for(
int ii = 0; ii < count; ++ii )
2375 prefix[ii + 1] = prefix[ii]
2376 + ( (double) a.
x + b.
x ) * ( (
double) a.
y - b.
y );
2380 double term = aFrom <= aTo ? prefix[aTo] - prefix[aFrom]
2381 : prefix[count] - prefix[aFrom] + prefix[aTo];
2386 return term + ( (double) last.
x + first.
x ) * ( (
double) last.
y - first.
y );
2390 [&](
int aFrom,
int aTo )
2392 return aFrom <= aTo ? aTo - aFrom + 1 : count - aFrom + aTo + 1;
2398 [&](
int aVertIdx,
int aSegIdx )
2400 const int start = ( aSegIdx + 1 ) % count;
2402 if( ringSize( start, aVertIdx ) < 3 || ringSize( aVertIdx, aSegIdx ) < 3 )
2405 const bool sign = ringTerm( 0, count - 1 ) > 0;
2406 const double term1 = ringTerm( start, aVertIdx );
2407 const double term2 = ringTerm( aVertIdx, aSegIdx );
2409 return term1 != 0.0 && term2 != 0.0 && ( term1 > 0 ) ==
sign
2410 && ( term2 > 0 ) ==
sign;
2413 int insertSegIdx = -1;
2414 int insertVertIdx = -1;
2417 constexpr int RTREE_THRESHOLD = 32;
2419 if( count < RTREE_THRESHOLD )
2421 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2424 const int prevSeg = ( vertIdx + count - 1 ) % count;
2426 for(
int segIdx = 0; segIdx < count; ++segIdx )
2429 if( segIdx == prevSeg || segIdx == vertIdx )
2440 && splittable( vertIdx, segIdx ) )
2442 insertSegIdx = segIdx;
2443 insertVertIdx = vertIdx;
2451 std::vector<SEG> segs;
2452 segs.reserve( count );
2454 for(
int i = 0; i < count; ++i )
2455 segs.emplace_back( outline.
CPoint( i ), outline.
CPoint( ( i + 1 ) % count ) );
2459 for(
int vertIdx = 0; vertIdx < count && insertSegIdx < 0; ++vertIdx )
2462 const int prevSeg = ( vertIdx + count - 1 ) % count;
2465 [&](
int segIdx ) ->
bool
2467 if( segIdx == prevSeg || segIdx == vertIdx )
2478 && splittable( vertIdx, segIdx ) )
2480 insertSegIdx = segIdx;
2481 insertVertIdx = vertIdx;
2488 index.VisitCandidates(
SEG( pt, pt ), 0, pinchVisitor );
2492 if( insertSegIdx < 0 )
2499 const int splitStart1 = ( insertSegIdx + 1 ) % count;
2500 const int size1 = ringSize( splitStart1, insertVertIdx );
2501 const int size2 = ringSize( insertVertIdx, insertSegIdx );
2508 int idx = splitStart1;
2510 for(
int i = 0; i < size1; ++i )
2513 idx = ( idx + 1 ) % count;
2518 idx = insertVertIdx;
2520 for(
int i = 0; i < size2; ++i )
2523 idx = ( idx + 1 ) % count;
2528 m_polys[polyIdx][0] = std::move( poly1 );
2531 np.push_back( std::move( poly2 ) );
2532 m_polys.push_back( std::move( np ) );
2556 path.Simplify( aTolerance );
2574 while( outline.size() > 1 )
2599 std::stringstream ss;
2601 ss <<
"SHAPE_LINE_CHAIN poly; \n";
2603 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2605 for(
unsigned j = 0; j <
m_polys[i].size(); j++ )
2608 ss <<
"{ auto tmp = " <<
m_polys[i][j].Format() <<
";\n";
2614 ss <<
" poly.AddOutline(tmp); } \n";
2618 ss <<
" poly.AddHole(tmp); } \n";
2634 if( tmp !=
"polyset" )
2639 int n_polys = atoi( tmp.c_str() );
2644 for(
int i = 0; i < n_polys; i++ )
2654 int n_outlines = atoi( tmp.c_str() );
2656 if( n_outlines < 0 )
2659 for(
int j = 0; j < n_outlines; j++ )
2666 int n_vertices = atoi( tmp.c_str() );
2668 for(
int v = 0; v < n_vertices; v++ )
2672 aStream >> tmp; p.
x = atoi( tmp.c_str() );
2673 aStream >> tmp; p.
y = atoi( tmp.c_str() );
2677 paths.push_back( std::move( outline ) );
2680 m_polys.push_back( std::move( paths ) );
2691 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2708 for(
unsigned i = 0; i <
m_polys.size(); i++ )
2711 bb = *
m_polys[i][0].GetCachedBBox();
2728 if( lineChain.PointOnEdge( aP, aAccuracy ) )
2743 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2746 *aLocation = nearest;
2749 *aActual = sqrt( dist_sq );
2767 if( dist_sq == 0 || dist_sq <
SEG::Square( aClearance ) )
2770 *aLocation = nearest;
2773 *aActual = sqrt( dist_sq );
2790 int extra = segment->
GetWidth() / 2;
2792 if(
Collide( segment->
GetSeg(), aClearance + extra, aActual, aLocation ) )
2795 *aActual = std::max( 0, *aActual - extra );
2806 int extra =
circle->GetRadius();
2808 if(
Collide(
circle->GetCenter(), aClearance + extra, aActual, aLocation ) )
2811 *aActual = std::max( 0, *aActual - extra );
2828 if( aActual || aLocation )
2833 if( aShape->
Collide( &tri, aClearance, &triActual, &triLocation ) )
2844 if( aShape->
Collide( &tri, aClearance ) )
2853 *aActual = std::max( 0,
actual );
2876 if( aPolygonIdx < 0 )
2877 aPolygonIdx +=
m_polys.size();
2879 m_polys[aPolygonIdx].erase(
m_polys[aPolygonIdx].begin() + aContourIdx );
2899 std::vector<VERTEX_INDEX> indices_to_remove;
2904 segmentStart = *iterator;
2910 segmentEnd = contourStart;
2918 contourStart = *iterator;
2926 wxCHECK_MSG( iterator, removed, wxT(
"Invalid polygon. Reached end without noticing. Please report this error" ) );
2928 segmentEnd = *iterator;
2932 if( segmentStart == segmentEnd )
2934 indices_to_remove.push_back( indexStart );
2941 for(
auto it = indices_to_remove.rbegin(); it != indices_to_remove.rend(); ++it )
2964 if( triangleSet->GetSourceOutlineIndex() == aIdx )
2966 else if( triangleSet->GetSourceOutlineIndex() > aIdx )
2967 triangleSet->SetSourceOutlineIndex( triangleSet->GetSourceOutlineIndex() - 1 );
2994 Append( aP.
x, aP.
y, aOutline, aHole );
3000 int aClearance )
const
3003 bool collision =
false;
3013 delta = *iterator - aPoint;
3016 distance_squared =
delta.SquaredEuclideanNorm();
3019 if( distance_squared <= clearance_squared )
3021 if( !aClosestVertex )
3027 clearance_squared = distance_squared;
3030 *aClosestVertex = iterator.GetIndex();
3040 int aClearance )
const
3043 bool collision =
false;
3048 const SEG currentSegment = *iterator;
3052 if( distance_squared <= clearance_squared )
3054 if( !aClosestVertex )
3060 clearance_squared = distance_squared;
3063 *aClosestVertex = iterator.GetIndex();
3073 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
3077 for(
int holeIdx = 0; holeIdx <
HoleCount( polygonIdx ); holeIdx++ )
3084 bool aUseBBoxCaches )
const
3090 if( aSubpolyIndex >= 0 )
3091 return containsSingle( aP, aSubpolyIndex, aAccuracy, aUseBBoxCaches );
3094 for(
int polygonIdx = 0; polygonIdx <
OutlineCount(); polygonIdx++ )
3096 if(
containsSingle( aP, polygonIdx, aAccuracy, aUseBBoxCaches ) )
3112 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
3129 throw( std::out_of_range(
"aGlobalIndex-th vertex does not exist" ) );
3140 bool aUseBBoxCaches )
const
3146 for(
int holeIdx = 0; holeIdx <
HoleCount( aSubpolyIndex ); holeIdx++ )
3168 path.Move( aVector );
3172 tri->Move( aVector );
3184 path.Mirror( aRef, aFlipDirection );
3197 path.Rotate( aAngle, aCenter );
3213 c +=
path.PointCount();
3250 SEG::ecoord minDistance = (*iterator).SquaredDistance( aPoint );
3253 *aNearest = ( *iterator ).NearestPoint( aPoint );
3255 for( iterator++; iterator && minDistance > 0; iterator++ )
3257 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aPoint );
3259 if( currentDistance < minDistance )
3262 *aNearest = (*iterator).NearestPoint( aPoint );
3264 minDistance = currentDistance;
3280 *aNearest = ( aSegment.
A + aSegment.
B ) / 2;
3286 SEG::ecoord minDistance = (*iterator).SquaredDistance( aSegment );
3289 *aNearest = ( *iterator ).NearestPoint( aSegment );
3291 for( iterator++; iterator && minDistance > 0; iterator++ )
3293 SEG::ecoord currentDistance = (*iterator).SquaredDistance( aSegment );
3295 if( currentDistance < minDistance )
3298 *aNearest = (*iterator).NearestPoint( aSegment );
3300 minDistance = currentDistance;
3305 return minDistance < 0 ? 0 : minDistance;
3312 wxASSERT_MSG( !aOutlineOnly, wxT(
"Warning: SHAPE_POLY_SET::SquaredDistance does not yet "
3313 "support aOutlineOnly==true" ) );
3320 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
3323 aNearest ? &nearest :
nullptr );
3325 if( currentDistance_sq < minDistance_sq )
3328 *aNearest = nearest;
3330 minDistance_sq = currentDistance_sq;
3334 return minDistance_sq;
3345 for(
unsigned int polygonIdx = 0; polygonIdx <
m_polys.size(); polygonIdx++ )
3348 aNearest ? &nearest :
nullptr );
3350 if( currentDistance_sq < minDistance_sq )
3353 *aNearest = nearest;
3355 minDistance_sq = currentDistance_sq;
3359 return minDistance_sq;
3372 return index.m_contour > 0;
3380 for(
unsigned int idx = 0; idx <
m_polys.size(); idx++ )
3391 for(
size_t idx = 0; idx <
m_polys.size(); idx++ )
3400 SHAPE::operator=( aOther );
3457 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* aHintData,
3479 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>& dest,
3480 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>* hintData,
3483 bool triangulationValid =
false;
3487 if( hintData && hintData->size() != (
unsigned) polySet.
OutlineCount() )
3492 if( !dest.empty() && dest.back()->GetTriangleCount() == 0 )
3493 dest.erase( dest.end() - 1 );
3501 if( partitionLeaves > 1 )
3503 std::vector<SHAPE_LINE_CHAIN> partitions =
3506 if( partitions.size() > 1 )
3510 if( taskSubmitter && partitions.size() > 2 )
3512 size_t leafCount = partitions.size();
3514 struct WorkStealState
3516 std::unique_ptr<std::atomic<bool>[]> claimed;
3517 std::unique_ptr<std::atomic<bool>[]> done;
3518 std::unique_ptr<std::atomic<bool>[]> ok;
3520 explicit WorkStealState(
size_t n ) :
3521 claimed(
new std::atomic<bool>[n] ),
3522 done(
new std::atomic<bool>[n] ),
3523 ok(
new std::atomic<bool>[n] )
3525 for(
size_t i = 0; i < n; ++i )
3527 claimed[i].store(
false );
3528 done[i].store(
false );
3529 ok[i].store(
false );
3534 auto state = std::make_shared<WorkStealState>( leafCount );
3536 std::vector<std::unique_ptr<TRIANGULATED_POLYGON>> results( leafCount );
3538 for(
size_t i = 0; i < leafCount; ++i )
3540 results[i] = std::make_unique<TRIANGULATED_POLYGON>( forOutline );
3543 for(
size_t i = 0; i < leafCount; ++i )
3545 auto* triPoly = results[i].get();
3546 auto* leaf = &partitions[i];
3548 taskSubmitter( [state, i, triPoly, leaf]()
3550 if( state->claimed[i].exchange(
true ) )
3555 state->done[i].store(
true, std::memory_order_release );
3559 for(
size_t i = 0; i < leafCount; ++i )
3561 if( state->claimed[i].exchange(
true ) )
3566 state->done[i].store(
true, std::memory_order_release );
3569 for(
size_t i = 0; i < leafCount; ++i )
3571 while( !state->done[i].load( std::memory_order_acquire ) )
3573 std::this_thread::yield();
3579 for(
size_t i = 0; i < leafCount; ++i )
3580 allOk = allOk && state->ok[i].load();
3584 for(
auto& r : results )
3586 if( r->GetTriangleCount() > 0 )
3587 dest.push_back( std::move( r ) );
3590 triangulationValid =
true;
3597 for(
auto it = partitions.rbegin(); it != partitions.rend(); ++it )
3606 dest.push_back( std::make_unique<TRIANGULATED_POLYGON>( forOutline ) );
3613 hintData ? hintData->at(
index ).get() :
nullptr ) )
3627 triangulationValid =
false;
3634 triangulationValid =
true;
3637 return triangulationValid;
3656 bool directOk =
true;
3658 for(
int ii = 0; ii < srcSet->
OutlineCount() && directOk; ++ii )
3663 if( poly.size() > 1 )
3676 for(
size_t jj = 1; jj < poly.size(); ++jj )
3716 double originalArea =
std::abs( poly.front().Area() );
3718 if( originalArea > 0.0 )
3720 double triArea = 0.0;
3725 double coverage = triArea / originalArea;
3727 if( coverage > 1.01 || coverage < 0.99 )
3740 bool splitOk =
true;
3742 for(
int jj = 0; jj < splitSet.
OutlineCount() && splitOk; ++jj )
3767 bool fallbackOk =
true;
3769 for(
int ii = 0; ii < srcSet->
OutlineCount() && fallbackOk; ++ii )
3774 for(
size_t jj = 1; jj < poly.size(); ++jj )
3831 hash.
add( outline.size() );
3835 hash.
add( lc.PointCount() );
3837 for(
int i = 0; i < lc.PointCount(); i++ )
3865 std::set<long long> ptHashes;
3869 for(
const VECTOR2I& pt : lc.CPoints() )
3871 const long long ptHash = (
long long) pt.x << 32 | pt.y;
3873 if( ptHashes.count( ptHash ) > 0 )
3876 ptHashes.insert( ptHash );
3895 n += t->GetTriangleCount();
3908 aSubshapes.push_back( &tri );
3919 if( aClearance != 0 )
3961 if( triCount < 2 || triCount >
static_cast<size_t>( std::numeric_limits<int>::max() ) / 6 )
3964 const int halfEdgeCount =
static_cast<int>( 3 * triCount );
3967 std::vector<int> heVertex( halfEdgeCount );
3969 for(
size_t k = 0; k < triCount; k++ )
3976 auto nextInTriangle = [](
int aEdge ) {
return aEdge - aEdge % 3 + ( aEdge + 1 ) % 3; };
3980 std::vector<int> twin( halfEdgeCount, -1 );
3984 while( tableSize < halfEdgeCount * 2 )
3987 const uint32_t tableMask =
static_cast<uint32_t
>( tableSize - 1 );
3988 std::vector<int> slotEdge( tableSize, -1 );
3992 std::vector<char> retired( tableSize, 0 );
3993 std::vector<int> toVisit;
3994 std::vector<char> queued( halfEdgeCount, 0 );
3996 for(
int edge = 0; edge < halfEdgeCount; edge++ )
3998 int from = heVertex[edge];
3999 int to = heVertex[nextInTriangle( edge )];
4000 uint32_t keyLow =
static_cast<uint32_t
>( from < to ? from : to );
4001 uint32_t keyHigh =
static_cast<uint32_t
>( from < to ? to : from );
4002 uint32_t slot = ( ( keyLow * 0x9e3779b1u ) ^ ( keyHigh * 0x85ebca6bu ) ) & tableMask;
4003 bool handled =
false;
4005 while( slotEdge[slot] != -1 )
4007 int other = slotEdge[slot];
4008 int otherFrom = heVertex[other];
4009 int otherTo = heVertex[nextInTriangle( other )];
4010 uint32_t otherLow =
static_cast<uint32_t
>( otherFrom < otherTo ? otherFrom : otherTo );
4011 uint32_t otherHigh =
static_cast<uint32_t
>( otherFrom < otherTo ? otherTo : otherFrom );
4013 if( otherLow == keyLow && otherHigh == keyHigh )
4019 else if( twin[other] == -1 )
4024 toVisit.push_back( other );
4029 int mate = twin[other];
4039 slot = ( slot + 1 ) & tableMask;
4043 slotEdge[slot] = edge;
4046 while( !toVisit.empty() )
4048 int edge = toVisit.back();
4052 int twinEdge = twin[edge];
4054 if( twinEdge == -1 )
4057 int tri = edge - edge % 3;
4058 int twinTri = twinEdge - twinEdge % 3;
4060 int apexAEdge = tri + ( edge + 2 ) % 3;
4061 int sharedEndEdge = tri + ( edge + 1 ) % 3;
4062 int apexBEdge = twinTri + ( twinEdge + 2 ) % 3;
4063 int twinSharedEdge = twinTri + ( twinEdge + 1 ) % 3;
4065 int apexA = heVertex[apexAEdge];
4066 int sharedFrom = heVertex[edge];
4067 int sharedTo = heVertex[sharedEndEdge];
4068 int apexB = heVertex[apexBEdge];
4090 if( legal && curSlivers == 0 )
4096 bool doFlip = ( flipSlivers != curSlivers ) ? ( flipSlivers < curSlivers ) : !legal;
4101 heVertex[edge] = apexB;
4102 heVertex[twinEdge] = apexA;
4104 int apexBOuterTwin = twin[apexBEdge];
4105 int apexAOuterTwin = twin[apexAEdge];
4107 twin[edge] = apexBOuterTwin;
4109 if( apexBOuterTwin != -1 )
4110 twin[apexBOuterTwin] = edge;
4112 twin[twinEdge] = apexAOuterTwin;
4114 if( apexAOuterTwin != -1 )
4115 twin[apexAOuterTwin] = twinEdge;
4117 twin[apexAEdge] = apexBEdge;
4118 twin[apexBEdge] = apexAEdge;
4121 for(
int outer : { edge, twinEdge, sharedEndEdge, twinSharedEdge } )
4123 if( twin[outer] != -1 && !queued[outer] )
4126 toVisit.push_back( outer );
4131 std::deque<TRI> refined;
4133 for(
size_t i = 0; i < triCount; i++ )
4134 refined.emplace_back( heVertex[3 * i + 0], heVertex[3 * i + 1], heVertex[3 * i + 2],
this );
4157 for(
int i = 0; i <
path.PointCount(); i++ )
4161 vec.
x = ( pt.
x - aCenter.
x ) * aScaleFactorX;
4162 vec.
y = ( pt.
y - aCenter.
y ) * aScaleFactorY;
4165 path.SetPoint( i, pt );
4179 Clipper2Lib::Clipper64 clipper;
4180 Clipper2Lib::PolyTree64 tree;
4181 Clipper2Lib::Paths64 paths;
4185 Clipper2Lib::Path64 lc;
4186 lc.reserve(
path.PointCount() );
4188 for(
int i = 0; i <
path.PointCount(); i++ )
4189 lc.emplace_back(
path.CPoint( i ).x,
path.CPoint( i ).y );
4191 paths.push_back( std::move( lc ) );
4194 clipper.AddSubject( paths );
4195 clipper.Execute( Clipper2Lib::ClipType::Union, aEvenOdd ? Clipper2Lib::FillRule::EvenOdd
4196 : Clipper2Lib::FillRule::NonZero, tree );
4198 std::vector<CLIPPER_Z_VALUE> zValues;
4199 std::vector<SHAPE_ARC> arcBuffer;
4219 int aSpacing,
int aLineLength )
const
4221 std::vector<SEG> hatchLines;
4234 if( iterator->x < min_x )
4235 min_x = iterator->x;
4237 if( iterator->x > max_x )
4238 max_x = iterator->x;
4240 if( iterator->y < min_y )
4241 min_y = iterator->y;
4243 if( iterator->y > max_y )
4244 max_y = iterator->y;
4247 auto sortEndsByDescendingX =
4250 return tst.x < ref.
x;
4253 for(
double slope : aSlopes )
4255 int64_t max_a, min_a;
4268 min_a = ( min_a / aSpacing ) * aSpacing;
4271 std::vector<VECTOR2I> pointbuffer;
4272 pointbuffer.reserve( 256 );
4274 for( int64_t a = min_a; a < max_a; a += aSpacing )
4276 pointbuffer.clear();
4281 const SEG seg = *iterator;
4287 if( pt.
x < min_x || pt.
x > max_x || pt.
y < min_y || pt.
y > max_y )
4298 if( pointbuffer.size() > 2 )
4299 sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX );
4302 for(
size_t ip = 0; ip + 1 < pointbuffer.size(); ip++ )
4304 const VECTOR2I& p1 = pointbuffer[ip];
4305 const VECTOR2I& p2 = pointbuffer[ip + 1];
4311 SEG candidate( p1, p2 );
4313 VECTOR2I mid( ( candidate.
A.
x + candidate.
B.
x ) / 2, ( candidate.
A.
y + candidate.
B.
y ) / 2 );
4318 int dx = p2.
x - p1.
x;
4322 if( aLineLength == -1 ||
std::abs( dx ) < 2 * aLineLength )
4324 hatchLines.emplace_back( candidate );
4328 double dy = p2.
y - p1.
y;
4338 int y1 =
KiROUND( p1.
y + dx * slope );
4339 int y2 =
KiROUND( p2.
y - dx * slope );
4341 hatchLines.emplace_back(
SEG( p1.
x, p1.
y, x1, y1 ) );
4343 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.
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).
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)
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.
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 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)
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