42#ifndef __POLYGON_TRIANGULATION_H
43#define __POLYGON_TRIANGULATION_H
62#if defined( __MINGW32__ )
63 #define TRIANGULATESIMPLIFICATIONLEVEL 50
64 #define TRIANGULATEMINIMUMAREA 1000
66 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
67 #define TRIANGULATEMINIMUMAREA ADVANCED_CFG::GetCfg().m_TriangulateMinimumArea
70#define TRIANGULATE_TRACE "triangulate"
87 if( aPolygon.empty() )
90 if( aPolygon.size() == 1 )
97 for(
size_t i = 1; i < aPolygon.size(); i++ )
98 m_bbox.Merge( aPolygon[i].BBox() );
115 if( !outerRing || outerRing->
prev == outerRing->
next )
118 std::vector<VERTEX*> holeRings;
120 for(
size_t i = 1; i < aPolygon.size(); i++ )
123 baseIndex += aPolygon[i].PointCount();
128 if( holeRing && holeRing->
prev != holeRing->
next )
129 holeRings.push_back( holeRing );
134 if( !holeRings.empty() )
146 outerRing = simplified;
154 wxLogTrace(
TRIANGULATE_TRACE,
"Tesselation with holes failed, logging remaining vertices" );
179 if( !firstVertex || firstVertex->
prev == firstVertex->
next )
187 firstVertex = simplified;
197 if( aHintData && aHintData->
Vertices().size() ==
m_result.GetVertexCount() )
208 wxLogTrace(
TRIANGULATE_TRACE,
"Tesselation failed, logging remaining vertices" );
218 size_t aTargetLeaves )
const
221 std::vector<double> fractions;
227 fractions.reserve( partitions.size() );
245 std::array<SCANLINE_HIT, 2>& aHits )
const
249 for(
int ii = 0; ii < aPoly.
PointCount(); ++ii )
256 if( a.
x == b.
x || aCut <= std::min( a.
x, b.
x ) || aCut >= std::max( a.
x, b.
x ) )
262 double t =
static_cast<double>( aCut - a.
x ) /
static_cast<double>( b.
x - a.
x );
263 int y =
static_cast<int>( std::lround( a.
y + t * ( b.
y - a.
y ) ) );
264 aHits[count++] = { ii,
VECTOR2I( aCut, y ) };
268 if( a.
y == b.
y || aCut <= std::min( a.
y, b.
y ) || aCut >= std::max( a.
y, b.
y ) )
274 double t =
static_cast<double>( aCut - a.
y ) /
static_cast<double>( b.
y - a.
y );
275 int x =
static_cast<int>( std::lround( a.
x + t * ( b.
x - a.
x ) ) );
276 aHits[count++] = { ii,
VECTOR2I( x, aCut ) };
293 idx = ( idx + 1 ) % count;
295 }
while( idx != ( aEnd + 1 ) % count && guard <= count + 2 );
303 std::array<SHAPE_LINE_CHAIN, 2>& aChildren,
double& aAreaA,
304 double& aAreaB )
const
306 std::array<SCANLINE_HIT, 2> hits;
312 augmented.
Split( hits[0].point,
true );
313 augmented.
Split( hits[1].point,
true );
315 int idxA = augmented.
Find( hits[0].point );
316 int idxB = augmented.
Find( hits[1].point );
318 if( idxA < 0 || idxB < 0 || idxA == idxB )
324 if( aChildren[0].PointCount() < 3 || aChildren[1].PointCount() < 3 )
327 aAreaA =
std::abs( aChildren[0].Area() );
328 aAreaB =
std::abs( aChildren[1].Area() );
329 return aAreaA > 0.0 && aAreaB > 0.0;
333 std::array<SHAPE_LINE_CHAIN, 2>& aChildren )
const
343 [&](
bool aVertical ) ->
bool
345 const int low = ( aVertical ? bbox.
GetX() : bbox.
GetY() ) + 1;
351 double bestImbalance = std::numeric_limits<double>::infinity();
352 std::array<SHAPE_LINE_CHAIN, 2> bestChildren;
354 constexpr int kSamples = 15;
356 for(
int ii = 1; ii <= kSamples; ++ii )
358 int cut = low + ( ( high - low ) * ii ) / ( kSamples + 1 );
359 std::array<SHAPE_LINE_CHAIN, 2> candidate;
368 if( imbalance < bestImbalance )
370 bestImbalance = imbalance;
371 bestChildren = std::move( candidate );
375 if( !std::isfinite( bestImbalance ) || bestImbalance > 0.35 )
378 aChildren = std::move( bestChildren );
382 return tryAxis( verticalFirst ) || tryAxis( !verticalFirst );
386 size_t aTargetLeaves )
const
388 std::vector<SHAPE_LINE_CHAIN> leaves = { aPoly };
390 if( aTargetLeaves < 2 )
393 while( leaves.size() < aTargetLeaves )
396 double bestArea = 0.0;
398 for(
size_t ii = 0; ii < leaves.size(); ++ii )
402 if(
area > bestArea )
405 bestLeaf =
static_cast<int>( ii );
412 std::array<SHAPE_LINE_CHAIN, 2> children;
417 leaves[bestLeaf] = std::move( children[0] );
418 leaves.push_back( std::move( children[1] ) );
426 constexpr size_t kVerticesPerLeaf = 50000;
427 constexpr size_t kMaxLeaves = 8;
430 while( leaves < kMaxLeaves
431 &&
static_cast<size_t>( aPoly.
PointCount() ) / leaves > kVerticesPerLeaf )
444 std::set<VERTEX*> seen;
445 wxLog::EnableLogging();
448 if( !p.next || p.next == &p || seen.find( &p ) != seen.end() )
457 if( aSeen && aSeen->count( aStart ) )
461 aSeen->insert( aStart );
465 wxString msg = wxString::Format(
"Vertices: %d,%d,",
static_cast<int>( aStart->
x ),
466 static_cast<int>( aStart->
y ) );
470 msg += wxString::Format(
"%d,%d,",
static_cast<int>( p->
x ),
static_cast<int>( p->
y ) );
477 }
while( p != aStart );
492 if( !aStart || aStart->
next == aStart->
prev )
527 }
while( p != aStart &&
next && p );
552 wxASSERT( aStart->
next && aStart->
prev );
556 while( p != aStart && p->
next && p->
prev )
626 constexpr int kMaxRecursion = 64;
628 if( pass >= kMaxRecursion )
630 wxLogTrace(
TRIANGULATE_TRACE,
"earcutList recursion limit reached; aborting triangulation", pass );
634 wxLogTrace(
TRIANGULATE_TRACE,
"earcutList starting at %p for pass %d", aPoint, pass );
642 int internal_pass = 1;
643 constexpr int kEarLookahead = 2;
645 while( aPoint->
prev != aPoint->
next )
650 VERTEX* bestEar =
nullptr;
651 double bestScore = -1.0;
654 for(
VERTEX* candidate = aPoint; candidate && lookahead < kEarLookahead;
655 candidate = candidate->
next, ++lookahead )
657 if( !candidate->isEar() ||
isTooSmall( candidate ) )
660 const double score =
earScore( candidate->prev, candidate, candidate->next );
662 if( !bestEar || score > bestScore )
671 prev = bestEar->
prev;
684 if( *prev != *nextNext &&
intersects( prev, aPoint,
next, nextNext ) &&
689 "Local intersection detected. Merging minor triangle with area %f",
690 area( prev, aPoint, nextNext ) );
691 m_result.AddTriangle( prev->
i, aPoint->
i, nextNext->
i );
709 if( aPoint == stop && aPoint->
prev != aPoint->
next )
720 if( newPoint->
next == newPoint->
prev )
734 if( internal_pass < 4 )
777 double prev_sq_len = ( aPoint->
prev->
x - aPoint->
x ) * ( aPoint->
prev->
x - aPoint->
x ) +
778 ( aPoint->
prev->
y - aPoint->
y ) * ( aPoint->
prev->
y - aPoint->
y );
779 double next_sq_len = ( aPoint->
next->
x - aPoint->
x ) * ( aPoint->
next->
x - aPoint->
x ) +
780 ( aPoint->
next->
y - aPoint->
y ) * ( aPoint->
next->
y - aPoint->
y );
781 double opp_sq_len = ( aPoint->
next->
x - aPoint->
prev->
x ) * ( aPoint->
next->
x - aPoint->
prev->
x ) +
784 return ( prev_sq_len < min_area || next_sq_len < min_area || opp_sq_len < min_area );
789 const double ab_sq = ( a->
x - b->
x ) * ( a->
x - b->
x ) + ( a->
y - b->
y ) * ( a->
y - b->
y );
790 const double bc_sq = ( b->
x - c->
x ) * ( b->
x - c->
x ) + ( b->
y - c->
y ) * ( b->
y - c->
y );
791 const double ca_sq = ( c->
x - a->
x ) * ( c->
x - a->
x ) + ( c->
y - a->
y ) * ( c->
y - a->
y );
792 const double norm = ab_sq + bc_sq + ca_sq;
811 for(
int i = 0; i < aPoints.
PointCount(); i++ )
815 sum += ( ( p2.
x - p1.
x ) * ( p2.
y + p1.
y ) );
818 bool isCW = sum > 0.0;
819 bool needReverse = ( aWantCCW == isCW );
821 auto addVertex = [&](
int i )
835 for(
int i = aPoints.
PointCount() - 1; i >= 0; i-- )
840 for(
int i = 0; i < aPoints.
PointCount(); i++ )
847 if( tail && tail->
next != tail && ( *tail == *tail->
next ) )
866 std::vector<HoleInfo> holes;
867 holes.reserve( aHoleRings.size() );
869 for(
VERTEX* hole : aHoleRings )
876 if( p->
x < leftmost->
x || ( p->
x == leftmost->
x && p->
y < leftmost->
y ) )
882 holes.push_back( { leftmost, leftmost->
x } );
885 std::sort( holes.begin(), holes.end(),
886 [](
const HoleInfo& a,
const HoleInfo& b ) { return a.leftX < b.leftX; } );
888 for(
const HoleInfo& hi : holes )
894 VERTEX* bridgeReverse = bridge->
split( hi.leftmost );
901 hi.leftmost->x, hi.leftmost->y );
930 if( toRemove == aEnd )
945 }
while( again || p != aEnd );
958 double hx = aHole->
x;
959 double hy = aHole->
y;
960 double qx = -std::numeric_limits<double>::infinity();
965 if( hy <= p->y && hy >= p->
next->
y && p->
next->
y != p->
y )
967 double x = p->
x + ( hy - p->
y ) * ( p->
next->
x - p->
x )
970 if( x <= hx && x > qx )
979 if( hy == p->
next->
y )
988 }
while( p != aOuterStart );
1000 double tanMin = std::numeric_limits<double>::infinity();
1006 if( hx >= p->
x && p->
x >= mx && hx != p->
x )
1011 inside =
triArea( hx, hy, mx, my, p->
x, p->
y ) >= 0
1012 &&
triArea( mx, my, qx, hy, p->
x, p->
y ) >= 0
1013 &&
triArea( qx, hy, hx, hy, p->
x, p->
y ) >= 0;
1015 inside =
triArea( qx, hy, mx, my, p->
x, p->
y ) >= 0
1016 &&
triArea( mx, my, hx, hy, p->
x, p->
y ) >= 0
1017 &&
triArea( hx, hy, qx, hy, p->
x, p->
y ) >= 0;
1021 double t =
std::abs( hy - p->
y ) / ( hx - p->
x );
1037 }
while( p != stop );
1045 static double triArea(
double ax,
double ay,
double bx,
double by,
1046 double cx,
double cy )
1048 return ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax );
1067 struct VertexComparator {
1068 bool operator()(
const std::pair<VERTEX*,double>& a,
const std::pair<VERTEX*,double>& b)
const {
1069 return a.second > b.second;
1073 std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
1078 double len = ( p->
x - p->
next->
x ) * ( p->
x - p->
next->
x ) +
1080 longest.emplace( p, len );
1084 }
while (p != aStart);
1086 avg /= longest.size();
1089 constexpr double kSubdivideThresholdFactor = 1.1;
1090 const double subdivideThreshold = avg * kSubdivideThresholdFactor;
1092 for(
auto it = longest.begin(); it != longest.end() && it->second > subdivideThreshold;
1102 int divisions = avg / it->second + 2 + pass;
1103 double step = 1.0 / divisions;
1105 for(
int i = 1; i < divisions; i++ )
1107 double x = a->
x * ( 1.0 - step * i ) + b->
x * ( step * i );
1108 double y = a->
y * ( 1.0 - step * i ) + b->
y * ( step * i );
1125 VERTEX* origPoly = start;
1128 if( !start || !start->
next || start->
next == start->
prev
1140 std::vector<VERTEX*> overlapPoints;
1143 while ( z_pt->
prevZ && *z_pt->
prevZ == *origPoly )
1146 overlapPoints.push_back( z_pt );
1148 while( z_pt->
nextZ && *z_pt->
nextZ == *origPoly )
1151 overlapPoints.push_back( z_pt );
1154 if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
1155 || overlapPoints[0]->prev == overlapPoints[1] )
1157 origPoly = origPoly->
next;
1161 if( overlapPoints[0]->
area( overlapPoints[1] ) < 0 || overlapPoints[1]->
area( overlapPoints[0] ) < 0 )
1164 origPoly = origPoly->
next;
1168 wxLogTrace(
TRIANGULATE_TRACE,
"Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
1169 std::swap( overlapPoints[0]->
next, overlapPoints[1]->
next );
1170 overlapPoints[0]->next->prev = overlapPoints[0];
1171 overlapPoints[1]->next->prev = overlapPoints[1];
1173 overlapPoints[0]->updateList();
1174 overlapPoints[1]->updateList();
1177 bool retval =
earcutList( overlapPoints[0], aPass )
1180 wxLogTrace(
TRIANGULATE_TRACE,
"%s at first overlap split", retval ?
"Success" :
"Failed" );
1184 }
while ( origPoly != start );
1194 while( marker != origPoly->
prev )
1197 if( origPoly->
next && origPoly->
i != marker->
i &&
goodSplit( origPoly, marker ) )
1210 marker = marker->
next;
1213 origPoly = origPoly->
next;
1214 }
while( origPoly != start );
1237 bool pos_area = a->
area( b ) > 0 && b->
area( a ) > 0;
1239 return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
1244 constexpr int sign(
double aVal )
const
1246 return ( aVal > 0 ) - ( aVal < 0 );
1254 return q->
x <= std::max( p->
x, r->
x ) &&
1255 q->
x >= std::min( p->
x, r->
x ) &&
1256 q->
y <= std::max( p->
y, r->
y ) &&
1257 q->
y >= std::min( p->
y, r->
y );
1267 int sign1 =
sign(
area( p1, q1, p2 ) );
1268 int sign2 =
sign(
area( p1, q1, q2 ) );
1269 int sign3 =
sign(
area( p2, q2, p1 ) );
1270 int sign4 =
sign(
area( p2, q2, q1 ) );
1272 if( sign1 != sign2 && sign3 != sign4 )
1304 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