From fa301b0d32ec8bca1397c13386dc3117d2fb4605 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 12 Aug 2023 10:38:04 +0200 Subject: [PATCH] WIP --- src/db/db/dbTriangle.cc | 58 +++++- src/db/db/dbTriangle.h | 84 ++++++-- src/db/unit_tests/dbTriangleTests.cc | 283 ++++++++++++++++++++++++++- 3 files changed, 404 insertions(+), 21 deletions(-) diff --git a/src/db/db/dbTriangle.cc b/src/db/db/dbTriangle.cc index babc271fa..fdddd733a 100644 --- a/src/db/db/dbTriangle.cc +++ b/src/db/db/dbTriangle.cc @@ -43,6 +43,22 @@ Vertex::Vertex (const db::DPoint &p) // .. nothing yet .. } +Vertex::Vertex (const Vertex &v) + : DPoint (), m_level (0) +{ + operator= (v); +} + +Vertex &Vertex::operator= (const Vertex &v) +{ + if (this != &v) { + // NOTE: edges are not copied! + db::DPoint::operator= (v); + m_level = v.m_level; + } + return *this; +} + Vertex::Vertex (db::DCoord x, db::DCoord y) : DPoint (x, y), m_level (0) { @@ -76,9 +92,30 @@ Vertex::triangles () const } std::string -Vertex::to_string () const +Vertex::to_string (bool with_id) const { - return db::DPoint::to_string () + tl::sprintf ("[%p]", (void *)this); + std::string res = tl::sprintf ("(%.12g, %.12g)", x (), y()); + if (with_id) { + res += tl::sprintf ("[%p]", (void *)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 = std::max (1.0, fabs (d2 + r2)) * db::epsilon; + if (d2 < r2 - delta) { + return 1; + } else if (d2 < r2 + delta) { + return 0; + } else { + return -1; + } } // ------------------------------------------------------------------------------------- @@ -141,9 +178,13 @@ TriangleEdge::common_vertex (const TriangleEdge &other) const } std::string -TriangleEdge::to_string () const +TriangleEdge::to_string (bool with_id) const { - return mp_v1->to_string () + "," + mp_v2->to_string () + tl::sprintf ("[%p]", (void *)this); + std::string res = std::string ("(") + mp_v1->to_string (with_id) + ", " + mp_v2->to_string (with_id) + ")"; + if (with_id) { + res += tl::sprintf ("[%p]", (void *)this); + } + return res; } double @@ -279,19 +320,20 @@ Triangle::Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3) } std::string -Triangle::to_string () const +Triangle::to_string (bool with_id) const { - std::string res; + std::string res = "("; for (int i = 0; i < 3; ++i) { if (i > 0) { res += ", "; } if (vertex (i)) { - res += vertex (i)->to_string (); + res += vertex (i)->to_string (with_id); } else { res += "(null)"; } } + res += ")"; return res; } @@ -395,7 +437,7 @@ Triangle::contains (const db::DPoint &point) const int s = db::DEdge (*vl, *v).side_of (point); if (s == 0) { res = 0; - } else if (s < 0) { + } else if (s > 0) { return -1; } vl = v; diff --git a/src/db/db/dbTriangle.h b/src/db/db/dbTriangle.h index e5ee02c3f..0f23873f0 100644 --- a/src/db/db/dbTriangle.h +++ b/src/db/db/dbTriangle.h @@ -44,7 +44,7 @@ class TriangleEdge; * an integer value that can be used in traversal algorithms * ("level") */ -class Vertex +class DB_PUBLIC Vertex : public db::DPoint { public: @@ -53,8 +53,11 @@ public: 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; @@ -64,7 +67,21 @@ public: size_t level () const { return m_level; } void set_level (size_t l) { m_level = l; } - std::string to_string () const; + 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: edges_type m_edges; @@ -74,7 +91,7 @@ private: /** * @brief A class representing an edge in the Delaunay triangulation graph */ -class TriangleEdge +class DB_PUBLIC TriangleEdge : public tl::Object { public: @@ -136,6 +153,11 @@ public: Vertex *v1 () const { return mp_v1; } Vertex *v2 () const { return mp_v2; } + void reverse () + { + std::swap (mp_v1, mp_v2); + } + Triangle *left () const { return const_cast (mp_left.get ()); } Triangle *right () const { return const_cast (mp_right.get ()); } void set_left (Triangle *t) { mp_left = t; } @@ -157,7 +179,7 @@ public: void set_is_segment (bool is_seg) { m_is_segment = is_seg; } bool is_segment () const { return m_is_segment; } - std::string to_string () const; + std::string to_string (bool with_id = false) const; /** * @brief Converts to an db::DEdge @@ -186,7 +208,7 @@ public: } /** - * @brief Returns a value indicating wether this edge crosses the other one + * @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. @@ -195,7 +217,7 @@ public: static bool crosses (const db::DEdge &e, const db::DEdge &other); /** - * @brief Returns a value indicating wether this edge crosses the other one + * @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. @@ -206,14 +228,25 @@ public: } /** - * @brief Returns a value indicating wether this edge crosses the other one + * @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 wether this edge crosses the other one + * @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 @@ -221,6 +254,15 @@ public: 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 @@ -253,10 +295,22 @@ public: * @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); + return -edge ().side_of (p); } /** @@ -317,12 +371,16 @@ private: tl::weak_ptr mp_left, mp_right; size_t m_level; bool m_is_segment; + + // no copying + TriangleEdge &operator= (const TriangleEdge &); + TriangleEdge (const TriangleEdge &); }; /** * @brief A class representing a triangle */ -class Triangle +class DB_PUBLIC Triangle : public tl::Object { public: @@ -332,7 +390,7 @@ public: bool is_outside () const { return m_is_outside; } void set_outside (bool o) { m_is_outside = o; } - std::string to_string () const; + std::string to_string (bool with_id = false) const; /** * @brief Gets the nth vertex (n wraps around and can be negative) @@ -379,6 +437,10 @@ private: bool m_is_outside; tl::weak_ptr mp_e1, mp_e2, mp_e3; db::Vertex *mp_v1, *mp_v2, *mp_v3; + + // no copying + Triangle &operator= (const Triangle &); + Triangle (const Triangle &); }; diff --git a/src/db/unit_tests/dbTriangleTests.cc b/src/db/unit_tests/dbTriangleTests.cc index a9be821e0..a35129236 100644 --- a/src/db/unit_tests/dbTriangleTests.cc +++ b/src/db/unit_tests/dbTriangleTests.cc @@ -24,7 +24,286 @@ #include "dbTriangle.h" #include "tlUnitTest.h" -TEST(1) -{ +#include +// Tests for Triangle class + +TEST(Triangle_basic) +{ + db::Vertex v1; + db::Vertex v2 (1, 2); + db::Vertex v3 (2, 1); + + db::TriangleEdge s1 (&v1, &v2); + db::TriangleEdge s2 (&v2, &v3); + db::TriangleEdge s3 (&v3, &v1); + + db::Triangle tri (&s1, &s2, &s3); + EXPECT_EQ (tri.to_string (), "((0, 0), (1, 2), (2, 1))"); + + // ordering + db::TriangleEdge s11 (&v1, &v2); + db::TriangleEdge s12 (&v3, &v2); + db::TriangleEdge s13 (&v1, &v3); + + db::Triangle tri2 (&s11, &s12, &s13); + EXPECT_EQ (tri2.to_string (), "((0, 0), (1, 2), (2, 1))"); } + +TEST(Triangle_find_segment_with) +{ + db::Vertex v1; + db::Vertex v2 (1, 2); + db::Vertex v3 (2, 1); + + db::TriangleEdge s1 (&v1, &v2); + db::TriangleEdge s2 (&v2, &v3); + db::TriangleEdge 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); + + db::TriangleEdge s1 (&v1, &v2); + db::TriangleEdge s2 (&v2, &v3); + db::TriangleEdge 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); + + db::TriangleEdge s1 (&v1, &v2); + db::TriangleEdge s2 (&v2, &v3); + db::TriangleEdge 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); + + db::TriangleEdge s1 (&v1, &v2); + db::TriangleEdge s2 (&v2, &v3); + db::TriangleEdge 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); + + db::TriangleEdge s1 (&v1, &v2); + db::TriangleEdge s2 (&v2, &v3); + db::TriangleEdge 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_circumcircle) +{ + db::Vertex v1; + db::Vertex v2 (1, 2); + db::Vertex v3 (2, 1); + + db::TriangleEdge s1 (&v1, &v2); + db::TriangleEdge s2 (&v2, &v3); + db::TriangleEdge 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); + + db::TriangleEdge edge (&v1, &v2); + EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); +} + +TEST(TriangleEdge_side_of) +{ + db::Vertex v1; + db::Vertex v2 (1, 0.5); + + db::TriangleEdge 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); + db::TriangleEdge 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; + + db::TriangleEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); + EXPECT_EQ (s1.crosses (db::TriangleEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); + EXPECT_EQ (s1.crosses (db::TriangleEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), false); // only cuts + EXPECT_EQ (s1.crosses (db::TriangleEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(1, 0.5))), false); + EXPECT_EQ (s1.crosses (db::TriangleEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(2, 0.5))), false); + EXPECT_EQ (s1.crosses (db::TriangleEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); + EXPECT_EQ (s1.crosses (db::TriangleEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(-0.1, 0.5))), false); + EXPECT_EQ (s1.crosses (db::TriangleEdge (heap.make_vertex (-1, 0.6), heap.make_vertex(0, 0.6))), false); + EXPECT_EQ (s1.crosses (db::TriangleEdge (heap.make_vertex (-1, 1), heap.make_vertex(1, 1))), false); + + EXPECT_EQ (s1.crosses_including (db::TriangleEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); + EXPECT_EQ (s1.crosses_including (db::TriangleEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), true); // only cuts + EXPECT_EQ (s1.crosses_including (db::TriangleEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); +} + +#if 0 +class TestSegment(unittest.TestCase): + + def test_crosses(self): + s1 = t.TriangleEdge(t.Vertex(0, 0), t.Vertex(1, 0.5)) + EXPECT_EQ (s1.crosses(t.TriangleEdge(t.Vertex(-1, -0.5), t.Vertex(1, -0.5))), False) + EXPECT_EQ (s1.crosses(t.TriangleEdge(t.Vertex(-1, 0), t.Vertex(1, 0))), False) # only cuts + EXPECT_EQ (s1.crosses(t.TriangleEdge(t.Vertex(-1, 0.5), t.Vertex(1, 0.5))), False) + EXPECT_EQ (s1.crosses(t.TriangleEdge(t.Vertex(-1, 0.5), t.Vertex(2, 0.5))), False) + EXPECT_EQ (s1.crosses(t.TriangleEdge(t.Vertex(-1, 0.25), t.Vertex(2, 0.25))), True) + EXPECT_EQ (s1.crosses(t.TriangleEdge(t.Vertex(-1, 0.5), t.Vertex(-0.1, 0.5))), False) + EXPECT_EQ (s1.crosses(t.TriangleEdge(t.Vertex(-1, 0.6), t.Vertex(0, 0.6))), False) + EXPECT_EQ (s1.crosses(t.TriangleEdge(t.Vertex(-1, 1), t.Vertex(1, 1))), False) + + def test_point_on(self): + s1 = t.TriangleEdge(t.Vertex(0, 0), t.Vertex(1, 0.5)) + EXPECT_EQ (s1.point_on(t.Point(0, 0)), False) # endpoints are not "on" + EXPECT_EQ (s1.point_on(t.Point(0, -0.5)), False) + EXPECT_EQ (s1.point_on(t.Point(0.5, 0)), False) + EXPECT_EQ (s1.point_on(t.Point(0.5, 0.25)), True) + EXPECT_EQ (s1.point_on(t.Point(1, 0.5)), False) # endpoints are not "on" + EXPECT_EQ (s1.point_on(t.Point(1, 1)), False) + EXPECT_EQ (s1.point_on(t.Point(2, 1)), False) + + def test_intersection_point(self): + s1 = t.TriangleEdge(t.Vertex(0, 0), t.Vertex(1, 0.5)) + EXPECT_EQ (str(s1.intersection_point(t.TriangleEdge(t.Vertex(-1, 0.25), t.Vertex(2, 0.25)))), "(0.5, 0.25)") + + def test_can_flip(self): + v1 = t.Vertex(2, -1) + v2 = t.Vertex(0, 0) + v3 = t.Vertex(1, 0) + v4 = t.Vertex(0.5, 1) + s1 = t.TriangleEdge(v1, v2) + s2 = t.TriangleEdge(v1, v3) + s3 = t.TriangleEdge(v2, v3) + s4 = t.TriangleEdge(v2, v4) + s5 = t.TriangleEdge(v3, v4) + t1 = t.Triangle(s1, s2, s3) + t2 = t.Triangle(s3, s4, s5) + s3.left = t1 + s3.right = t2 + EXPECT_EQ (s3.can_flip(), False) + v1.x = 0.5 + EXPECT_EQ (s3.can_flip(), True) + v1.x = -0.25 + EXPECT_EQ (s3.can_flip(), True) + v1.x = -0.5 + EXPECT_EQ (s3.can_flip(), False) + v1.x = -1.0 + EXPECT_EQ (s3.can_flip(), False) + + def test_distance(self): + seg = t.TriangleEdge(t.Vertex(0, 0), t.Vertex(1, 0)) + EXPECT_EQ (seg.distance(t.Point(0, 0)), 0) + EXPECT_EQ (seg.distance(t.Point(0, 1)), 1) + EXPECT_EQ (seg.distance(t.Point(1, 2)), 2) + EXPECT_EQ (seg.distance(t.Point(1, -1)), 1) + EXPECT_EQ (seg.distance(t.Point(2, 0)), 1) + EXPECT_EQ (seg.distance(t.Point(-2, 0)), 2) + seg.reverse() + EXPECT_EQ (seg.distance(t.Point(0, 0)), 0) + EXPECT_EQ (seg.distance(t.Point(0, 1)), 1) + EXPECT_EQ (seg.distance(t.Point(1, 2)), 2) + EXPECT_EQ (seg.distance(t.Point(1, -1)), 1) + EXPECT_EQ (seg.distance(t.Point(2, 0)), 1) + EXPECT_EQ (seg.distance(t.Point(-2, 0)), 2) +#endif