From 76e039bd2a04a196c78c6574d3033e91e013bfe3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 13 Apr 2025 23:06:49 +0200 Subject: [PATCH] Removing obsolete dbTriangle/dbTriangles --- src/db/db/db.pro | 4 - src/db/db/dbPLC.h | 1 - src/db/db/dbRegionProcessors.cc | 8 +- src/db/db/dbRegionProcessors.h | 4 +- src/db/db/dbTriangle.cc | 607 --------- src/db/db/dbTriangle.h | 579 -------- src/db/db/dbTriangles.cc | 1784 ------------------------- src/db/db/dbTriangles.h | 360 ----- src/db/db/gsiDeclDbPolygon.cc | 36 +- src/db/unit_tests/dbTriangleTests.cc | 549 -------- src/db/unit_tests/dbTrianglesTests.cc | 1538 --------------------- src/db/unit_tests/unit_tests.pro | 2 - 12 files changed, 26 insertions(+), 5446 deletions(-) delete mode 100644 src/db/db/dbTriangle.cc delete mode 100644 src/db/db/dbTriangle.h delete mode 100644 src/db/db/dbTriangles.cc delete mode 100644 src/db/db/dbTriangles.h delete mode 100644 src/db/unit_tests/dbTriangleTests.cc delete mode 100644 src/db/unit_tests/dbTrianglesTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 888a22dcc..93795a97d 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -107,8 +107,6 @@ SOURCES = \ dbTextWriter.cc \ dbTilingProcessor.cc \ dbTrans.cc \ - dbTriangle.cc \ - dbTriangles.cc \ dbUserObject.cc \ dbUtils.cc \ dbVector.cc \ @@ -347,8 +345,6 @@ HEADERS = \ dbTextWriter.h \ dbTilingProcessor.h \ dbTrans.h \ - dbTriangle.h \ - dbTriangles.h \ dbTypes.h \ dbUserObject.h \ dbUtils.h \ diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index 9d7b35e43..14ac8d01c 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -24,7 +24,6 @@ #define HDR_dbPLC #include "dbCommon.h" -#include "dbTriangle.h" #include "dbBox.h" #include "dbRegion.h" diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index 3c3449107..730f6ea7a 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -453,8 +453,8 @@ TriangulationProcessor::process (const db::Polygon &poly, std::vector - -namespace db -{ - -// ------------------------------------------------------------------------------------- -// Vertex implementation - -Vertex::Vertex () - : DPoint (), m_is_precious (false) -{ - // .. nothing yet .. -} - -Vertex::Vertex (const db::DPoint &p) - : DPoint (p), m_is_precious (false) -{ - // .. nothing yet .. -} - -Vertex::Vertex (const Vertex &v) - : DPoint (), m_is_precious (false) -{ - operator= (v); -} - -Vertex &Vertex::operator= (const Vertex &v) -{ - if (this != &v) { - // NOTE: edges are not copied! - db::DPoint::operator= (v); - m_is_precious = v.m_is_precious; - } - return *this; -} - -Vertex::Vertex (db::DCoord x, db::DCoord y) - : DPoint (x, y), m_is_precious (false) -{ - // .. nothing yet .. -} - -bool -Vertex::is_outside () const -{ - for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { - if ((*e)->is_outside ()) { - return true; - } - } - return false; -} - -std::vector -Vertex::triangles () const -{ - std::set seen; - std::vector res; - for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { - for (auto t = (*e)->begin_triangles (); t != (*e)->end_triangles (); ++t) { - if (seen.insert (t.operator-> ()).second) { - res.push_back (t.operator-> ()); - } - } - } - return res; -} - -bool -Vertex::has_edge (const TriangleEdge *edge) const -{ - for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { - if (*e == edge) { - return true; - } - } - return false; -} - -size_t -Vertex::num_edges (int max_count) const -{ - if (max_count < 0) { - // NOTE: this can be slow for a std::list, so we have max_count to limit this effort - return mp_edges.size (); - } else { - size_t n = 0; - for (auto i = mp_edges.begin (); i != mp_edges.end () && --max_count >= 0; ++i) { - ++n; - } - return n; - } -} - -std::string -Vertex::to_string (bool with_id) const -{ - std::string res = tl::sprintf ("(%.12g, %.12g)", x (), y()); - if (with_id) { - res += tl::sprintf ("[%x]", (size_t)this); - } - return res; -} - -int -Vertex::in_circle (const DPoint &point, const DPoint ¢er, double radius) -{ - double dx = point.x () - center.x (); - double dy = point.y () - center.y (); - double d2 = dx * dx + dy * dy; - double r2 = radius * radius; - double delta = fabs (d2 + r2) * db::epsilon; - if (d2 < r2 - delta) { - return 1; - } else if (d2 < r2 + delta) { - return 0; - } else { - return -1; - } -} - -// ------------------------------------------------------------------------------------- -// TriangleEdge implementation - -TriangleEdge::TriangleEdge () - : mp_v1 (0), mp_v2 (0), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) -{ - // .. nothing yet .. -} - -TriangleEdge::TriangleEdge (Vertex *v1, Vertex *v2) - : mp_v1 (v1), mp_v2 (v2), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) -{ - // .. nothing yet .. -} - -void -TriangleEdge::set_left (Triangle *t) -{ - mp_left = t; -} - -void -TriangleEdge::set_right (Triangle *t) -{ - mp_right = t; -} - -void -TriangleEdge::link () -{ - mp_v1->mp_edges.push_back (this); - m_ec_v1 = --mp_v1->mp_edges.end (); - - mp_v2->mp_edges.push_back (this); - m_ec_v2 = --mp_v2->mp_edges.end (); -} - -void -TriangleEdge::unlink () -{ - if (mp_v1) { - mp_v1->remove_edge (m_ec_v1); - } - if (mp_v2) { - mp_v2->remove_edge (m_ec_v2); - } - mp_v1 = mp_v2 = 0; -} - -Triangle * -TriangleEdge::other (const Triangle *t) const -{ - if (t == mp_left) { - return mp_right; - } - if (t == mp_right) { - return mp_left; - } - tl_assert (false); - return 0; -} - -Vertex * -TriangleEdge::other (const Vertex *t) const -{ - if (t == mp_v1) { - return mp_v2; - } - if (t == mp_v2) { - return mp_v1; - } - tl_assert (false); - return 0; -} - -bool -TriangleEdge::has_vertex (const Vertex *v) const -{ - return mp_v1 == v || mp_v2 == v; -} - -Vertex * -TriangleEdge::common_vertex (const TriangleEdge *other) const -{ - if (has_vertex (other->v1 ())) { - return (other->v1 ()); - } - if (has_vertex (other->v2 ())) { - return (other->v2 ()); - } - return 0; -} - -std::string -TriangleEdge::to_string (bool with_id) const -{ - std::string res = std::string ("(") + mp_v1->to_string (with_id) + ", " + mp_v2->to_string (with_id) + ")"; - if (with_id) { - res += tl::sprintf ("[%x]", (size_t)this); - } - return res; -} - -double -TriangleEdge::distance (const db::DEdge &e, const db::DPoint &p) -{ - double l = db::sprod (p - e.p1 (), e.d ()) / e.d ().sq_length (); - db::DPoint pp; - if (l <= 0.0) { - pp = e.p1 (); - } else if (l >= 1.0) { - pp = e.p2 (); - } else { - pp = e.p1 () + e.d () * l; - } - return (p - pp).length (); -} - -bool -TriangleEdge::crosses (const db::DEdge &e, const db::DEdge &other) -{ - return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) < 0 && - other.side_of (e.p1 ()) * other.side_of (e.p2 ()) < 0; -} - -bool -TriangleEdge::crosses_including (const db::DEdge &e, const db::DEdge &other) -{ - return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) <= 0 && - other.side_of (e.p1 ()) * other.side_of (e.p2 ()) <= 0; -} - -db::DPoint -TriangleEdge::intersection_point (const db::DEdge &e, const db::DEdge &other) -{ - return e.intersect_point (other).second; -} - -bool -TriangleEdge::point_on (const db::DEdge &edge, const db::DPoint &point) -{ - if (edge.side_of (point) != 0) { - return false; - } else { - return db::sprod_sign (point - edge.p1 (), edge.d ()) * db::sprod_sign(point - edge.p2 (), edge.d ()) < 0; - } -} - -bool -TriangleEdge::can_flip () const -{ - if (! left () || ! right ()) { - return false; - } - - const db::Vertex *v1 = left ()->opposite (this); - const db::Vertex *v2 = right ()->opposite (this); - return crosses (db::DEdge (*v1, *v2)); -} - -bool -TriangleEdge::can_join_via (const Vertex *vertex) const -{ - if (! left () || ! right ()) { - return false; - } - - tl_assert (has_vertex (vertex)); - const db::Vertex *v1 = left ()->opposite (this); - const db::Vertex *v2 = right ()->opposite (this); - return db::DEdge (*v1, *v2).side_of (*vertex) == 0; -} - -bool -TriangleEdge::is_outside () const -{ - return left () == 0 || right () == 0; -} - -bool -TriangleEdge::is_for_outside_triangles () const -{ - return (left () && left ()->is_outside ()) || (right () && right ()->is_outside ()); -} - -bool -TriangleEdge::has_triangle (const Triangle *t) const -{ - return t != 0 && (left () == t || right () == t); -} - -// ------------------------------------------------------------------------------------- -// Triangle implementation - -Triangle::Triangle () - : m_is_outside (false), m_id (0) -{ - for (int i = 0; i < 3; ++i) { - mp_v[i] = 0; - mp_e[i] = 0; - } -} - -Triangle::Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3) - : m_is_outside (false), m_id (0) -{ - mp_e[0] = e1; - mp_v[0] = e1->v1 (); - mp_v[1] = e1->v2 (); - - if (e2->has_vertex (mp_v[1])) { - mp_e[1] = e2; - mp_e[2] = e3; - } else { - mp_e[1] = e3; - mp_e[2] = e2; - } - mp_v[2] = mp_e[1]->other (mp_v[1]); - - // enforce clockwise orientation - int s = db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]); - if (s < 0) { - std::swap (mp_v[2], mp_v[1]); - } else if (s == 0) { - // Triangle is not orientable - tl_assert (false); - } - - // establish link to edges - for (int i = 0; i < 3; ++i) { - - TriangleEdge *e = mp_e[i]; - - unsigned int i1 = 0; - for ( ; e->v1 () != mp_v[i1] && i1 < 3; ++i1) - ; - unsigned int i2 = 0; - for ( ; e->v2 () != mp_v[i2] && i2 < 3; ++i2) - ; - - if ((i1 + 1) % 3 == i2) { - e->set_right (this); - } else { - e->set_left (this); - } - - } -} - -Triangle::~Triangle () -{ - unlink (); -} - -void -Triangle::unlink () -{ - for (int i = 0; i != 3; ++i) { - db::TriangleEdge *e = mp_e[i]; - if (e->left () == this) { - e->set_left (0); - } - if (e->right () == this) { - e->set_right (0); - } - } -} - -std::string -Triangle::to_string (bool with_id) const -{ - std::string res = "("; - for (int i = 0; i < 3; ++i) { - if (i > 0) { - res += ", "; - } - if (vertex (i)) { - res += vertex (i)->to_string (with_id); - } else { - res += "(null)"; - } - } - res += ")"; - return res; -} - -double -Triangle::area () const -{ - return fabs (db::vprod (mp_e[0]->d (), mp_e[1]->d ())) * 0.5; -} - -db::DBox -Triangle::bbox () const -{ - db::DBox box; - for (int i = 0; i < 3; ++i) { - box += *mp_v[i]; - } - return box; -} - - -std::pair -Triangle::circumcircle (bool *ok) const -{ - // see https://en.wikipedia.org/wiki/Circumcircle - // we set A=(0,0), so the formulas simplify - - if (ok) { - *ok = true; - } - - db::DVector b = *mp_v[1] - *mp_v[0]; - db::DVector c = *mp_v[2] - *mp_v[0]; - - double b2 = b.sq_length (); - double c2 = c.sq_length (); - - double sx = 0.5 * (b2 * c.y () - c2 * b.y ()); - double sy = 0.5 * (b.x () * c2 - c.x() * b2); - - double a1 = b.x() * c.y(); - double a2 = c.x() * b.y(); - double a = a1 - a2; - double a_abs = std::abs (a); - - if (a_abs < (std::abs (a1) + std::abs (a2)) * db::epsilon) { - if (ok) { - *ok = false; - return std::make_pair (db::DPoint (), 0.0); - } else { - tl_assert (false); - } - } - - double radius = sqrt (sx * sx + sy * sy) / a_abs; - db::DPoint center = *mp_v[0] + db::DVector (sx / a, sy / a); - - return std::make_pair (center, radius); -} - -Vertex * -Triangle::opposite (const TriangleEdge *edge) const -{ - for (int i = 0; i < 3; ++i) { - Vertex *v = mp_v[i]; - if (! edge->has_vertex (v)) { - return v; - } - } - tl_assert (false); -} - -TriangleEdge * -Triangle::opposite (const Vertex *vertex) const -{ - for (int i = 0; i < 3; ++i) { - TriangleEdge *e = mp_e[i]; - if (! e->has_vertex (vertex)) { - return e; - } - } - tl_assert (false); -} - -TriangleEdge * -Triangle::find_edge_with (const Vertex *v1, const Vertex *v2) const -{ - for (int i = 0; i < 3; ++i) { - TriangleEdge *e = mp_e[i]; - if (e->has_vertex (v1) && e->has_vertex (v2)) { - return e; - } - } - tl_assert (false); -} - -TriangleEdge * -Triangle::common_edge (const Triangle *other) const -{ - for (int i = 0; i < 3; ++i) { - TriangleEdge *e = mp_e[i];; - if (e->other (this) == other) { - return e; - } - } - return 0; -} - -int -Triangle::contains (const db::DPoint &point) const -{ - auto c = *mp_v[2] - *mp_v[0]; - auto b = *mp_v[1] - *mp_v[0]; - - int vps = db::vprod_sign (c, b); - if (vps == 0) { - return db::vprod_sign (point - *mp_v[0], b) == 0 && db::vprod_sign (point - *mp_v[0], c) == 0 ? 0 : -1; - } - - int res = 1; - - const Vertex *vl = mp_v[2]; - for (int i = 0; i < 3; ++i) { - const Vertex *v = mp_v[i]; - int n = db::vprod_sign (point - *vl, *v - *vl) * vps; - if (n < 0) { - return -1; - } else if (n == 0) { - res = 0; - } - vl = v; - } - - return res; -} - -double -Triangle::min_edge_length () const -{ - double lmin = mp_e[0]->d ().length (); - for (int i = 1; i < 3; ++i) { - lmin = std::min (lmin, mp_e[i]->d ().length ()); - } - return lmin; -} - -double -Triangle::b () const -{ - double lmin = min_edge_length (); - bool ok = false; - auto cr = circumcircle (&ok); - return ok ? lmin / cr.second : 0.0; -} - -bool -Triangle::has_segment () const -{ - for (int i = 0; i < 3; ++i) { - if (mp_e[i]->is_segment ()) { - return true; - } - } - return false; -} - -unsigned int -Triangle::num_segments () const -{ - unsigned int n = 0; - for (int i = 0; i < 3; ++i) { - if (mp_e[i]->is_segment ()) { - ++n; - } - } - return n; -} - -} diff --git a/src/db/db/dbTriangle.h b/src/db/db/dbTriangle.h deleted file mode 100644 index 332d1c695..000000000 --- a/src/db/db/dbTriangle.h +++ /dev/null @@ -1,579 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 Matthias Koefferlein - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - - - -#ifndef HDR_dbTriangle -#define HDR_dbTriangle - -#include "dbCommon.h" -#include "dbPoint.h" -#include "dbEdge.h" - -#include "tlObjectCollection.h" -#include "tlList.h" - -#include -#include -#include -#include - -namespace db -{ - -class Triangle; -class TriangleEdge; - -/** - * @brief A class representing a vertex in a Delaunay triangulation graph - * - * The vertex carries information about the connected edges and - * an integer value that can be used in traversal algorithms - * ("level") - */ -class DB_PUBLIC Vertex - : public db::DPoint -{ -public: - typedef std::list edges_type; - typedef edges_type::const_iterator edges_iterator; - typedef edges_type::iterator edges_iterator_non_const; - - Vertex (); - Vertex (const DPoint &p); - Vertex (const Vertex &v); - Vertex (db::DCoord x, db::DCoord y); - - Vertex &operator= (const Vertex &v); - - bool is_outside () const; - std::vector triangles () const; - - edges_iterator begin_edges () const { return mp_edges.begin (); } - edges_iterator end_edges () const { return mp_edges.end (); } - size_t num_edges (int max_count = -1) const; - - bool has_edge (const TriangleEdge *edge) const; - - void set_is_precious (bool f) { m_is_precious = f; } - bool is_precious () const { return m_is_precious; } - - std::string to_string (bool with_id = false) const; - - /** - * @brief Returns 1 is the point is inside the circle, 0 if on the circle and -1 if outside - * TODO: Move to db::DPoint - */ - static int in_circle (const db::DPoint &point, const db::DPoint ¢er, double radius); - - /** - * @brief Returns 1 is this point is inside the circle, 0 if on the circle and -1 if outside - */ - int in_circle (const db::DPoint ¢er, double radius) const - { - return in_circle (*this, center, radius); - } - -private: - friend class TriangleEdge; - - void remove_edge (const edges_iterator_non_const &ec) - { - mp_edges.erase (ec); - } - - edges_type mp_edges; - bool m_is_precious; -}; - -/** - * @brief A class representing an edge in the Delaunay triangulation graph - */ -class DB_PUBLIC TriangleEdge -{ -public: - class TriangleIterator - { - public: - typedef Triangle value_type; - typedef Triangle &reference; - typedef Triangle *pointer; - - reference operator*() const - { - return *operator-> (); - } - - pointer operator->() const - { - return m_index ? mp_edge->right () : mp_edge->left (); - } - - bool operator== (const TriangleIterator &other) const - { - return m_index == other.m_index; - } - - bool operator!= (const TriangleIterator &other) const - { - return !operator== (other); - } - - TriangleIterator &operator++ () - { - while (++m_index < 2 && operator-> () == 0) - ; - return *this; - } - - private: - friend class TriangleEdge; - - TriangleIterator (const TriangleEdge *edge) - : mp_edge (edge), m_index (0) - { - if (! edge) { - m_index = 2; - } else { - --m_index; - operator++ (); - } - } - - const TriangleEdge *mp_edge; - unsigned int m_index; - }; - - TriangleEdge (); - TriangleEdge (Vertex *v1, Vertex *v2); - - Vertex *v1 () const { return mp_v1; } - Vertex *v2 () const { return mp_v2; } - - void reverse () - { - std::swap (mp_v1, mp_v2); - std::swap (mp_left, mp_right); - } - - Triangle *left () const { return mp_left; } - Triangle *right () const { return mp_right; } - - TriangleIterator begin_triangles () const - { - return TriangleIterator (this); - } - - TriangleIterator end_triangles () const - { - return TriangleIterator (0); - } - - void set_level (size_t l) { m_level = l; } - size_t level () const { return m_level; } - - void set_id (size_t id) { m_id = id; } - size_t id () const { return m_id; } - - void set_is_segment (bool is_seg) { m_is_segment = is_seg; } - bool is_segment () const { return m_is_segment; } - - std::string to_string (bool with_id = false) const; - - /** - * @brief Converts to an db::DEdge - */ - db::DEdge edge () const - { - return db::DEdge (*mp_v1, *mp_v2); - } - - /** - * @brief Returns the distance of the given point to the edge - * - * The distance is the minimum distance of the point to one point from the edge. - * TODO: Move to db::DEdge - */ - static double distance (const db::DEdge &e, const db::DPoint &p); - - /** - * @brief Returns the distance of the given point to the edge - * - * The distance is the minimum distance of the point to one point from the edge. - */ - double distance (const db::DPoint &p) const - { - return distance (edge (), p); - } - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * - * "crosses" is true, if both edges share at least one point which is not an endpoint - * of one of the edges. - * TODO: Move to db::DEdge - */ - static bool crosses (const db::DEdge &e, const db::DEdge &other); - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * - * "crosses" is true, if both edges share at least one point which is not an endpoint - * of one of the edges. - */ - bool crosses (const db::DEdge &other) const - { - return crosses (edge (), other); - } - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * - * "crosses" is true, if both edges share at least one point which is not an endpoint - * of one of the edges. - */ - bool crosses (const db::TriangleEdge &other) const - { - return crosses (edge (), other.edge ()); - } - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * "crosses" is true, if both edges share at least one point. - * TODO: Move to db::DEdge - */ - static bool crosses_including (const db::DEdge &e, const db::DEdge &other); - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * "crosses" is true, if both edges share at least one point. - */ - bool crosses_including (const db::DEdge &other) const - { - return crosses_including (edge (), other); - } - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * "crosses" is true, if both edges share at least one point. - */ - bool crosses_including (const db::TriangleEdge &other) const - { - return crosses_including (edge (), other.edge ()); - } - - /** - * @brief Gets the intersection point - * TODO: Move to db::DEdge - */ - static db::DPoint intersection_point (const db::DEdge &e, const DEdge &other); - - /** - * @brief Gets the intersection point - */ - db::DPoint intersection_point (const db::DEdge &other) const - { - return intersection_point (edge (), other); - } - - /** - * @brief Gets the intersection point - */ - db::DPoint intersection_point (const TriangleEdge &other) const - { - return intersection_point (edge (), other.edge ()); - } - - /** - * @brief Returns a value indicating whether the point is on the edge - * TODO: Move to db::DEdge - */ - static bool point_on (const db::DEdge &edge, const db::DPoint &point); - - /** - * @brief Returns a value indicating whether the point is on the edge - */ - bool point_on (const db::DPoint &point) const - { - return point_on (edge (), point); - } - - /** - * @brief Gets the side the point is on - * - * -1 is for "left", 0 is "on" and +1 is "right" - * TODO: correct to same definition as db::Edge (negative) - */ - static int side_of (const db::DEdge &e, const db::DPoint &point) - { - return -e.side_of (point); - } - - /** - * @brief Gets the side the point is on - * - * -1 is for "left", 0 is "on" and +1 is "right" - * TODO: correct to same definition as db::Edge (negative) - */ - int side_of (const db::DPoint &p) const - { - return -edge ().side_of (p); - } - - /** - * @brief Gets the distance vector - */ - db::DVector d () const - { - return *mp_v2 - *mp_v1; - } - - /** - * @brief Gets the other triangle for the given one - */ - Triangle *other (const Triangle *) const; - - /** - * @brief Gets the other vertex for the given one - */ - Vertex *other (const Vertex *) const; - - /** - * @brief Gets a value indicating whether the edge has the given vertex - */ - bool has_vertex (const Vertex *) const; - - /** - * @brief Gets the common vertex of the other edge and this edge or null if there is no common vertex - */ - Vertex *common_vertex (const TriangleEdge *other) const; - - /** - * @brief Returns a value indicating whether this edge can be flipped - */ - bool can_flip () const; - - /** - * @brief Returns a value indicating whether the edge separates two triangles that can be joined into one (via the given vertex) - */ - bool can_join_via (const Vertex *vertex) const; - - /** - * @brief Returns a value indicating whether this edge is an outside edge (no other triangles) - */ - bool is_outside () const; - - /** - * @brief Returns a value indicating whether this edge belongs to outside triangles - */ - bool is_for_outside_triangles () const; - - /** - * @brief Returns a value indicating whether t is attached to this edge - */ - bool has_triangle (const Triangle *t) const; - -protected: - void unlink (); - void link (); - -private: - friend class Triangle; - friend class Triangles; - - Vertex *mp_v1, *mp_v2; - Triangle *mp_left, *mp_right; - Vertex::edges_iterator_non_const m_ec_v1, m_ec_v2; - size_t m_level; - size_t m_id; - bool m_is_segment; - - void set_left (Triangle *t); - void set_right (Triangle *t); -}; - -/** - * @brief A compare function that compares triangles by ID - * - * The ID acts as a more predicable unique ID for the object in sets and maps. - */ -struct TriangleEdgeLessFunc -{ - bool operator () (TriangleEdge *a, TriangleEdge *b) const - { - return a->id () < b->id (); - } -}; - -/** - * @brief A class representing a triangle - */ -class DB_PUBLIC Triangle - : public tl::list_node, public tl::Object -{ -public: - Triangle (); - Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3); - - ~Triangle (); - - void unlink (); - - void set_id (size_t id) { m_id = id; } - size_t id () const { return m_id; } - - bool is_outside () const { return m_is_outside; } - void set_outside (bool o) { m_is_outside = o; } - - std::string to_string (bool with_id = false) const; - - /** - * @brief Gets the nth vertex (n wraps around and can be negative) - * The vertexes are oriented clockwise. - */ - inline Vertex *vertex (int n) const - { - if (n >= 0 && n < 3) { - return mp_v[n]; - } else { - return mp_v[(n + 3) % 3]; - } - } - - /** - * @brief Gets the nth edge (n wraps around and can be negative) - */ - inline TriangleEdge *edge (int n) const - { - if (n >= 0 && n < 3) { - return mp_e[n]; - } else { - return mp_e[(n + 3) % 3]; - } - } - - /** - * @brief Gets the area - */ - double area () const; - - /** - * @brief Returns the bounding box of the triangle - */ - db::DBox bbox () const; - - /** - * @brief Gets the center point and radius of the circumcircle - * If ok is non-null, it will receive a boolean value indicating whether the circumcircle is valid. - * An invalid circumcircle is an indicator for a degenerated triangle with area 0 (or close to). - */ - std::pair circumcircle (bool *ok = 0) const; - - /** - * @brief Gets the vertex opposite of the given edge - */ - Vertex *opposite (const TriangleEdge *edge) const; - - /** - * @brief Gets the edge opposite of the given vertex - */ - TriangleEdge *opposite (const Vertex *vertex) const; - - /** - * @brief Gets the edge with the given vertexes - */ - TriangleEdge *find_edge_with (const Vertex *v1, const Vertex *v2) const; - - /** - * @brief Finds the common edge for both triangles - */ - TriangleEdge *common_edge (const Triangle *other) const; - - /** - * @brief Returns a value indicating whether the point is inside (1), on the triangle (0) or outside (-1) - */ - int contains (const db::DPoint &point) const; - - /** - * @brief Gets a value indicating whether the triangle has the given vertex - */ - inline bool has_vertex (const db::Vertex *v) const - { - return mp_v[0] == v || mp_v[1] == v || mp_v[2] == v; - } - - /** - * @brief Gets a value indicating whether the triangle has the given edge - */ - inline bool has_edge (const db::TriangleEdge *e) const - { - return mp_e[0] == e || mp_e[1] == e || mp_e[2] == e; - } - - /** - * @brief Returns the minimum edge length - */ - double min_edge_length () const; - - /** - * @brief Returns the min edge length to circumcircle radius ratio - */ - double b () const; - - /** - * @brief Returns a value indicating whether the triangle borders to a segment - */ - bool has_segment () const; - - /** - * @brief Returns the number of segments the triangle borders to - */ - unsigned int num_segments () const; - -private: - bool m_is_outside; - TriangleEdge *mp_e[3]; - db::Vertex *mp_v[3]; - size_t m_id; - - // no copying - Triangle &operator= (const Triangle &); - Triangle (const Triangle &); -}; - -/** - * @brief A compare function that compares triangles by ID - * - * The ID acts as a more predicable unique ID for the object in sets and maps. - */ -struct TriangleLessFunc -{ - bool operator () (Triangle *a, Triangle *b) const - { - return a->id () < b->id (); - } -}; - -} - -#endif - diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc deleted file mode 100644 index e712abaa2..000000000 --- a/src/db/db/dbTriangles.cc +++ /dev/null @@ -1,1784 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 Matthias Koefferlein - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - - -#include "dbTriangles.h" -#include "dbLayout.h" -#include "dbWriter.h" -#include "tlStream.h" -#include "tlLog.h" -#include "tlTimer.h" - -#include -#include -#include -#include - -namespace db -{ - -static inline bool is_equal (const db::DPoint &a, const db::DPoint &b) -{ - return std::abs (a.x () - b.x ()) < std::max (1.0, (std::abs (a.x ()) + std::abs (b.x ()))) * db::epsilon && - std::abs (a.y () - b.y ()) < std::max (1.0, (std::abs (a.y ()) + std::abs (b.y ()))) * db::epsilon; -} - -Triangles::Triangles () - : m_is_constrained (false), m_level (0), m_id (0), m_flips (0), m_hops (0) -{ - // .. nothing yet .. -} - -Triangles::~Triangles () -{ - clear (); -} - -db::Vertex * -Triangles::create_vertex (double x, double y) -{ - m_vertex_heap.push_back (db::Vertex (x, y)); - return &m_vertex_heap.back (); -} - -db::Vertex * -Triangles::create_vertex (const db::DPoint &pt) -{ - m_vertex_heap.push_back (pt); - return &m_vertex_heap.back (); -} - -db::TriangleEdge * -Triangles::create_edge (db::Vertex *v1, db::Vertex *v2) -{ - db::TriangleEdge *edge = 0; - - if (! m_returned_edges.empty ()) { - edge = m_returned_edges.back (); - m_returned_edges.pop_back (); - *edge = db::TriangleEdge (v1, v2); - } else { - m_edges_heap.push_back (db::TriangleEdge (v1, v2)); - edge = &m_edges_heap.back (); - } - - edge->link (); - edge->set_id (++m_id); - return edge; -} - -db::Triangle * -Triangles::create_triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3) -{ - db::Triangle *res = new db::Triangle (e1, e2, e3); - res->set_id (++m_id); - mp_triangles.push_back (res); - - return res; -} - -void -Triangles::remove_triangle (db::Triangle *tri) -{ - db::TriangleEdge *edges [3]; - for (int i = 0; i < 3; ++i) { - edges [i] = tri->edge (i); - } - - delete tri; - - // clean up edges we do no longer need - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = edges [i]; - if (e && e->left () == 0 && e->right () == 0 && e->v1 ()) { - e->unlink (); - m_returned_edges.push_back (e); - } - } -} - -void -Triangles::init_box (const db::DBox &box) -{ - double xmin = box.left (), xmax = box.right (); - double ymin = box.bottom (), ymax = box.top (); - - db::Vertex *vbl = create_vertex (xmin, ymin); - db::Vertex *vtl = create_vertex (xmin, ymax); - db::Vertex *vbr = create_vertex (xmax, ymin); - db::Vertex *vtr = create_vertex (xmax, ymax); - - db::TriangleEdge *sl = create_edge (vbl, vtl); - db::TriangleEdge *sd = create_edge (vtl, vbr); - db::TriangleEdge *sb = create_edge (vbr, vbl); - - db::TriangleEdge *sr = create_edge (vbr, vtr); - db::TriangleEdge *st = create_edge (vtr, vtl); - - create_triangle (sl, sd, sb); - create_triangle (sd, sr, st); -} - -std::string -Triangles::to_string () -{ - std::string res; - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - if (! res.empty ()) { - res += ", "; - } - res += t->to_string (); - } - return res; -} - -db::DBox -Triangles::bbox () const -{ - db::DBox box; - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - box += t->bbox (); - } - return box; -} - -bool -Triangles::check (bool check_delaunay) const -{ - bool res = true; - - if (check_delaunay) { - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - auto cp = t->circumcircle (); - auto vi = find_inside_circle (cp.first, cp.second); - if (! vi.empty ()) { - res = false; - tl::error << "(check error) triangle does not meet Delaunay criterion: " << t->to_string (); - for (auto v = vi.begin (); v != vi.end (); ++v) { - tl::error << " vertex inside circumcircle: " << (*v)->to_string (true); - } - } - } - } - - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - for (int i = 0; i < 3; ++i) { - if (! t->edge (i)->has_triangle (t.operator-> ())) { - tl::error << "(check error) edges " << t->edge (i)->to_string (true) - << " attached to triangle " << t->to_string (true) << " does not refer to this triangle"; - res = false; - } - } - } - - for (auto e = m_edges_heap.begin (); e != m_edges_heap.end (); ++e) { - - if (!e->left () && !e->right ()) { - continue; - } - - if (e->left () && e->right ()) { - if (e->left ()->is_outside () != e->right ()->is_outside () && ! e->is_segment ()) { - tl::error << "(check error) edge " << e->to_string (true) << " splits an outside and inside triangle, but is not a segment"; - res = false; - } - } - - for (auto t = e->begin_triangles (); t != e->end_triangles (); ++t) { - if (! t->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " not found in adjacent triangle " << t->to_string (true); - res = false; - } - if (! t->has_vertex (e->v1 ())) { - tl::error << "(check error) edges " << e->to_string (true) << " vertex 1 not found in adjacent triangle " << t->to_string (true); - res = false; - } - if (! t->has_vertex (e->v2 ())) { - tl::error << "(check error) edges " << e->to_string (true) << " vertex 2 not found in adjacent triangle " << t->to_string (true); - res = false; - } - db::Vertex *vopp = t->opposite (e.operator-> ()); - double sgn = (e->left () == t.operator-> ()) ? 1.0 : -1.0; - double vp = db::vprod (e->d(), *vopp - *e->v1 ()); // positive if on left side - if (vp * sgn <= 0.0) { - const char * side_str = sgn > 0.0 ? "left" : "right"; - tl::error << "(check error) external point " << vopp->to_string (true) << " not on " << side_str << " side of edge " << e->to_string (true); - res = false; - } - } - - if (! e->v1 ()->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " vertex 1 does not list this edge"; - res = false; - } - if (! e->v2 ()->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " vertex 2 does not list this edge"; - res = false; - } - - } - - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - unsigned int num_outside_edges = 0; - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if ((*e)->is_outside ()) { - ++num_outside_edges; - } - } - if (num_outside_edges > 0 && num_outside_edges != 2) { - tl::error << "(check error) vertex " << v->to_string (true) << " has " << num_outside_edges << " outside edges (can only be 2)"; - res = false; - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if ((*e)->is_outside ()) { - tl::error << " Outside edge is " << (*e)->to_string (true); - } - } - } - } - - return res; -} - -db::Layout * -Triangles::to_layout (bool decompose_by_id) const -{ - db::Layout *layout = new db::Layout (); - layout->dbu (0.001); - - auto dbu_trans = db::CplxTrans (layout->dbu ()).inverted (); - - db::Cell &top = layout->cell (layout->add_cell ("DUMP")); - unsigned int l1 = layout->insert_layer (db::LayerProperties (1, 0)); - unsigned int l2 = layout->insert_layer (db::LayerProperties (2, 0)); - unsigned int l10 = layout->insert_layer (db::LayerProperties (10, 0)); - unsigned int l20 = layout->insert_layer (db::LayerProperties (20, 0)); - unsigned int l21 = layout->insert_layer (db::LayerProperties (21, 0)); - unsigned int l22 = layout->insert_layer (db::LayerProperties (22, 0)); - - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - db::DPoint pts[3]; - for (int i = 0; i < 3; ++i) { - pts[i] = *t->vertex (i); - } - db::DPolygon poly; - poly.assign_hull (pts + 0, pts + 3); - top.shapes (t->is_outside () ? l2 : l1).insert (dbu_trans * poly); - if (decompose_by_id) { - if ((t->id () & 1) != 0) { - top.shapes (l20).insert (dbu_trans * poly); - } - if ((t->id () & 2) != 0) { - top.shapes (l21).insert (dbu_trans * poly); - } - if ((t->id () & 4) != 0) { - top.shapes (l22).insert (dbu_trans * poly); - } - } - } - - for (auto e = m_edges_heap.begin (); e != m_edges_heap.end (); ++e) { - if ((e->left () || e->right ()) && e->is_segment ()) { - top.shapes (l10).insert (dbu_trans * e->edge ()); - } - } - - return layout; -} - -void -Triangles::dump (const std::string &path, bool decompose_by_id) const -{ - std::unique_ptr ly (to_layout (decompose_by_id)); - - tl::OutputStream stream (path); - - db::SaveLayoutOptions opt; - db::Writer writer (opt); - writer.write (*ly, stream); - - tl::info << "Triangles written to " << path; -} - -std::vector -Triangles::find_points_around (db::Vertex *vertex, double radius) -{ - std::set seen; - seen.insert (vertex); - - std::vector res; - std::vector new_vertexes, next_vertexes; - new_vertexes.push_back (vertex); - - while (! new_vertexes.empty ()) { - next_vertexes.clear (); - for (auto v = new_vertexes.begin (); v != new_vertexes.end (); ++v) { - for (auto e = (*v)->begin_edges (); e != (*v)->end_edges (); ++e) { - db::Vertex *ov = (*e)->other (*v); - if (ov->in_circle (*vertex, radius) == 1 && seen.insert (ov).second) { - next_vertexes.push_back (ov); - res.push_back (ov); - } - } - } - new_vertexes.swap (next_vertexes); - } - - return res; -} - -db::Vertex * -Triangles::insert_point (const db::DPoint &point, std::list > *new_triangles) -{ - return insert (create_vertex (point), new_triangles); -} - -db::Vertex * -Triangles::insert_point (db::DCoord x, db::DCoord y, std::list > *new_triangles) -{ - return insert (create_vertex (x, y), new_triangles); -} - -db::Vertex * -Triangles::insert (db::Vertex *vertex, std::list > *new_triangles) -{ - std::vector tris = find_triangle_for_point (*vertex); - - // the new vertex is outside the domain - if (tris.empty ()) { - tl_assert (! m_is_constrained); - insert_new_vertex (vertex, new_triangles); - return vertex; - } - - // check, if the new vertex is on an edge (may be edge between triangles or edge on outside) - std::vector on_edges; - std::vector on_vertex; - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = tris.front ()->edge (i); - if (e->side_of (*vertex) == 0) { - if (is_equal (*vertex, *e->v1 ()) || is_equal (*vertex, *e->v2 ())) { - on_vertex.push_back (e); - } else { - on_edges.push_back (e); - } - } - } - - if (! on_vertex.empty ()) { - - tl_assert (on_vertex.size () == size_t (2)); - return on_vertex.front ()->common_vertex (on_vertex [1]); - - } else if (! on_edges.empty ()) { - - tl_assert (on_edges.size () == size_t (1)); - split_triangles_on_edge (vertex, on_edges.front (), new_triangles); - return vertex; - - } else if (tris.size () == size_t (1)) { - - // the new vertex is inside one triangle - split_triangle (tris.front (), vertex, new_triangles); - return vertex; - - } - - tl_assert (false); -} - -std::vector -Triangles::find_triangle_for_point (const db::DPoint &point) -{ - db::TriangleEdge *edge = find_closest_edge (point); - - std::vector res; - if (edge) { - for (auto t = edge->begin_triangles (); t != edge->end_triangles (); ++t) { - if (t->contains (point) >= 0) { - res.push_back (t.operator-> ()); - } - } - } - - return res; -} - -db::TriangleEdge * -Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool inside_only) -{ - if (!vstart) { - - if (! mp_triangles.empty ()) { - - unsigned int ls = 0; - size_t n = m_vertex_heap.size (); - size_t m = n; - - // A simple heuristics that takes a sqrt(N) sample from the - // vertexes to find a good starting point - - vstart = mp_triangles.begin ()->vertex (0); - double dmin = vstart->distance (p); - - while (ls * ls < m) { - m /= 2; - for (size_t i = m / 2; i < n; i += m) { - ++ls; - // NOTE: this assumes the heap is not too loaded with orphan vertexes - db::Vertex *v = (m_vertex_heap.begin () + i).operator-> (); - if (v->begin_edges () != v->end_edges ()) { - double d = v->distance (p); - if (d < dmin) { - vstart = v; - dmin = d; - } - } - } - } - - } else { - - return 0; - - } - - } - - db::DEdge line (*vstart, p); - - double d = -1.0; - db::TriangleEdge *edge = 0; - db::Vertex *v = vstart; - - while (v) { - - db::Vertex *vnext = 0; - - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - - if (inside_only) { - // NOTE: in inside mode we stay on the line of sight as we don't - // want to walk around outside pockets. - if (! (*e)->is_segment () && (*e)->is_for_outside_triangles ()) { - continue; - } - if (! (*e)->crosses_including (line)) { - continue; - } - } - - double ds = (*e)->distance (p); - - if (d < 0.0) { - - d = ds; - edge = *e; - vnext = edge->other (v); - - } else if (fabs (ds - d) < std::max (1.0, fabs (ds) + fabs (d)) * db::epsilon) { - - // this differentiation selects the edge which bends further towards - // the target point if both edges share a common point and that - // is the one the determines the distance. - db::Vertex *cv = edge->common_vertex (*e); - if (cv) { - db::DVector edge_d = *edge->other (cv) - *cv; - db::DVector e_d = *(*e)->other(cv) - *cv; - db::DVector r = p - *cv; - double edge_sp = db::sprod (r, edge_d) / edge_d.length (); - double s_sp = db::sprod (r, e_d) / e_d.length (); - if (s_sp > edge_sp + db::epsilon) { - edge = *e; - vnext = edge->other (v); - } - } - - } else if (ds < d) { - - d = ds; - edge = *e; - vnext = edge->other (v); - - } - - } - - ++m_hops; - - v = vnext; - - } - - return edge; -} - -void -Triangles::insert_new_vertex (db::Vertex *vertex, std::list > *new_triangles_out) -{ - if (mp_triangles.empty ()) { - - tl_assert (m_vertex_heap.size () <= size_t (3)); // fails if vertexes were created but not inserted. - - if (m_vertex_heap.size () == 3) { - - std::vector vv; - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - vv.push_back (v.operator-> ()); - } - - // form the first triangle - db::TriangleEdge *s1 = create_edge (vv[0], vv[1]); - db::TriangleEdge *s2 = create_edge (vv[1], vv[2]); - db::TriangleEdge *s3 = create_edge (vv[2], vv[0]); - - if (db::vprod_sign (s1->d (), s2->d ()) == 0) { - // avoid degenerate Triangles to happen here - tl_assert (false); - } else { - db::Triangle *t = create_triangle (s1, s2, s3); - if (new_triangles_out) { - new_triangles_out->push_back (t); - } - } - - } - - return; - - } - - std::vector new_triangles; - - // Find closest edge - db::TriangleEdge *closest_edge = find_closest_edge (*vertex); - tl_assert (closest_edge != 0); - - db::TriangleEdge *s1 = create_edge (vertex, closest_edge->v1 ()); - db::TriangleEdge *s2 = create_edge (vertex, closest_edge->v2 ()); - - db::Triangle *t = create_triangle (s1, closest_edge, s2); - new_triangles.push_back (t); - - add_more_triangles (new_triangles, closest_edge, closest_edge->v1 (), vertex, s1); - add_more_triangles (new_triangles, closest_edge, closest_edge->v2 (), vertex, s2); - - if (new_triangles_out) { - new_triangles_out->insert (new_triangles_out->end (), new_triangles.begin (), new_triangles.end ()); - } - - fix_triangles (new_triangles, std::vector (), new_triangles_out); -} - -void -Triangles::add_more_triangles (std::vector &new_triangles, - db::TriangleEdge *incoming_edge, - db::Vertex *from_vertex, db::Vertex *to_vertex, - db::TriangleEdge *conn_edge) -{ - while (true) { - - db::TriangleEdge *next_edge = 0; - - for (auto e = from_vertex->begin_edges (); e != from_vertex->end_edges (); ++e) { - if (! (*e)->has_vertex (to_vertex) && (*e)->is_outside ()) { - // TODO: remove and break - tl_assert (next_edge == 0); - next_edge = *e; - } - } - - tl_assert (next_edge != 0); - db::Vertex *next_vertex = next_edge->other (from_vertex); - - db::DVector d_from_to = *to_vertex - *from_vertex; - db::Vertex *incoming_vertex = incoming_edge->other (from_vertex); - if (db::vprod_sign(*from_vertex - *incoming_vertex, d_from_to) * db::vprod_sign(*from_vertex - *next_vertex, d_from_to) >= 0) { - return; - } - - db::TriangleEdge *next_conn_edge = create_edge (next_vertex, to_vertex); - db::Triangle *t = create_triangle (next_conn_edge, next_edge, conn_edge); - new_triangles.push_back (t); - - incoming_edge = next_edge; - conn_edge = next_conn_edge; - from_vertex = next_vertex; - - } -} - -void -Triangles::split_triangle (db::Triangle *t, db::Vertex *vertex, std::list > *new_triangles_out) -{ - t->unlink (); - - std::map v2new_edges; - std::vector new_edges; - for (int i = 0; i < 3; ++i) { - db::Vertex *v = t->vertex (i); - db::TriangleEdge *e = create_edge (v, vertex); - v2new_edges[v] = e; - new_edges.push_back (e); - } - - std::vector new_triangles; - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = t->edge (i); - db::Triangle *new_triangle = create_triangle (e, v2new_edges[e->v1 ()], v2new_edges[e->v2 ()]); - if (new_triangles_out) { - new_triangles_out->push_back (new_triangle); - } - new_triangle->set_outside (t->is_outside ()); - new_triangles.push_back (new_triangle); - } - - remove_triangle (t); - - fix_triangles (new_triangles, new_edges, new_triangles_out); -} - -void -Triangles::split_triangles_on_edge (db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out) -{ - TriangleEdge *s1 = create_edge (split_edge->v1 (), vertex); - TriangleEdge *s2 = create_edge (split_edge->v2 (), vertex); - s1->set_is_segment (split_edge->is_segment ()); - s2->set_is_segment (split_edge->is_segment ()); - - std::vector new_triangles; - - std::vector tris; - tris.reserve (2); - for (auto t = split_edge->begin_triangles (); t != split_edge->end_triangles (); ++t) { - tris.push_back (t.operator-> ()); - } - - for (auto t = tris.begin (); t != tris.end (); ++t) { - - (*t)->unlink (); - - db::Vertex *ext_vertex = (*t)->opposite (split_edge); - TriangleEdge *new_edge = create_edge (ext_vertex, vertex); - - for (int i = 0; i < 3; ++i) { - - db::TriangleEdge *e = (*t)->edge (i); - if (e->has_vertex (ext_vertex)) { - - TriangleEdge *partial = e->has_vertex (split_edge->v1 ()) ? s1 : s2; - db::Triangle *new_triangle = create_triangle (new_edge, partial, e); - - if (new_triangles_out) { - new_triangles_out->push_back (new_triangle); - } - new_triangle->set_outside ((*t)->is_outside ()); - new_triangles.push_back (new_triangle); - - } - - } - - } - - for (auto t = tris.begin (); t != tris.end (); ++t) { - remove_triangle (*t); - } - - std::vector fixed_edges; - fixed_edges.push_back (s1); - fixed_edges.push_back (s2); - fix_triangles (new_triangles, fixed_edges, new_triangles_out); -} - -std::vector -Triangles::find_touching (const db::DBox &box) const -{ - // NOTE: this is a naive, slow implementation for test purposes - std::vector res; - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - if (v->begin_edges () != v->end_edges ()) { - if (box.contains (*v)) { - res.push_back (const_cast (v.operator-> ())); - } - } - } - return res; -} - -std::vector -Triangles::find_inside_circle (const db::DPoint ¢er, double radius) const -{ - // NOTE: this is a naive, slow implementation for test purposes - std::vector res; - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - if (v->begin_edges () != v->end_edges ()) { - if (v->in_circle (center, radius) == 1) { - res.push_back (const_cast (v.operator-> ())); - } - } - } - return res; -} - -void -Triangles::remove (db::Vertex *vertex, std::list > *new_triangles) -{ - if (vertex->begin_edges () == vertex->end_edges ()) { - // removing an orphan vertex -> ignore - } else if (vertex->is_outside ()) { - remove_outside_vertex (vertex, new_triangles); - } else { - remove_inside_vertex (vertex, new_triangles); - } -} - -void -Triangles::remove_outside_vertex (db::Vertex *vertex, std::list > *new_triangles_out) -{ - auto to_remove = vertex->triangles (); - - std::vector outer_edges; - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - outer_edges.push_back ((*t)->opposite (vertex)); - } - - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - (*t)->unlink (); - } - - auto new_triangles = fill_concave_corners (outer_edges); - - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - remove_triangle (*t); - } - - fix_triangles (new_triangles, std::vector (), new_triangles_out); -} - -void -Triangles::remove_inside_vertex (db::Vertex *vertex, std::list > *new_triangles_out) -{ - std::set triangles_to_fix; - - bool make_new_triangle = true; - - while (vertex->num_edges (4) > 3) { - - db::TriangleEdge *to_flip = 0; - for (auto e = vertex->begin_edges (); e != vertex->end_edges () && to_flip == 0; ++e) { - if ((*e)->can_flip ()) { - to_flip = *e; - } - } - if (! to_flip) { - break; - } - - // NOTE: in the "can_join" case zero-area triangles are created which we will sort out later - triangles_to_fix.erase (to_flip->left ()); - triangles_to_fix.erase (to_flip->right ()); - - auto pp = flip (to_flip); - triangles_to_fix.insert (pp.first.first); - triangles_to_fix.insert (pp.first.second); - - } - - if (vertex->num_edges (4) > 3) { - - tl_assert (vertex->num_edges (5) == 4); - - // This case can happen if two edges attached to the vertex are collinear - // in this case choose the "join" strategy - db::TriangleEdge *jseg = 0; - for (auto e = vertex->begin_edges (); e != vertex->end_edges () && !jseg; ++e) { - if ((*e)->can_join_via (vertex)) { - jseg = *e; - } - } - tl_assert (jseg != 0); - - db::Vertex *v1 = jseg->left ()->opposite (jseg); - db::TriangleEdge *s1 = jseg->left ()->opposite (vertex); - db::Vertex *v2 = jseg->right ()->opposite (jseg); - db::TriangleEdge *s2 = jseg->right ()->opposite (vertex); - - db::TriangleEdge *jseg_opp = 0; - for (auto e = vertex->begin_edges (); e != vertex->end_edges () && !jseg_opp; ++e) { - if (!(*e)->has_triangle (jseg->left ()) && !(*e)->has_triangle (jseg->right ())) { - jseg_opp = *e; - } - } - - db::TriangleEdge *s1opp = jseg_opp->left ()->opposite (vertex); - db::TriangleEdge *s2opp = jseg_opp->right ()->opposite (vertex); - - db::TriangleEdge *new_edge = create_edge (v1, v2); - db::Triangle *t1 = create_triangle (s1, s2, new_edge); - db::Triangle *t2 = create_triangle (s1opp, s2opp, new_edge); - - triangles_to_fix.insert (t1); - triangles_to_fix.insert (t2); - - make_new_triangle = false; - - } - - auto to_remove = vertex->triangles (); - - std::vector outer_edges; - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - outer_edges.push_back ((*t)->opposite (vertex)); - } - - if (make_new_triangle) { - - tl_assert (outer_edges.size () == size_t (3)); - - db::Triangle *nt = create_triangle (outer_edges[0], outer_edges[1], outer_edges[2]); - triangles_to_fix.insert (nt); - - } - - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - triangles_to_fix.erase (*t); - remove_triangle (*t); - } - - if (new_triangles_out) { - for (auto t = triangles_to_fix.begin (); t != triangles_to_fix.end (); ++t) { - new_triangles_out->push_back (*t); - } - } - - std::vector to_fix_a (triangles_to_fix.begin (), triangles_to_fix.end ()); - fix_triangles (to_fix_a, std::vector (), new_triangles_out); -} - -void -Triangles::fix_triangles (const std::vector &tris, const std::vector &fixed_edges, std::list > *new_triangles) -{ - m_level += 1; - for (auto e = fixed_edges.begin (); e != fixed_edges.end (); ++e) { - (*e)->set_level (m_level); - } - - std::set queue, todo; - - for (auto t = tris.begin (); t != tris.end (); ++t) { - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = (*t)->edge (i); - if (e->level () < m_level && ! e->is_segment ()) { - queue.insert (e); - } - } - } - - while (! queue.empty ()) { - - todo.clear (); - todo.swap (queue); - - // NOTE: we cannot be sure that already treated edges will not become - // illegal by neighbor edges flipping .. - // for s in todo: - // s.level = self.level - - for (auto e = todo.begin (); e != todo.end (); ++e) { - - if (is_illegal_edge (*e)) { - - queue.erase (*e); - - auto pp = flip (*e); - auto t1 = pp.first.first; - auto t2 = pp.first.second; - auto s12 = pp.second; - - if (new_triangles) { - new_triangles->push_back (t1); - new_triangles->push_back (t2); - } - - ++m_flips; - tl_assert (! is_illegal_edge (s12)); // TODO: remove later! - - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *s1 = t1->edge (i); - if (s1->level () < m_level && ! s1->is_segment ()) { - queue.insert (s1); - } - } - - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *s2 = t2->edge (i); - if (s2->level () < m_level && ! s2->is_segment ()) { - queue.insert (s2); - } - } - - } - - } - - } -} - -bool -Triangles::is_illegal_edge (db::TriangleEdge *edge) -{ - db::Triangle *left = edge->left (); - db::Triangle *right = edge->right (); - if (!left || !right) { - return false; - } - - bool ok = false; - - auto lr = left->circumcircle (&ok); - if (! ok || right->opposite (edge)->in_circle (lr.first, lr.second) > 0) { - return true; - } - - auto rr = right->circumcircle(&ok); - if (! ok || left->opposite (edge)->in_circle (rr.first, rr.second) > 0) { - return true; - } - - return false; -} - -std::pair, TriangleEdge *> -Triangles::flip (TriangleEdge *edge) -{ - db::Triangle *t1 = edge->left (); - db::Triangle *t2 = edge->right (); - - bool outside = t1->is_outside (); - tl_assert (t1->is_outside () == outside); - - // prepare for the new triangle to replace this one - t1->unlink (); - t2->unlink (); - - db::Vertex *t1_vext = t1->opposite (edge); - db::TriangleEdge *t1_sext1 = t1->find_edge_with (t1_vext, edge->v1 ()); - db::TriangleEdge *t1_sext2 = t1->find_edge_with (t1_vext, edge->v2 ()); - - db::Vertex *t2_vext = t2->opposite (edge); - db::TriangleEdge *t2_sext1 = t2->find_edge_with (t2_vext, edge->v1 ()); - db::TriangleEdge *t2_sext2 = t2->find_edge_with (t2_vext, edge->v2 ()); - - db::TriangleEdge *s_new = create_edge (t1_vext, t2_vext); - - db::Triangle *t1_new = create_triangle (s_new, t1_sext1, t2_sext1); - t1_new->set_outside (outside); - db::Triangle *t2_new = create_triangle (s_new, t1_sext2, t2_sext2); - t2_new->set_outside (outside); - - remove_triangle (t1); - remove_triangle (t2); - - return std::make_pair (std::make_pair (t1_new, t2_new), s_new); -} - -std::vector -Triangles::fill_concave_corners (const std::vector &edges) -{ - std::vector res; - std::vector points, terminals; - - std::map > vertex2edge; - for (auto e = edges.begin (); e != edges.end (); ++e) { - - auto i = vertex2edge.insert (std::make_pair ((*e)->v1 (), std::vector ())); - if (i.second) { - points.push_back ((*e)->v1 ()); - } - i.first->second.push_back (*e); - - i = vertex2edge.insert (std::make_pair ((*e)->v2 (), std::vector ())); - if (i.second) { - points.push_back ((*e)->v2 ()); - } - i.first->second.push_back (*e); - - } - - while (points.size () > size_t (2)) { - - terminals.clear (); - for (auto p = points.begin (); p != points.end (); ++p) { - if (vertex2edge [*p].size () == 1) { - terminals.push_back (*p); - } - } - tl_assert (terminals.size () == size_t (2)); - db::Vertex *v = terminals[0]; - - bool any_connected = false; - db::Vertex *vp = 0; - - std::set to_remove; - - while (vertex2edge [v].size () >= size_t (2) || ! vp) { - - db::TriangleEdge *seg = 0; - std::vector &ee = vertex2edge [v]; - for (auto e = ee.begin (); e != ee.end (); ++e) { - if (! (*e)->has_vertex (vp)) { - seg = (*e); - break; - } - } - - tl_assert (seg != 0); - db::Triangle *tri = seg->left () ? seg->left () : seg->right (); - db::Vertex *vn = seg->other (v); - - std::vector &een = vertex2edge [vn]; - if (een.size () < size_t (2)) { - break; - } - tl_assert (een.size () == size_t (2)); - - db::TriangleEdge *segn = 0; - for (auto e = een.begin (); e != een.end (); ++e) { - if (! (*e)->has_vertex (v)) { - segn = (*e); - break; - } - } - - tl_assert (segn != 0); - db::Vertex *vnn = segn->other (vn); - std::vector &eenn = vertex2edge [vnn]; - - // NOTE: tri can be None in case a lonely edge stays after removing - // attached triangles - if (! tri || seg->side_of (*vnn) * seg->side_of (*tri->opposite (seg)) < 0) { - - // is concave - db::TriangleEdge *new_edge = create_edge (v, vnn); - for (auto e = ee.begin (); e != ee.end (); ++e) { - if (*e == seg) { - ee.erase (e); - break; - } - } - ee.push_back (new_edge); - - for (auto e = eenn.begin (); e != eenn.end (); ++e) { - if (*e == segn) { - eenn.erase (e); - break; - } - } - eenn.push_back (new_edge); - - vertex2edge.erase (vn); - to_remove.insert (vn); - - db::Triangle *new_triangle = create_triangle (seg, segn, new_edge); - res.push_back (new_triangle); - any_connected = true; - - } else { - - vp = v; - v = vn; - - } - - } - - if (! any_connected) { - break; - } - - std::vector::iterator wp = points.begin (); - for (auto p = points.begin (); p != points.end (); ++p) { - if (to_remove.find (*p) == to_remove.end ()) { - *wp++ = *p; - } - } - points.erase (wp, points.end ()); - - } - - return res; -} - -std::vector -Triangles::search_edges_crossing (Vertex *from, Vertex *to) -{ - db::Vertex *v = from; - db::Vertex *vv = to; - db::DEdge edge (*from, *to); - - db::Triangle *current_triangle = 0; - db::TriangleEdge *next_edge = 0; - - std::vector result; - - for (auto e = v->begin_edges (); e != v->end_edges () && ! next_edge; ++e) { - for (auto t = (*e)->begin_triangles (); t != (*e)->end_triangles (); ++t) { - db::TriangleEdge *os = t->opposite (v); - if (os->has_vertex (vv)) { - return result; - } - if (os->crosses (edge)) { - result.push_back (os); - current_triangle = t.operator-> (); - next_edge = os; - break; - } - } - } - - tl_assert (current_triangle != 0); - - while (true) { - - current_triangle = next_edge->other (current_triangle); - - // Note that we're convex, so there has to be a path across triangles - tl_assert (current_triangle != 0); - - db::TriangleEdge *cs = next_edge; - next_edge = 0; - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = current_triangle->edge (i); - if (e != cs) { - if (e->has_vertex (vv)) { - return result; - } - if (e->crosses (edge)) { - result.push_back (e); - next_edge = e; - break; - } - } - } - - tl_assert (next_edge != 0); - - } -} - -db::Vertex * -Triangles::find_vertex_for_point (const db::DPoint &point) -{ - db::TriangleEdge *edge = find_closest_edge (point); - if (!edge) { - return 0; - } - db::Vertex *v = 0; - if (is_equal (*edge->v1 (), point)) { - v = edge->v1 (); - } else if (is_equal (*edge->v2 (), point)) { - v = edge->v2 (); - } - return v; -} - -db::TriangleEdge * -Triangles::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) -{ - db::Vertex *v = find_vertex_for_point (p1); - if (!v) { - return 0; - } - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if (is_equal (*(*e)->other (v), p2)) { - return *e; - } - } - return 0; -} - -std::vector -Triangles::ensure_edge_inner (db::Vertex *from, db::Vertex *to) -{ - auto crossed_edges = search_edges_crossing (from, to); - std::vector result; - - if (crossed_edges.empty ()) { - - // no crossing edge - there should be a edge already - db::TriangleEdge *res = find_edge_for_points (*from, *to); - tl_assert (res != 0); - result.push_back (res); - - } else if (crossed_edges.size () == 1) { - - // can be solved by flipping - auto pp = flip (crossed_edges.front ()); - db::TriangleEdge *res = pp.second; - tl_assert (res->has_vertex (from) && res->has_vertex (to)); - result.push_back (res); - - } else { - - // split edge close to center - db::DPoint split_point; - double d = -1.0; - double l_half = 0.25 * (*to - *from).sq_length (); - for (auto e = crossed_edges.begin (); e != crossed_edges.end (); ++e) { - db::DPoint p = (*e)->intersection_point (db::DEdge (*from, *to)); - double dp = fabs ((p - *from).sq_length () - l_half); - if (d < 0.0 || dp < d) { - dp = d; - split_point = p; - } - } - - db::Vertex *split_vertex = insert_point (split_point); - - result = ensure_edge_inner (from, split_vertex); - - auto result2 = ensure_edge_inner (split_vertex, to); - result.insert (result.end (), result2.begin (), result2.end ()); - - } - - return result; -} - -std::vector -Triangles::ensure_edge (db::Vertex *from, db::Vertex *to) -{ -#if 0 - // NOTE: this should not be required if the original segments are non-overlapping - // TODO: this is inefficient - for v in self.vertexes: - if edge.point_on(v): - return self.ensure_edge(Edge(edge.p1, v)) + self.ensure_edge(Edge(v, edge.p2)) -#endif - - auto edges = ensure_edge_inner (from, to); - for (auto e = edges.begin (); e != edges.end (); ++e) { - // mark the edges as fixed "forever" so we don't modify them when we ensure other edges - (*e)->set_level (std::numeric_limits::max ()); - } - return edges; -} - -void -Triangles::join_edges (std::vector &edges) -{ - // edges are supposed to be ordered - for (size_t i = 1; i < edges.size (); ) { - - db::TriangleEdge *s1 = edges [i - 1]; - db::TriangleEdge *s2 = edges [i]; - tl_assert (s1->is_segment () == s2->is_segment ()); - db::Vertex *cp = s1->common_vertex (s2); - tl_assert (cp != 0); - - std::vector join_edges; - for (auto e = cp->begin_edges (); e != cp->end_edges (); ++e) { - if (*e != s1 && *e != s2) { - if ((*e)->can_join_via (cp)) { - join_edges.push_back (*e); - } else { - join_edges.clear (); - break; - } - } - } - - if (! join_edges.empty ()) { - - tl_assert (join_edges.size () <= 2); - - TriangleEdge *new_edge = create_edge (s1->other (cp), s2->other (cp)); - new_edge->set_is_segment (s1->is_segment ()); - - for (auto js = join_edges.begin (); js != join_edges.end (); ++js) { - - db::Triangle *t1 = (*js)->left (); - db::Triangle *t2 = (*js)->right (); - db::TriangleEdge *tedge1 = t1->opposite (cp); - db::TriangleEdge *tedge2 = t2->opposite (cp); - t1->unlink (); - t2->unlink (); - db::Triangle *tri = create_triangle (tedge1, tedge2, new_edge); - tri->set_outside (t1->is_outside ()); - remove_triangle (t1); - remove_triangle (t2); - } - - edges [i - 1] = new_edge; - edges.erase (edges.begin () + i); - - } else { - ++i; - } - - } -} - -void -Triangles::constrain (const std::vector > &contours) -{ - tl_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 (); - } - db::DEdge e (**v, **vv); - resolved_edges.push_back (std::make_pair (e, 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; -} - -void -Triangles::remove_outside_triangles () -{ - tl_assert (m_is_constrained); - - // 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-> ())); - } - } - - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - remove_triangle (*t); - } -} - -void -Triangles::clear () -{ - mp_triangles.clear (); - m_edges_heap.clear (); - m_vertex_heap.clear (); - m_returned_edges.clear (); - m_is_constrained = false; - m_level = 0; - m_id = 0; -} - -template -void -Triangles::make_contours (const Poly &poly, const Trans &trans, std::vector > &edge_contours) -{ - edge_contours.push_back (std::vector ()); - for (auto pt = poly.begin_hull (); pt != poly.end_hull (); ++pt) { - edge_contours.back ().push_back (insert_point (trans * *pt)); - } - - for (unsigned int h = 0; h < poly.holes (); ++h) { - edge_contours.push_back (std::vector ()); - for (auto pt = poly.begin_hole (h); pt != poly.end_hole (h); ++pt) { - edge_contours.back ().push_back (insert_point (trans * *pt)); - } - } -} - -void -Triangles::create_constrained_delaunay (const db::Region ®ion, const CplxTrans &trans) -{ - clear (); - - std::vector > edge_contours; - - for (auto p = region.begin_merged (); ! p.at_end (); ++p) { - make_contours (*p, trans, edge_contours); - } - - constrain (edge_contours); -} - -void -Triangles::create_constrained_delaunay (const db::Polygon &p, const std::vector &vertexes, const CplxTrans &trans) -{ - clear (); - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v)->set_is_precious (true); - } - - std::vector > edge_contours; - make_contours (p, trans, edge_contours); - - constrain (edge_contours); -} - -void -Triangles::create_constrained_delaunay (const db::DPolygon &p, const std::vector &vertexes, const DCplxTrans &trans) -{ - clear (); - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v)->set_is_precious (true); - } - - std::vector > edge_contours; - make_contours (p, trans, edge_contours); - - constrain (edge_contours); -} - -static bool is_skinny (const db::Triangle *tri, const Triangles::TriangulateParameters ¶m) -{ - if (param.min_b < db::epsilon) { - return false; - } else { - double b = tri->b (); - double delta = (b + param.min_b) * db::epsilon; - return b < param.min_b - delta; - } -} - -static bool is_invalid (const db::Triangle *tri, const Triangles::TriangulateParameters ¶m) -{ - if (is_skinny (tri, param)) { - return true; - } - - double amax = param.max_area; - if (param.max_area_border > db::epsilon) { - if (tri->has_segment ()) { - amax = param.max_area_border; - } - } - - if (amax > db::epsilon) { - double a = tri->area (); - double delta = (a + amax) * db::epsilon; - return tri->area () > amax + delta; - } else { - return false; - } -} - -void -Triangles::triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, double dbu) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (region, db::CplxTrans (dbu)); - refine (parameters); -} - -void -Triangles::triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, const db::CplxTrans &trans) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (region, trans); - refine (parameters); -} - -void -Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu) -{ - triangulate (poly, std::vector (), parameters, dbu); -} - -void -Triangles::triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, double dbu) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (poly, vertexes, db::CplxTrans (dbu)); - refine (parameters); -} - -void -Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, const db::CplxTrans &trans) -{ - triangulate (poly, std::vector (), parameters, trans); -} - -void -Triangles::triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::CplxTrans &trans) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (poly, vertexes, trans); - refine (parameters); -} - -void -Triangles::triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const DCplxTrans &trans) -{ - triangulate (poly, std::vector (), parameters, trans); -} - -void -Triangles::triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const DCplxTrans &trans) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (poly, vertexes, trans); - refine (parameters); -} - -void -Triangles::refine (const TriangulateParameters ¶meters) -{ - if (parameters.min_b < db::epsilon && parameters.max_area < db::epsilon && parameters.max_area_border < db::epsilon) { - - // no refinement requested - we're done. - remove_outside_triangles (); - return; - - } - - unsigned int nloop = 0; - std::list > new_triangles; - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - new_triangles.push_back (t.operator-> ()); - } - - // TODO: break if iteration gets stuck - while (nloop < parameters.max_iterations) { - - ++nloop; - if (tl::verbosity () >= parameters.base_verbosity + 10) { - tl::info << "Iteration " << nloop << " .."; - } - - std::list > to_consider; - for (auto t = new_triangles.begin (); t != new_triangles.end (); ++t) { - if (t->get () && ! (*t)->is_outside () && is_invalid (t->get (), parameters)) { - to_consider.push_back (*t); - } - } - - if (to_consider.empty ()) { - break; - } - - if (tl::verbosity () >= parameters.base_verbosity + 10) { - tl::info << to_consider.size() << " triangles to consider"; - } - - new_triangles.clear (); - - for (auto t = to_consider.begin (); t != to_consider.end (); ++t) { - - if (! t->get ()) { - // triangle got removed during loop - continue; - } - - auto cr = (*t)->circumcircle(); - auto center = cr.first; - - int s = (*t)->contains (center); - if (s >= 0) { - - if (s > 0) { - - double snap = 1e-3; - - // Snap the center to a segment center if "close" to it. - // This avoids generating very skinny triangles that can't be fixed as the - // segment cannot be flipped. This a part of the issue #1996 problem. - for (unsigned int i = 0; i < 3; ++i) { - if ((*t)->edge (i)->is_segment ()) { - auto e = (*t)->edge (i)->edge (); - auto c = e.p1 () + e.d () * 0.5; - if (c.distance (center) < e.length () * 0.5 * snap - db::epsilon) { - center = c; - break; - } - } - } - - } - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << "Inserting in-triangle center " << center.to_string () << " of " << (*t)->to_string (true); - } - insert_point (center, &new_triangles); - - } else { - - db::Vertex *vstart = 0; - for (unsigned int i = 0; i < 3; ++i) { - db::TriangleEdge *edge = (*t)->edge (i); - vstart = (*t)->opposite (edge); - if (edge->side_of (*vstart) * edge->side_of (center) < 0) { - break; - } - } - - db::TriangleEdge *edge = find_closest_edge (center, vstart, true /*inside only*/); - tl_assert (edge != 0); - - if (! edge->is_segment () || edge->side_of (*vstart) * edge->side_of (center) >= 0) { - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << "Inserting out-of-triangle center " << center << " of " << (*t)->to_string (true); - } - insert_point (center, &new_triangles); - - } else { - - double sr = edge->d ().length () * 0.5; - if (sr >= parameters.min_length) { - - db::DPoint pnew = *edge->v1 () + edge->d () * 0.5; - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << "Split edge " << edge->to_string (true) << " at " << pnew.to_string (); - } - db::Vertex *vnew = insert_point (pnew, &new_triangles); - auto vertexes_in_diametral_circle = find_points_around (vnew, sr); - - std::vector to_delete; - for (auto v = vertexes_in_diametral_circle.begin (); v != vertexes_in_diametral_circle.end (); ++v) { - bool has_segment = false; - for (auto e = (*v)->begin_edges (); e != (*v)->end_edges () && ! has_segment; ++e) { - has_segment = (*e)->is_segment (); - } - if (! has_segment && ! (*v)->is_precious ()) { - to_delete.push_back (*v); - } - } - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << " -> found " << to_delete.size () << " vertexes to remove"; - } - for (auto v = to_delete.begin (); v != to_delete.end (); ++v) { - remove (*v, &new_triangles); - } - - } - - } - - } - - } - - } - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << "Finishing .."; - } - - if (parameters.mark_triangles) { - - for (auto t = begin (); t != end (); ++t) { - - size_t id = 0; - - if (! t->is_outside ()) { - - if (is_skinny (t.operator-> (), parameters)) { - id |= 1; - } - if (is_invalid (t.operator-> (), parameters)) { - id |= 2; - } - auto cp = t->circumcircle (); - auto vi = find_inside_circle (cp.first, cp.second); - if (! vi.empty ()) { - id |= 4; - } - - } - - (const_cast (t.operator->()))->set_id (id); - - } - - } - - remove_outside_triangles (); -} - -} diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h deleted file mode 100644 index 3bb34c87d..000000000 --- a/src/db/db/dbTriangles.h +++ /dev/null @@ -1,360 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 Matthias Koefferlein - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#ifndef HDR_dbTriangles -#define HDR_dbTriangles - -#include "dbCommon.h" -#include "dbTriangle.h" -#include "dbBox.h" -#include "dbRegion.h" - -#include "tlObjectCollection.h" -#include "tlStableVector.h" - -#include -#include -#include -#include - -namespace db -{ - -class Layout; - -class DB_PUBLIC Triangles -{ -public: - struct TriangulateParameters - { - TriangulateParameters () - : min_b (1.0), - min_length (0.0), - max_area (0.0), - max_area_border (0.0), - max_iterations (std::numeric_limits::max ()), - base_verbosity (30), - mark_triangles (false) - { } - - /** - * @brief Min. readius-to-shortest edge ratio - */ - double min_b; - - /** - * @brief Min. edge length - * - * This parameter does not provide a guarantee about a minimume edge length, but - * helps avoiding ever-reducing triangle splits in acute corners of the input polygon. - * Splitting of edges stops when the edge is less than the min length. - */ - double min_length; - - /** - * @brief Max area or zero for "no constraint" - */ - double max_area; - - /** - * @brief Max area for border triangles or zero for "use max_area" - */ - double max_area_border; - - /** - * @brief Max number of iterations - */ - size_t max_iterations; - - /** - * @brief The verbosity level above which triangulation reports details - */ - int base_verbosity; - - /** - * @brief If true, final triangles are marked using the "id" integer as a bit field - * - * This provides information about the result quality. - * - * Bit 0: skinny triangle - * Bit 1: bad-quality (skinny or area too large) - * Bit 2: non-Delaunay (in the strict sense) - */ - bool mark_triangles; - }; - - typedef tl::list triangles_type; - typedef triangles_type::const_iterator triangle_iterator; - - Triangles (); - ~Triangles (); - - /** - * @brief Initializes the triangle collection with a box - * Two triangles will be created. - */ - void init_box (const db::DBox &box); - - /** - * @brief Returns a string representation of the triangle graph. - */ - std::string to_string (); - - /** - * @brief Returns the bounding box of the triangle graph. - */ - db::DBox bbox () const; - - /** - * @brief Iterates the triangles in the graph (begin iterator) - */ - triangle_iterator begin () const { return mp_triangles.begin (); } - - /** - * @brief Iterates the triangles in the graph (end iterator) - */ - triangle_iterator end () const { return mp_triangles.end (); } - - /** - * @brief Returns the number of triangles in the graph - */ - size_t num_triangles () const { return mp_triangles.size (); } - - /** - * @brief Clears the triangle set - */ - void clear (); - - /** - * @brief Creates a refined Delaunay triangulation for the given region - * - * The database unit should be chosen in a way that target area values are "in the order of 1". - * For inputs featuring acute angles (angles < ~25 degree), the parameters should defined a min - * edge length ("min_length"). - * "min_length" should be at least 1e-4. If a min edge length is given, the max area constaints - * may not be satisfied. - * - * Edges in the input should not be shorter than 1e-4. - */ - void triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, double dbu = 1.0); - - // more versions - void triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); - void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu = 1.0); - void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, double dbu = 1.0); - void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); - void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); - - /** - * @brief Triangulates a floating-point polygon - */ - void triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); - void triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); - - /** - * @brief Inserts a new vertex as the given point - * - * If "new_triangles" is not null, it will receive the list of new triangles created during - * the remove step. - * - * This method can be called after "triangulate" to add new points and adjust the triangulation. - * Inserting new points will maintain the (constrained) Delaunay condition. - */ - db::Vertex *insert_point (const db::DPoint &point, std::list > *new_triangles = 0); - - /** - * @brief Statistics: number of flips (fixing) - */ - size_t flips () const - { - return m_flips; - } - - /** - * @brief Statistics: number of hops (searching) - */ - size_t hops () const - { - return m_hops; - } - -protected: - /** - * @brief Checks the triangle graph for consistency - * This method is for testing purposes mainly. - */ - bool check (bool check_delaunay = true) const; - - /** - * @brief Dumps the triangle graph to a GDS file at the given path - * This method is for testing purposes mainly. - * - * "decompose_id" will map triangles to layer 20, 21 and 22. - * according to bit 0, 1 and 2 of the ID (useful with the 'mark_triangles' - * flat in TriangulateParameters). - */ - void dump (const std::string &path, bool decompose_by_id = false) const; - - /** - * @brief Creates a new layout object representing the triangle graph - * This method is for testing purposes mainly. - */ - db::Layout *to_layout (bool decompose_by_id = false) const; - - /** - * @brief Finds the points within (not "on") a circle of radius "radius" around the given vertex. - */ - std::vector find_points_around (Vertex *vertex, double radius); - - /** - * @brief Inserts a new vertex as the given point - * - * If "new_triangles" is not null, it will receive the list of new triangles created during - * the remove step. - */ - db::Vertex *insert_point (db::DCoord x, db::DCoord y, std::list > *new_triangles = 0); - - /** - * @brief Removes the given vertex - * - * If "new_triangles" is not null, it will receive the list of new triangles created during - * the remove step. - */ - void remove (db::Vertex *vertex, std::list > *new_triangles = 0); - - /** - * @brief Flips the given edge - */ - std::pair, db::TriangleEdge *> flip (TriangleEdge *edge); - - /** - * @brief Finds all edges that cross the given one for a convex triangulation - * - * Requirements: - * * self must be a convex triangulation - * * edge must not contain another vertex from the triangulation except p1 and p2 - */ - std::vector search_edges_crossing (db::Vertex *from, db::Vertex *to); - - /** - * @brief Finds the edge for two given points - */ - db::TriangleEdge *find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2); - - /** - * @brief Finds the vertex for a point - */ - db::Vertex *find_vertex_for_point (const db::DPoint &pt); - - /** - * @brief Ensures all points between from an to are connected by edges and makes these segments - */ - 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 (); - - /** - * @brief Creates a constrained Delaunay triangulation from the given Region - */ - void create_constrained_delaunay (const db::Region ®ion, const db::CplxTrans &trans = db::CplxTrans ()); - - /** - * @brief Creates a constrained Delaunay triangulation from the given Polygon - */ - void create_constrained_delaunay (const db::Polygon &poly, const std::vector &vertexes, const db::CplxTrans &trans = db::CplxTrans ()); - - /** - * @brief Creates a constrained Delaunay triangulation from the given DPolygon - */ - void create_constrained_delaunay (const db::DPolygon &poly, const std::vector &vertexes, const DCplxTrans &trans); - - /** - * @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion) - */ - static bool is_illegal_edge (db::TriangleEdge *edge); - - // NOTE: these functions are SLOW and intended to test purposes only - std::vector find_touching (const db::DBox &box) const; - std::vector find_inside_circle (const db::DPoint ¢er, double radius) const; - -private: - struct TriangleBoxConvert - { - typedef db::DBox box_type; - box_type operator() (db::Triangle *t) const - { - return t ? t->bbox () : box_type (); - } - }; - - tl::list mp_triangles; - tl::stable_vector m_edges_heap; - std::vector m_returned_edges; - tl::stable_vector m_vertex_heap; - bool m_is_constrained; - size_t m_level; - size_t m_id; - size_t m_flips, m_hops; - - db::Vertex *create_vertex (double x, double y); - db::Vertex *create_vertex (const db::DPoint &pt); - db::TriangleEdge *create_edge (db::Vertex *v1, db::Vertex *v2); - db::Triangle *create_triangle (db::TriangleEdge *e1, db::TriangleEdge *e2, db::TriangleEdge *e3); - void remove_triangle (db::Triangle *tri); - - void remove_outside_vertex (db::Vertex *vertex, std::list > *new_triangles = 0); - void remove_inside_vertex (db::Vertex *vertex, std::list > *new_triangles_out = 0); - std::vector fill_concave_corners (const std::vector &edges); - void fix_triangles (const std::vector &tris, const std::vector &fixed_edges, std::list > *new_triangles); - std::vector find_triangle_for_point (const db::DPoint &point); - db::TriangleEdge *find_closest_edge (const db::DPoint &p, db::Vertex *vstart = 0, bool inside_only = false); - db::Vertex *insert (db::Vertex *vertex, std::list > *new_triangles = 0); - void split_triangle (db::Triangle *t, db::Vertex *vertex, std::list > *new_triangles_out); - void split_triangles_on_edge (db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out); - void add_more_triangles (std::vector &new_triangles, - db::TriangleEdge *incoming_edge, - db::Vertex *from_vertex, db::Vertex *to_vertex, - db::TriangleEdge *conn_edge); - void insert_new_vertex(db::Vertex *vertex, std::list > *new_triangles_out); - std::vector ensure_edge_inner (db::Vertex *from, db::Vertex *to); - void join_edges (std::vector &edges); - void refine (const TriangulateParameters ¶m); - template void make_contours (const Poly &poly, const Trans &trans, std::vector > &contours); -}; - -} - -#endif - diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 8282198ab..1a4fa5335 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -28,13 +28,13 @@ #include "dbPolygonTools.h" #include "dbPolygonGenerators.h" #include "dbHash.h" -#include "dbTriangles.h" +#include "dbPLCTriangulation.h" namespace gsi { template -static db::Region region_from_triangles (const db::Triangles &tri, const T &trans) +static db::Region region_from_triangles (const db::plc::Graph &tri, const T &trans) { db::Region result; @@ -53,10 +53,10 @@ static db::Region region_from_triangles (const db::Triangles &tri, const T &tran } template -static std::vector

polygons_from_triangles (const db::Triangles &tri, const T &trans) +static std::vector

polygons_from_triangles (const db::plc::Graph &tri, const T &trans) { std::vector

result; - result.reserve (tri.num_triangles ()); + result.reserve (tri.num_polygons ()); typename P::point_type pts [3]; @@ -89,14 +89,15 @@ static db::polygon to_polygon (const db::polygon &p) template static db::Region triangulate_ipolygon (const P *p, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001) { - db::Triangles tris; - db::Triangles::TriangulateParameters param; + db::plc::Graph tris; + db::plc::Triangulation triangulation (&tris); + db::plc::TriangulationParameters param; param.min_b = min_b; param.max_area = max_area * dbu * dbu; db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ())); - tris.triangulate (to_polygon (*p), param, trans); + triangulation.triangulate (to_polygon (*p), param, trans); return region_from_triangles (tris, trans.inverted ()); } @@ -104,14 +105,15 @@ static db::Region triangulate_ipolygon (const P *p, double max_area = 0.0, doubl template static db::Region triangulate_ipolygon_v (const P *p, const std::vector &vertexes, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001) { - db::Triangles tris; - db::Triangles::TriangulateParameters param; + db::plc::Graph tris; + db::plc::Triangulation triangulation (&tris); + db::plc::TriangulationParameters param; param.min_b = min_b; param.max_area = max_area * dbu * dbu; db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ())); - tris.triangulate (to_polygon (*p), vertexes, param, trans); + triangulation.triangulate (to_polygon (*p), vertexes, param, trans); return region_from_triangles (tris, trans.inverted ()); } @@ -119,14 +121,15 @@ static db::Region triangulate_ipolygon_v (const P *p, const std::vector static std::vector

triangulate_dpolygon (const P *p, double max_area = 0.0, double min_b = 0.0) { - db::Triangles tris; - db::Triangles::TriangulateParameters param; + db::plc::Graph tris; + db::plc::Triangulation triangulation (&tris); + db::plc::TriangulationParameters param; param.min_b = min_b; param.max_area = max_area; db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ())); - tris.triangulate (to_polygon (*p), param, trans); + triangulation.triangulate (to_polygon (*p), param, trans); return polygons_from_triangles (tris, trans.inverted ()); } @@ -134,14 +137,15 @@ static std::vector

triangulate_dpolygon (const P *p, double max_area = 0.0, d template static std::vector

triangulate_dpolygon_v (const P *p, const std::vector &vertexes, double max_area = 0.0, double min_b = 0.0) { - db::Triangles tris; - db::Triangles::TriangulateParameters param; + db::plc::Graph tris; + db::plc::Triangulation triangulation (&tris); + db::plc::TriangulationParameters param; param.min_b = min_b; param.max_area = max_area; db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ())); - tris.triangulate (to_polygon (*p), vertexes, param, trans); + triangulation.triangulate (to_polygon (*p), vertexes, param, trans); return polygons_from_triangles (tris, trans.inverted ()); } diff --git a/src/db/unit_tests/dbTriangleTests.cc b/src/db/unit_tests/dbTriangleTests.cc deleted file mode 100644 index b460f96ec..000000000 --- a/src/db/unit_tests/dbTriangleTests.cc +++ /dev/null @@ -1,549 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 Matthias Koefferlein - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - - -#include "dbTriangle.h" -#include "tlUnitTest.h" - -#include -#include - -class TestableEdge - : public db::TriangleEdge -{ -public: - using db::TriangleEdge::TriangleEdge; - using db::TriangleEdge::link; - using db::TriangleEdge::unlink; - - TestableEdge (db::Vertex *v1, db::Vertex *v2) - : db::TriangleEdge (v1, v2) - { } -}; - -// Tests for Vertex class - -TEST(Vertex_basic) -{ - db::Vertex v; - - v.set_x (1.5); - v.set_y (0.5); - EXPECT_EQ (v.to_string (), "(1.5, 0.5)"); - EXPECT_EQ (v.x (), 1.5); - EXPECT_EQ (v.y (), 0.5); - - v = db::Vertex (db::DPoint (2, 3)); - EXPECT_EQ (v.to_string (), "(2, 3)"); -} - -static std::string edges_from_vertex (const db::Vertex &v) -{ - std::string res; - for (auto e = v.begin_edges (); e != v.end_edges (); ++e) { - if (! res.empty ()) { - res += ", "; - } - res += (*e)->to_string (); - } - return res; -} - -static std::string triangles_from_vertex (const db::Vertex &v) -{ - auto tri = v.triangles (); - std::string res; - for (auto t = tri.begin (); t != tri.end (); ++t) { - if (! res.empty ()) { - res += ", "; - } - res += (*t)->to_string (); - } - return res; -} - -TEST(Vertex_edge_registration) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - std::unique_ptr e1 (new TestableEdge (&v1, &v2)); - e1->link (); - EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v3), ""); - - std::unique_ptr e2 (new TestableEdge (&v2, &v3)); - e2->link (); - EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2)), ((1, 2), (2, 1))"); - EXPECT_EQ (edges_from_vertex (v3), "((1, 2), (2, 1))"); - - e2->unlink (); - e2.reset (0); - EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v3), ""); - - e1->unlink (); - e1.reset (0); - EXPECT_EQ (edges_from_vertex (v1), ""); - EXPECT_EQ (edges_from_vertex (v2), ""); - EXPECT_EQ (edges_from_vertex (v3), ""); -} - -TEST(Vertex_triangles) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - db::Vertex v4 (-1, 2); - EXPECT_EQ (triangles_from_vertex (v1), ""); - - std::unique_ptr e1 (new TestableEdge (&v1, &v2)); - e1->link (); - std::unique_ptr e2 (new TestableEdge (&v2, &v3)); - e2->link (); - std::unique_ptr e3 (new TestableEdge (&v3, &v1)); - e3->link (); - - std::unique_ptr tri (new db::Triangle (e1.get (), e2.get (), e3.get ())); - EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v2), "((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v3), "((0, 0), (1, 2), (2, 1))"); - - std::unique_ptr e4 (new TestableEdge (&v1, &v4)); - e4->link (); - std::unique_ptr e5 (new TestableEdge (&v2, &v4)); - e5->link (); - std::unique_ptr tri2 (new db::Triangle (e1.get (), e4.get (), e5.get ())); - EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (-1, 2), (1, 2)), ((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v2), "((0, 0), (-1, 2), (1, 2)), ((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v3), "((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v4), "((0, 0), (-1, 2), (1, 2))"); - - tri->unlink (); - EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (-1, 2), (1, 2))"); - - tri2->unlink (); - EXPECT_EQ (triangles_from_vertex (v1), ""); -} - -// Tests for Triangle class - -TEST(Triangle_basic) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - EXPECT_EQ (s1.v1 () == &v1, true); - EXPECT_EQ (s2.v2 () == &v3, true); - - db::Triangle tri (&s1, &s2, &s3); - EXPECT_EQ (tri.to_string (), "((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (tri.edge (-1) == &s3, true); - EXPECT_EQ (tri.edge (0) == &s1, true); - EXPECT_EQ (tri.edge (1) == &s2, true); - EXPECT_EQ (tri.edge (3) == &s1, true); - - // ordering - TestableEdge s11 (&v1, &v2); - TestableEdge s12 (&v3, &v2); - TestableEdge s13 (&v1, &v3); - - db::Triangle tri2 (&s11, &s12, &s13); - EXPECT_EQ (tri2.to_string (), "((0, 0), (1, 2), (2, 1))"); - - // triangle registration - EXPECT_EQ (s11.right () == &tri2, true); - EXPECT_EQ (s11.left () == 0, true); - EXPECT_EQ (s12.left () == &tri2, true); - EXPECT_EQ (s12.right () == 0, true); - EXPECT_EQ (s13.left () == &tri2, true); - EXPECT_EQ (s13.right () == 0, true); - - EXPECT_EQ (s13.to_string (), "((0, 0), (2, 1))"); - s13.reverse (); - EXPECT_EQ (s13.to_string (), "((2, 1), (0, 0))"); - EXPECT_EQ (s13.right () == &tri2, true); - EXPECT_EQ (s13.left () == 0, true); - - // flags - EXPECT_EQ (tri.is_outside (), false); - tri.set_outside (true); - EXPECT_EQ (tri.is_outside (), true); -} - -TEST(Triangle_find_segment_with) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - EXPECT_EQ (tri.find_edge_with (&v1, &v2)->to_string (), "((0, 0), (1, 2))"); - EXPECT_EQ (tri.find_edge_with (&v2, &v1)->to_string (), "((0, 0), (1, 2))"); -} - -TEST(Triangle_ext_vertex) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - EXPECT_EQ (tri.opposite (&s1)->to_string (), "(2, 1)"); - EXPECT_EQ (tri.opposite (&s3)->to_string (), "(1, 2)"); -} - -TEST(Triangle_opposite_vertex) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - EXPECT_EQ (tri.opposite (&s1)->to_string (), "(2, 1)"); - EXPECT_EQ (tri.opposite (&s3)->to_string (), "(1, 2)"); -} - -TEST(Triangle_opposite_edge) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - EXPECT_EQ (tri.opposite (&v1)->to_string (), "((1, 2), (2, 1))"); - EXPECT_EQ (tri.opposite (&v3)->to_string (), "((0, 0), (1, 2))"); -} - -TEST(Triangle_contains) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - { - db::Triangle tri (&s1, &s2, &s3); - EXPECT_EQ (tri.contains (db::DPoint (0, 0)), 0); - EXPECT_EQ (tri.contains (db::DPoint (-1, -2)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.5, 1)), 0); - EXPECT_EQ (tri.contains (db::DPoint (0.5, 2)), -1); - EXPECT_EQ (tri.contains (db::DPoint (2.5, 1)), -1); - EXPECT_EQ (tri.contains (db::DPoint (1, -1)), -1); - EXPECT_EQ (tri.contains (db::DPoint (1, 1)), 1); - } - - s1.reverse (); - s2.reverse (); - s3.reverse (); - - { - db::Triangle tri2 (&s3, &s2, &s1); - EXPECT_EQ (tri2.contains(db::DPoint(0, 0)), 0); - EXPECT_EQ (tri2.contains(db::DPoint(0.5, 1)), 0); - EXPECT_EQ (tri2.contains(db::DPoint(0.5, 2)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(2.5, 1)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(1, -1)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(1, 1)), 1); - } -} - -TEST(Triangle_contains_small) -{ - db::Vertex v1; - db::Vertex v2 (0.001, 0.002); - db::Vertex v3 (0.002, 0.001); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - { - db::Triangle tri (&s1, &s2, &s3); - EXPECT_EQ (tri.contains (db::DPoint (0, 0)), 0); - EXPECT_EQ (tri.contains (db::DPoint (-0.001, -0.002)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.0005, 0.001)), 0); - EXPECT_EQ (tri.contains (db::DPoint (0.0005, 0.002)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.0025, 0.001)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.001, -0.001)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.001, 0.001)), 1); - } - - s1.reverse (); - s2.reverse (); - s3.reverse (); - - { - db::Triangle tri2 (&s3, &s2, &s1); - EXPECT_EQ (tri2.contains(db::DPoint(0, 0)), 0); - EXPECT_EQ (tri2.contains(db::DPoint(0.0005, 0.001)), 0); - EXPECT_EQ (tri2.contains(db::DPoint(0.0005, 0.002)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(0.0025, 0.001)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(0.001, -0.001)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(0.001, 0.001)), 1); - } -} - -TEST(Triangle_circumcircle) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - auto cc = tri.circumcircle (); - auto center = cc.first; - auto radius = cc.second; - - EXPECT_EQ (tl::to_string (center), "0.833333333333,0.833333333333"); - EXPECT_EQ (tl::to_string (radius), "1.17851130198"); - - EXPECT_EQ (db::Vertex::in_circle (center, center, radius), 1); - EXPECT_EQ (db::Vertex::in_circle (db::DPoint (-1, -1), center, radius), -1); - EXPECT_EQ (v1.in_circle (center, radius), 0); - EXPECT_EQ (v2.in_circle (center, radius), 0); - EXPECT_EQ (v3.in_circle (center, radius), 0); -} - -// Tests for TriangleEdge class - -TEST(TriangleEdge_basic) -{ - db::Vertex v1; - db::Vertex v2 (1, 0.5); - - TestableEdge edge (&v1, &v2); - EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); - - EXPECT_EQ (edge.is_segment (), false); - edge.set_is_segment (true); - EXPECT_EQ (edge.is_segment (), true); - - EXPECT_EQ (edge.level (), size_t (0)); - edge.set_level (42); - EXPECT_EQ (edge.level (), size_t (42)); - - EXPECT_EQ (edge.other (&v1) == &v2, true); - EXPECT_EQ (edge.other (&v2) == &v1, true); -} - -TEST(TriangleEdge_triangles) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - db::Vertex v4 (-1, 2); - - std::unique_ptr e1 (new TestableEdge (&v1, &v2)); - std::unique_ptr e2 (new TestableEdge (&v2, &v3)); - std::unique_ptr e3 (new TestableEdge (&v3, &v1)); - - std::unique_ptr tri (new db::Triangle (e1.get (), e2.get (), e3.get ())); - - std::unique_ptr e4 (new TestableEdge (&v1, &v4)); - std::unique_ptr e5 (new TestableEdge (&v2, &v4)); - std::unique_ptr tri2 (new db::Triangle (e1.get (), e4.get (), e5.get ())); - - EXPECT_EQ (e1->is_outside (), false); - EXPECT_EQ (e2->is_outside (), true); - EXPECT_EQ (e4->is_outside (), true); - - EXPECT_EQ (e1->is_for_outside_triangles (), false); - tri->set_outside (true); - EXPECT_EQ (e1->is_for_outside_triangles (), true); - - EXPECT_EQ (e1->has_triangle (tri.get ()), true); - EXPECT_EQ (e1->has_triangle (tri2.get ()), true); - EXPECT_EQ (e4->has_triangle (tri.get ()), false); - EXPECT_EQ (e4->has_triangle (tri2.get ()), true); - - EXPECT_EQ (e1->other (tri.get ()) == tri2.get (), true); - EXPECT_EQ (e1->other (tri2.get ()) == tri.get (), true); - - EXPECT_EQ (e1->common_vertex (e2.get ()) == &v2, true); - EXPECT_EQ (e2->common_vertex (e4.get ()) == 0, true); - - tri->unlink (); - EXPECT_EQ (e1->has_triangle (tri.get ()), false); - EXPECT_EQ (e1->has_triangle (tri2.get ()), true); -} - -TEST(TriangleEdge_side_of) -{ - db::Vertex v1; - db::Vertex v2 (1, 0.5); - - TestableEdge edge (&v1, &v2); - EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); - - EXPECT_EQ (edge.side_of (db::Vertex (0, 0)), 0) - EXPECT_EQ (edge.side_of (db::Vertex (0.5, 0.25)), 0) - EXPECT_EQ (edge.side_of (db::Vertex (0, 1)), -1) - EXPECT_EQ (edge.side_of (db::Vertex (0, -1)), 1) - EXPECT_EQ (edge.side_of (db::Vertex (0.5, 0.5)), -1) - EXPECT_EQ (edge.side_of (db::Vertex (0.5, 0)), 1) - - db::Vertex v3 (1, 0); - db::Vertex v4 (0, 1); - TestableEdge edge2 (&v3, &v4); - - EXPECT_EQ (edge2.side_of (db::Vertex(0.2, 0.2)), -1); -} - -namespace { - class VertexHeap - { - public: - db::Vertex *make_vertex (double x, double y) - { - m_heap.push_back (db::Vertex (x, y)); - return &m_heap.back (); - } - private: - std::list m_heap; - }; -} - -TEST(TriangleEdge_crosses) -{ - VertexHeap heap; - - TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), false); // only cuts - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(1, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(2, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(-0.1, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.6), heap.make_vertex(0, 0.6))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 1), heap.make_vertex(1, 1))), false); - - EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); - EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), true); // only cuts - EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); -} - -TEST(TriangleEdge_point_on) -{ - VertexHeap heap; - - TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); - EXPECT_EQ (s1.point_on (db::DPoint (0, 0)), false); // endpoints are not "on" - EXPECT_EQ (s1.point_on (db::DPoint (0, -0.5)), false); - EXPECT_EQ (s1.point_on (db::DPoint (0.5, 0)), false); - EXPECT_EQ (s1.point_on (db::DPoint (0.5, 0.25)), true); - EXPECT_EQ (s1.point_on (db::DPoint (1, 0.5)), false); // endpoints are not "on" - EXPECT_EQ (s1.point_on (db::DPoint (1, 1)), false); - EXPECT_EQ (s1.point_on (db::DPoint (2, 1)), false); -} - -TEST(TriangleEdge_intersection_point) -{ - VertexHeap heap; - - TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); - EXPECT_EQ (s1.intersection_point (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex (2, 0.25))).to_string (), "0.5,0.25"); -} - -TEST(TriangleEdge_can_flip) -{ - db::Vertex v1 (2, -1); - db::Vertex v2 (0, 0); - db::Vertex v3 (1, 0); - db::Vertex v4 (0.5, 1); - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v1, &v3); - TestableEdge s3 (&v2, &v3); - TestableEdge s4 (&v2, &v4); - TestableEdge s5 (&v3, &v4); - db::Triangle t1 (&s1, &s2, &s3); - db::Triangle t2 (&s3, &s4, &s5); - EXPECT_EQ (s3.left () == &t2, true); - EXPECT_EQ (s3.right () == &t1, true); - EXPECT_EQ (s3.can_flip(), false); - v1.set_x (0.5); - EXPECT_EQ (s3.can_flip(), true); - v1.set_x (-0.25); - EXPECT_EQ (s3.can_flip(), true); - v1.set_x (-0.5); - EXPECT_EQ (s3.can_flip(), false); - v1.set_x (-1.0); - EXPECT_EQ (s3.can_flip(), false); -} - -TEST(TriangleEdge_distance) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1, 0); - - TestableEdge seg (&v1, &v2); - EXPECT_EQ (seg.distance (db::DPoint (0, 0)), 0); - EXPECT_EQ (seg.distance (db::DPoint (0, 1)), 1); - EXPECT_EQ (seg.distance (db::DPoint (1, 2)), 2); - EXPECT_EQ (seg.distance (db::DPoint (1, -1)), 1); - EXPECT_EQ (seg.distance (db::DPoint (2, 0)), 1); - EXPECT_EQ (seg.distance (db::DPoint (-2, 0)), 2); - seg.reverse (); - EXPECT_EQ (seg.distance (db::DPoint (0, 0)), 0); - EXPECT_EQ (seg.distance (db::DPoint (0, 1)), 1); - EXPECT_EQ (seg.distance (db::DPoint (1, 2)), 2); - EXPECT_EQ (seg.distance (db::DPoint (1, -1)), 1); - EXPECT_EQ (seg.distance (db::DPoint (2, 0)), 1); - EXPECT_EQ (seg.distance (db::DPoint (-2, 0)), 2); -} diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc deleted file mode 100644 index cd5779f2d..000000000 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ /dev/null @@ -1,1538 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 Matthias Koefferlein - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - - -#include "dbTriangles.h" -#include "dbWriter.h" -#include "dbRegionProcessors.h" -#include "tlUnitTest.h" -#include "tlStream.h" -#include "tlFileUtils.h" - -#include -#include -#include -#include - -class TestableTriangles - : public db::Triangles -{ -public: - using db::Triangles::Triangles; - using db::Triangles::check; - using db::Triangles::dump; - using db::Triangles::flip; - using db::Triangles::insert_point; - using db::Triangles::search_edges_crossing; - using db::Triangles::find_edge_for_points; - using db::Triangles::find_points_around; - using db::Triangles::find_inside_circle; - using db::Triangles::create_constrained_delaunay; - using db::Triangles::is_illegal_edge; - using db::Triangles::find_vertex_for_point; - using db::Triangles::remove; - using db::Triangles::ensure_edge; - using db::Triangles::constrain; - using db::Triangles::remove_outside_triangles; -}; - -TEST(basic) -{ - TestableTriangles tris; - tris.init_box (db::DBox (1, 0, 5, 4)); - - EXPECT_EQ (tris.bbox ().to_string (), "(1,0;5,4)"); - EXPECT_EQ (tris.to_string (), "((1, 0), (1, 4), (5, 0)), ((1, 4), (5, 4), (5, 0))"); - - EXPECT_EQ (tris.check (), true); - - tris.clear (); - - EXPECT_EQ (tris.bbox ().to_string (), "()"); - EXPECT_EQ (tris.to_string (), ""); - - EXPECT_EQ (tris.check (), true); -} - -TEST(flip) -{ - TestableTriangles tris; - tris.init_box (db::DBox (0, 0, 1, 1)); - EXPECT_EQ (tris.to_string (), "((0, 0), (0, 1), (1, 0)), ((0, 1), (1, 1), (1, 0))"); - - EXPECT_EQ (tris.num_triangles (), size_t (2)); - - const db::Triangle &t1 = *tris.begin (); - db::TriangleEdge *diag_segment; - for (int i = 0; i < 3; ++i) { - diag_segment = t1.edge (i); - if (diag_segment->side_of (db::DPoint (0.5, 0.5)) == 0) { - break; - } - } - tris.flip (diag_segment); - EXPECT_EQ (tris.to_string (), "((1, 1), (0, 0), (0, 1)), ((1, 1), (1, 0), (0, 0))"); - EXPECT_EQ (tris.check (), true); -} - -TEST(insert) -{ - TestableTriangles tris; - tris.init_box (db::DBox (0, 0, 1, 1)); - - tris.insert_point (0.2, 0.2); - EXPECT_EQ (tris.to_string (), "((0, 0), (0, 1), (0.2, 0.2)), ((1, 0), (0, 0), (0.2, 0.2)), ((1, 1), (0.2, 0.2), (0, 1)), ((1, 1), (1, 0), (0.2, 0.2))"); - EXPECT_EQ (tris.check (), true); -} - -TEST(split_segment) -{ - TestableTriangles tris; - tris.init_box (db::DBox (0, 0, 1, 1)); - - tris.insert_point (0.5, 0.5); - EXPECT_EQ (tris.to_string (), "((1, 1), (1, 0), (0.5, 0.5)), ((1, 1), (0.5, 0.5), (0, 1)), ((0, 0), (0, 1), (0.5, 0.5)), ((0, 0), (0.5, 0.5), (1, 0))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(insert_vertex_twice) -{ - TestableTriangles tris; - tris.init_box (db::DBox (0, 0, 1, 1)); - - tris.insert_point (0.5, 0.5); - // inserted a vertex twice does not change anything - tris.insert_point (0.5, 0.5); - EXPECT_EQ (tris.to_string (), "((1, 1), (1, 0), (0.5, 0.5)), ((1, 1), (0.5, 0.5), (0, 1)), ((0, 0), (0, 1), (0.5, 0.5)), ((0, 0), (0.5, 0.5), (1, 0))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(insert_vertex_convex) -{ - TestableTriangles tris; - tris.insert_point (0.2, 0.2); - tris.insert_point (0.2, 0.8); - tris.insert_point (0.6, 0.5); - tris.insert_point (0.7, 0.5); - tris.insert_point (0.6, 0.4); - EXPECT_EQ (tris.to_string (), "((0.2, 0.2), (0.2, 0.8), (0.6, 0.5)), ((0.2, 0.8), (0.7, 0.5), (0.6, 0.5)), ((0.6, 0.4), (0.6, 0.5), (0.7, 0.5)), ((0.6, 0.4), (0.2, 0.2), (0.6, 0.5))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(insert_vertex_convex2) -{ - TestableTriangles tris; - tris.insert_point (0.25, 0.1); - tris.insert_point (0.1, 0.4); - tris.insert_point (0.4, 0.15); - tris.insert_point (1, 0.7); - EXPECT_EQ (tris.to_string (), "((0.25, 0.1), (0.1, 0.4), (0.4, 0.15)), ((1, 0.7), (0.4, 0.15), (0.1, 0.4))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(insert_vertex_convex3) -{ - TestableTriangles tris; - tris.insert_point (0.25, 0.5); - tris.insert_point (0.25, 0.55); - tris.insert_point (0.15, 0.8); - tris.insert_point (1, 0.4); - EXPECT_EQ (tris.to_string (), "((0.25, 0.5), (0.15, 0.8), (0.25, 0.55)), ((1, 0.4), (0.25, 0.5), (0.25, 0.55)), ((0.15, 0.8), (1, 0.4), (0.25, 0.55))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(search_edges_crossing) -{ - TestableTriangles tris; - db::Vertex *v1 = tris.insert_point (0.2, 0.2); - db::Vertex *v2 = tris.insert_point (0.2, 0.8); - db::Vertex *v3 = tris.insert_point (0.6, 0.5); - /*db::Vertex *v4 =*/ tris.insert_point (0.7, 0.5); - db::Vertex *v5 = tris.insert_point (0.6, 0.4); - db::Vertex *v6 = tris.insert_point (0.7, 0.2); - EXPECT_EQ (tris.check(), true); - - auto xedges = tris.search_edges_crossing (v2, v6); - - EXPECT_EQ (xedges.size (), size_t (2)); - auto s1 = tris.find_edge_for_points (*v1, *v3); - auto s2 = tris.find_edge_for_points (*v1, *v5); - EXPECT_EQ (std::find (xedges.begin (), xedges.end (), s1) != xedges.end (), true); - EXPECT_EQ (std::find (xedges.begin (), xedges.end (), s2) != xedges.end (), true); -} - -TEST(illegal_edge1) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1.6, 1.6); - db::Vertex v3 (1, 2); - db::Vertex v4 (2, 1); - - { - db::TriangleEdge e1 (&v1, &v3); - db::TriangleEdge e2 (&v3, &v4); - db::TriangleEdge e3 (&v4, &v1); - - db::Triangle t1 (&e1, &e2, &e3); - - db::TriangleEdge ee1 (&v2, &v3); - db::TriangleEdge ee2 (&v4, &v2); - - db::Triangle t2 (&ee1, &e2, &ee2); - - EXPECT_EQ (TestableTriangles::is_illegal_edge (&e2), true); - } - - { - // flipped - db::TriangleEdge e1 (&v1, &v2); - db::TriangleEdge e2 (&v2, &v3); - db::TriangleEdge e3 (&v3, &v1); - - db::Triangle t1 (&e1, &e2, &e3); - - db::TriangleEdge ee1 (&v1, &v4); - db::TriangleEdge ee2 (&v4, &v2); - - db::Triangle t2 (&ee1, &ee2, &e1); - - EXPECT_EQ (TestableTriangles::is_illegal_edge (&e2), false); - } -} - -TEST(illegal_edge2) -{ - // numerical border case - db::Vertex v1 (773.94756216690905, 114.45875269431208); - db::Vertex v2 (773.29574734131643, 113.47402096138073); - db::Vertex v3 (773.10652961562653, 114.25497975904504); - db::Vertex v4 (774.08856345337881, 113.60495072750861); - - { - db::TriangleEdge e1 (&v1, &v2); - db::TriangleEdge e2 (&v2, &v4); - db::TriangleEdge e3 (&v4, &v1); - - db::Triangle t1 (&e1, &e2, &e3); - - db::TriangleEdge ee1 (&v2, &v3); - db::TriangleEdge ee2 (&v3, &v4); - - db::Triangle t2 (&ee1, &ee2, &e2); - - EXPECT_EQ (TestableTriangles::is_illegal_edge (&e2), false); - } - - { - // flipped - db::TriangleEdge e1 (&v1, &v2); - db::TriangleEdge e2 (&v2, &v3); - db::TriangleEdge e3 (&v3, &v1); - - db::Triangle t1 (&e1, &e2, &e3); - - db::TriangleEdge ee1 (&v1, &v4); - db::TriangleEdge ee2 (&v4, &v2); - - db::Triangle t2 (&ee1, &ee2, &e1); - - EXPECT_EQ (TestableTriangles::is_illegal_edge (&e1), false); - } -} - -// Returns a random float number between 0.0 and 1.0 -inline double flt_rand () -{ - return rand () * (1.0 / double (RAND_MAX)); -} - -namespace { - struct PointLessOp - { - bool operator() (const db::DPoint &a, const db::DPoint &b) const - { - return a.less (b); - } - }; -} - -TEST(insert_many) -{ - srand (0); - - TestableTriangles tris; - double res = 65536.0; - - db::DBox bbox; - - unsigned int n = 200000; - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * 0.0001; - double y = round (flt_rand () * res) * 0.0001; - tris.insert_point (x, y); - } - - EXPECT_LT (double (tris.flips ()) / double (n), 3.1); - EXPECT_LT (double (tris.hops ()) / double (n), 23.0); -} - -TEST(heavy_insert) -{ - tl::info << "Running test_heavy_insert " << tl::noendl; - - for (unsigned int l = 0; l < 100; ++l) { - - srand (l); - tl::info << "." << tl::noendl; - - TestableTriangles tris; - double res = 128.0; - - unsigned int n = rand () % 190 + 10; - - db::DBox bbox; - std::map vmap; - - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - db::Vertex *v = tris.insert_point (x, y); - bbox += db::DPoint (x, y); - vmap.insert (std::make_pair (*v, false)); - } - - // not strictly true, but very likely with at least 10 vertexes: - EXPECT_GT (tris.num_triangles (), size_t (0)); - EXPECT_EQ (tris.bbox ().to_string (), bbox.to_string ()); - - bool ok = true; - for (auto t = tris.begin (); t != tris.end (); ++t) { - for (int i = 0; i < 3; ++i) { - auto f = vmap.find (*t->vertex (i)); - if (f == vmap.end ()) { - tl::error << "Could not identify triangle vertex " << t->vertex (i)->to_string () << " as inserted vertex"; - ok = false; - } else { - f->second = true; - } - } - } - for (auto m = vmap.begin (); m != vmap.end (); ++m) { - if (!m->second) { - tl::error << "Could not identify vertex " << m->first.to_string () << " with a triangle"; - ok = false; - } - } - EXPECT_EQ (ok, true); - - EXPECT_EQ (tris.check(), true); - - } - - tl::info << tl::endl << "done."; -} - -TEST(heavy_remove) -{ - tl::info << "Running test_heavy_remove " << tl::noendl; - - for (unsigned int l = 0; l < 100; ++l) { - - srand (l); - tl::info << "." << tl::noendl; - - TestableTriangles tris; - double res = 128.0; - - unsigned int n = rand () % 190 + 10; - - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - tris.insert_point (x, y); - } - - EXPECT_EQ (tris.check(), true); - - std::set vset; - std::vector vertexes; - for (auto t = tris.begin (); t != tris.end (); ++t) { - for (int i = 0; i < 3; ++i) { - db::Vertex *v = t->vertex (i); - if (vset.insert (v).second) { - vertexes.push_back (v); - } - } - } - - while (! vertexes.empty ()) { - - unsigned int n = rand () % (unsigned int) vertexes.size (); - db::Vertex *v = vertexes [n]; - tris.remove (v); - vertexes.erase (vertexes.begin () + n); - - // just a few times as it wastes time otherwise - if (vertexes.size () % 10 == 0) { - EXPECT_EQ (tris.check (), true); - } - - } - - EXPECT_EQ (tris.num_triangles (), size_t (0)); - - } - - tl::info << tl::endl << "done."; -} - -TEST(ensure_edge) -{ - srand (0); - - TestableTriangles 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); - } - } - - for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { - tris.insert_point (ee[i].p1 ()); - } - - EXPECT_EQ (tris.check (), true); - - 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 ())); - } - - EXPECT_EQ (tris.check (false), 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) { - if (clip_box.overlaps (t->bbox ())) { - EXPECT_EQ (t->bbox ().inside (clip_box), true); - area_in += t->area (); - } - } - - EXPECT_EQ (tl::to_string (area_in), "0.25"); -} - -static bool safe_inside (const db::DBox &b1, const db::DBox &b2) -{ - typedef db::coord_traits ct; - - return (ct::less (b2.left (), b1.left ()) || ct::equal (b2.left (), b1.left ())) && - (ct::less (b1.right (), b2.right ()) || ct::equal (b1.right (), b2.right ())) && - (ct::less (b2.bottom (), b1.bottom ()) || ct::equal (b2.bottom (), b1.bottom ())) && - (ct::less (b1.top (), b2.top ()) || ct::equal (b1.top (), b2.top ())); -} - -TEST(constrain) -{ - srand (0); - - TestableTriangles 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 (safe_inside (t->bbox (), clip_box), true); - area_in += t->area (); - } - - EXPECT_EQ (tl::to_string (area_in), "0.25"); -} - -TEST(heavy_constrain) -{ - tl::info << "Running test_heavy_constrain " << tl::noendl; - - for (unsigned int l = 0; l < 100; ++l) { - - srand (l); - tl::info << "." << tl::noendl; - - TestableTriangles 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) - }; - - unsigned int n = rand () % 150 + 50; - - for (unsigned int i = 0; i < n; ++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 (safe_inside (t->bbox (), clip_box), true); - area_in += t->area (); - } - - EXPECT_EQ (tl::to_string (area_in), "0.25"); - - } - - tl::info << tl::endl << "done."; -} - -TEST(heavy_find_point_around) -{ - tl::info << "Running Triangle_test_heavy_find_point_around " << tl::noendl; - - for (unsigned int l = 0; l < 100; ++l) { - - srand (l); - tl::info << "." << tl::noendl; - - TestableTriangles tris; - double res = 128.0; - - unsigned int n = rand () % 190 + 10; - - std::vector vertexes; - - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - vertexes.push_back (tris.insert_point (x, y)); - } - - EXPECT_EQ (tris.check(), true); - - for (int i = 0; i < 100; ++i) { - - unsigned int nv = rand () % (unsigned int) vertexes.size (); - auto vertex = vertexes [nv]; - - double r = round (flt_rand () * res) * (1.0 / res); - auto p1 = tris.find_points_around (vertex, r); - auto p2 = tris.find_inside_circle (*vertex, r); - - std::set sp1 (p1.begin (), p1.end ()); - std::set sp2 (p2.begin (), p2.end ()); - sp2.erase (vertex); - - EXPECT_EQ (sp1 == sp2, true); - - } - - } - - tl::info << tl::endl << "done."; -} - -TEST(create_constrained_delaunay) -{ - db::Region r; - r.insert (db::Box (0, 0, 1000, 1000)); - - db::Region r2; - r2.insert (db::Box (200, 200, 800, 800)); - - r -= r2; - - TestableTriangles tri; - tri.create_constrained_delaunay (r); - tri.remove_outside_triangles (); - - EXPECT_EQ (tri.check (), true); - - EXPECT_EQ (tri.to_string (), - "((1000, 0), (0, 0), (200, 200)), " - "((0, 1000), (200, 200), (0, 0)), " - "((1000, 0), (200, 200), (800, 200)), " - "((1000, 0), (800, 200), (1000, 1000)), " - "((800, 200), (800, 800), (1000, 1000)), " - "((0, 1000), (1000, 1000), (800, 800)), " - "((0, 1000), (800, 800), (200, 800)), " - "((0, 1000), (200, 800), (200, 200))"); -} - -TEST(triangulate_basic) -{ - db::Region r; - r.insert (db::Box (0, 0, 10000, 10000)); - - db::Region r2; - r2.insert (db::Box (2000, 2000, 8000, 8000)); - - r -= r2; - - db::Triangles::TriangulateParameters param; - param.min_b = 1.2; - param.max_area = 1.0; - - TestableTriangles tri; - tri.triangulate (r, param, 0.001); - - EXPECT_EQ (tri.check (), true); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (100)); - EXPECT_LT (tri.num_triangles (), size_t (150)); - - // for debugging: - // tri.dump ("debug.gds"); - - param.min_b = 1.0; - param.max_area = 0.1; - - tri.triangulate (r, param, 0.001); - - EXPECT_EQ (tri.check (), true); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (900)); - EXPECT_LT (tri.num_triangles (), size_t (1000)); -} - -void read_polygons (const std::string &path, db::Region ®ion, double dbu) -{ - tl::InputStream is (path); - tl::TextInputStream ti (is); - - unsigned int nvert = 0, nedges = 0; - - { - tl::Extractor ex (ti.get_line ().c_str ()); - ex.read (nvert); - ex.read (nedges); - } - - std::vector v; - auto dbu_trans = db::CplxTrans (dbu).inverted (); - for (unsigned int i = 0; i < nvert; ++i) { - double x = 0, y = 0; - tl::Extractor ex (ti.get_line ().c_str ()); - ex.read (x); - ex.read (y); - v.push_back (dbu_trans * db::DPoint (x, y)); - } - - unsigned int nstart = 0; - bool new_contour = true; - std::vector contour; - - for (unsigned int i = 0; i < nedges; ++i) { - - unsigned int n1 = 0, n2 = 0; - - tl::Extractor ex (ti.get_line ().c_str ()); - ex.read (n1); - ex.read (n2); - - if (new_contour) { - nstart = n1; - new_contour = false; - } - - contour.push_back (v[n1]); - - if (n2 == nstart) { - // finish contour - db::SimplePolygon sp; - sp.assign_hull (contour.begin (), contour.end ()); - region.insert (sp); - new_contour = true; - contour.clear (); - } else if (n2 <= n1) { - tl::error << "Invalid polygon wrap in line " << ti.line_number (); - tl_assert (false); - } - - } -} - -TEST(triangulate_geo) -{ - double dbu = 0.001; - - db::Region r; - read_polygons (tl::combine_path (tl::testsrc (), "testdata/algo/triangles1.txt"), r, dbu); - - // for debugging purposes dump the inputs - if (false) { - - db::Layout layout = db::Layout (); - layout.dbu (dbu); - db::Cell &top = layout.cell (layout.add_cell ("DUMP")); - unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0)); - r.insert_into (&layout, top.cell_index (), l1); - - { - tl::OutputStream stream ("input.gds"); - db::SaveLayoutOptions opt; - db::Writer writer (opt); - writer.write (layout, stream); - } - - } - - db::Triangles::TriangulateParameters param; - param.min_b = 1.0; - param.max_area = 0.1; - param.min_length = 0.001; - - TestableTriangles tri; - tri.triangulate (r, param, dbu); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - size_t n_skinny = 0; - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - if (t->b () < param.min_b) { - ++n_skinny; - } - } - - EXPECT_LT (n_skinny, size_t (20)); - EXPECT_GT (tri.num_triangles (), size_t (29000)); - EXPECT_LT (tri.num_triangles (), size_t (30000)); -} - -TEST(triangulate_analytic) -{ - double dbu = 0.0001; - - double star1 = 9.0, star2 = 5.0; - double r = 1.0; - int n = 100; - - auto dbu_trans = db::CplxTrans (dbu).inverted (); - - std::vector contour1, contour2; - for (int i = 0; i < n; ++i) { - double a = -M_PI * 2.0 * double (i) / double (n); // "-" for clockwise orientation - double rr, x, y; - rr = r * (1.0 + 0.4 * cos (star1 * a)); - x = rr * cos (a); - y = rr * sin (a); - contour1.push_back (dbu_trans * db::DPoint (x, y)); - rr = r * (0.1 + 0.03 * cos (star2 * a)); - x = rr * cos (a); - y = rr * sin (a); - contour2.push_back (dbu_trans * db::DPoint (x, y)); - } - - db::Region rg; - - db::SimplePolygon sp1; - sp1.assign_hull (contour1.begin (), contour1.end ()); - db::SimplePolygon sp2; - sp2.assign_hull (contour2.begin (), contour2.end ()); - - rg = db::Region (sp1) - db::Region (sp2); - - db::Triangles::TriangulateParameters param; - param.min_b = 1.0; - param.max_area = 0.01; - - TestableTriangles tri; - tri.triangulate (rg, param, dbu); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (1250)); - EXPECT_LT (tri.num_triangles (), size_t (1300)); -} - -TEST(triangulate_problematic) -{ - db::DPoint contour[] = { - db::DPoint (129145.00000, -30060.80000), - db::DPoint (129145.00000, -28769.50000), - db::DPoint (129159.50000, -28754.90000), // this is a very short edge <-- from here. - db::DPoint (129159.60000, -28754.80000), // <-- to here. - db::DPoint (129159.50000, -28754.70000), - db::DPoint (129366.32200, -28547.90000), - db::DPoint (130958.54600, -26955.84600), - db::DPoint (131046.25000, -27043.55000), - db::DPoint (130152.15000, -27937.65000), - db::DPoint (130152.15000, -30060.80000) - }; - - db::DPolygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - - db::Triangles::TriangulateParameters param; - param.min_b = 1.0; - param.max_area = 100000.0; - param.min_length = 0.002; - - TestableTriangles tri; - tri.triangulate (poly, param); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (540)); - EXPECT_LT (tri.num_triangles (), size_t (560)); -} - -TEST(triangulate_thin) -{ - db::DPoint contour[] = { - db::DPoint (18790, 58090), - db::DPoint (18790, 58940), - db::DPoint (29290, 58940), - db::DPoint (29290, 58090) - }; - - db::DPoint hole[] = { - db::DPoint (18791, 58091), - db::DPoint (29289, 58091), - db::DPoint (29289, 58939), - db::DPoint (18791, 58939) - }; - - db::DPolygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - poly.insert_hole (hole + 0, hole + sizeof (hole) / sizeof (hole[0])); - - double dbu = 0.001; - - db::Triangles::TriangulateParameters param; - param.min_b = 0.5; - param.max_area = 0.0; - param.min_length = 2 * dbu; - - TestableTriangles tri; - db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); - tri.triangulate (trans * poly, param); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (13000)); - EXPECT_LT (tri.num_triangles (), size_t (13200)); -} - -TEST(triangulate_issue1996) -{ - db::DPoint contour[] = { - db::DPoint (-8000, -8075), - db::DPoint (-8000, 8075), - db::DPoint (18000, 8075), - db::DPoint (18000, -8075) - }; - - db::DPolygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - - double dbu = 0.001; - - db::Triangles::TriangulateParameters param; - param.min_b = 0.5; - param.max_area = 5000.0 * dbu * dbu; - - TestableTriangles tri; - db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); - tri.triangulate (trans * poly, param); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (128000)); - EXPECT_LT (tri.num_triangles (), size_t (132000)); -} - -TEST(triangulate_with_vertexes) -{ - db::Point contour[] = { - db::Point (0, 0), - db::Point (0, 100), - db::Point (1000, 100), - db::Point (1000, 0) - }; - - db::Polygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - - double dbu = 0.001; - - db::Triangles::TriangulateParameters param; - param.min_b = 0.0; - param.max_area = 0.0; - - std::vector vertexes; - - TestableTriangles tri; - db::CplxTrans trans = db::DCplxTrans (dbu) * db::CplxTrans (db::Trans (db::Point () - poly.box ().center ())); - tri.triangulate (poly, param, trans); - - EXPECT_EQ (tri.to_string (), "((-0.5, -0.05), (-0.5, 0.05), (0.5, 0.05)), ((0.5, -0.05), (-0.5, -0.05), (0.5, 0.05))"); - - vertexes.clear (); - - // outside vertexes are ignored, but lead to a different triangulation - vertexes.push_back (db::Point (50, 150)); - tri.triangulate (poly, vertexes, param, trans); - - EXPECT_EQ (tri.to_string (), "((-0.5, -0.05), (-0.133333333333, 0.05), (0.5, -0.05)), ((0.5, 0.05), (0.5, -0.05), (-0.133333333333, 0.05)), ((-0.133333333333, 0.05), (-0.5, -0.05), (-0.5, 0.05))"); - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - auto *vp = tri.find_vertex_for_point (trans * *v); - EXPECT_EQ (vp, 0); - } - - vertexes.clear (); - vertexes.push_back (db::Point (50, 50)); - tri.triangulate (poly, vertexes, param, trans); - - EXPECT_EQ (tri.to_string (), "((-0.45, 0), (-0.5, -0.05), (-0.5, 0.05)), ((0.5, 0.05), (-0.45, 0), (-0.5, 0.05)), ((-0.45, 0), (0.5, -0.05), (-0.5, -0.05)), ((-0.45, 0), (0.5, 0.05), (0.5, -0.05))"); - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - auto *vp = tri.find_vertex_for_point (trans * *v); - if (! vp) { - tl::warn << "Vertex not present in output: " << v->to_string (); - EXPECT_EQ (1, 0); - } - } - - // aggressive triangulation - param.min_b = 1.0; - param.max_area = 20 * 20 * dbu * dbu; - - tri.triangulate (poly, vertexes, param, trans); - - EXPECT_GT (tri.num_triangles (), size_t (380)); - EXPECT_LT (tri.num_triangles (), size_t (400)); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - auto *vp = tri.find_vertex_for_point (trans * *v); - if (! vp) { - tl::warn << "Vertex not present in output: " << v->to_string (); - EXPECT_EQ (1, 0); - } - } -} - -// @@@@@@@@@@@ - -struct SortAngleAndEdgesByEdgeLength -{ - typedef std::list > angle_and_edges_list; - - bool operator() (const angle_and_edges_list::iterator &a, const angle_and_edges_list::iterator &b) const - { - double la = a->second->edge ().double_sq_length (); - double lb = b->second->edge ().double_sq_length (); - if (fabs (la - lb) > db::epsilon) { - return la < lb; - } else { - return a->second->edge ().less (b->second->edge ()); - } - } -}; - -// TODO: move to some generic header -template -struct less_compare_func -{ - bool operator() (const T &a, const T &b) const - { - return a.less (b); - } -}; - -// TODO: move to some generic header -template -struct equal_compare_func -{ - bool operator() (const T &a, const T &b) const - { - return a.equal (b); - } -}; - -struct ConcaveCorner -{ - ConcaveCorner () - : corner (0), incoming (0), outgoing (0) - { - // .. nothing yet .. - } - - ConcaveCorner (db::Vertex *_corner, db::TriangleEdge *_incoming, db::TriangleEdge *_outgoing) - : corner (_corner), incoming (_incoming), outgoing (_outgoing) - { - // .. nothing yet .. - } - - db::Vertex *corner; - db::TriangleEdge *incoming, *outgoing; -}; - -db::TriangleEdge *find_outgoing_segment (db::Vertex *vertex, db::TriangleEdge *incoming, int &vp_max_sign) -{ - db::Vertex *vfrom = incoming->other (vertex); - db::DEdge e1 (*vfrom, *vertex); - - double vp_max = 0.0; - vp_max_sign = 0; - db::TriangleEdge *outgoing = 0; - - // Look for the outgoing edge. We pick the one which bends "most", favoring - // convex corners. Multiple edges per vertex are possible is corner cases such as the - // "hourglass" configuration. - - for (auto e = vertex->begin_edges (); e != vertex->end_edges (); ++e) { - - db::TriangleEdge *en = *e; - if (en != incoming && en->is_segment ()) { - - db::Vertex *v = en->other (vertex); - db::DEdge e2 (*vertex, *v); - double vp = double (db::vprod (e1, e2)) / (e1.double_length () * e2.double_length ()); - - // vp > 0: concave, vp < 0: convex - - if (! outgoing || vp > vp_max) { - vp_max_sign = db::vprod_sign (e1, e2); - vp_max = vp; - outgoing = en; - } - - } - - } - - tl_assert (outgoing != 0); - return outgoing; -} - -void collect_concave_vertexes (db::Triangles &tris, std::vector &concave_vertexes) -{ - concave_vertexes.clear (); - - // @@@ use edge "level" - // @@@ use edges from heap - std::unordered_set left; - - for (auto it = tris.begin (); it != tris.end (); ++it) { - for (unsigned int i = 0; i < 3; ++i) { - db::TriangleEdge *e = it->edge (i); - if (e->is_segment ()) { - left.insert (e); - } - } - } - - while (! left.empty ()) { - - // First segment for a new loop - db::TriangleEdge *segment = *left.begin (); - - // walk along the segments in clockwise direction. Find concave - // vertexes and create new vertexes perpendicular to the incoming - // and outgoing edge. - - db::TriangleEdge *start_segment = segment; - db::Vertex *vto = segment->right () ? segment->v2 () : segment->v1 (); - - do { - - left.erase (segment); - - db::TriangleEdge *prev_segment = segment; - - int vp_sign = 0; - segment = find_outgoing_segment (vto, prev_segment, vp_sign); - - if (vp_sign > 0) { - concave_vertexes.push_back (ConcaveCorner (vto, prev_segment, segment)); - } - - vto = segment->other (vto); - - } while (segment != start_segment); - - } -} - -std::pair -search_crossing_with_next_segment (const db::Vertex *v0, const db::DVector &direction) -{ - auto vtri = v0->triangles (); // TODO: slow? - std::vector nvv, nvv_next; - - for (auto it = vtri.begin (); it != vtri.end (); ++it) { - - // Search for a segment in the direction perpendicular to the edge - nvv.clear (); - nvv.push_back (v0); - const db::Triangle *t = *it; - - while (! nvv.empty ()) { - - nvv_next.clear (); - - for (auto iv = nvv.begin (); iv != nvv.end (); ++iv) { - - const db::Vertex *v = *iv; - const db::TriangleEdge *oe = t->opposite (v); - const db::Triangle *tt = oe->other (t); - const db::Vertex *v1 = oe->v1 (); - const db::Vertex *v2 = oe->v2 (); - - if (db::sprod_sign (*v2 - *v, direction) >= 0 && db::sprod_sign (*v1 - *v, direction) >= 0 && - db::vprod_sign (*v2 - *v, direction) * db::vprod_sign (*v1 - *v, direction) < 0) { - - // this triangle covers the normal vector of e1 -> stop here or continue searching in that direction - if (oe->is_segment ()) { - auto cp = oe->edge ().cut_point (db::DEdge (*v0, *v0 + direction)); - if (cp.first) { - return std::make_pair (true, cp.second); - } - } else { - // continue searching in that direction - nvv_next.push_back (v1); - nvv_next.push_back (v2); - t = tt; - } - - break; - - } - - } - - nvv.swap (nvv_next); - - } - - } - - return std::make_pair (false, db::DPoint ()); -} - -void -hertel_mehlhorn_decomposition (db::Triangles &tri, bool diagonals_only, bool no_collinear_edges, std::list &polygons) -{ - std::vector concave_vertexes; - collect_concave_vertexes (tri, concave_vertexes); - - // @@@ return if no concave corners - - // @@@ sort concave vertexes - - std::vector new_points; - - if (! diagonals_only) { - - // Create internal segments cutting off pieces orthogonal to the edges - // connecting the concave vertex. - - for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { - - for (unsigned int ei = 0; ei < 2; ++ei) { - - db::DEdge ee; - const db::Vertex *v0 = cc->corner; - if (ei == 0) { - ee = db::DEdge (*cc->incoming->other (v0), *v0); - } else { - ee = db::DEdge (*v0, *cc->outgoing->other (v0)); - } - - auto cp = search_crossing_with_next_segment (v0, db::DVector (ee.dy (), -ee.dx ())); - if (cp.first) { - new_points.push_back (cp.second); - } - - } - - } - - } - - // eliminate duplicates and put the new points in some order - - if (! new_points.empty ()) { - - std::sort (new_points.begin (), new_points.end (), less_compare_func ()); - new_points.erase (std::unique (new_points.begin (), new_points.end (), equal_compare_func ()), new_points.end ()); - - // Insert the new points and make connections - for (auto p = new_points.begin (); p != new_points.end (); ++p) { - tri.insert_point (*p); - } - - // As the insertion invalidates the edges, we need to collect the concave vertexes again - collect_concave_vertexes (tri, concave_vertexes); - - } - - // Collect essential edges - // Every concave vertex can have up to two essential edges. - // Other then suggested by Hertel-Mehlhorn we don't pick - // them one-by-one, but using them in length order, from the - - std::unordered_set essential_edges; - - typedef std::list > angles_and_edges_list; - angles_and_edges_list angles_and_edges; - std::vector sorted_edges; - - for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { - - angles_and_edges.clear (); - const db::Vertex *v0 = cc->corner; - - const db::TriangleEdge *e = cc->incoming; - while (e) { - - const db::Triangle *t = e->v2 () == v0 ? e->right () : e->left (); - tl_assert (t != 0); - - // @@@ make a method of triangle - const db::TriangleEdge *en = 0; - for (unsigned int i = 0; i < 3; ++i) { - const db::TriangleEdge *ee = t->edge (i); - if (ee != e && (ee->v1 () == v0 || ee->v2 () == v0)) { - en = ee; - break; - } - } - - 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 < (no_collinear_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) { - essential_edges.insert (i->second); - } - - } - - // Combine triangles, but don't cross essential edges - - std::unordered_set left_triangles; - for (auto it = tri.begin (); it != tri.end (); ++it) { - left_triangles.insert (it.operator-> ()); - } - - while (! left_triangles.empty ()) { - - std::unordered_map edges; - - const db::Triangle *tri = *left_triangles.begin (); - std::vector queue, next_queue; - queue.push_back (tri); - - while (! queue.empty ()) { - - next_queue.clear (); - - for (auto q = queue.begin (); q != queue.end (); ++q) { - - left_triangles.erase (*q); - - for (unsigned int i = 0; i < 3; ++i) { - - const db::TriangleEdge *e = (*q)->edge (i); - const db::Triangle *qq = e->other (*q); - - if (! qq || essential_edges.find (e) != essential_edges.end ()) { - if (e->right () == *q) { - edges.insert (std::make_pair (e->v1 (), e->v2 ())); - } else { - edges.insert (std::make_pair (e->v2 (), e->v1 ())); - } - } else if (left_triangles.find (qq) != left_triangles.end ()) { - next_queue.push_back (qq); - } - } - - } - - queue.swap (next_queue); - - } - - // stitch the loop points into a polygon - - tl_assert (! edges.empty ()); - - const db::Vertex *v = edges.begin ()->first; - const db::Vertex *v0 = v; - const db::Vertex *vv = edges.begin ()->second; - - std::vector polygon_points; - - do { - - polygon_points.push_back (*v); - - auto i = edges.find (vv); - tl_assert (i != edges.end ()); - - v = i->first; - vv = i->second; - - } while (v != v0); - - polygons.push_back (db::DPolygon ()); - polygons.back ().assign_hull (polygon_points.begin (), polygon_points.end ()); - - } -} - - -// Hertel-Mehlhorn :) -TEST(JoinTriangles) -{ -#if 0 - db::Point contour[] = { - db::Point (0, 0), - db::Point (0, 100), - db::Point (1000, 100), - db::Point (1000, 500), - db::Point (1100, 500), - db::Point (1100, 100), - db::Point (2100, 100), - db::Point (2100, 0) - }; -#else - - db::Point contour[] = { - db::Point (0, 0), - db::Point (0, 100), - db::Point (1000, 100), - db::Point (1000, 500), - db::Point (1100, 500), - db::Point (1100, 100), - db::Point (2100, 100), - db::Point (2100, -1000), - db::Point (150, -1000), - db::Point (150, 0) - }; -#endif - - db::Polygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - - // @@@ don't to anything if already convex - - double dbu = 0.001; - - db::Triangles::TriangulateParameters param; - param.min_b = 0.0; - - TestableTriangles tri; - db::CplxTrans trans = db::CplxTrans (dbu); - tri.triangulate (poly, param, trans); - - std::list polygons; - hertel_mehlhorn_decomposition (tri, false, true, polygons); - - db::Region result; - for (auto p = polygons.begin (); p != polygons.end (); ++p) { - result.insert (trans.inverted () * *p); - } - - // @@@ - tri.dump ("debugt.gds"); - result.write ("debug.gds"); - -} - -// @@@@@@@@@@q diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index f16d29b44..b5aadac53 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -20,8 +20,6 @@ SOURCES = \ dbQuadTreeTests.cc \ dbRecursiveInstanceIteratorTests.cc \ dbRegionCheckUtilsTests.cc \ - dbTriangleTests.cc \ - dbTrianglesTests.cc \ dbUtilsTests.cc \ dbWriterTools.cc \ dbLoadLayoutOptionsTests.cc \