This commit is contained in:
Matthias Koefferlein 2023-08-12 10:38:04 +02:00
parent 495da3de23
commit fa301b0d32
3 changed files with 404 additions and 21 deletions

View File

@ -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 &center, 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;

View File

@ -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<db::Triangle *> 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 &center, 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 &center, 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<Triangle *> (mp_left.get ()); }
Triangle *right () const { return const_cast<Triangle *> (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<Triangle> 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<TriangleEdge> mp_e1, mp_e2, mp_e3;
db::Vertex *mp_v1, *mp_v2, *mp_v3;
// no copying
Triangle &operator= (const Triangle &);
Triangle (const Triangle &);
};

View File

@ -24,7 +24,286 @@
#include "dbTriangle.h"
#include "tlUnitTest.h"
TEST(1)
{
#include <list>
// 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<db::Vertex> 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