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();
129 if( holeRing && holeRing->
next != holeRing )
130 holeRings.push_back( holeRing );
135 if( !holeRings.empty() )
147 outerRing = simplified;
155 wxLogTrace(
TRIANGULATE_TRACE,
"Tesselation with holes failed, logging remaining vertices" );
180 if( !firstVertex || firstVertex->
prev == firstVertex->
next )
188 firstVertex = simplified;
198 if( aHintData && aHintData->
Vertices().size() ==
m_result.GetVertexCount() )
209 wxLogTrace(
TRIANGULATE_TRACE,
"Tesselation failed, logging remaining vertices" );
219 size_t aTargetLeaves )
const
222 std::vector<double> fractions;
228 fractions.reserve( partitions.size() );
246 std::array<SCANLINE_HIT, 2>& aHits )
const
250 for(
int ii = 0; ii < aPoly.
PointCount(); ++ii )
257 if( a.
x == b.
x || aCut <= std::min( a.
x, b.
x ) || aCut >= std::max( a.
x, b.
x ) )
263 double t =
static_cast<double>( aCut - a.
x ) /
static_cast<double>( b.
x - a.
x );
264 int y =
static_cast<int>( std::lround( a.
y + t * ( b.
y - a.
y ) ) );
265 aHits[count++] = { ii,
VECTOR2I( aCut, y ) };
269 if( a.
y == b.
y || aCut <= std::min( a.
y, b.
y ) || aCut >= std::max( a.
y, b.
y ) )
275 double t =
static_cast<double>( aCut - a.
y ) /
static_cast<double>( b.
y - a.
y );
276 int x =
static_cast<int>( std::lround( a.
x + t * ( b.
x - a.
x ) ) );
277 aHits[count++] = { ii,
VECTOR2I( x, aCut ) };
294 idx = ( idx + 1 ) % count;
296 }
while( idx != ( aEnd + 1 ) % count && guard <= count + 2 );
304 std::array<SHAPE_LINE_CHAIN, 2>& aChildren,
double& aAreaA,
305 double& aAreaB )
const
307 std::array<SCANLINE_HIT, 2> hits;
313 augmented.
Split( hits[0].point,
true );
314 augmented.
Split( hits[1].point,
true );
316 int idxA = augmented.
Find( hits[0].point );
317 int idxB = augmented.
Find( hits[1].point );
319 if( idxA < 0 || idxB < 0 || idxA == idxB )
325 if( aChildren[0].PointCount() < 3 || aChildren[1].PointCount() < 3 )
328 aAreaA =
std::abs( aChildren[0].Area() );
329 aAreaB =
std::abs( aChildren[1].Area() );
330 return aAreaA > 0.0 && aAreaB > 0.0;
334 std::array<SHAPE_LINE_CHAIN, 2>& aChildren )
const
344 [&](
bool aVertical ) ->
bool
346 const int low = ( aVertical ? bbox.
GetX() : bbox.
GetY() ) + 1;
352 double bestImbalance = std::numeric_limits<double>::infinity();
353 std::array<SHAPE_LINE_CHAIN, 2> bestChildren;
355 constexpr int kSamples = 15;
357 for(
int ii = 1; ii <= kSamples; ++ii )
359 int cut = low + ( ( high - low ) * ii ) / ( kSamples + 1 );
360 std::array<SHAPE_LINE_CHAIN, 2> candidate;
369 if( imbalance < bestImbalance )
371 bestImbalance = imbalance;
372 bestChildren = std::move( candidate );
376 if( !std::isfinite( bestImbalance ) || bestImbalance > 0.35 )
379 aChildren = std::move( bestChildren );
383 return tryAxis( verticalFirst ) || tryAxis( !verticalFirst );
387 size_t aTargetLeaves )
const
389 std::vector<SHAPE_LINE_CHAIN> leaves = { aPoly };
391 if( aTargetLeaves < 2 )
394 while( leaves.size() < aTargetLeaves )
397 double bestArea = 0.0;
399 for(
size_t ii = 0; ii < leaves.size(); ++ii )
403 if(
area > bestArea )
406 bestLeaf =
static_cast<int>( ii );
413 std::array<SHAPE_LINE_CHAIN, 2> children;
418 leaves[bestLeaf] = std::move( children[0] );
419 leaves.push_back( std::move( children[1] ) );
427 constexpr size_t kVerticesPerLeaf = 50000;
428 constexpr size_t kMaxLeaves = 8;
431 while( leaves < kMaxLeaves
432 &&
static_cast<size_t>( aPoly.
PointCount() ) / leaves > kVerticesPerLeaf )
445 std::set<VERTEX*> seen;
446 wxLog::EnableLogging();
449 if( !p.next || p.next == &p || seen.find( &p ) != seen.end() )
458 if( aSeen && aSeen->count( aStart ) )
462 aSeen->insert( aStart );
466 wxString msg = wxString::Format(
"Vertices: %d,%d,",
static_cast<int>( aStart->
x ),
467 static_cast<int>( aStart->
y ) );
471 msg += wxString::Format(
"%d,%d,",
static_cast<int>( p->
x ),
static_cast<int>( p->
y ) );
478 }
while( p != aStart );
493 if( !aStart || aStart->
next == aStart->
prev )
528 }
while( p != aStart &&
next && p );
553 wxASSERT( aStart->
next && aStart->
prev );
557 while( p != aStart && p->
next && p->
prev )
627 constexpr int kMaxRecursion = 64;
629 if( pass >= kMaxRecursion )
631 wxLogTrace(
TRIANGULATE_TRACE,
"earcutList recursion limit reached; aborting triangulation", pass );
635 wxLogTrace(
TRIANGULATE_TRACE,
"earcutList starting at %p for pass %d", aPoint, pass );
643 int internal_pass = 1;
644 constexpr int kEarLookahead = 2;
646 while( aPoint->
prev != aPoint->
next )
651 VERTEX* bestEar =
nullptr;
652 double bestScore = -1.0;
655 for(
VERTEX* candidate = aPoint; candidate && lookahead < kEarLookahead;
656 candidate = candidate->
next, ++lookahead )
658 if( !candidate->isEar() ||
isTooSmall( candidate ) )
661 const double score =
earScore( candidate->prev, candidate, candidate->next );
663 if( !bestEar || score > bestScore )
672 prev = bestEar->
prev;
685 if( *prev != *nextNext &&
intersects( prev, aPoint,
next, nextNext ) &&
690 "Local intersection detected. Merging minor triangle with area %f",
691 area( prev, aPoint, nextNext ) );
692 m_result.AddTriangle( prev->
i, aPoint->
i, nextNext->
i );
710 if( aPoint == stop && aPoint->
prev != aPoint->
next )
721 if( newPoint->
next == newPoint->
prev )
735 if( internal_pass < 4 )
778 double prev_sq_len = ( aPoint->
prev->
x - aPoint->
x ) * ( aPoint->
prev->
x - aPoint->
x ) +
779 ( aPoint->
prev->
y - aPoint->
y ) * ( aPoint->
prev->
y - aPoint->
y );
780 double next_sq_len = ( aPoint->
next->
x - aPoint->
x ) * ( aPoint->
next->
x - aPoint->
x ) +
781 ( aPoint->
next->
y - aPoint->
y ) * ( aPoint->
next->
y - aPoint->
y );
782 double opp_sq_len = ( aPoint->
next->
x - aPoint->
prev->
x ) * ( aPoint->
next->
x - aPoint->
prev->
x ) +
785 return ( prev_sq_len < min_area || next_sq_len < min_area || opp_sq_len < min_area );
790 const double ab_sq = ( a->
x - b->
x ) * ( a->
x - b->
x ) + ( a->
y - b->
y ) * ( a->
y - b->
y );
791 const double bc_sq = ( b->
x - c->
x ) * ( b->
x - c->
x ) + ( b->
y - c->
y ) * ( b->
y - c->
y );
792 const double ca_sq = ( c->
x - a->
x ) * ( c->
x - a->
x ) + ( c->
y - a->
y ) * ( c->
y - a->
y );
793 const double norm = ab_sq + bc_sq + ca_sq;
812 for(
int i = 0; i < aPoints.
PointCount(); i++ )
816 sum += ( ( p2.
x - p1.
x ) * ( p2.
y + p1.
y ) );
819 bool isCW = sum > 0.0;
820 bool needReverse = ( aWantCCW == isCW );
822 auto addVertex = [&](
int i )
836 for(
int i = aPoints.
PointCount() - 1; i >= 0; i-- )
841 for(
int i = 0; i < aPoints.
PointCount(); i++ )
845 if( tail && ( *tail == *tail->
next ) )
864 std::vector<HoleInfo> holes;
865 holes.reserve( aHoleRings.size() );
867 for(
VERTEX* hole : aHoleRings )
874 if( p->
x < leftmost->
x || ( p->
x == leftmost->
x && p->
y < leftmost->
y ) )
880 holes.push_back( { leftmost, leftmost->
x } );
883 std::sort( holes.begin(), holes.end(),
884 [](
const HoleInfo& a,
const HoleInfo& b ) { return a.leftX < b.leftX; } );
886 for(
const HoleInfo& hi : holes )
892 VERTEX* bridgeReverse = bridge->
split( hi.leftmost );
899 hi.leftmost->x, hi.leftmost->y );
938 }
while( again || p != aEnd );
948 double hx = aHole->
x;
949 double hy = aHole->
y;
950 double qx = -std::numeric_limits<double>::infinity();
955 if( hy <= p->y && hy >= p->
next->
y && p->
next->
y != p->
y )
957 double x = p->
x + ( hy - p->
y ) * ( p->
next->
x - p->
x )
960 if( x <= hx && x > qx )
969 if( hy == p->
next->
y )
978 }
while( p != aOuterStart );
990 double tanMin = std::numeric_limits<double>::infinity();
996 if( hx >= p->
x && p->
x >= mx && hx != p->
x )
1001 inside =
triArea( hx, hy, mx, my, p->
x, p->
y ) >= 0
1002 &&
triArea( mx, my, qx, hy, p->
x, p->
y ) >= 0
1003 &&
triArea( qx, hy, hx, hy, p->
x, p->
y ) >= 0;
1005 inside =
triArea( qx, hy, mx, my, p->
x, p->
y ) >= 0
1006 &&
triArea( mx, my, hx, hy, p->
x, p->
y ) >= 0
1007 &&
triArea( hx, hy, qx, hy, p->
x, p->
y ) >= 0;
1011 double t =
std::abs( hy - p->
y ) / ( hx - p->
x );
1027 }
while( p != stop );
1035 static double triArea(
double ax,
double ay,
double bx,
double by,
1036 double cx,
double cy )
1038 return ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax );
1057 struct VertexComparator {
1058 bool operator()(
const std::pair<VERTEX*,double>& a,
const std::pair<VERTEX*,double>& b)
const {
1059 return a.second > b.second;
1063 std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
1068 double len = ( p->
x - p->
next->
x ) * ( p->
x - p->
next->
x ) +
1070 longest.emplace( p, len );
1074 }
while (p != aStart);
1076 avg /= longest.size();
1079 constexpr double kSubdivideThresholdFactor = 1.1;
1080 const double subdivideThreshold = avg * kSubdivideThresholdFactor;
1082 for(
auto it = longest.begin(); it != longest.end() && it->second > subdivideThreshold;
1092 int divisions = avg / it->second + 2 + pass;
1093 double step = 1.0 / divisions;
1095 for(
int i = 1; i < divisions; i++ )
1097 double x = a->
x * ( 1.0 - step * i ) + b->
x * ( step * i );
1098 double y = a->
y * ( 1.0 - step * i ) + b->
y * ( step * i );
1115 VERTEX* origPoly = start;
1118 if( !start || !start->
next || start->
next == start->
prev
1130 std::vector<VERTEX*> overlapPoints;
1133 while ( z_pt->
prevZ && *z_pt->
prevZ == *origPoly )
1136 overlapPoints.push_back( z_pt );
1138 while( z_pt->
nextZ && *z_pt->
nextZ == *origPoly )
1141 overlapPoints.push_back( z_pt );
1144 if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
1145 || overlapPoints[0]->prev == overlapPoints[1] )
1147 origPoly = origPoly->
next;
1151 if( overlapPoints[0]->
area( overlapPoints[1] ) < 0 || overlapPoints[1]->
area( overlapPoints[0] ) < 0 )
1154 origPoly = origPoly->
next;
1158 wxLogTrace(
TRIANGULATE_TRACE,
"Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
1159 std::swap( overlapPoints[0]->
next, overlapPoints[1]->
next );
1160 overlapPoints[0]->next->prev = overlapPoints[0];
1161 overlapPoints[1]->next->prev = overlapPoints[1];
1163 overlapPoints[0]->updateList();
1164 overlapPoints[1]->updateList();
1167 bool retval =
earcutList( overlapPoints[0], aPass )
1170 wxLogTrace(
TRIANGULATE_TRACE,
"%s at first overlap split", retval ?
"Success" :
"Failed" );
1174 }
while ( origPoly != start );
1184 while( marker != origPoly->
prev )
1187 if( origPoly->
next && origPoly->
i != marker->
i &&
goodSplit( origPoly, marker ) )
1200 marker = marker->
next;
1203 origPoly = origPoly->
next;
1204 }
while( origPoly != start );
1227 bool pos_area = a->
area( b ) > 0 && b->
area( a ) > 0;
1229 return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
1234 constexpr int sign(
double aVal )
const
1236 return ( aVal > 0 ) - ( aVal < 0 );
1244 return q->
x <= std::max( p->
x, r->
x ) &&
1245 q->
x >= std::min( p->
x, r->
x ) &&
1246 q->
y <= std::max( p->
y, r->
y ) &&
1247 q->
y >= std::min( p->
y, r->
y );
1257 int sign1 =
sign(
area( p1, q1, p2 ) );
1258 int sign2 =
sign(
area( p1, q1, q2 ) );
1259 int sign3 =
sign(
area( p2, q2, p1 ) );
1260 int sign4 =
sign(
area( p2, q2, q1 ) );
1262 if( sign1 != sign2 && sign3 != sign4 )
1294 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
void filterPoints(VERTEX *aStart, VERTEX *aEnd=nullptr)
Remove consecutive duplicate vertices from the linked list.
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.
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