46#ifndef __POLYGON_TRIANGULATION_H
47#define __POLYGON_TRIANGULATION_H
66#if defined( __MINGW32__ )
67 #define TRIANGULATESIMPLIFICATIONLEVEL 50
68 #define TRIANGULATEMINIMUMAREA 1000
70 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
71 #define TRIANGULATEMINIMUMAREA ADVANCED_CFG::GetCfg().m_TriangulateMinimumArea
74#define TRIANGULATE_TRACE "triangulate"
91 if( aPolygon.empty() )
94 if( aPolygon.size() == 1 )
101 for(
size_t i = 1; i < aPolygon.size(); i++ )
102 m_bbox.Merge( aPolygon[i].BBox() );
119 if( !outerRing || outerRing->
prev == outerRing->
next )
122 std::vector<VERTEX*> holeRings;
124 for(
size_t i = 1; i < aPolygon.size(); i++ )
127 baseIndex += aPolygon[i].PointCount();
132 if( holeRing && holeRing->
prev != holeRing->
next )
133 holeRings.push_back( holeRing );
138 if( !holeRings.empty() )
150 outerRing = simplified;
158 wxLogTrace(
TRIANGULATE_TRACE,
"Tesselation with holes failed, logging remaining vertices" );
183 if( !firstVertex || firstVertex->
prev == firstVertex->
next )
191 firstVertex = simplified;
201 if( aHintData && aHintData->
Vertices().size() ==
m_result.GetVertexCount() )
212 wxLogTrace(
TRIANGULATE_TRACE,
"Tesselation failed, logging remaining vertices" );
222 size_t aTargetLeaves )
const
225 std::vector<double> fractions;
231 fractions.reserve( partitions.size() );
249 std::array<SCANLINE_HIT, 2>& aHits )
const
253 for(
int ii = 0; ii < aPoly.
PointCount(); ++ii )
260 if( a.
x == b.
x || aCut <= std::min( a.
x, b.
x ) || aCut >= std::max( a.
x, b.
x ) )
266 double t =
static_cast<double>( aCut - a.
x ) /
static_cast<double>( b.
x - a.
x );
267 int y =
static_cast<int>( std::lround( a.
y + t * ( b.
y - a.
y ) ) );
268 aHits[count++] = { ii,
VECTOR2I( aCut, y ) };
272 if( a.
y == b.
y || aCut <= std::min( a.
y, b.
y ) || aCut >= std::max( a.
y, b.
y ) )
278 double t =
static_cast<double>( aCut - a.
y ) /
static_cast<double>( b.
y - a.
y );
279 int x =
static_cast<int>( std::lround( a.
x + t * ( b.
x - a.
x ) ) );
280 aHits[count++] = { ii,
VECTOR2I( x, aCut ) };
297 idx = ( idx + 1 ) % count;
299 }
while( idx != ( aEnd + 1 ) % count && guard <= count + 2 );
307 std::array<SHAPE_LINE_CHAIN, 2>& aChildren,
double& aAreaA,
308 double& aAreaB )
const
310 std::array<SCANLINE_HIT, 2> hits;
316 augmented.
Split( hits[0].point,
true );
317 augmented.
Split( hits[1].point,
true );
319 int idxA = augmented.
Find( hits[0].point );
320 int idxB = augmented.
Find( hits[1].point );
322 if( idxA < 0 || idxB < 0 || idxA == idxB )
328 if( aChildren[0].PointCount() < 3 || aChildren[1].PointCount() < 3 )
331 aAreaA =
std::abs( aChildren[0].Area() );
332 aAreaB =
std::abs( aChildren[1].Area() );
333 return aAreaA > 0.0 && aAreaB > 0.0;
337 std::array<SHAPE_LINE_CHAIN, 2>& aChildren )
const
347 [&](
bool aVertical ) ->
bool
349 const int low = ( aVertical ? bbox.
GetX() : bbox.
GetY() ) + 1;
355 double bestImbalance = std::numeric_limits<double>::infinity();
356 std::array<SHAPE_LINE_CHAIN, 2> bestChildren;
358 constexpr int kSamples = 15;
360 for(
int ii = 1; ii <= kSamples; ++ii )
362 int cut = low + ( ( high - low ) * ii ) / ( kSamples + 1 );
363 std::array<SHAPE_LINE_CHAIN, 2> candidate;
372 if( imbalance < bestImbalance )
374 bestImbalance = imbalance;
375 bestChildren = std::move( candidate );
379 if( !std::isfinite( bestImbalance ) || bestImbalance > 0.35 )
382 aChildren = std::move( bestChildren );
386 return tryAxis( verticalFirst ) || tryAxis( !verticalFirst );
390 size_t aTargetLeaves )
const
392 std::vector<SHAPE_LINE_CHAIN> leaves = { aPoly };
394 if( aTargetLeaves < 2 )
397 while( leaves.size() < aTargetLeaves )
400 double bestArea = 0.0;
402 for(
size_t ii = 0; ii < leaves.size(); ++ii )
406 if(
area > bestArea )
409 bestLeaf =
static_cast<int>( ii );
416 std::array<SHAPE_LINE_CHAIN, 2> children;
421 leaves[bestLeaf] = std::move( children[0] );
422 leaves.push_back( std::move( children[1] ) );
430 constexpr size_t kVerticesPerLeaf = 50000;
431 constexpr size_t kMaxLeaves = 8;
434 while( leaves < kMaxLeaves
435 &&
static_cast<size_t>( aPoly.
PointCount() ) / leaves > kVerticesPerLeaf )
448 std::set<VERTEX*> seen;
449 wxLog::EnableLogging();
452 if( !p.next || p.next == &p || seen.find( &p ) != seen.end() )
461 if( aSeen && aSeen->count( aStart ) )
465 aSeen->insert( aStart );
469 wxString msg = wxString::Format(
"Vertices: %d,%d,",
static_cast<int>( aStart->
x ),
470 static_cast<int>( aStart->
y ) );
474 msg += wxString::Format(
"%d,%d,",
static_cast<int>( p->
x ),
static_cast<int>( p->
y ) );
481 }
while( p != aStart );
496 if( !aStart || aStart->
next == aStart->
prev )
531 }
while( p != aStart &&
next && p );
556 wxASSERT( aStart->
next && aStart->
prev );
560 while( p != aStart && p->
next && p->
prev )
630 constexpr int kMaxRecursion = 64;
632 if( pass >= kMaxRecursion )
634 wxLogTrace(
TRIANGULATE_TRACE,
"earcutList recursion limit reached; aborting triangulation", pass );
638 wxLogTrace(
TRIANGULATE_TRACE,
"earcutList starting at %p for pass %d", aPoint, pass );
646 int internal_pass = 1;
647 constexpr int kEarLookahead = 2;
649 while( aPoint->
prev != aPoint->
next )
654 VERTEX* bestEar =
nullptr;
655 double bestScore = -1.0;
658 for(
VERTEX* candidate = aPoint; candidate && lookahead < kEarLookahead;
659 candidate = candidate->
next, ++lookahead )
661 if( !candidate->isEar() ||
isTooSmall( candidate ) )
664 const double score =
earScore( candidate->prev, candidate, candidate->next );
666 if( !bestEar || score > bestScore )
675 prev = bestEar->
prev;
688 if( *prev != *nextNext &&
intersects( prev, aPoint,
next, nextNext ) &&
693 "Local intersection detected. Merging minor triangle with area %f",
694 area( prev, aPoint, nextNext ) );
695 m_result.AddTriangle( prev->
i, aPoint->
i, nextNext->
i );
713 if( aPoint == stop && aPoint->
prev != aPoint->
next )
724 if( newPoint->
next == newPoint->
prev )
738 if( internal_pass < 4 )
781 double prev_sq_len = ( aPoint->
prev->
x - aPoint->
x ) * ( aPoint->
prev->
x - aPoint->
x ) +
782 ( aPoint->
prev->
y - aPoint->
y ) * ( aPoint->
prev->
y - aPoint->
y );
783 double next_sq_len = ( aPoint->
next->
x - aPoint->
x ) * ( aPoint->
next->
x - aPoint->
x ) +
784 ( aPoint->
next->
y - aPoint->
y ) * ( aPoint->
next->
y - aPoint->
y );
785 double opp_sq_len = ( aPoint->
next->
x - aPoint->
prev->
x ) * ( aPoint->
next->
x - aPoint->
prev->
x ) +
788 return ( prev_sq_len < min_area || next_sq_len < min_area || opp_sq_len < min_area );
793 const double ab_sq = ( a->
x - b->
x ) * ( a->
x - b->
x ) + ( a->
y - b->
y ) * ( a->
y - b->
y );
794 const double bc_sq = ( b->
x - c->
x ) * ( b->
x - c->
x ) + ( b->
y - c->
y ) * ( b->
y - c->
y );
795 const double ca_sq = ( c->
x - a->
x ) * ( c->
x - a->
x ) + ( c->
y - a->
y ) * ( c->
y - a->
y );
796 const double norm = ab_sq + bc_sq + ca_sq;
815 for(
int i = 0; i < aPoints.
PointCount(); i++ )
819 sum += ( ( p2.
x - p1.
x ) * ( p2.
y + p1.
y ) );
822 bool isCW = sum > 0.0;
823 bool needReverse = ( aWantCCW == isCW );
825 auto addVertex = [&](
int i )
839 for(
int i = aPoints.
PointCount() - 1; i >= 0; i-- )
844 for(
int i = 0; i < aPoints.
PointCount(); i++ )
851 if( tail && tail->
next != tail && ( *tail == *tail->
next ) )
870 std::vector<HoleInfo> holes;
871 holes.reserve( aHoleRings.size() );
873 for(
VERTEX* hole : aHoleRings )
880 if( p->
x < leftmost->
x || ( p->
x == leftmost->
x && p->
y < leftmost->
y ) )
886 holes.push_back( { leftmost, leftmost->
x } );
889 std::sort( holes.begin(), holes.end(),
890 [](
const HoleInfo& a,
const HoleInfo& b ) { return a.leftX < b.leftX; } );
892 for(
const HoleInfo& hi : holes )
898 VERTEX* bridgeReverse = bridge->
split( hi.leftmost );
905 hi.leftmost->x, hi.leftmost->y );
934 if( toRemove == aEnd )
949 }
while( again || p != aEnd );
962 double hx = aHole->
x;
963 double hy = aHole->
y;
964 double qx = -std::numeric_limits<double>::infinity();
969 if( hy <= p->y && hy >= p->
next->
y && p->
next->
y != p->
y )
971 double x = p->
x + ( hy - p->
y ) * ( p->
next->
x - p->
x )
974 if( x <= hx && x > qx )
983 if( hy == p->
next->
y )
992 }
while( p != aOuterStart );
1004 double tanMin = std::numeric_limits<double>::infinity();
1010 if( hx >= p->
x && p->
x >= mx && hx != p->
x )
1015 inside =
triArea( hx, hy, mx, my, p->
x, p->
y ) >= 0
1016 &&
triArea( mx, my, qx, hy, p->
x, p->
y ) >= 0
1017 &&
triArea( qx, hy, hx, hy, p->
x, p->
y ) >= 0;
1019 inside =
triArea( qx, hy, mx, my, p->
x, p->
y ) >= 0
1020 &&
triArea( mx, my, hx, hy, p->
x, p->
y ) >= 0
1021 &&
triArea( hx, hy, qx, hy, p->
x, p->
y ) >= 0;
1025 double t =
std::abs( hy - p->
y ) / ( hx - p->
x );
1041 }
while( p != stop );
1049 static double triArea(
double ax,
double ay,
double bx,
double by,
1050 double cx,
double cy )
1052 return ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax );
1071 struct VertexComparator {
1072 bool operator()(
const std::pair<VERTEX*,double>& a,
const std::pair<VERTEX*,double>& b)
const {
1073 return a.second > b.second;
1077 std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
1082 double len = ( p->
x - p->
next->
x ) * ( p->
x - p->
next->
x ) +
1084 longest.emplace( p, len );
1088 }
while (p != aStart);
1090 avg /= longest.size();
1093 constexpr double kSubdivideThresholdFactor = 1.1;
1094 const double subdivideThreshold = avg * kSubdivideThresholdFactor;
1096 for(
auto it = longest.begin(); it != longest.end() && it->second > subdivideThreshold;
1106 int divisions = avg / it->second + 2 + pass;
1107 double step = 1.0 / divisions;
1109 for(
int i = 1; i < divisions; i++ )
1111 double x = a->
x * ( 1.0 - step * i ) + b->
x * ( step * i );
1112 double y = a->
y * ( 1.0 - step * i ) + b->
y * ( step * i );
1129 VERTEX* origPoly = start;
1132 if( !start || !start->
next || start->
next == start->
prev
1144 std::vector<VERTEX*> overlapPoints;
1147 while ( z_pt->
prevZ && *z_pt->
prevZ == *origPoly )
1150 overlapPoints.push_back( z_pt );
1152 while( z_pt->
nextZ && *z_pt->
nextZ == *origPoly )
1155 overlapPoints.push_back( z_pt );
1158 if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
1159 || overlapPoints[0]->prev == overlapPoints[1] )
1161 origPoly = origPoly->
next;
1165 if( overlapPoints[0]->
area( overlapPoints[1] ) < 0 || overlapPoints[1]->
area( overlapPoints[0] ) < 0 )
1168 origPoly = origPoly->
next;
1172 wxLogTrace(
TRIANGULATE_TRACE,
"Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
1173 std::swap( overlapPoints[0]->
next, overlapPoints[1]->
next );
1174 overlapPoints[0]->next->prev = overlapPoints[0];
1175 overlapPoints[1]->next->prev = overlapPoints[1];
1177 overlapPoints[0]->updateList();
1178 overlapPoints[1]->updateList();
1181 bool retval =
earcutList( overlapPoints[0], aPass )
1184 wxLogTrace(
TRIANGULATE_TRACE,
"%s at first overlap split", retval ?
"Success" :
"Failed" );
1188 }
while ( origPoly != start );
1198 while( marker != origPoly->
prev )
1201 if( origPoly->
next && origPoly->
i != marker->
i &&
goodSplit( origPoly, marker ) )
1214 marker = marker->
next;
1217 origPoly = origPoly->
next;
1218 }
while( origPoly != start );
1241 bool pos_area = a->
area( b ) > 0 && b->
area( a ) > 0;
1243 return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
1248 constexpr int sign(
double aVal )
const
1250 return ( aVal > 0 ) - ( aVal < 0 );
1258 return q->
x <= std::max( p->
x, r->
x ) &&
1259 q->
x >= std::min( p->
x, r->
x ) &&
1260 q->
y <= std::max( p->
y, r->
y ) &&
1261 q->
y >= std::min( p->
y, r->
y );
1271 int sign1 =
sign(
area( p1, q1, p2 ) );
1272 int sign2 =
sign(
area( p1, q1, q2 ) );
1273 int sign3 =
sign(
area( p2, q2, p1 ) );
1274 int sign4 =
sign(
area( p2, q2, q1 ) );
1276 if( sign1 != sign2 && sign3 != sign4 )
1308 if( p->
i == a->
i || p->
i == b->
i || q->
i == a->
i || q->
i == b->
i )
constexpr coord_type GetY() const
constexpr size_type GetWidth() const
constexpr coord_type GetX() const
constexpr size_type GetHeight() const
constexpr coord_type GetRight() const
constexpr coord_type GetBottom() const
SHAPE_POLY_SET::TRIANGULATED_POLYGON & m_result
VERTEX * createRing(const SHAPE_LINE_CHAIN &aPoints, int aBaseIndex, bool aWantCCW)
Create a VERTEX linked list from a SHAPE_LINE_CHAIN with a global index offset.
bool intersects(const VERTEX *p1, const VERTEX *q1, const VERTEX *p2, const VERTEX *q2) const
Check for intersection between two segments, end points included.
VERTEX * eliminateHoles(VERTEX *aOuterRing, std::vector< VERTEX * > &aHoleRings)
Bridge all hole rings into the outer ring by sorting holes left-to-right and connecting each hole's l...
friend struct POLYGON_TRIANGULATION_TEST_ACCESS
constexpr int sign(double aVal) const
static double triArea(double ax, double ay, double bx, double by, double cx, double cy)
Signed area of triangle (ax,ay), (bx,by), (cx,cy).
bool splitPolygon(VERTEX *start, int aPass)
If we cannot find an ear to slice in the current polygon list, we use this to split the polygon into ...
void logVertices(VERTEX *aStart, std::set< VERTEX * > *aSeen)
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,...
friend class SHAPE_POLY_SET
VERTEX * insertTriVertex(const VECTOR2I &pt, VERTEX *last)
Create an entry in the vertices lookup and optionally inserts the newly created vertex into an existi...
bool goodSplit(const VERTEX *a, const VERTEX *b) const
Check if a segment joining two vertices lies fully inside the polygon.
double earScore(const VERTEX *a, const VERTEX *b, const VERTEX *c) const
std::vector< SHAPE_LINE_CHAIN > partitionPolygonBalanced(const SHAPE_LINE_CHAIN &aPoly, size_t aTargetLeaves) const
VERTEX * simplifyList(VERTEX *aStart)
Simplify the line chain by removing points that are too close to each other.
POLYGON_TRIANGULATION(SHAPE_POLY_SET::TRIANGULATED_POLYGON &aResult)
bool collectScanlineHits(const SHAPE_LINE_CHAIN &aPoly, bool aVertical, int aCut, std::array< SCANLINE_HIT, 2 > &aHits) const
constexpr bool overlapping(const VERTEX *p, const VERTEX *q, const VERTEX *r) const
If p, q, and r are collinear and r lies between p and q, then return true.
bool splitPolygonBalanced(const SHAPE_LINE_CHAIN &aPoly, std::array< SHAPE_LINE_CHAIN, 2 > &aChildren) const
void logRemaining()
Outputs a list of vertices that have not yet been triangulated.
VERTEX * removeNullTriangles(VERTEX *aStart)
Iterate through the list to remove NULL triangles if they exist.
bool intersectsPolygon(const VERTEX *a, const VERTEX *b) const
Check whether the segment from vertex a -> vertex b crosses any of the segments of the polygon of whi...
bool isTooSmall(const VERTEX *aPoint) const
Check whether a given vertex is too small to matter.
bool earcutList(VERTEX *aPoint, int pass=0)
Walk through a circular linked list starting at aPoint.
bool splitPolygonAtCoordinate(const SHAPE_LINE_CHAIN &aPoly, bool aVertical, int aCut, std::array< SHAPE_LINE_CHAIN, 2 > &aChildren, double &aAreaA, double &aAreaB) const
size_t suggestedPartitionLeafCount(const SHAPE_LINE_CHAIN &aPoly) const
std::vector< double > PartitionAreaFractionsForTesting(const SHAPE_LINE_CHAIN &aPoly, size_t aTargetLeaves) const
size_t m_vertices_original_size
bool sectorContainsSector(const VERTEX *m, const VERTEX *p) const
Whether sector in vertex m contains sector in vertex p in the same coordinate frame.
SHAPE_LINE_CHAIN createSplitChild(const SHAPE_LINE_CHAIN &aPoly, int aStart, int aEnd) const
void subdividePolygon(VERTEX *aStart, int pass=0)
Inserts a new vertex halfway between each existing pair of vertices.
VERTEX * filterPoints(VERTEX *aStart, VERTEX *aEnd=nullptr)
Remove consecutive duplicate vertices from the linked list.
bool TesselatePolygon(const SHAPE_LINE_CHAIN &aPoly, SHAPE_POLY_SET::TRIANGULATED_POLYGON *aHintData)
VERTEX * findHoleBridge(VERTEX *aHole, VERTEX *aOuterStart)
Find a vertex on the outer ring visible from the hole's leftmost vertex by casting a horizontal ray t...
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int Split(const VECTOR2I &aP, bool aExact=false)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
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.
double Area(bool aAbsolute=true) const
Return the area of this chain.
SHAPE_LINE_CHAIN & Simplify2(bool aRemoveColinear=true)
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int Find(const VECTOR2I &aP, int aThreshold=0) const
Search for point aP.
const std::vector< VECTOR2I > & CPoints() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
const std::deque< VECTOR2I > & Vertices() const
const std::deque< TRI > & Triangles() const
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
std::deque< VERTEX > m_vertices
bool middleInside(const VERTEX *a, const VERTEX *b) const
Check if the middle of the segment from a to b is inside the polygon.
VERTEX * createList(const SHAPE_LINE_CHAIN &points, VERTEX *aTail=nullptr, void *aUserData=nullptr)
Create a list of vertices from a line chain.
bool locallyInside(const VERTEX *a, const VERTEX *b) const
Check whether the segment from vertex a -> vertex b is inside the polygon around the immediate area o...
double area(const VERTEX *p, const VERTEX *q, const VERTEX *r) const
Return the twice the signed area of the triangle formed by vertices p, q, and r.
VERTEX * insertVertex(int aIndex, const VECTOR2I &pt, VERTEX *last, void *aUserData=nullptr)
Insert a vertex into the vertex set.
VERTEX_SET(int aSimplificationLevel)
VECTOR2I::extended_type m_simplificationLevel
VERTEX * split(VERTEX *b)
Split the referenced polygon between the reference point and vertex b, assuming they are in the same ...
void updateList()
After inserting or changing nodes, this function should be called to remove duplicate vertices and en...
void remove()
Remove the node from the linked list and z-ordered linked list.
double area(const VERTEX *aEnd=nullptr) const
Returns the signed area of the polygon connected to the current vertex, optionally ending at a specif...
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
#define TRIANGULATE_TRACE
#define TRIANGULATEMINIMUMAREA
#define TRIANGULATESIMPLIFICATIONLEVEL
const SHAPE_LINE_CHAIN chain
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D
VECTOR2< int64_t > VECTOR2L