From 853a7d7a13584cc69f3ad8e92722b597b1d5335c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 23 Oct 2025 00:25:47 +0200 Subject: [PATCH] Refinement of HM fix --- src/db/db/dbPLC.cc | 11 ++ src/db/db/dbPLC.h | 5 + src/db/db/dbPLCConvexDecomposition.cc | 149 +++++++++++++----- src/db/db/dbPLCConvexDecomposition.h | 6 +- .../dbPLCConvexDecompositionTests.cc | 46 ++++++ 5 files changed, 175 insertions(+), 42 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index 07b7b8fa5..8fb64104d 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -110,6 +110,17 @@ Vertex::is_outside () const return false; } +bool +Vertex::is_on_outline () const +{ + for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { + if ((*e)->is_segment ()) { + return true; + } + } + return false; +} + void Vertex::set_is_precious (bool f, unsigned int id) { diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index 6a65e61d1..275277c70 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -96,6 +96,11 @@ public: */ bool is_outside () const; + /** + * @brief Gets a value indicating whether is on the outline - i.e. one edge is a segment + */ + bool is_on_outline () const; + /** * @brief Gets a list of polygons that are attached to this vertex */ diff --git a/src/db/db/dbPLCConvexDecomposition.cc b/src/db/db/dbPLCConvexDecomposition.cc index 17b664cb5..17a482462 100644 --- a/src/db/db/dbPLCConvexDecomposition.cc +++ b/src/db/db/dbPLCConvexDecomposition.cc @@ -31,6 +31,8 @@ #include #include +// #define DEBUG_DUMP_ESSENTIAL_EDGES + namespace db { @@ -286,65 +288,134 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C // them one-by-one, but using them in length order, from the std::unordered_set essential_edges; + std::unordered_set concave_vertexes_seen; - typedef std::list > angles_and_edges_list; - angles_and_edges_list angles_and_edges; - std::vector sorted_edges; + while (! concave_vertexes.empty ()) { - for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { + typedef std::list > angles_and_edges_list; + angles_and_edges_list angles_and_edges; + std::vector sorted_edges; - angles_and_edges.clear (); - const Vertex *v0 = cc->corner; + std::unordered_set new_inner_vertexes; - const Edge *e = cc->incoming; - while (e) { + for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { - const Polygon *t = e->v2 () == v0 ? e->right () : e->left (); - tl_assert (t != 0); + angles_and_edges.clear (); + const Vertex *v0 = cc->corner; - const Edge *en = t->next_edge (e, v0); - tl_assert (en != 0); + concave_vertexes_seen.insert (v0); - db::DVector v1 = e->edge ().d () * (e->v1 () == v0 ? 1.0 : -1.0); - db::DVector v2 = en->edge ().d () * (en->v1 () == v0 ? 1.0 : -1.0); + const Edge *e = cc->incoming; + while (e) { - double angle = atan2 (db::vprod (v1, v2), db::sprod (v1, v2)); + const Polygon *t = e->v2 () == v0 ? e->right () : e->left (); + tl_assert (t != 0); - e = (en == cc->outgoing) ? 0 : en; - angles_and_edges.push_back (std::make_pair (angle, e)); + const Edge *en = t->next_edge (e, v0); + tl_assert (en != 0); + + db::DVector v1 = e->edge ().d () * (e->v1 () == v0 ? 1.0 : -1.0); + db::DVector v2 = en->edge ().d () * (en->v1 () == v0 ? 1.0 : -1.0); + + double angle = atan2 (db::vprod (v1, v2), db::sprod (v1, v2)); + + e = (en == cc->outgoing) ? 0 : en; + angles_and_edges.push_back (std::make_pair (angle, e)); + + } + + sorted_edges.clear (); + + for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { + if (i->second) { + sorted_edges.push_back (i); + } + } + + std::sort (sorted_edges.begin (), sorted_edges.end (), SortAngleAndEdgesByEdgeLength ()); + + for (auto i = sorted_edges.end (); i != sorted_edges.begin (); ) { + --i; + angles_and_edges_list::iterator ii = *i; + angles_and_edges_list::iterator iin = ii; + ++iin; + if (ii->first + iin->first < (split_edges ? M_PI + db::epsilon : M_PI - db::epsilon)) { + // not an essential edge -> remove + iin->first += ii->first; + angles_and_edges.erase (ii); + } + } + + for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { + if (i->second) { + essential_edges.insert (i->second); + // record new endpoints of essential edges which are inside the polygon - i.e. they + // have a segment attached. Below we will turn them into new concave "corners" and + // continue deriving essential edges from there. + if (! i->second->v1 ()->is_on_outline () && concave_vertexes_seen.find (i->second->v1 ()) == concave_vertexes_seen.end ()) { + new_inner_vertexes.insert (i->second->v1 ()); + } + if (! i->second->v2 ()->is_on_outline () && concave_vertexes_seen.find (i->second->v2 ()) == concave_vertexes_seen.end ()) { + new_inner_vertexes.insert (i->second->v2 ()); + } + } + } } - sorted_edges.clear (); + // new inner vertexes (i.e. endpoints of essential edges inside the polygon) are treated as new convex vertexes - for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { - if (i->second) { - sorted_edges.push_back (i); - } - } + concave_vertexes.clear (); - std::sort (sorted_edges.begin (), sorted_edges.end (), SortAngleAndEdgesByEdgeLength ()); + for (auto i = new_inner_vertexes.begin (); i != new_inner_vertexes.end (); ++i) { - for (auto i = sorted_edges.end (); i != sorted_edges.begin (); ) { - --i; - angles_and_edges_list::iterator ii = *i; - angles_and_edges_list::iterator iin = ii; - ++iin; - if (ii->first + iin->first < (split_edges ? M_PI + db::epsilon : M_PI - db::epsilon)) { - // not an essential edge -> remove - iin->first += ii->first; - angles_and_edges.erase (ii); - } - } + const Vertex *v0 = *i; + auto ie = v0->begin_edges (); + for ( ; ie != v0->end_edges () && essential_edges.find (*ie) == essential_edges.end (); ++ie) + ; + tl_assert (ie != v0->end_edges ()); + const Edge *e = *ie; - for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { - if (i->second) { - essential_edges.insert (i->second); - } + const Edge *en = e; + + do { + + const Edge *enn = en; + + // look for the next edge (clockwise) which is an essential edge + do { + const Polygon *t = enn->v2 () == v0 ? enn->right () : enn->left (); + tl_assert (t != 0); + enn = t->next_edge (enn, v0); + tl_assert (enn != 0); + } while (enn != en && essential_edges.find (enn) == essential_edges.end ()); + + db::DEdge e1 (*en->other (v0), *v0); + db::DEdge e2 (*v0, *enn->other (v0)); + + // vp > 0: concave, vp < 0: convex + int vp_sign = db::vprod_sign (e1, e2); + if (vp_sign > 0 || en == enn /*folding back*/) { + concave_vertexes.push_back (ConcaveCorner (v0, en, enn)); + } + + en = enn; + + } while (en != e); } } +#if defined(DEBUG_DUMP_ESSENTIAL_EDGES) + // dump the essential edges for debugging + db::Edges edges; + for (auto e = essential_edges.begin (); e != essential_edges.end (); ++e) { + db::DEdge de = (*e)->edge (); + edges.insert (db::VCplxTrans (1000.0) * de); + } + edges.write ("debug_dump_essential_edges.gds"); +#endif + // Combine triangles, but don't cross essential edges std::unordered_set left_triangles; diff --git a/src/db/db/dbPLCConvexDecomposition.h b/src/db/db/dbPLCConvexDecomposition.h index d236a29ef..8e1c6ba3b 100644 --- a/src/db/db/dbPLCConvexDecomposition.h +++ b/src/db/db/dbPLCConvexDecomposition.h @@ -136,14 +136,14 @@ private: // .. nothing yet .. } - ConcaveCorner (Vertex *_corner, Edge *_incoming, Edge *_outgoing) + ConcaveCorner (const Vertex *_corner, const Edge *_incoming, const Edge *_outgoing) : corner (_corner), incoming (_incoming), outgoing (_outgoing) { // .. nothing yet .. } - Vertex *corner; - Edge *incoming, *outgoing; + const Vertex *corner; + const Edge *incoming, *outgoing; }; void hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters ¶m); diff --git a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc index 22b8282b6..de65dce88 100644 --- a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc +++ b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc @@ -144,3 +144,49 @@ TEST(problematic_polygon) db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au5.gds"); } +TEST(problematic_polygon2) +{ + db::Point contour[] = { + db::Point (-2100, 200), + db::Point (-2100, 2000), + db::Point (-500, 2000), + db::Point (-500, 1700), + db::Point (-849, 1700), + db::Point (-947, 1690), + db::Point (-1043, 1671), + db::Point (-1137, 1643), + db::Point (-1228, 1605), + db::Point (-1315, 1559), + db::Point (-1396, 1504), + db::Point (-1472, 1442), + db::Point (-1542, 1372), + db::Point (-1604, 1296), + db::Point (-1659, 1215), + db::Point (-1705, 1128), + db::Point (-1743, 1037), + db::Point (-1771, 943), + db::Point (-1790, 847), + db::Point (-1800, 749), + db::Point (-1800, 200) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::plc::ConvexDecompositionParameters param; + param.with_segments = false; + param.split_edges = false; + param.tri_param.max_area = 1000000; + param.tri_param.min_b = 0.5; + + db::plc::Graph plc; + TestableConvexDecomposition decomp (&plc); + + decomp.decompose (poly, param, dbu); + + std::unique_ptr ly (plc.to_layout ()); + db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au6.gds"); +} +