Compare commits

...

5 Commits

Author SHA1 Message Date
Matthias Köfferlein f80c51bfd2
Merge 4ee73b5f86 into 8b010d61d2 2025-10-27 08:05:52 +01:00
Matthias Koefferlein 8b010d61d2 Trying to fix unit tests 2025-10-26 22:25:28 +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 232 additions and 42 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,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<const Edge *> essential_edges;
std::unordered_set<const Vertex *> concave_vertexes_seen;
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;
while (! concave_vertexes.empty ()) {
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 ();
const Vertex *v0 = cc->corner;
std::unordered_set<const Vertex *> 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<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");
}

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.

View File

@ -121,6 +121,12 @@ class DBLibrary_TestClass < TestBase
def test_4_library_registration_and_rename
# clean up before
[ "RBA-unit-test", "RBA-unit-test2" ].each do |name|
l = RBA::Library::library_by_name(name)
l && l.unregister
end
lib = RBA::Library::new
lib.description = "LIB1"
lib.delete
@ -159,6 +165,12 @@ class DBLibrary_TestClass < TestBase
def test_5_reload
# clean up before
[ "RBA-unit-test", "RBA-unit-test2" ].each do |name|
l = RBA::Library::library_by_name(name)
l && l.unregister
end
lib = MyLibImpl::new
lib.description = "LIB1"
lib.register("RBA-unit-test")
@ -178,6 +190,12 @@ class DBLibrary_TestClass < TestBase
def test_6_cells_become_defunct_after_unregister
# clean up before
[ "RBA-unit-test", "RBA-unit-test2" ].each do |name|
l = RBA::Library::library_by_name(name)
l && l.unregister
end
lib = RBA::Library::new
lib.description = "LIB1"
lib.register("RBA-unit-test")
@ -209,6 +227,12 @@ class DBLibrary_TestClass < TestBase
def test_7_change_ref
# clean up before
[ "RBA-unit-test", "RBA-unit-test2" ].each do |name|
l = RBA::Library::library_by_name(name)
l && l.unregister
end
lib = RBA::Library::new
lib.description = "LIB1"
lib.register("RBA-unit-test")