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 wxLogTrace(
TRIANGULATE_TRACE,
"earcutList starting at %p for pass %d", aPoint, pass );
635 int internal_pass = 1;
636 constexpr int kEarLookahead = 2;
638 while( aPoint->
prev != aPoint->
next )
643 VERTEX* bestEar =
nullptr;
644 double bestScore = -1.0;
647 for(
VERTEX* candidate = aPoint; candidate && lookahead < kEarLookahead;
648 candidate = candidate->
next, ++lookahead )
650 if( !candidate->isEar() ||
isTooSmall( candidate ) )
653 const double score =
earScore( candidate->prev, candidate, candidate->next );
655 if( !bestEar || score > bestScore )
664 prev = bestEar->
prev;
677 if( *prev != *nextNext &&
intersects( prev, aPoint,
next, nextNext ) &&
682 "Local intersection detected. Merging minor triangle with area %f",
683 area( prev, aPoint, nextNext ) );
684 m_result.AddTriangle( prev->
i, aPoint->
i, nextNext->
i );
702 if( aPoint == stop && aPoint->
prev != aPoint->
next )
713 if( newPoint->
next == newPoint->
prev )
727 if( internal_pass < 4 )
770 double prev_sq_len = ( aPoint->
prev->
x - aPoint->
x ) * ( aPoint->
prev->
x - aPoint->
x ) +
771 ( aPoint->
prev->
y - aPoint->
y ) * ( aPoint->
prev->
y - aPoint->
y );
772 double next_sq_len = ( aPoint->
next->
x - aPoint->
x ) * ( aPoint->
next->
x - aPoint->
x ) +
773 ( aPoint->
next->
y - aPoint->
y ) * ( aPoint->
next->
y - aPoint->
y );
774 double opp_sq_len = ( aPoint->
next->
x - aPoint->
prev->
x ) * ( aPoint->
next->
x - aPoint->
prev->
x ) +
777 return ( prev_sq_len < min_area || next_sq_len < min_area || opp_sq_len < min_area );
782 const double ab_sq = ( a->
x - b->
x ) * ( a->
x - b->
x ) + ( a->
y - b->
y ) * ( a->
y - b->
y );
783 const double bc_sq = ( b->
x - c->
x ) * ( b->
x - c->
x ) + ( b->
y - c->
y ) * ( b->
y - c->
y );
784 const double ca_sq = ( c->
x - a->
x ) * ( c->
x - a->
x ) + ( c->
y - a->
y ) * ( c->
y - a->
y );
785 const double norm = ab_sq + bc_sq + ca_sq;
804 for(
int i = 0; i < aPoints.
PointCount(); i++ )
808 sum += ( ( p2.
x - p1.
x ) * ( p2.
y + p1.
y ) );
811 bool isCW = sum > 0.0;
812 bool needReverse = ( aWantCCW == isCW );
814 auto addVertex = [&](
int i )
828 for(
int i = aPoints.
PointCount() - 1; i >= 0; i-- )
833 for(
int i = 0; i < aPoints.
PointCount(); i++ )
837 if( tail && ( *tail == *tail->
next ) )
856 std::vector<HoleInfo> holes;
857 holes.reserve( aHoleRings.size() );
859 for(
VERTEX* hole : aHoleRings )
866 if( p->
x < leftmost->
x || ( p->
x == leftmost->
x && p->
y < leftmost->
y ) )
872 holes.push_back( { leftmost, leftmost->
x } );
875 std::sort( holes.begin(), holes.end(),
876 [](
const HoleInfo& a,
const HoleInfo& b ) { return a.leftX < b.leftX; } );
878 for(
const HoleInfo& hi : holes )
884 VERTEX* bridgeReverse = bridge->
split( hi.leftmost );
891 hi.leftmost->x, hi.leftmost->y );
930 }
while( again || p != aEnd );
940 double hx = aHole->
x;
941 double hy = aHole->
y;
942 double qx = -std::numeric_limits<double>::infinity();
947 if( hy <= p->y && hy >= p->
next->
y && p->
next->
y != p->
y )
949 double x = p->
x + ( hy - p->
y ) * ( p->
next->
x - p->
x )
952 if( x <= hx && x > qx )
961 if( hy == p->
next->
y )
970 }
while( p != aOuterStart );
982 double tanMin = std::numeric_limits<double>::infinity();
988 if( hx >= p->
x && p->
x >= mx && hx != p->
x )
993 inside =
triArea( hx, hy, mx, my, p->
x, p->
y ) >= 0
994 &&
triArea( mx, my, qx, hy, p->
x, p->
y ) >= 0
995 &&
triArea( qx, hy, hx, hy, p->
x, p->
y ) >= 0;
997 inside =
triArea( qx, hy, mx, my, p->
x, p->
y ) >= 0
998 &&
triArea( mx, my, hx, hy, p->
x, p->
y ) >= 0
999 &&
triArea( hx, hy, qx, hy, p->
x, p->
y ) >= 0;
1003 double t =
std::abs( hy - p->
y ) / ( hx - p->
x );
1019 }
while( p != stop );
1027 static double triArea(
double ax,
double ay,
double bx,
double by,
1028 double cx,
double cy )
1030 return ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax );
1049 struct VertexComparator {
1050 bool operator()(
const std::pair<VERTEX*,double>& a,
const std::pair<VERTEX*,double>& b)
const {
1051 return a.second > b.second;
1055 std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
1060 double len = ( p->
x - p->
next->
x ) * ( p->
x - p->
next->
x ) +
1062 longest.emplace( p, len );
1066 }
while (p != aStart);
1068 avg /= longest.size();
1071 constexpr double kSubdivideThresholdFactor = 1.1;
1072 const double subdivideThreshold = avg * kSubdivideThresholdFactor;
1074 for(
auto it = longest.begin(); it != longest.end() && it->second > subdivideThreshold;
1084 int divisions = avg / it->second + 2 + pass;
1085 double step = 1.0 / divisions;
1087 for(
int i = 1; i < divisions; i++ )
1089 double x = a->
x * ( 1.0 - step * i ) + b->
x * ( step * i );
1090 double y = a->
y * ( 1.0 - step * i ) + b->
y * ( step * i );
1107 VERTEX* origPoly = start;
1110 if( !start || !start->
next || start->
next == start->
prev
1122 std::vector<VERTEX*> overlapPoints;
1125 while ( z_pt->
prevZ && *z_pt->
prevZ == *origPoly )
1128 overlapPoints.push_back( z_pt );
1130 while( z_pt->
nextZ && *z_pt->
nextZ == *origPoly )
1133 overlapPoints.push_back( z_pt );
1136 if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
1137 || overlapPoints[0]->prev == overlapPoints[1] )
1139 origPoly = origPoly->
next;
1143 if( overlapPoints[0]->
area( overlapPoints[1] ) < 0 || overlapPoints[1]->
area( overlapPoints[0] ) < 0 )
1146 origPoly = origPoly->
next;
1150 wxLogTrace(
TRIANGULATE_TRACE,
"Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
1151 std::swap( overlapPoints[0]->
next, overlapPoints[1]->
next );
1152 overlapPoints[0]->next->prev = overlapPoints[0];
1153 overlapPoints[1]->next->prev = overlapPoints[1];
1155 overlapPoints[0]->updateList();
1156 overlapPoints[1]->updateList();
1161 wxLogTrace(
TRIANGULATE_TRACE,
"%s at first overlap split", retval ?
"Success" :
"Failed" );
1165 }
while ( origPoly != start );
1175 while( marker != origPoly->
prev )
1178 if( origPoly->
next && origPoly->
i != marker->
i &&
goodSplit( origPoly, marker ) )
1191 marker = marker->
next;
1194 origPoly = origPoly->
next;
1195 }
while( origPoly != start );
1218 bool pos_area = a->
area( b ) > 0 && b->
area( a ) > 0;
1220 return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
1225 constexpr int sign(
double aVal )
const
1227 return ( aVal > 0 ) - ( aVal < 0 );
1235 return q->
x <= std::max( p->
x, r->
x ) &&
1236 q->
x >= std::min( p->
x, r->
x ) &&
1237 q->
y <= std::max( p->
y, r->
y ) &&
1238 q->
y >= std::min( p->
y, r->
y );
1248 int sign1 =
sign(
area( p1, q1, p2 ) );
1249 int sign2 =
sign(
area( p1, q1, q2 ) );
1250 int sign3 =
sign(
area( p2, q2, p1 ) );
1251 int sign4 =
sign(
area( p2, q2, q1 ) );
1253 if( sign1 != sign2 && sign3 != sign4 )
1285 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...
bool splitPolygon(VERTEX *start)
If we cannot find an ear to slice in the current polygon list, we use this to split the polygon into ...
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).
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