From 6e9eb922a2197f090e308ea0f5bb4854ad6c14b5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 15 Aug 2023 14:55:41 +0200 Subject: [PATCH] Triangles: constrain --- src/db/db/dbTriangles.cc | 226 ++++++++++++++------------ src/db/db/dbTriangles.h | 31 +++- src/db/unit_tests/dbTrianglesTests.cc | 67 +++++++- 3 files changed, 212 insertions(+), 112 deletions(-) diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index 2418c92c0..47611bfcf 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -1254,121 +1254,137 @@ Triangles::join_edges (std::vector &edges) } } +void +Triangles::constrain (const std::vector > &contours) +{ + assert (! m_is_constrained); + + std::vector > > resolved_edges; + + for (auto c = contours.begin (); c != contours.end (); ++c) { + for (auto v = c->begin (); v != c->end (); ++v) { + auto vv = v; + ++vv; + if (vv == c->end ()) { + vv = c->begin (); + } + resolved_edges.push_back (std::make_pair (db::DEdge (**v, **vv), std::vector ())); + resolved_edges.back ().second = ensure_edge (*v, *vv); + } + } + + for (auto tri = mp_triangles.begin (); tri != mp_triangles.end (); ++tri) { + tri->set_outside (false); + for (int i = 0; i < 3; ++i) { + tri->edge (i)->set_is_segment (false); + } + } + + std::set new_tri; + + for (auto re = resolved_edges.begin (); re != resolved_edges.end (); ++re) { + auto edge = re->first; + auto edges = re->second; + for (auto e = edges.begin (); e != edges.end (); ++e) { + (*e)->set_is_segment (true); + db::Triangle *outer_tri = 0; + int d = db::sprod_sign (edge.d (), (*e)->d ()); + if (d > 0) { + outer_tri = (*e)->left (); + } + if (d < 0) { + outer_tri = (*e)->right (); + } + if (outer_tri) { + new_tri.insert (outer_tri); + outer_tri->set_outside (true); + } + } + } + + while (! new_tri.empty ()) { + + std::set next_tris; + + for (auto tri = new_tri.begin (); tri != new_tri.end (); ++tri) { + for (int i = 0; i < 3; ++i) { + auto e = (*tri)->edge (i); + if (! e->is_segment ()) { + auto ot = e->other (*tri); + if (ot && ! ot->is_outside ()) { + next_tris.insert (ot); + ot->set_outside (true); + } + } + } + } + + new_tri.swap (next_tris); + + } + + // join edges where possible + for (auto re = resolved_edges.begin (); re != resolved_edges.end (); ++re) { + auto edges = re->second; + join_edges (edges); + } + + m_is_constrained = true; } -#if 0 -import math -import sys -from .triangle import * +void +Triangles::remove_outside_triangles () +{ + tl_assert (m_is_constrained); -class Triangles(object): + // NOTE: don't remove while iterating + std::vector to_remove; + for (auto tri = begin (); tri != end (); ++tri) { + if (tri->is_outside ()) { + to_remove.push_back (const_cast (tri.operator-> ())); + } + } - def constrain(self, contours: [[Edge]]) -> object: + for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { + remove (*t); + } +} - """ - Given a set of contours with edges, mark outer triangles +void +Triangles::clear () +{ + mp_edges.clear (); + mp_triangles.clear (); + m_vertex_heap.clear (); + m_is_constrained = false; + m_level = 0; + m_id = 0; +} - The edges must be made from existing vertexes. Edge orientation is - clockwise. - """ +void +Triangles::create_constrained_delaunay (const db::Region ®ion, double dbu) +{ + clear (); - assert(not self.is_constrained) + std::vector > edge_contours; - resolved_edges = [] + for (auto p = region.begin_merged (); ! p.at_end (); ++p) { - for c in contours: - for edge in c: - edges = self.ensure_edge(edge) - resolved_edges.append( (edge, edges) ) + edge_contours.push_back (std::vector ()); + for (auto pt = p->begin_hull (); pt != p->end_hull (); ++pt) { + edge_contours.back ().push_back (insert_point (db::DPoint (*pt) * dbu)); + } - for tri in self.triangles: - tri.is_outside = False - for s in tri.edges(): - s.is_segment = False + for (unsigned int h = 0; h < p->holes (); ++h) { + edge_contours.push_back (std::vector ()); + for (auto pt = p->begin_hole (h); pt != p->end_hole (h); ++pt) { + edge_contours.back ().push_back (insert_point (db::DPoint (*pt) * dbu)); + } + } - new_tri = set() + } - for re in resolved_edges: - edge, edges = re - for s in edges: - s.is_segment = True - outer_tri = None - d = sprod_sign(edge.d(), s.d()) - if d > 0: - outer_tri = s.left - if d < 0: - outer_tri = s.right - if outer_tri is not None: - assert (outer_tri in self.triangles) - new_tri.add(outer_tri) - outer_tri.is_outside = True + constrain (edge_contours); +} - while len(new_tri) > 0: - - next_tris = set() - - for tri in new_tri: - for s in tri.edges(): - if not s.is_segment: - ot = s.other(tri) - if ot is not None and not ot.is_outside: - next_tris.add(ot) - ot.is_outside = True - - new_tri = next_tris - - # join edges where possible - for re in resolved_edges: - _, edges = re - self._join_edges(edges) - - self.is_constrained = True - - def remove_outside_triangles(self): - - assert self.is_constrained - - for tri in self.triangles: - for s in tri.edges(): - s.level = 0 - - to_remove = [tri for tri in self.triangles if tri.is_outside] - for tri in to_remove: - for s in tri.edges(): - if not s.is_segment and s.level == 0: - s.level = 1 - s.unlink() - tri.unlink() - self.triangles.remove(tri) - - to_remove = [v for v in self.vertexes if len(v.edges) == 0] - for v in to_remove: - self.vertexes.remove(v) - - def create_constrained_delaunay(self, contours: [[Edge]]): - - edge_contours = [] - - for c in contours: - - if len(c) < 3: - continue - - edges = [] - edge_contours.append(edges) - - vl = None - vfirst = None - for pt in c: - v = self.insert(Vertex(pt.x, pt.y)) - if vfirst is None: - vfirst = v - else: - edges.append(Edge(vl, v)) - vl = v - edges.append(Edge(vl, vfirst)) - - self.constrain(edge_contours) - - -#endif +} diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h index 7e433308b..ca2241345 100644 --- a/src/db/db/dbTriangles.h +++ b/src/db/db/dbTriangles.h @@ -28,6 +28,7 @@ #include "dbCommon.h" #include "dbTriangle.h" #include "dbBox.h" +#include "dbRegion.h" #include "tlObjectCollection.h" @@ -76,6 +77,18 @@ public: */ size_t num_triangles () const { return mp_triangles.size (); } + /** + * @brief Clears the triangle set + */ + void clear (); + + /** + * @brief Creates a constrained Delaunay triangulation from the given Region + */ + void create_constrained_delaunay (const db::Region ®ion, double dbu = 1.0); + + // exposed for testing purposes: + /** * @brief Checks the triangle graph for consistency * This method is for testing purposes mainly. @@ -123,9 +136,6 @@ public: */ void remove (db::Vertex *vertex, std::vector *new_triangles = 0); - - // exposed for testing purposes: - /** * @brief Flips the given edge */ @@ -155,6 +165,21 @@ public: */ std::vector ensure_edge (db::Vertex *from, db::Vertex *to); + /** + * @brief Given a set of contours with edges, mark outer triangles + * + * The edges must be made from existing vertexes. Edge orientation is + * clockwise. + * + * This will also mark triangles as outside ones. + */ + void constrain (const std::vector > &contours); + + /** + * @brief Removes the outside triangles. + */ + void remove_outside_triangles (); + private: tl::shared_collection mp_triangles; tl::weak_collection mp_edges; diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index f177072a3..d7dd45c6d 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -291,7 +291,7 @@ TEST(Triangle_test_ensure_edge) double x = round (flt_rand () * res) * (1.0 / res); double y = round (flt_rand () * res) * (1.0 / res); bool ok = true; - for (int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { + for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { if (ee[j].side_of (db::DPoint (x, y)) == 0) { --i; ok = false; @@ -302,13 +302,13 @@ TEST(Triangle_test_ensure_edge) } } - for (int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { tris.insert_point (ee[i].p1 ()); } EXPECT_EQ (tris.check (), true); - for (int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { tris.ensure_edge (tris.find_vertex_for_point (ee[i].p1 ()), tris.find_vertex_for_point (ee[i].p2 ())); } @@ -316,7 +316,7 @@ TEST(Triangle_test_ensure_edge) double area_in = 0.0; db::DBox clip_box; - for (int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { clip_box += ee[i].p1 (); } for (auto t = tris.begin (); t != tris.end (); ++t) { @@ -329,6 +329,65 @@ TEST(Triangle_test_ensure_edge) EXPECT_EQ (tl::to_string (area_in), "0.25"); } +TEST(Triangle_test_constrain) +{ + srand (0); + + db::Triangles tris; + double res = 128.0; + + db::DEdge ee[] = { + db::DEdge (0.25, 0.25, 0.25, 0.75), + db::DEdge (0.25, 0.75, 0.75, 0.75), + db::DEdge (0.75, 0.75, 0.75, 0.25), + db::DEdge (0.75, 0.25, 0.25, 0.25) + }; + + for (unsigned int i = 0; i < 200; ++i) { + double x = round (flt_rand () * res) * (1.0 / res); + double y = round (flt_rand () * res) * (1.0 / res); + bool ok = true; + for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { + if (ee[j].side_of (db::DPoint (x, y)) == 0) { + --i; + ok = false; + } + } + if (ok) { + tris.insert_point (x, y); + } + } + + std::vector contour; + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + contour.push_back (tris.insert_point (ee[i].p1 ())); + } + std::vector > contours; + contours.push_back (contour); + + EXPECT_EQ (tris.check (), true); + + tris.constrain (contours); + EXPECT_EQ (tris.check (false), true); + + tris.remove_outside_triangles (); + + EXPECT_EQ (tris.check (), true); + + double area_in = 0.0; + db::DBox clip_box; + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + clip_box += ee[i].p1 (); + } + for (auto t = tris.begin (); t != tris.end (); ++t) { + EXPECT_EQ (clip_box.overlaps (t->bbox ()), true); + EXPECT_EQ (t->bbox ().inside (clip_box), true); + area_in += t->area (); + } + + EXPECT_EQ (tl::to_string (area_in), "0.25"); +} + #if 0 def test_heavy_constrain(self):