From a435b186a7bf8b1e2d1ce4c6ec3f0e67b6179ae6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Aug 2026 00:52:11 +0200 Subject: [PATCH 1/6] A proposal to fix the issue --- src/db/db/dbPLC.cc | 16 ++++++++-------- src/db/db/dbPLCTriangulation.cc | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index 96431b6fc..01981ff27 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -918,24 +918,24 @@ Graph::to_layout (bool decompose_by_id) const unsigned int l21 = layout->insert_layer (db::LayerProperties (21, 0)); unsigned int l22 = layout->insert_layer (db::LayerProperties (22, 0)); - std::vector pts; + std::vector pts; for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { pts.clear (); for (int i = 0; i < int (t->size ()); ++i) { - pts.push_back (*t->vertex (i)); + pts.push_back (dbu_trans * *t->vertex (i)); } - db::DPolygon poly; - poly.assign_hull (pts.begin (), pts.end ()); - top.shapes (t->is_outside () ? l2 : l1).insert (dbu_trans * poly); + db::Polygon poly; + poly.assign_hull (pts.begin (), pts.end (), false, false); + top.shapes (t->is_outside () ? l2 : l1).insert (poly); if (decompose_by_id) { if ((t->id () & 1) != 0) { - top.shapes (l20).insert (dbu_trans * poly); + top.shapes (l20).insert (poly); } if ((t->id () & 2) != 0) { - top.shapes (l21).insert (dbu_trans * poly); + top.shapes (l21).insert (poly); } if ((t->id () & 4) != 0) { - top.shapes (l22).insert (dbu_trans * poly); + top.shapes (l22).insert (poly); } } } diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 13da1cf7c..face8c8f0 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -245,7 +245,13 @@ Triangulation::insert (Vertex *vertex, std::list > *new_tr std::vector on_vertex; for (int i = 0; i < 3; ++i) { Edge *e = tris.front ()->edge (i); +#if 0 // @@@ if (e->side_of (*vertex) == 0) { +#else // @@@ + auto ee = e->edge (); + double snap = 1e-3; + if (std::abs (ee.distance (*vertex)) < snap * ee.length ()) { +#endif // @@@ if (is_equal (*vertex, *e->v1 ()) || is_equal (*vertex, *e->v2 ())) { on_vertex.push_back (e); } else { From ca45619b2b4cc595fd9441ec518f64587646fcd6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Aug 2026 01:17:25 +0200 Subject: [PATCH 2/6] WIP --- src/db/db/dbPLCTriangulation.cc | 34 +++++------ src/db/unit_tests/dbPLCTriangulationTests.cc | 60 ++++++++++++++++++++ 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index face8c8f0..a00cf49b9 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -241,35 +241,35 @@ Triangulation::insert (Vertex *vertex, std::list > *new_tr } // check, if the new vertex is on an edge (may be edge between triangles or edge on outside) - std::vector on_edges; + Edge *on_edge = 0; std::vector on_vertex; for (int i = 0; i < 3; ++i) { + Edge *e = tris.front ()->edge (i); -#if 0 // @@@ - if (e->side_of (*vertex) == 0) { -#else // @@@ + auto ee = e->edge (); - double snap = 1e-3; - if (std::abs (ee.distance (*vertex)) < snap * ee.length ()) { -#endif // @@@ - if (is_equal (*vertex, *e->v1 ()) || is_equal (*vertex, *e->v2 ())) { + double snap_range = 1e-5 * ee.length (); + + // @@@ refine? generalize "snap_range"? change "is_equal" in general? + if (std::abs (ee.distance (*vertex)) < snap_range) { + if (vertex->distance (*e->v1 ()) < snap_range || vertex->distance (*e->v2 ()) < snap_range) { on_vertex.push_back (e); - } else { - on_edges.push_back (e); + } else if (! on_edge) { + on_edge = e; } } + } - if (! on_vertex.empty ()) { + if (on_edge) { - tl_assert (on_vertex.size () == size_t (2)); - return on_vertex.front ()->common_vertex (on_vertex [1]); + split_triangles_on_edge (vertex, on_edge, new_triangles); + return vertex; - } else if (! on_edges.empty ()) { + } else if (! on_vertex.empty ()) { - tl_assert (on_edges.size () == size_t (1)); - split_triangles_on_edge (vertex, on_edges.front (), new_triangles); - return vertex; + tl_assert (on_vertex.size () == size_t (2)); + return on_vertex.front ()->common_vertex (on_vertex [1]); } else if (tris.size () == size_t (1)) { diff --git a/src/db/unit_tests/dbPLCTriangulationTests.cc b/src/db/unit_tests/dbPLCTriangulationTests.cc index f867ee40b..44b90ff97 100644 --- a/src/db/unit_tests/dbPLCTriangulationTests.cc +++ b/src/db/unit_tests/dbPLCTriangulationTests.cc @@ -1078,6 +1078,65 @@ TEST(triangulate_issue1996) EXPECT_LT (plc.num_polygons (), size_t (132000)); } +TEST(triangulate_discussion_2883) +{ + db::DPoint contour[] = { + db::DPoint (-13025.428, -33338.541), + db::DPoint (-13127.959, -33236.01), + db::DPoint (-13132.732, -33240.783), + db::DPoint (-13135.914, -33237.601), + db::DPoint (-13026.136, -33127.823), + db::DPoint (-12923.605, -33230.354), + db::DPoint (-12920.423, -33227.172), + db::DPoint (-13022.954, -33124.641), + db::DPoint (-13014.999, -33116.686), + db::DPoint (-13011.817, -33119.868), + db::DPoint (-13016.59, -33124.641), + db::DPoint (-12914.059, -33227.172), + db::DPoint (-12923.605, -33236.718), + db::DPoint (-13026.136, -33134.187), + db::DPoint (-13029.317, -33137.369), + db::DPoint (-12926.787, -33239.899), + db::DPoint (-12936.333, -33249.445), + db::DPoint (-13038.863, -33146.915), + db::DPoint (-13042.045, -33150.097), + db::DPoint (-12939.515, -33252.627), + db::DPoint (-12949.061, -33262.173), + db::DPoint (-13051.591, -33159.643), + db::DPoint (-13054.773, -33162.825), + db::DPoint (-12952.243, -33265.355), + db::DPoint (-13012.701, -33325.813), + db::DPoint (-13115.231, -33223.283), + db::DPoint (-13118.413, -33226.464), + db::DPoint (-13015.882, -33328.995) + }; + + db::DPolygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::plc::TriangulationParameters param; + param.min_b = 0.3; + + db::plc::Graph plc; + TestableTriangulation tri (&plc); + db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); + tri.triangulate (trans * poly, param); + + EXPECT_EQ (tri.check (false), true); + + // for debugging: + // tri.dump ("debug.gds"); + + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GE (plc.num_polygons (), size_t (70)); + EXPECT_LE (plc.num_polygons (), size_t (72)); +} + TEST(triangulate_with_vertexes) { db::Point contour[] = { @@ -1172,3 +1231,4 @@ TEST(triangulate_with_vertexes) } } } + From 67d47583546a1f2cd87c5c9852165429d888f117 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Aug 2026 16:58:41 +0200 Subject: [PATCH 3/6] Refined bug fix --- src/db/db/dbPLC.cc | 2 +- src/db/db/dbPLC.h | 16 ++++++++++++++++ src/db/db/dbPLCTriangulation.cc | 19 +++++++++++-------- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index 01981ff27..8d85580ba 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -377,7 +377,7 @@ Edge::point_on (const db::DEdge &edge, const db::DPoint &point) if (edge.side_of (point) != 0) { return false; } else { - return db::sprod_sign (point - edge.p1 (), edge.d ()) * db::sprod_sign(point - edge.p2 (), edge.d ()) < 0; + return db::sprod_sign (point - edge.p1 (), edge.d ()) * db::sprod_sign (point - edge.p2 (), edge.d ()) < 0; } } diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index 3e224053d..6e098e81c 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -457,6 +457,22 @@ class DB_PUBLIC Edge return *mp_v2 - *mp_v1; } + /** + * @brief Gets the length of the edge + */ + double length () const + { + return mp_v1->double_distance (*mp_v2); + } + + /** + * @brief Gets the squared length of the edge + */ + double sq_length () const + { + return mp_v1->sq_double_distance (*mp_v2); + } + /** * @brief Gets the other triangle for the given one */ diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index a00cf49b9..f59ec1a68 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -42,6 +42,13 @@ static inline bool is_equal (const db::DPoint &a, const db::DPoint &b) std::abs (a.y () - b.y ()) < std::max (1.0, (std::abs (a.y ()) + std::abs (b.y ()))) * db::epsilon; } +// distance of point to vertex to be considered "on edge vertex" relative to edge length involved +const double snap_to_edge_vertex = 1e-5; + +// distance of point to edge center to be considered "on edge center" relative to edge length involved +double snap_to_edge_center = 1e-3; + + Triangulation::Triangulation (Graph *graph) { mp_graph = graph; @@ -247,12 +254,10 @@ Triangulation::insert (Vertex *vertex, std::list > *new_tr Edge *e = tris.front ()->edge (i); - auto ee = e->edge (); - double snap_range = 1e-5 * ee.length (); + double snap_range = snap_to_edge_vertex * e->length (); - // @@@ refine? generalize "snap_range"? change "is_equal" in general? - if (std::abs (ee.distance (*vertex)) < snap_range) { - if (vertex->distance (*e->v1 ()) < snap_range || vertex->distance (*e->v2 ()) < snap_range) { + if (std::abs (e->edge ().distance (*vertex)) < snap_range - db::epsilon) { + if (vertex->distance (*e->v1 ()) < snap_range + db::epsilon || vertex->distance (*e->v2 ()) < snap_range + db::epsilon) { on_vertex.push_back (e); } else if (! on_edge) { on_edge = e; @@ -1630,8 +1635,6 @@ Triangulation::refine (const TriangulationParameters ¶meters) if (s > 0) { - double snap = 1e-3; - // Snap the center to a segment center if "close" to it. // This avoids generating very skinny triangles that can't be fixed as the // segment cannot be flipped. This a part of the issue #1996 problem. @@ -1639,7 +1642,7 @@ Triangulation::refine (const TriangulationParameters ¶meters) if ((*t)->edge (i)->is_segment ()) { auto e = (*t)->edge (i)->edge (); auto c = e.p1 () + e.d () * 0.5; - if (c.distance (center) < e.length () * 0.5 * snap - db::epsilon) { + if (c.distance (center) < e.length () * 0.5 * snap_to_edge_center - db::epsilon) { center = c; break; } From daa1d094d195e798e4b3d9ea30c9a76c7b091aac Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Aug 2026 17:43:00 +0200 Subject: [PATCH 4/6] Updating test data: required, because we changed the dumper to not compress polygons --- testdata/algo/hm_decomposition_au2.gds | Bin 1922 -> 1946 bytes testdata/algo/hm_decomposition_au3.gds | Bin 1460 -> 1492 bytes testdata/algo/hm_decomposition_au4.gds | Bin 1786 -> 1850 bytes testdata/algo/hm_decomposition_au5.gds | Bin 1314 -> 1322 bytes testdata/algo/hm_decomposition_au7.gds | Bin 14896 -> 14920 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/testdata/algo/hm_decomposition_au2.gds b/testdata/algo/hm_decomposition_au2.gds index b6d27f37383a77f99f441557ab03b8e610c8ed35..279d6e3c44487a9b9a5775341f197837decceaf4 100644 GIT binary patch delta 285 zcmZqTpT%F#z{bGD6u}_F$i)7NfrCMmL6AX}fg734z`?}khX z7zE5FHknQQad5IDllJ5#j3EHRW{Sf6WHENp2n&?c{Pjab|06LujnQo~53ADVWlU9!lfBr@CM&SyOuolv wHo1qzZgK}Z&*WW9N|PJdIVPWCnKfAiC_jtUY;p>#`Q(eNui4lc7+6>s0F`Vr3IG5A diff --git a/testdata/algo/hm_decomposition_au3.gds b/testdata/algo/hm_decomposition_au3.gds index bdfe51da09444106f9095b48157a4ca8577eeeeb..197dedc1665a65ae8b1237e4624b4366486dbd0e 100644 GIT binary patch delta 221 zcmdnOeT6%UfsKKQDS|{L4=vr&au0Tcq#EItsL!vu*2GFjOsUP_!?&1k5{ z!N9=A%f!IQz{bY}l&}GrCSdme{~rku4HN+a;SL}HG)?%;WG_ag$!g3>o6j*mV+H`C C@gm9q delta 156 zcmcb@y@fl9fsKKQDS|{L4=vr&au#5Rl$lV`AM KZT`l1j~M_wVjX}0 diff --git a/testdata/algo/hm_decomposition_au4.gds b/testdata/algo/hm_decomposition_au4.gds index 3595c5d171cc9cf3830423f4bee574d96e9043ca..3331beee2aa07b442b7ef3e8e0efdac684747c4e 100644 GIT binary patch delta 339 zcmeyxyNj=$fsKKQDS|{L4=vr&au7frW#CfsL1mfsuiYkBNbSAw+%u17$Sj;A;v6@Y8U^biF$0EYU&cMLJ!T{L4=vr&au7frW#CfsL1mfsuiYkBNbSK}Ud@fq}&b$YNlaT+S#t zc?P2b8}kb=SC3JAvM-~YJXno@+5i84Bp4V3%z$z!3=HB|VD!XYYLj;`=1g9~C^GSZ z&SVD`rOj?kRg9Bgv6)Riz??I=4oK^;*iF`8=b0SEq%>K9onvwq%dE*;fZ|rHW|LW1 Q%_kSKzGhfsKKQDS|{L4=vr&aurZ6xt-(z49zXGB+*D)?(1^|TW8CU=S delta 114 zcmZ3*wTLT{L4=vr&au5=Ai8=j*VNkrpQW73C6<`st7?@5TTOX^dN{( z2?*f>FLNmBj!0>3d5hD!!TT$B9~s^u5kX?rRn{jH-yNq!tn41A+cLLA8{N` z{rxZVVB#K_;9_5P5J)!0l8aG99Q;|jVqAt`Zg(^SAkn=#a z(T+G!Vm2lUMLT{UO2S4{xr1P48A9?pUe5TClm-R~ib|ZKqdIsWtD%!{;PToX#l=AN zFiv>b=X!!l6D&d!^g<y>l6oC9ow95>K@KoHmd10#Ek`f zOL^!R6g}?W()f<48g|n@s)mE1u3*Eez%(1M^#-Ig#S}Fzk~n) delta 550 zcmZ`!OK1~O6n&Y=e3Hp;CjXBjjT<{+6CdKUL&|7u8L27Jb}D!a7iU3 zC_hP*8y~7^j}}MQ|TVmx__pO z)AxkPyFoDdMTqhs2u_djJXG&)w6%*VF$jKt41!<|mqJ(I3pTv4jcVAzJDH&$I>cCD z=!p(Y(FT=loX8D~L<&#jyu>gJFX5>)3s0WMNZP_)*up?);G9C?$F{H$b>K(ZFxAJX zX%?0h1~JO8tvq=hqZo5I=5P>g;B$ncuQ{MG2NuWcS`#$Bj%{@T7b16Yq`H{I_+DpF zGZwzZAHvfUxRaSiPoKwH(!nTU;Y8>0lRQqUiDtTuVX}ob06K3AV zgx9c;z@ywWP`J-^5qv1lpirVPvkXTi2i4*Nx}{mJW3UP?{Bj+;`Fj}gf70bOXt@@? PX3yhCb*lfV{Os&sKa{)q From 8cc9b9964931b78e3677f67e927dc43d1eca56dd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Aug 2026 17:47:05 +0200 Subject: [PATCH 5/6] Updating changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 0a27d71dc..4332444ab 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ 0.30.10 (2026-08-22): +* Bugfix: %GITHUB%/issues/2428 Internal error on triangulation * Enhancement: %GITHUB%/issues/2374 LEF/DEF Import - skip duplicate Macros feature * Enhancement: %GITHUB%/issues/2407 Visualize "Select" and "Partial" via selection box style * Bugfix: %GITHUB%/issues/2416 Two-layer DRC check without merged first input produces separation violations that 0.30.9 does not reportbug From 5f9ece406d3b18f6acb4aaf39fc6704214a90626 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Aug 2026 23:09:13 +0200 Subject: [PATCH 6/6] Updating Changelog --- Changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index 4332444ab..5f96efca7 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,5 @@ -0.30.10 (2026-08-22): -* Bugfix: %GITHUB%/issues/2428 Internal error on triangulation +0.30.11 (2026-08-22): +* Bugfix: %GITHUB%/issues/2429 Internal error on triangulation * Enhancement: %GITHUB%/issues/2374 LEF/DEF Import - skip duplicate Macros feature * Enhancement: %GITHUB%/issues/2407 Visualize "Select" and "Partial" via selection box style * Bugfix: %GITHUB%/issues/2416 Two-layer DRC check without merged first input produces separation violations that 0.30.9 does not reportbug