34 size_t aTargetLeaves )
40namespace fs = std::filesystem;
54 return std::make_unique<POLYGON_TRIANGULATION>( *
m_result );
58 std::unique_ptr<SHAPE_POLY_SET::TRIANGULATED_POLYGON>
m_result;
65 chain.Append( offset.x, offset.y );
66 chain.Append( offset.x + size, offset.y );
67 chain.Append( offset.x + size, offset.y + size );
68 chain.Append( offset.x, offset.y + size );
69 chain.SetClosed(
true );
77 chain.Append( offset.x, offset.y );
78 chain.Append( offset.x + size, offset.y );
79 chain.Append( offset.x + size/2, offset.y + size );
80 chain.SetClosed(
true );
89 chain.Append( size, 0 );
90 chain.Append( size, size/2 );
91 chain.Append( size/2, size/2 );
92 chain.Append( size/2, size );
93 chain.Append( 0, size );
94 chain.SetClosed(
true );
101 chain.Append( 0, 0 );
105 for(
int ii = 0; ii < teeth; ++ii )
108 chain.Append( x, 0 );
109 chain.Append( x, step * 3 );
111 chain.Append( x, step * 3 );
112 chain.Append( x, step * 4 );
115 chain.Append( 0, step * 4 );
116 chain.SetClosed(
true );
125 if(
result.GetVertexCount() == 0 )
128 size_t triangleCount =
result.GetTriangleCount();
129 if( triangleCount == 0 )
133 for(
size_t i = 0; i < triangleCount; i++ )
135 const auto& triangle =
result.Triangles()[i];
138 if( triangle.a >= (
int)
result.GetVertexCount() ||
139 triangle.b >= (
int)
result.GetVertexCount() ||
140 triangle.c >= (
int)
result.GetVertexCount() )
146 if( triangle.a == triangle.b || triangle.b == triangle.c || triangle.a == triangle.c )
150 if( strict && triangle.Area() <= 0 )
157 const auto& vertices =
result.Vertices();
158 for(
int i = 0; i < original.
PointCount(); i++ )
161 for(
size_t j = 0; j < vertices.size(); j++ )
163 if( vertices[j] == original.
CPoint( i ) )
181 for(
const auto& tri : aResult.
Triangles() )
191 double longest = std::max( { ab, bc, ca } );
192 double shortest = std::min( { ab, bc, ca } );
194 if( shortest > 0.0 && longest / shortest > 10.0 )
203 std::ifstream file( aPath );
205 if( !file.is_open() )
208 std::string content( ( std::istreambuf_iterator<char>( file ) ),
209 std::istreambuf_iterator<char>() );
213 while( ( zonePos = content.find(
"(zone (layer \"", zonePos ) ) != std::string::npos )
215 size_t polysetStart = content.find(
"polyset ", zonePos );
217 if( polysetStart != std::string::npos )
220 std::string remainder = content.substr( polysetStart );
221 std::stringstream ss( remainder );
223 if( polySet.
Parse( ss ) )
224 aZones.push_back( std::move( polySet ) );
227 size_t layerEnd = content.find(
"\")", zonePos + 14 );
229 if( layerEnd == std::string::npos )
232 zonePos = layerEnd + 1;
235 return !aZones.empty();
240 std::vector<SHAPE_POLY_SET> zones;
250 polySet.CacheTriangulation();
252 for(
unsigned int i = 0; i < polySet.TriangulatedPolyCount(); ++i )
254 const auto* triPoly = polySet.TriangulatedPolygon(
static_cast<int>( i ) );
271 bool success = triangulator->TesselatePolygon( triangle,
nullptr );
286 bool success = triangulator->TesselatePolygon(
square,
nullptr );
299 std::vector<double> fractions =
305 for(
double fraction : fractions )
311 fs::path polyPath = fs::path( __FILE__ ).parent_path().parent_path().parent_path().parent_path()
312 .parent_path() /
"data/triangulation/bad_triangulation_case.kicad_polys";
325 bool success = triangulator->TesselatePolygon( concave,
nullptr );
331 size_t triangleCount =
result.GetTriangleCount();
334 if( !success || !isValid || triangleCount < 4 )
336 std::cout <<
"\n=== ConcavePolygonTriangulation Diagnostic Output ===" << std::endl;
337 std::cout <<
"Success: " << (success ?
"true" :
"false") << std::endl;
338 std::cout <<
"Validation: " << (isValid ?
"true" :
"false") << std::endl;
339 std::cout <<
"Triangle count: " << triangleCount <<
" (expected >= 4)" << std::endl;
340 std::cout <<
"Vertex count: " <<
result.GetVertexCount() << std::endl;
343 std::cout <<
"\nInput polygon vertices (" << concave.
PointCount() <<
" points):" << std::endl;
344 for(
int i = 0; i < concave.
PointCount(); i++ )
347 std::cout <<
" [" << i <<
"]: (" << pt.
x <<
", " << pt.
y <<
")" << std::endl;
351 std::cout <<
"\nResult vertices (" <<
result.GetVertexCount() <<
" points):" << std::endl;
352 const auto& vertices =
result.Vertices();
353 for(
size_t i = 0; i < vertices.size(); i++ )
355 std::cout <<
" [" << i <<
"]: (" << vertices[i].x <<
", " << vertices[i].y <<
")" << std::endl;
359 std::cout <<
"\nTriangles found (" << triangleCount <<
" triangles):" << std::endl;
360 const auto& triangles =
result.Triangles();
361 for(
size_t i = 0; i < triangles.size(); i++ )
363 const auto& tri = triangles[i];
367 double area = tri.Area();
369 std::cout <<
" Triangle[" << i <<
"]: indices(" << tri.a <<
"," << tri.b <<
"," << tri.c <<
")" << std::endl;
370 std::cout <<
" A: (" << va.
x <<
", " << va.
y <<
")" << std::endl;
371 std::cout <<
" B: (" << vb.
x <<
", " << vb.
y <<
")" << std::endl;
372 std::cout <<
" C: (" << vc.
x <<
", " << vc.
y <<
")" << std::endl;
373 std::cout <<
" Area: " << area << std::endl;
377 std::cout <<
" *** DEGENERATE TRIANGLE (area <= 0) ***" << std::endl;
378 if( tri.a == tri.b || tri.b == tri.c || tri.a == tri.c )
379 std::cout <<
" *** INVALID TRIANGLE (duplicate vertex indices) ***" << std::endl;
383 if( triangleCount > 0 )
386 for(
const auto& tri : triangles )
388 std::cout <<
"\nTotal triangulated area: " <<
totalArea << std::endl;
392 std::cout <<
"Original polygon area: " << originalArea << std::endl;
393 std::cout <<
"Area difference: " <<
std::abs(
totalArea - originalArea ) << std::endl;
396 std::cout <<
"================================================\n" << std::endl;
413 bool success1 = triangulator1->TesselatePolygon(
square,
nullptr );
420 bool success2 = triangulator2->TesselatePolygon(
square, &fixture1.
GetResult() );
431 noisySquare.
Append( 0, 0 );
432 noisySquare.
Append( 100, 0 );
433 noisySquare.
Append( 100, 10 );
434 noisySquare.
Append( 100, 100 );
435 noisySquare.
Append( 0, 100 );
441 bool success1 = hintTriangulator->TesselatePolygon( noisySquare,
nullptr );
445 BOOST_REQUIRE_GE( poisonedTriangles.size(), 2U );
446 std::reverse( poisonedTriangles.begin(), poisonedTriangles.end() );
452 bool success2 = triangulator->TesselatePolygon( noisySquare, &hintFixture.
GetResult() );
456 for(
size_t i = 0; i < poisonedTriangles.size(); ++i )
470 hintTriangulator->TesselatePolygon( triangle,
nullptr );
477 bool success = triangulator->TesselatePolygon(
square, &hintFixture.
GetResult() );
490 bool success = triangulator->TesselatePolygon(
empty,
nullptr );
497 singlePoint.
Append( 0, 0 );
499 success = triangulator2->TesselatePolygon( singlePoint,
nullptr );
509 success = triangulator3->TesselatePolygon( line,
nullptr );
521 zeroArea.
Append( 100, 0 );
526 bool success = triangulator->TesselatePolygon( zeroArea,
nullptr );
535 for(
int i = 0; i < 100; i++ )
541 bool success = triangulator->TesselatePolygon( poly,
nullptr );
555 int numVertices = 1000;
558 for(
int i = 0; i < numVertices; i++ )
560 double angle = 2.0 *
M_PI * i / numVertices;
561 int x =
static_cast<int>(
radius * cos( angle ) );
562 int y =
static_cast<int>(
radius * sin( angle ) );
567 auto start = std::chrono::high_resolution_clock::now();
568 bool success = triangulator->TesselatePolygon( largePoly,
nullptr );
569 auto end = std::chrono::high_resolution_clock::now();
571 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end - start );
587 const int numThreads = 4;
588 const int numTriangulationsPerThread = 10;
590 std::vector<std::future<bool>> futures;
592 for(
int t = 0; t < numThreads; t++ )
594 futures.push_back( std::async( std::launch::async, [t, numTriangulationsPerThread]()
596 for(
int i = 0; i < numTriangulationsPerThread; i++ )
604 bool success = triangulator->TesselatePolygon( poly,
nullptr );
613 for(
auto& future : futures )
628 bowtie.
Append( 100, 100 );
633 bool success = triangulator->TesselatePolygon( bowtie,
nullptr );
662 const int SCALE = 1000000;
663 outline.
Append( 165 * SCALE, 87 * SCALE );
664 outline.
Append( 179 * SCALE, 87 * SCALE );
665 outline.
Append( 174 * SCALE, 94 * SCALE );
666 outline.
Append( 169 * SCALE, 87 * SCALE );
667 outline.
Append( 167 * SCALE, 94 * SCALE );
680 double triangulatedArea = 0.0;
686 for(
const auto& tri : triPoly->Triangles() )
687 triangulatedArea +=
std::abs( tri.Area() );
693 double expectedAreaMmSq = 49.0 * SCALE * SCALE;
696 BOOST_TEST(
std::abs( triangulatedArea - expectedAreaMmSq ) < expectedAreaMmSq * 0.01,
697 "Triangulated area should match expected area of 49 mm²" );
707 nearlyCollinear.
Append( 0, 0 );
708 nearlyCollinear.
Append( 1000000, 0 );
709 nearlyCollinear.
Append( 2000000, 1 );
710 nearlyCollinear.
Append( 3000000, 0 );
711 nearlyCollinear.
Append( 1500000, 1000000 );
714 bool success = triangulator->TesselatePolygon( nearlyCollinear,
nullptr );
740 bool success = triangulator->TesselatePolygon(
duplicate,
nullptr );
756 int large = 1000000000;
758 extreme.
Append( large, 0 );
759 extreme.
Append( large, large );
760 extreme.
Append( 0, large );
763 bool success = triangulator->TesselatePolygon( extreme,
nullptr );
779 std::vector<SHAPE_LINE_CHAIN> testPolygons;
786 degenerate.Append( 0, 0 );
787 degenerate.SetClosed(
true );
788 testPolygons.push_back( degenerate );
793 for(
const auto& poly : testPolygons )
796 bool success = triangulator->TesselatePolygon( poly,
nullptr );
810 bool success = triangulator->TesselatePolygon(
square,
nullptr );
817 if(
result.GetTriangleCount() > 0 )
820 result.GetTriangle( 0, a, b, c );
829 for(
const auto& tri :
result.Triangles() )
845 const int expectedOutlineIndex = 5;
862 std::vector<int> testSizes = { 10, 50, 100, 500, 1000 };
863 std::vector<long long> durations;
865 for(
int size : testSizes )
872 for(
int i = 0; i < size; i++ )
874 double angle = 2.0 *
M_PI * i / size;
875 int x =
static_cast<int>( 1000 * cos( angle ) );
876 int y =
static_cast<int>( 1000 * sin( angle ) );
881 auto start = std::chrono::high_resolution_clock::now();
882 bool success = triangulator->TesselatePolygon( poly,
nullptr );
883 auto end = std::chrono::high_resolution_clock::now();
887 auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
end - start );
888 durations.push_back( duration.count() );
893 for(
size_t i = 1; i < durations.size(); i++ )
895 double scaleFactor =
static_cast<double>( durations[i] ) / durations[i-1];
896 double sizeFactor =
static_cast<double>( testSizes[i] ) / testSizes[i-1];
899 BOOST_TEST( scaleFactor < sizeFactor * sizeFactor * 2 );
912 constexpr int vertexCount = 120000;
913 constexpr int centerX = 5000000;
914 constexpr int centerY = 5000000;
915 constexpr int radius = 4000000;
917 for(
int i = 0; i < vertexCount; ++i )
919 double angle = 2.0 *
M_PI * i / vertexCount;
920 int x = centerX +
static_cast<int>(
radius * cos( angle ) );
921 int y = centerY +
static_cast<int>(
radius * sin( angle ) );
928 std::atomic<int> tasksSubmitted( 0 );
931 [&tasksSubmitted]( std::function<void()> aTask )
934 std::thread( std::move( aTask ) ).detach();
943 double triArea = 0.0;
949 for(
const auto& tri : triPoly->Triangles() )
954 if( originalArea > 0.0 )
956 double coverage = triArea / originalArea;
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< double > PartitionAreaFractionsForTesting(const SHAPE_LINE_CHAIN &aPoly, size_t aTargetLeaves) const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
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.
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.
size_t GetTriangleCount() const
const std::deque< TRI > & Triangles() const
size_t GetVertexCount() const
void SetTriangles(const std::deque< TRI > &aTriangles)
Represent a set of closed polygons.
bool IsTriangulationUpToDate() const
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool Parse(std::stringstream &aStream) override
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.
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
unsigned int TriangulatedPolyCount() const
Return the number of triangulated polygons.
std::function< void(std::function< void()>)> TASK_SUBMITTER
Callback that submits a unit of work for asynchronous execution.
bool IsSelfIntersecting() const
Check whether any of the polygons in the set is self intersecting.
SHAPE_POLY_SET::TRIANGULATED_POLYGON & GetResult()
std::unique_ptr< POLYGON_TRIANGULATION > CreateTriangulator()
std::unique_ptr< SHAPE_POLY_SET::TRIANGULATED_POLYGON > m_result
TRIANGULATION_TEST_FIXTURE()
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
static bool empty(const wxTextEntryBase *aCtrl)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Numerical test predicates.
static std::vector< double > PartitionAreaFractions(POLYGON_TRIANGULATION &aTriangulator, const SHAPE_LINE_CHAIN &aPoly, size_t aTargetLeaves)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_TEST(contains==c.ExpectedContains)
BOOST_AUTO_TEST_SUITE_END()
bool parsePolyFileForTest(const fs::path &aPath, std::vector< SHAPE_POLY_SET > &aZones)
SHAPE_LINE_CHAIN createConcavePolygon(int size=100)
int countSpikeyTriangles(const SHAPE_POLY_SET::TRIANGULATED_POLYGON &aResult)
SHAPE_LINE_CHAIN createSquare(int size=100, VECTOR2I offset=VECTOR2I(0, 0))
double computeBoardSpikeyRatio(const fs::path &aPath)
bool validateTriangulation(const SHAPE_POLY_SET::TRIANGULATED_POLYGON &result, const SHAPE_LINE_CHAIN &original, bool strict=true)
SHAPE_LINE_CHAIN createTriangle(int size=100, VECTOR2I offset=VECTOR2I(0, 0))
BOOST_AUTO_TEST_CASE(BasicTriangleTriangulation)
SHAPE_LINE_CHAIN createSerpentinePolygon(int step=20000, int teeth=16)
const SHAPE_LINE_CHAIN chain
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I