Compare commits

...

6 Commits

Author SHA1 Message Date
Matthias Köfferlein 701bd3b543
Merge ddc847ac1f into 8b010d61d2 2025-11-01 15:44:51 +00:00
Matthias Koefferlein ddc847ac1f Object snapping: now snapping to edge centers for polygons and boxes, snapping to box centers 2025-11-01 16:44:40 +01:00
Matthias Koefferlein 289facd5ee Merge branch 'master' into devel 2025-10-26 16:36:03 +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
9 changed files with 233 additions and 47 deletions

View File

@ -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)
{

View File

@ -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
*/

View File

@ -31,6 +31,8 @@
#include <vector>
#include <map>
// #define DEBUG_DUMP_ESSENTIAL_EDGES
namespace db
{
@ -286,16 +288,23 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C
// them one-by-one, but using them in length order, from the
std::unordered_set<const Edge *> essential_edges;
std::unordered_set<const Vertex *> concave_vertexes_seen;
while (! concave_vertexes.empty ()) {
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;
std::unordered_set<const Vertex *> new_inner_vertexes;
for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) {
angles_and_edges.clear ();
const Vertex *v0 = cc->corner;
concave_vertexes_seen.insert (v0);
const Edge *e = cc->incoming;
while (e) {
@ -340,11 +349,73 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C
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 ());
}
}
}
}
// new inner vertexes (i.e. endpoints of essential edges inside the polygon) are treated as new convex vertexes
concave_vertexes.clear ();
for (auto i = new_inner_vertexes.begin (); i != new_inner_vertexes.end (); ++i) {
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;
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<const Polygon *> left_triangles;

View File

@ -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 &param);

View File

@ -23,6 +23,8 @@
#include "dbPLCConvexDecomposition.h"
#include "dbWriter.h"
#include "dbReader.h"
#include "dbLayout.h"
#include "dbRegionProcessors.h"
#include "dbTestSupport.h"
#include "tlUnitTest.h"
@ -144,3 +146,80 @@ 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<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");
}

View File

@ -613,7 +613,7 @@ private:
if (shape->is_polygon ()) {
for (db::Shape::polygon_edge_iterator e = shape->begin_edge (); ! e.at_end (); ++e) {
test_edge (t * *e);
test_edge_with_center (t * *e);
}
} else if (shape->is_path ()) {
@ -649,16 +649,20 @@ private:
}
test_edge (db::DEdge (*p, pts [0]));
}
} else if (shape->is_box ()) {
const db::Box &box = shape->box ();
test_edge (t * db::Edge (box.p1 (), db::Point (box.left (), box.top ())));
test_edge (t * db::Edge (db::Point (box.left (), box.top ()), box.p2 ()));
test_edge (t * db::Edge (box.p2 (), db::Point (box.right (), box.bottom ())));
test_edge (t * db::Edge (db::Point (box.right (), box.bottom ()), box.p1 ()));
test_edge_with_center (t * db::Edge (box.p1 (), db::Point (box.left (), box.top ())));
test_edge_with_center (t * db::Edge (db::Point (box.left (), box.top ()), box.p2 ()));
test_edge_with_center (t * db::Edge (box.p2 (), db::Point (box.right (), box.bottom ())));
test_edge_with_center (t * db::Edge (db::Point (box.right (), box.bottom ()), box.p1 ()));
// test for box center
test_edge (t * db::Edge (box.center (), box.center ()));
} else if (shape->is_point ()) {
@ -697,6 +701,22 @@ private:
}
void
test_edge_with_center (const db::DEdge &edg)
{
if (m_with_vertex && ! edg.is_degenerate ()) {
db::DPoint c = edg.p1 () + (edg.p2 () - edg.p1 ()) * 0.5;
if (m_region.contains (c)) {
closest (c);
}
}
test_edge (edg);
}
void
test_edge (const db::DEdge &edg)
{

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.