Triangles: Performance improvement

This commit is contained in:
Matthias Koefferlein 2023-08-19 00:39:31 +02:00
parent 2f8144b507
commit 5c46cdfda3
6 changed files with 134 additions and 85 deletions

View File

@ -68,8 +68,8 @@ Vertex::Vertex (db::DCoord x, db::DCoord y)
bool
Vertex::is_outside () const
{
for (auto e = m_edges.begin (); e != m_edges.end (); ++e) {
if (e->is_outside ()) {
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
if ((*e)->is_outside ()) {
return true;
}
}
@ -81,8 +81,8 @@ Vertex::triangles () const
{
std::set<db::Triangle *> seen;
std::vector<db::Triangle *> res;
for (auto e = m_edges.begin (); e != m_edges.end (); ++e) {
for (auto t = e->begin_triangles (); t != e->end_triangles (); ++t) {
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-> ());
}
@ -94,8 +94,8 @@ Vertex::triangles () const
bool
Vertex::has_edge (const TriangleEdge *edge) const
{
for (auto e = m_edges.begin (); e != m_edges.end (); ++e) {
if (e.operator-> () == edge) {
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
if (*e == edge) {
return true;
}
}
@ -107,11 +107,23 @@ Vertex::to_string (bool with_id) const
{
std::string res = tl::sprintf ("(%.12g, %.12g)", x (), y());
if (with_id) {
res += tl::sprintf ("[%p]", (void *)this);
res += tl::sprintf ("[%x]", (size_t)this);
}
return res;
}
void
Vertex::remove_edge (db::TriangleEdge *edge)
{
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
if (*e == edge) {
mp_edges.erase (e);
return;
}
}
tl_assert (false);
}
int
Vertex::in_circle (const DPoint &point, const DPoint &center, double radius)
{
@ -141,8 +153,7 @@ TriangleEdge::TriangleEdge ()
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)
{
v1->m_edges.push_back (this);
v2->m_edges.push_back (this);
// .. nothing yet ..
}
void
@ -157,14 +168,33 @@ TriangleEdge::set_right (Triangle *t)
mp_right = t;
}
void
TriangleEdge::link ()
{
mp_v1->mp_edges.push_back (this);
mp_v2->mp_edges.push_back (this);
}
void
TriangleEdge::unlink ()
{
if (mp_v1) {
mp_v1->remove_edge (this);
}
if (mp_v2) {
mp_v2->remove_edge (this);
}
mp_v1 = mp_v2 = 0;
}
Triangle *
TriangleEdge::other (const Triangle *t) const
{
if (t == mp_left.get ()) {
return const_cast<Triangle *> (mp_right.get ());
if (t == mp_left) {
return mp_right;
}
if (t == mp_right.get ()) {
return const_cast<Triangle *> (mp_left.get ());
if (t == mp_right) {
return mp_left;
}
tl_assert (false);
return 0;
@ -206,7 +236,7 @@ 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 ("[%p]", (void *)this);
res += tl::sprintf ("[%x]", (size_t)this);
}
return res;
}
@ -341,6 +371,11 @@ Triangle::Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3)
}
}
Triangle::~Triangle ()
{
unlink ();
}
void
Triangle::unlink ()
{
@ -376,7 +411,6 @@ Triangle::to_string (bool with_id) const
Vertex *
Triangle::vertex (int n) const
{
tl_assert (mp_e1 && mp_e2 && mp_e3);
n = (n + 3) % 3;
if (n == 0) {
return mp_v1;
@ -392,11 +426,11 @@ Triangle::edge (int n) const
{
n = (n + 3) % 3;
if (n == 0) {
return const_cast<TriangleEdge *> (mp_e1.get ());
return mp_e1;
} else if (n == 1) {
return const_cast<TriangleEdge *> (mp_e2.get ());
return mp_e2;
} else {
return const_cast<TriangleEdge *> (mp_e3.get ());
return mp_e3;
}
}

View File

@ -48,7 +48,7 @@ class DB_PUBLIC Vertex
: public db::DPoint
{
public:
typedef tl::weak_collection<db::TriangleEdge> edges_type;
typedef std::vector<TriangleEdge *> edges_type;
typedef edges_type::const_iterator edges_iterator;
Vertex ();
@ -61,9 +61,9 @@ public:
bool is_outside () const;
std::vector<db::Triangle *> triangles () const;
edges_iterator begin_edges () const { return m_edges.begin (); }
edges_iterator end_edges () const { return m_edges.end (); }
size_t num_edges () const { return m_edges.size (); }
edges_iterator begin_edges () const { return mp_edges.begin (); }
edges_iterator end_edges () const { return mp_edges.end (); }
size_t num_edges () const { return mp_edges.size (); }
bool has_edge (const TriangleEdge *edge) const;
@ -88,8 +88,9 @@ public:
private:
friend class TriangleEdge;
void remove_edge (db::TriangleEdge *edge);
edges_type m_edges;
edges_type mp_edges;
size_t m_level;
};
@ -97,7 +98,6 @@ private:
* @brief A class representing an edge in the Delaunay triangulation graph
*/
class DB_PUBLIC TriangleEdge
: public tl::Object
{
public:
class TriangleIterator
@ -153,7 +153,6 @@ public:
};
TriangleEdge ();
TriangleEdge (Vertex *v1, Vertex *v2);
Vertex *v1 () const { return mp_v1; }
Vertex *v2 () const { return mp_v2; }
@ -162,14 +161,14 @@ public:
{
std::swap (mp_v1, mp_v2);
Triangle *l = mp_left.get ();
Triangle *r = mp_right.get ();
Triangle *l = mp_left;
Triangle *r = mp_right;
mp_left = r;
mp_right = l;
}
Triangle *left () const { return const_cast<Triangle *> (mp_left.get ()); }
Triangle *right () const { return const_cast<Triangle *> (mp_right.get ()); }
Triangle *left () const { return mp_left; }
Triangle *right () const { return mp_right; }
TriangleIterator begin_triangles () const
{
@ -385,19 +384,23 @@ public:
*/
bool has_triangle (const Triangle *t) const;
// --- exposed for test purposes only ---
TriangleEdge (Vertex *v1, Vertex *v2);
void unlink ();
void link ();
private:
friend class Triangle;
friend class Triangles;
Vertex *mp_v1, *mp_v2;
tl::weak_ptr<Triangle> mp_left, mp_right;
Triangle *mp_left, *mp_right;
size_t m_level;
size_t m_id;
bool m_is_segment;
// no copying
// @@@ TriangleEdge &operator= (const TriangleEdge &);
// @@@ TriangleEdge (const TriangleEdge &);
void set_left (Triangle *t);
void set_right (Triangle *t);
};
@ -425,6 +428,8 @@ public:
Triangle ();
Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3);
~Triangle ();
void unlink ();
void set_id (size_t id) { m_id = id; }
@ -499,7 +504,7 @@ public:
*/
bool has_edge (const db::TriangleEdge *e) const
{
return mp_e1.get () == e || mp_e2.get () == e || mp_e3.get () == e;
return mp_e1 == e || mp_e2 == e || mp_e3 == e;
}
/**
@ -524,7 +529,7 @@ public:
private:
bool m_is_outside;
tl::weak_ptr<TriangleEdge> mp_e1, mp_e2, mp_e3;
TriangleEdge *mp_e1, *mp_e2, *mp_e3;
db::Vertex *mp_v1, *mp_v2, *mp_v3;
size_t m_id;

View File

@ -44,8 +44,6 @@ Triangles::~Triangles ()
while (! mp_triangles.empty ()) {
remove (mp_triangles.front ());
}
tl_assert (mp_edges.empty ());
}
db::Vertex *
@ -65,10 +63,10 @@ Triangles::create_vertex (const db::DPoint &pt)
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;
m_edges_heap.push_back (db::TriangleEdge (v1, v2));
m_edges_heap.back ().link ();
m_edges_heap.back ().set_id (++m_id);
return &m_edges_heap.back ();
}
db::Triangle *
@ -93,7 +91,7 @@ Triangles::remove (db::Triangle *tri)
// clean up edges we do no longer need
for (int i = 0; i < 3; ++i) {
if (edges [i] && edges [i]->left () == 0 && edges [i]->right () == 0) {
delete edges [i];
edges [i]->unlink ();
}
}
}
@ -172,7 +170,11 @@ Triangles::check (bool check_delaunay) const
}
}
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
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 ()) {
@ -181,11 +183,6 @@ 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);
@ -223,7 +220,7 @@ Triangles::check (bool check_delaunay) const
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 ()) {
if ((*e)->is_outside ()) {
++num_outside_edges;
}
}
@ -231,8 +228,8 @@ Triangles::check (bool check_delaunay) const
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);
if ((*e)->is_outside ()) {
tl::error << " Outside edge is " << (*e)->to_string (true);
}
}
}
@ -264,8 +261,8 @@ Triangles::to_layout () const
top.shapes (t->is_outside () ? l2 : l1).insert (dbu_trans * poly);
}
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
if (e->is_segment ()) {
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 ());
}
}
@ -301,7 +298,7 @@ Triangles::find_points_around (db::Vertex *vertex, double radius)
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);
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);
@ -386,7 +383,7 @@ Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool insi
{
if (!vstart) {
if (! mp_edges.empty ()) {
if (! mp_triangles.empty ()) {
unsigned int ls = 0;
size_t n = m_vertex_heap.size ();
@ -395,13 +392,14 @@ Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool insi
// A sample heuristics that takes a sqrt(N) sample from the
// vertexes to find a good starting point
vstart = mp_edges.front ()->v1 ();
vstart = mp_triangles.front ()->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);
@ -435,20 +433,20 @@ Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool insi
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 ()) {
if (! (*e)->is_segment () && (*e)->is_for_outside_triangles ()) {
continue;
}
if (! e->crosses_including (line)) {
if (! (*e)->crosses_including (line)) {
continue;
}
}
double ds = e->distance (p);
double ds = (*e)->distance (p);
if (d < 0.0 || ds < d) {
d = ds;
edge = const_cast<db::TriangleEdge *> (e.operator-> ());
edge = *e;
vnext = edge->other (v);
} else if (fabs (ds - d) < std::max (1.0, fabs (ds) + fabs (d)) * db::epsilon) {
@ -456,15 +454,15 @@ Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool insi
// 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.operator-> ());
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 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) {
edge = const_cast<db::TriangleEdge *> (e.operator-> ());
edge = *e;
vnext = edge->other (v);
}
}
@ -550,10 +548,10 @@ Triangles::add_more_triangles (std::vector<db::Triangle *> &new_triangles,
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 ()) {
if (! (*e)->has_vertex (to_vertex) && (*e)->is_outside ()) {
// TODO: remove and break
tl_assert (next_edge == 0);
next_edge = const_cast<db::TriangleEdge *> (e.operator-> ());
next_edge = *e;
}
}
@ -730,8 +728,8 @@ Triangles::remove_inside_vertex (db::Vertex *vertex, std::list<tl::weak_ptr<db::
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 = const_cast<db::TriangleEdge *> (e.operator-> ());
if ((*e)->can_flip ()) {
to_flip = *e;
}
}
if (! to_flip) {
@ -756,8 +754,8 @@ Triangles::remove_inside_vertex (db::Vertex *vertex, std::list<tl::weak_ptr<db::
// 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 = const_cast <db::TriangleEdge *> (e.operator-> ());
if ((*e)->can_join_via (vertex)) {
jseg = *e;
}
}
tl_assert (jseg != 0);
@ -769,8 +767,8 @@ Triangles::remove_inside_vertex (db::Vertex *vertex, std::list<tl::weak_ptr<db::
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 = const_cast <db::TriangleEdge *> (e.operator-> ());
if (!(*e)->has_triangle (jseg->left ()) && !(*e)->has_triangle (jseg->right ())) {
jseg_opp = *e;
}
}
@ -1084,7 +1082,7 @@ Triangles::search_edges_crossing (Vertex *from, Vertex *to)
std::vector<db::TriangleEdge *> 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) {
for (auto t = (*e)->begin_triangles (); t != (*e)->end_triangles (); ++t) {
db::TriangleEdge *os = t->opposite (v);
if (os->has_vertex (vv)) {
return result;
@ -1152,8 +1150,8 @@ Triangles::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2)
return 0;
}
for (auto e = v->begin_edges (); e != v->end_edges (); ++e) {
if (e->other (v)->equal (p2)) {
return const_cast <db::TriangleEdge *>(e.operator-> ());
if ((*e)->other (v)->equal (p2)) {
return *e;
}
}
return 0;
@ -1240,9 +1238,9 @@ Triangles::join_edges (std::vector<db::TriangleEdge *> &edges)
std::vector<db::TriangleEdge *> join_edges;
for (auto e = cp->begin_edges (); e != cp->end_edges (); ++e) {
if (e.operator-> () != s1 && e.operator-> () != s2) {
if (e->can_join_via (cp)) {
join_edges.push_back (const_cast<db::TriangleEdge *> (e.operator-> ()));
if (*e != s1 && *e != s2) {
if ((*e)->can_join_via (cp)) {
join_edges.push_back (*e);
} else {
join_edges.clear ();
break;
@ -1380,7 +1378,7 @@ Triangles::remove_outside_triangles ()
void
Triangles::clear ()
{
mp_edges.clear ();
m_edges_heap.clear ();
mp_triangles.clear ();
m_vertex_heap.clear ();
m_is_constrained = false;
@ -1549,7 +1547,7 @@ Triangles::triangulate (const db::Region &region, const TriangulateParameters &p
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 ();
has_segment = (*e)->is_segment ();
}
if (! has_segment) {
to_delete.push_back (*v);

View File

@ -253,7 +253,7 @@ public:
private:
tl::shared_collection<db::Triangle> mp_triangles;
tl::weak_collection<db::TriangleEdge> mp_edges;
tl::stable_vector<db::TriangleEdge> m_edges_heap;
tl::stable_vector<db::Vertex> m_vertex_heap;
bool m_is_constrained;
size_t m_level;

View File

@ -52,7 +52,7 @@ static std::string edges_from_vertex (const db::Vertex &v)
if (! res.empty ()) {
res += ", ";
}
res += e->to_string ();
res += (*e)->to_string ();
}
return res;
}
@ -77,20 +77,24 @@ TEST(Vertex_edge_registration)
db::Vertex v3 (2, 1);
std::unique_ptr<db::TriangleEdge> e1 (new db::TriangleEdge (&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<db::TriangleEdge> e2 (new db::TriangleEdge (&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), "");
@ -106,8 +110,11 @@ TEST(Vertex_triangles)
EXPECT_EQ (triangles_from_vertex (v1), "");
std::unique_ptr<db::TriangleEdge> e1 (new db::TriangleEdge (&v1, &v2));
e1->link ();
std::unique_ptr<db::TriangleEdge> e2 (new db::TriangleEdge (&v2, &v3));
e2->link ();
std::unique_ptr<db::TriangleEdge> e3 (new db::TriangleEdge (&v3, &v1));
e3->link ();
std::unique_ptr<db::Triangle> tri (new db::Triangle (e1.get (), e2.get (), e3.get ()));
EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (1, 2), (2, 1))");
@ -115,12 +122,20 @@ TEST(Vertex_triangles)
EXPECT_EQ (triangles_from_vertex (v3), "((0, 0), (1, 2), (2, 1))");
std::unique_ptr<db::TriangleEdge> e4 (new db::TriangleEdge (&v1, &v4));
e4->link ();
std::unique_ptr<db::TriangleEdge> e5 (new db::TriangleEdge (&v2, &v4));
e5->link ();
std::unique_ptr<db::Triangle> 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

View File

@ -643,8 +643,6 @@ TEST(create_constrained_delaunay)
TEST(triangulate)
{
tl::equals(1.0, 2.0);
return; // @@@
db::Region r;
r.insert (db::Box (0, 0, 10000, 10000));
@ -669,7 +667,6 @@ TEST(triangulate)
EXPECT_GT (tri.num_triangles (), size_t (100));
EXPECT_LT (tri.num_triangles (), size_t (150));
tl::info << tri.num_triangles ();
param.min_b = 1.0;
param.max_area = 0.1;