This commit is contained in:
Matthias Koefferlein 2023-08-13 21:14:58 +02:00
parent 82b49dfb77
commit 576eacd0bf
5 changed files with 134 additions and 55 deletions

View File

@ -133,13 +133,13 @@ Vertex::in_circle (const DPoint &point, const DPoint &center, double radius)
// TriangleEdge implementation
TriangleEdge::TriangleEdge ()
: mp_v1 (0), mp_v2 (0), mp_left (), mp_right (), m_level (0), m_is_segment (false)
: 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_is_segment (false)
: mp_v1 (v1), mp_v2 (v2), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false)
{
v1->m_edges.push_back (this);
v2->m_edges.push_back (this);
@ -148,14 +148,12 @@ TriangleEdge::TriangleEdge (Vertex *v1, Vertex *v2)
void
TriangleEdge::set_left (Triangle *t)
{
tl_assert (t == 0 || left () == 0);
mp_left = t;
}
void
TriangleEdge::set_right (Triangle *t)
{
tl_assert (t == 0 || right () == 0);
mp_right = t;
}
@ -305,13 +303,13 @@ TriangleEdge::has_triangle (const Triangle *t) const
// Triangle implementation
Triangle::Triangle ()
: m_is_outside (false), mp_v1 (0), mp_v2 (0), mp_v3 (0)
: m_is_outside (false), mp_v1 (0), mp_v2 (0), mp_v3 (0), m_id (0)
{
// .. nothing yet ..
}
Triangle::Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3)
: m_is_outside (false), mp_e1 (e1), mp_e2 (e2), mp_e3 (e3)
: m_is_outside (false), mp_e1 (e1), mp_e2 (e2), mp_e3 (e3), m_id (0)
{
mp_v1 = e1->v1 ();
mp_v2 = e1->other (mp_v1);
@ -348,6 +346,7 @@ Triangle::Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3)
void
Triangle::unlink ()
{
// @@@ Is this really needed???
for (int i = 0; i != 3; ++i) {
db::TriangleEdge *e = edge (i);
if (e->left () == this) {

View File

@ -184,6 +184,9 @@ public:
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; }
@ -388,6 +391,7 @@ private:
Vertex *mp_v1, *mp_v2;
tl::weak_ptr<Triangle> mp_left, mp_right;
size_t m_level;
size_t m_id;
bool m_is_segment;
// no copying
@ -398,6 +402,19 @@ private:
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
*/
@ -410,6 +427,9 @@ public:
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; }
@ -476,12 +496,25 @@ private:
bool m_is_outside;
tl::weak_ptr<TriangleEdge> mp_e1, mp_e2, mp_e3;
db::Vertex *mp_v1, *mp_v2, *mp_v3;
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 ();
}
};
}

View File

@ -65,6 +65,7 @@ db::TriangleEdge *
Triangles::create_edge (db::Vertex *v1, db::Vertex *v2)
{
db::TriangleEdge *res = new db::TriangleEdge (v1, v2);
res->set_id (++m_id);
mp_edges.push_back (res);
return res;
}
@ -73,6 +74,7 @@ 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;
}
@ -180,6 +182,11 @@ Triangles::check (bool check_delaunay) const
}
}
if (!e->left () && !e->right ()) {
tl::error << "(check error) found orphan edge " << e->to_string (true);
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);
@ -259,7 +266,9 @@ Triangles::to_layout () const
}
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
top.shapes (l10).insert (dbu_trans * e->edge ());
if (e->is_segment ()) {
top.shapes (l10).insert (dbu_trans * e->edge ());
}
}
return layout;
@ -677,8 +686,7 @@ Triangles::remove_outside_vertex (db::Vertex *vertex, std::vector<db::Triangle *
void
Triangles::remove_inside_vertex (db::Vertex *vertex, std::vector<db::Triangle *> *new_triangles_out)
{
std::vector<db::Triangle * > triangles_to_fix;
std::set<db::Triangle * > triangles_to_fix_set;
std::set<db::Triangle *, TriangleLessFunc> triangles_to_fix;
bool make_new_triangle = true;
@ -695,18 +703,16 @@ Triangles::remove_inside_vertex (db::Vertex *vertex, std::vector<db::Triangle *>
}
// NOTE: in the "can_join" case zero-area triangles are created which we will sort out later
triangles_to_fix_set.erase (to_flip->left ());
triangles_to_fix_set.erase (to_flip->right ());
triangles_to_fix.erase (to_flip->left ());
triangles_to_fix.erase (to_flip->right ());
auto pp = flip (to_flip);
triangles_to_fix.push_back (pp.first.first);
triangles_to_fix_set.insert (pp.first.first);
triangles_to_fix.push_back (pp.first.second);
triangles_to_fix_set.insert (pp.first.second);
triangles_to_fix.insert (pp.first.first);
triangles_to_fix.insert (pp.first.second);
}
while (vertex->num_edges () > 3) {
if (vertex->num_edges () > 3) {
tl_assert (vertex->num_edges () == 4);
@ -739,10 +745,8 @@ Triangles::remove_inside_vertex (db::Vertex *vertex, std::vector<db::Triangle *>
db::Triangle *t1 = create_triangle (s1, s2, new_edge);
db::Triangle *t2 = create_triangle (s1opp, s2opp, new_edge);
triangles_to_fix.push_back (t1);
triangles_to_fix_set.insert (t1);
triangles_to_fix.push_back (t2);
triangles_to_fix_set.insert (t2);
triangles_to_fix.insert (t1);
triangles_to_fix.insert (t2);
make_new_triangle = false;
@ -755,33 +759,28 @@ Triangles::remove_inside_vertex (db::Vertex *vertex, std::vector<db::Triangle *>
outer_edges.push_back ((*t)->opposite (vertex));
}
for (auto t = to_remove.begin (); t != to_remove.end (); ++t) {
triangles_to_fix_set.erase (*t);
remove (*t);
}
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.push_back (nt);
triangles_to_fix_set.insert (nt);
triangles_to_fix.insert (nt);
}
std::vector<Triangle *>::iterator wp = triangles_to_fix.begin ();
for (auto t = triangles_to_fix.begin (); t != triangles_to_fix.end (); ++t) {
if (triangles_to_fix_set.find (*t) != triangles_to_fix_set.end ()) {
*wp++ = *t;
if (new_triangles_out) {
new_triangles_out->push_back (*t);
}
for (auto t = to_remove.begin (); t != to_remove.end (); ++t) {
triangles_to_fix.erase (*t);
remove (*t);
}
if (new_triangles_out) {
for (auto t = triangles_to_fix.begin (); t != triangles_to_fix.end (); ++t) {
new_triangles_out->push_back (*t);
}
}
triangles_to_fix.erase (wp, triangles_to_fix.end ());
fix_triangles (triangles_to_fix, std::vector<db::TriangleEdge *> (), new_triangles_out);
std::vector<db::Triangle *> to_fix_a (triangles_to_fix.begin (), triangles_to_fix.end ());
fix_triangles (to_fix_a, std::vector<db::TriangleEdge *> (), new_triangles_out);
}
int
@ -794,13 +793,13 @@ Triangles::fix_triangles (const std::vector<db::Triangle *> &tris, const std::ve
(*e)->set_level (m_level);
}
std::vector<db::TriangleEdge *> queue, todo;
std::set<db::TriangleEdge *, TriangleEdgeLessFunc> 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.push_back (e);
queue.insert (e);
}
}
}
@ -809,7 +808,6 @@ Triangles::fix_triangles (const std::vector<db::Triangle *> &tris, const std::ve
todo.clear ();
todo.swap (queue);
std::set<db::TriangleEdge *> queued;
// NOTE: we cannot be sure that already treated edges will not become
// illegal by neighbor edges flipping ..
@ -820,7 +818,7 @@ Triangles::fix_triangles (const std::vector<db::Triangle *> &tris, const std::ve
if (is_illegal_edge (*e)) {
queued.erase (*e);
queue.erase (*e);
auto pp = flip (*e);
auto t1 = pp.first.first;
@ -837,17 +835,15 @@ Triangles::fix_triangles (const std::vector<db::Triangle *> &tris, const std::ve
for (int i = 0; i < 3; ++i) {
db::TriangleEdge *s1 = t1->edge (i);
if (s1->level () < m_level && ! s1->is_segment () && queued.find (s1) == queued.end ()) {
queue.push_back (s1);
queued.insert (s1);
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 () && queued.find (s2) == queued.end ()) {
queue.push_back (s2);
queued.insert (s2);
if (s2->level () < m_level && ! s2->is_segment ()) {
queue.insert (s2);
}
}
@ -855,14 +851,6 @@ Triangles::fix_triangles (const std::vector<db::Triangle *> &tris, const std::ve
}
std::vector<db::TriangleEdge *>::iterator wp = queue.begin ();
for (auto e = queue.begin (); e != queue.end (); ++e) {
if (queued.find (*e) != queued.end ()) {
*wp++ = *e;
}
}
queue.erase (wp, queue.end ());
}
return flips;

View File

@ -156,6 +156,7 @@ private:
std::list<db::Vertex> m_vertex_heap;
bool m_is_constrained;
size_t m_level;
size_t m_id;
db::Vertex *create_vertex (double x, double y);
db::Vertex *create_vertex (const db::DPoint &pt);

View File

@ -24,6 +24,8 @@
#include "dbTriangles.h"
#include "tlUnitTest.h"
#include <set>
#include <vector>
#include <cstdlib>
#include <cmath>
@ -187,7 +189,7 @@ TEST(Triangle_test_heavy_insert)
}
// not strictly true, but very likely with at least 10 vertexes:
EXPECT_EQ (tris.num_triangles () >= 1, true);
EXPECT_EQ (tris.num_triangles () > 0, true);
EXPECT_EQ (tris.bbox ().to_string (), bbox.to_string ());
bool ok = true;
@ -216,3 +218,59 @@ TEST(Triangle_test_heavy_insert)
tl::info << tl::endl << "done.";
}
TEST(Triangle_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;
db::Triangles 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<db::Vertex *> vset;
std::vector<db::Vertex *> 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);
}
}
}
int loop = 0; // @@@
while (! vertexes.empty ()) {
++loop; printf("@@@ %d\n", loop); fflush(stdout);
if (loop == 38) {
printf("@@@BANG!\n"); // @@@
}
unsigned int n = rand () % (unsigned int) vertexes.size ();
db::Vertex *v = vertexes [n];
tris.remove (v);
vertexes.erase (vertexes.begin () + n);
EXPECT_EQ (tris.check (), true);
}
EXPECT_EQ (tris.num_triangles (), size_t (0));
}
tl::info << tl::endl << "done.";
}