Compare commits

...

4 Commits

Author SHA1 Message Date
Matthias Köfferlein f80c51bfd2
Merge 4ee73b5f86 into 8b010d61d2 2025-10-27 08:05:52 +01:00
Matthias Koefferlein 4ee73b5f86 Added one more test 2025-10-23 18:50:44 +02:00
Matthias Koefferlein 853a7d7a13 Refinement of HM fix 2025-10-23 00:25:47 +02:00
Matthias Koefferlein eb06679c44 Fixing problem with HM decomposition with non-trivial triangulation constraints 2025-10-22 23:19:10 +02:00
8 changed files with 208 additions and 42 deletions

View File

@ -110,6 +110,17 @@ Vertex::is_outside () const
return false; 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 void
Vertex::set_is_precious (bool f, unsigned int id) Vertex::set_is_precious (bool f, unsigned int id)
{ {

View File

@ -96,6 +96,11 @@ public:
*/ */
bool is_outside () const; 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 * @brief Gets a list of polygons that are attached to this vertex
*/ */

View File

@ -31,6 +31,8 @@
#include <vector> #include <vector>
#include <map> #include <map>
// #define DEBUG_DUMP_ESSENTIAL_EDGES
namespace db 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 // them one-by-one, but using them in length order, from the
std::unordered_set<const Edge *> essential_edges; std::unordered_set<const Edge *> essential_edges;
std::unordered_set<const Vertex *> concave_vertexes_seen;
typedef std::list<std::pair<double, const Edge *> > angles_and_edges_list; while (! concave_vertexes.empty ()) {
angles_and_edges_list angles_and_edges;
std::vector<angles_and_edges_list::iterator> sorted_edges;
for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { typedef std::list<std::pair<double, const Edge *> > angles_and_edges_list;
angles_and_edges_list angles_and_edges;
std::vector<angles_and_edges_list::iterator> sorted_edges;
angles_and_edges.clear (); std::unordered_set<const Vertex *> new_inner_vertexes;
const Vertex *v0 = cc->corner;
const Edge *e = cc->incoming; for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) {
while (e) {
const Polygon *t = e->v2 () == v0 ? e->right () : e->left (); angles_and_edges.clear ();
tl_assert (t != 0); const Vertex *v0 = cc->corner;
const Edge *en = t->next_edge (e, v0); concave_vertexes_seen.insert (v0);
tl_assert (en != 0);
db::DVector v1 = e->edge ().d () * (e->v1 () == v0 ? 1.0 : -1.0); const Edge *e = cc->incoming;
db::DVector v2 = en->edge ().d () * (en->v1 () == v0 ? 1.0 : -1.0); 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; const Edge *en = t->next_edge (e, v0);
angles_and_edges.push_back (std::make_pair (angle, e)); 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) { concave_vertexes.clear ();
if (i->second) {
sorted_edges.push_back (i);
}
}
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 (); ) { const Vertex *v0 = *i;
--i; auto ie = v0->begin_edges ();
angles_and_edges_list::iterator ii = *i; for ( ; ie != v0->end_edges () && essential_edges.find (*ie) == essential_edges.end (); ++ie)
angles_and_edges_list::iterator iin = ii; ;
++iin; tl_assert (ie != v0->end_edges ());
if (ii->first + iin->first < (split_edges ? M_PI + db::epsilon : M_PI - db::epsilon)) { const Edge *e = *ie;
// 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) { const Edge *en = e;
if (i->second) {
essential_edges.insert (i->second); 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 // Combine triangles, but don't cross essential edges
std::unordered_set<const Polygon *> left_triangles; std::unordered_set<const Polygon *> left_triangles;

View File

@ -136,14 +136,14 @@ private:
// .. nothing yet .. // .. 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) : corner (_corner), incoming (_incoming), outgoing (_outgoing)
{ {
// .. nothing yet .. // .. nothing yet ..
} }
Vertex *corner; const Vertex *corner;
Edge *incoming, *outgoing; const Edge *incoming, *outgoing;
}; };
void hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters &param); void hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters &param);

View File

@ -23,6 +23,8 @@
#include "dbPLCConvexDecomposition.h" #include "dbPLCConvexDecomposition.h"
#include "dbWriter.h" #include "dbWriter.h"
#include "dbReader.h"
#include "dbLayout.h"
#include "dbRegionProcessors.h" #include "dbRegionProcessors.h"
#include "dbTestSupport.h" #include "dbTestSupport.h"
#include "tlUnitTest.h" #include "tlUnitTest.h"
@ -144,3 +146,80 @@ TEST(problematic_polygon)
db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au5.gds"); 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<db::Layout> ly (plc.to_layout ());
db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au6.gds");
}
TEST(polygon_with_holes)
{
db::Layout ly;
tl::InputStream s (tl::testdata () + "/algo/hm_decomposition_7.gds");
db::Reader reader (s);
reader.read (ly);
unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0));
const db::Cell &top = ly.cell (*ly.begin_top_down ());
db::Region r (db::RecursiveShapeIterator (ly, top, l1));
r.merge ();
db::Polygon poly = *r.begin ();
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<db::Layout> ly_out (plc.to_layout ());
db::compare_layouts (_this, *ly_out, tl::testdata () + "/algo/hm_decomposition_au7.gds");
}

BIN
testdata/algo/hm_decomposition_7.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/hm_decomposition_au6.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/hm_decomposition_au7.gds vendored Normal file

Binary file not shown.