Refactoring: generalization of Triangles into db::plc::Graph in dbPLC.h

This commit is contained in:
Matthias Koefferlein 2025-04-13 22:44:14 +02:00
parent 291c91fcaa
commit 627e244443
9 changed files with 3602 additions and 404 deletions

View File

@ -70,13 +70,14 @@ SOURCES = \
dbNetlistSpiceReaderExpressionParser.cc \
dbObject.cc \
dbObjectWithProperties.cc \
dbPLC.cc \
dbPLCTriangulation.cc \
dbPath.cc \
dbPCellDeclaration.cc \
dbPCellHeader.cc \
dbPCellVariant.cc \
dbPoint.cc \
dbPolygon.cc \
dbPolygonGraph.cc \
dbPolygonNeighborhood.cc \
dbPolygonTools.cc \
dbPolygonGenerators.cc \
@ -309,13 +310,14 @@ HEADERS = \
dbObject.h \
dbObjectTag.h \
dbObjectWithProperties.h \
dbPLC.h \
dbPLCTriangulation.h \
dbPath.h \
dbPCellDeclaration.h \
dbPCellHeader.h \
dbPCellVariant.h \
dbPoint.h \
dbPolygon.h \
dbPolygonGraph.h \
dbPolygonNeighborhood.h \
dbPolygonTools.h \
dbPolygonGenerators.h \

View File

@ -21,7 +21,7 @@
*/
#include "dbPolygonGraph.h"
#include "dbPLC.h"
#include "dbLayout.h"
#include "dbWriter.h"
#include "tlStream.h"
@ -36,28 +36,43 @@
namespace db
{
namespace plc
{
// -------------------------------------------------------------------------------------
// GVertex implementation
// Vertex implementation
GVertex::GVertex ()
: DPoint (), m_is_precious (false)
Vertex::Vertex (Graph *graph)
: DPoint (), mp_graph (graph), m_is_precious (false)
{
// .. nothing yet ..
}
GVertex::GVertex (const db::DPoint &p)
: DPoint (p), m_is_precious (false)
Vertex::Vertex (Graph *graph, const db::DPoint &p)
: DPoint (p), mp_graph (graph), m_is_precious (false)
{
// .. nothing yet ..
}
GVertex::GVertex (const GVertex &v)
: DPoint (), m_is_precious (false)
Vertex::Vertex (Graph *graph, const Vertex &v)
: DPoint (), mp_graph (graph), m_is_precious (false)
{
operator= (v);
}
GVertex &GVertex::operator= (const GVertex &v)
Vertex::Vertex (Graph *graph, db::DCoord x, db::DCoord y)
: DPoint (x, y), mp_graph (graph), m_is_precious (false)
{
// .. nothing yet ..
}
Vertex::Vertex (const Vertex &v)
: DPoint (v), mp_graph (v.mp_graph), m_is_precious (v.m_is_precious)
{
// NOTE: edges are not copied!
}
Vertex &Vertex::operator= (const Vertex &v)
{
if (this != &v) {
// NOTE: edges are not copied!
@ -67,15 +82,8 @@ GVertex &GVertex::operator= (const GVertex &v)
return *this;
}
GVertex::GVertex (db::DCoord x, db::DCoord y)
: DPoint (x, y), m_is_precious (false)
{
// .. nothing yet ..
}
#if 0 // @@@
bool
GVertex::is_outside () const
Vertex::is_outside () const
{
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
if ((*e)->is_outside ()) {
@ -84,13 +92,12 @@ GVertex::is_outside () const
}
return false;
}
#endif
std::vector<db::GPolygon *>
GVertex::polygons () const
std::vector<Polygon *>
Vertex::polygons () const
{
std::set<db::GPolygon *> seen;
std::vector<db::GPolygon *> res;
std::set<Polygon *> seen;
std::vector<Polygon *> res;
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
for (auto t = (*e)->begin_polygons (); t != (*e)->end_polygons (); ++t) {
if (seen.insert (t.operator-> ()).second) {
@ -102,7 +109,7 @@ GVertex::polygons () const
}
bool
GVertex::has_edge (const GPolygonEdge *edge) const
Vertex::has_edge (const Edge *edge) const
{
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
if (*e == edge) {
@ -113,7 +120,7 @@ GVertex::has_edge (const GPolygonEdge *edge) const
}
size_t
GVertex::num_edges (int max_count) const
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
@ -128,7 +135,7 @@ GVertex::num_edges (int max_count) const
}
std::string
GVertex::to_string (bool with_id) const
Vertex::to_string (bool with_id) const
{
std::string res = tl::sprintf ("(%.12g, %.12g)", x (), y());
if (with_id) {
@ -138,7 +145,7 @@ GVertex::to_string (bool with_id) const
}
int
GVertex::in_circle (const DPoint &point, const DPoint &center, double radius)
Vertex::in_circle (const DPoint &point, const DPoint &center, double radius)
{
double dx = point.x () - center.x ();
double dy = point.y () - center.y ();
@ -155,34 +162,34 @@ GVertex::in_circle (const DPoint &point, const DPoint &center, double radius)
}
// -------------------------------------------------------------------------------------
// GPolygonEdge implementation
// Edge implementation
GPolygonEdge::GPolygonEdge ()
: mp_v1 (0), mp_v2 (0), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false)
Edge::Edge (Graph *graph)
: mp_graph (graph), mp_v1 (0), mp_v2 (0), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false)
{
// .. nothing yet ..
}
GPolygonEdge::GPolygonEdge (GVertex *v1, GVertex *v2)
: mp_v1 (v1), mp_v2 (v2), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false)
Edge::Edge (Graph *graph, Vertex *v1, Vertex *v2)
: mp_graph (graph), mp_v1 (v1), mp_v2 (v2), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false)
{
// .. nothing yet ..
}
void
GPolygonEdge::set_left (GPolygon *t)
Edge::set_left (Polygon *t)
{
mp_left = t;
}
void
GPolygonEdge::set_right (GPolygon *t)
Edge::set_right (Polygon *t)
{
mp_right = t;
}
void
GPolygonEdge::link ()
Edge::link ()
{
mp_v1->mp_edges.push_back (this);
m_ec_v1 = --mp_v1->mp_edges.end ();
@ -192,7 +199,7 @@ GPolygonEdge::link ()
}
void
GPolygonEdge::unlink ()
Edge::unlink ()
{
if (mp_v1) {
mp_v1->remove_edge (m_ec_v1);
@ -203,8 +210,8 @@ GPolygonEdge::unlink ()
mp_v1 = mp_v2 = 0;
}
GPolygon *
GPolygonEdge::other (const GPolygon *t) const
Polygon *
Edge::other (const Polygon *t) const
{
if (t == mp_left) {
return mp_right;
@ -216,8 +223,8 @@ GPolygonEdge::other (const GPolygon *t) const
return 0;
}
GVertex *
GPolygonEdge::other (const GVertex *t) const
Vertex *
Edge::other (const Vertex *t) const
{
if (t == mp_v1) {
return mp_v2;
@ -230,13 +237,13 @@ GPolygonEdge::other (const GVertex *t) const
}
bool
GPolygonEdge::has_vertex (const GVertex *v) const
Edge::has_vertex (const Vertex *v) const
{
return mp_v1 == v || mp_v2 == v;
}
GVertex *
GPolygonEdge::common_vertex (const GPolygonEdge *other) const
Vertex *
Edge::common_vertex (const Edge *other) const
{
if (has_vertex (other->v1 ())) {
return (other->v1 ());
@ -248,7 +255,7 @@ GPolygonEdge::common_vertex (const GPolygonEdge *other) const
}
std::string
GPolygonEdge::to_string (bool with_id) const
Edge::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) {
@ -258,7 +265,7 @@ GPolygonEdge::to_string (bool with_id) const
}
double
GPolygonEdge::distance (const db::DEdge &e, const db::DPoint &p)
Edge::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;
@ -273,27 +280,27 @@ GPolygonEdge::distance (const db::DEdge &e, const db::DPoint &p)
}
bool
GPolygonEdge::crosses (const db::DEdge &e, const db::DEdge &other)
Edge::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
GPolygonEdge::crosses_including (const db::DEdge &e, const db::DEdge &other)
Edge::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
GPolygonEdge::intersection_point (const db::DEdge &e, const db::DEdge &other)
Edge::intersection_point (const db::DEdge &e, const db::DEdge &other)
{
return e.intersect_point (other).second;
}
bool
GPolygonEdge::point_on (const db::DEdge &edge, const db::DPoint &point)
Edge::point_on (const db::DEdge &edge, const db::DPoint &point)
{
if (edge.side_of (point) != 0) {
return false;
@ -302,42 +309,72 @@ GPolygonEdge::point_on (const db::DEdge &edge, const db::DPoint &point)
}
}
#if 0 // @@@
bool
GPolygonEdge::is_for_outside_polygons () const
Edge::can_flip () const
{
if (! left () || ! right ()) {
return false;
}
const Vertex *v1 = left ()->opposite (this);
const Vertex *v2 = right ()->opposite (this);
return crosses (db::DEdge (*v1, *v2));
}
bool
Edge::can_join_via (const Vertex *vertex) const
{
if (! left () || ! right ()) {
return false;
}
tl_assert (has_vertex (vertex));
const Vertex *v1 = left ()->opposite (this);
const Vertex *v2 = right ()->opposite (this);
return db::DEdge (*v1, *v2).side_of (*vertex) == 0;
}
bool
Edge::is_outside () const
{
return left () == 0 || right () == 0;
}
bool
Edge::is_for_outside_triangles () const
{
return (left () && left ()->is_outside ()) || (right () && right ()->is_outside ());
}
#endif
bool
GPolygonEdge::has_polygon (const GPolygon *t) const
Edge::has_polygon (const Polygon *t) const
{
return t != 0 && (left () == t || right () == t);
}
// -------------------------------------------------------------------------------------
// GPolygon implementation
// Polygon implementation
GPolygon::GPolygon ()
: m_id (0)
Polygon::Polygon (Graph *graph)
: mp_graph (graph), m_is_outside (false), m_id (0)
{
// .. nothing yet ..
}
void
GPolygon::init ()
Polygon::init ()
{
m_id = 0;
m_is_outside = false;
if (mp_e.empty ()) {
return;
}
std::vector<GPolygonEdge *> e;
std::vector<Edge *> e;
e.swap (mp_e);
std::multimap<db::GVertex *, GPolygonEdge *> v2e;
std::multimap<Vertex *, Edge *> v2e;
for (auto i = e.begin (); i != e.end (); ++i) {
if (i != e.begin ()) {
@ -379,7 +416,7 @@ GPolygon::init ()
// establish clockwise order of the vertexes
double area = 0.0;
const db::GVertex *vm1 = vertex (-1), *v0;
const Vertex *vm1 = vertex (-1), *v0;
for (auto i = mp_v.begin (); i != mp_v.end (); ++i) {
v0 = *i;
area += db::vprod (*vm1 - db::DPoint (), *v0 - *vm1);
@ -394,8 +431,8 @@ GPolygon::init ()
// link the polygon to the edges
for (size_t i = 0; i < size (); ++i) {
db::GVertex *v = mp_v[i];
db::GPolygonEdge *e = mp_e[i];
Vertex *v = mp_v[i];
Edge *e = mp_e[i];
if (e->v1 () == v) {
e->set_right (this);
} else {
@ -404,13 +441,62 @@ GPolygon::init ()
}
}
GPolygon::~GPolygon ()
Polygon::Polygon (Graph *graph, Edge *e1, Edge *e2, Edge *e3)
: mp_graph (graph), m_is_outside (false), m_id (0)
{
mp_e.resize (3, 0);
mp_v.resize (3, 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) {
Edge *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);
}
}
}
Polygon::~Polygon ()
{
unlink ();
}
void
GPolygon::unlink ()
Polygon::unlink ()
{
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
if ((*e)->left () == this) {
@ -423,7 +509,7 @@ GPolygon::unlink ()
}
std::string
GPolygon::to_string (bool with_id) const
Polygon::to_string (bool with_id) const
{
std::string res = "(";
for (int i = 0; i < int (size ()); ++i) {
@ -441,13 +527,13 @@ GPolygon::to_string (bool with_id) const
}
double
GPolygon::area () const
Polygon::area () const
{
return fabs (db::vprod (mp_e[0]->d (), mp_e[1]->d ())) * 0.5;
}
db::DBox
GPolygon::bbox () const
Polygon::bbox () const
{
db::DBox box;
for (auto i = mp_v.begin (); i != mp_v.end (); ++i) {
@ -456,8 +542,77 @@ GPolygon::bbox () const
return box;
}
GPolygonEdge *
GPolygon::find_edge_with (const GVertex *v1, const GVertex *v2) const
std::pair<db::DPoint, double>
Polygon::circumcircle (bool *ok) const
{
tl_assert (mp_v.size () == 3);
// 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 *
Polygon::opposite (const Edge *edge) const
{
tl_assert (mp_v.size () == 3);
for (int i = 0; i < 3; ++i) {
Vertex *v = mp_v[i];
if (! edge->has_vertex (v)) {
return v;
}
}
tl_assert (false);
}
Edge *
Polygon::opposite (const Vertex *vertex) const
{
tl_assert (mp_v.size () == 3);
for (int i = 0; i < 3; ++i) {
Edge *e = mp_e[i];
if (! e->has_vertex (vertex)) {
return e;
}
}
tl_assert (false);
}
Edge *
Polygon::find_edge_with (const Vertex *v1, const Vertex *v2) const
{
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
if ((*e)->has_vertex (v1) && (*e)->has_vertex (v2)) {
@ -467,8 +622,8 @@ GPolygon::find_edge_with (const GVertex *v1, const GVertex *v2) const
tl_assert (false);
}
GPolygonEdge *
GPolygon::common_edge (const GPolygon *other) const
Edge *
Polygon::common_edge (const Polygon *other) const
{
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
if ((*e)->other (this) == other) {
@ -478,10 +633,11 @@ GPolygon::common_edge (const GPolygon *other) const
return 0;
}
#if 0 // @@@
int
GPolygon::contains (const db::DPoint &point) const
Polygon::contains (const db::DPoint &point) const
{
tl_assert (mp_v.size () == 3);
auto c = *mp_v[2] - *mp_v[0];
auto b = *mp_v[1] - *mp_v[0];
@ -492,9 +648,9 @@ GPolygon::contains (const db::DPoint &point) const
int res = 1;
const GVertex *vl = mp_v[2];
const Vertex *vl = mp_v[2];
for (int i = 0; i < 3; ++i) {
const GVertex *v = mp_v[i];
const Vertex *v = mp_v[i];
int n = db::vprod_sign (point - *vl, *v - *vl) * vps;
if (n < 0) {
return -1;
@ -506,10 +662,9 @@ GPolygon::contains (const db::DPoint &point) const
return res;
}
#endif
double
GPolygon::min_edge_length () const
Polygon::min_edge_length () const
{
double lmin = mp_e[0]->d ().length ();
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
@ -518,19 +673,17 @@ GPolygon::min_edge_length () const
return lmin;
}
#if 0 // @@@
double
GPolygon::b () const
Polygon::b () const
{
double lmin = min_edge_length ();
bool ok = false;
auto cr = circumcircle (&ok);
return ok ? lmin / cr.second : 0.0;
}
#endif
bool
GPolygon::has_segment () const
Polygon::has_segment () const
{
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
if ((*e)->is_segment ()) {
@ -541,7 +694,7 @@ GPolygon::has_segment () const
}
unsigned int
GPolygon::num_segments () const
Polygon::num_segments () const
{
unsigned int n = 0;
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
@ -554,49 +707,42 @@ GPolygon::num_segments () const
// -----------------------------------------------------------------------------------
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;
}
PolygonGraph::PolygonGraph ()
Graph::Graph ()
: m_id (0)
// @@@: m_is_constrained (false), m_level (0), m_id (0), m_flips (0), m_hops (0)
{
// .. nothing yet ..
}
PolygonGraph::~PolygonGraph ()
Graph::~Graph ()
{
clear ();
}
db::GVertex *
PolygonGraph::create_vertex (double x, double y)
Vertex *
Graph::create_vertex (double x, double y)
{
m_vertex_heap.push_back (db::GVertex (x, y));
m_vertex_heap.push_back (Vertex (this, x, y));
return &m_vertex_heap.back ();
}
db::GVertex *
PolygonGraph::create_vertex (const db::DPoint &pt)
Vertex *
Graph::create_vertex (const db::DPoint &pt)
{
m_vertex_heap.push_back (pt);
m_vertex_heap.push_back (Vertex (this, pt));
return &m_vertex_heap.back ();
}
db::GPolygonEdge *
PolygonGraph::create_edge (db::GVertex *v1, db::GVertex *v2)
Edge *
Graph::create_edge (Vertex *v1, Vertex *v2)
{
db::GPolygonEdge *edge = 0;
Edge *edge = 0;
if (! m_returned_edges.empty ()) {
edge = m_returned_edges.back ();
m_returned_edges.pop_back ();
*edge = db::GPolygonEdge (v1, v2);
*edge = Edge (this, v1, v2);
} else {
m_edges_heap.push_back (db::GPolygonEdge (v1, v2));
m_edges_heap.push_back (Edge (this, v1, v2));
edge = &m_edges_heap.back ();
}
@ -605,11 +751,21 @@ PolygonGraph::create_edge (db::GVertex *v1, db::GVertex *v2)
return edge;
}
void
PolygonGraph::remove_polygon (db::GPolygon *poly)
Polygon *
Graph::create_triangle (Edge *e1, Edge *e2, Edge *e3)
{
std::vector<db::GPolygonEdge *> edges;
edges.reserve (poly->size ());
Polygon *res = new Polygon (this, e1, e2, e3);
res->set_id (++m_id);
mp_polygons.push_back (res);
return res;
}
void
Graph::remove_polygon (Polygon *poly)
{
std::vector<Edge *> edges;
edges.resize (poly->size (), 0);
for (int i = 0; i < int (poly->size ()); ++i) {
edges [i] = poly->edge (i);
}
@ -625,63 +781,8 @@ PolygonGraph::remove_polygon (db::GPolygon *poly)
}
}
#if 0 // @@@
void
PolygonGraph::convex_decompose (const db::DPolygon &polygon)
{
clear ();
if (polygon.begin_edge ().at_end ()) {
return;
}
std::vector<db::GPolygonEdge *> edges;
for (unsigned int c = 0; c < polygon.holes () + 1; ++c) {
const db::DSimplePolygon::contour_type &ctr = polygon.contour (c);
db::GVertex *v0 = 0, *vv, *v;
size_t n = ctr.size ();
for (size_t i = 0; i < n; ++i) {
db::DPoint pm1 = ctr [i > 0 ? i - 1 : n - 1];
db::DPoint pp1 = ctr [i + 1 < n ? i + 1 : 0];
db::DPoint p = ctr [i];
bool is_convex = db::vprod_sign (p - pm1, pp1 - p);
// @@@
v = create_vertex (p.x (), p.y ());
if (! v0) {
v0 = v;
} else {
edges.push_back (create_edge (vv, v));
}
vv = v;
}
if (v0 && v0 != v) {
edges.push_back (create_edge (v, v0));
}
}
}
#endif
void
PolygonGraph::convex_decompose (const db::DPolygon &poly)
{
// @@@
}
std::string
PolygonGraph::to_string ()
Graph::to_string ()
{
std::string res;
for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) {
@ -694,7 +795,7 @@ PolygonGraph::to_string ()
}
db::DBox
PolygonGraph::bbox () const
Graph::bbox () const
{
db::DBox box;
for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) {
@ -705,7 +806,7 @@ PolygonGraph::bbox () const
#if 0 // @@@
bool
PolygonGraph::check (bool check_delaunay) const
Graph::check (bool check_delaunay) const
{
bool res = true;
@ -759,7 +860,7 @@ PolygonGraph::check (bool check_delaunay) const
tl::error << "(check error) edges " << e->to_string (true) << " vertex 2 not found in adjacent polygon " << t->to_string (true);
res = false;
}
db::GVertex *vopp = t->opposite (e.operator-> ());
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) {
@ -803,7 +904,7 @@ PolygonGraph::check (bool check_delaunay) const
#endif
db::Layout *
PolygonGraph::to_layout (bool decompose_by_id) const
Graph::to_layout (bool decompose_by_id) const
{
db::Layout *layout = new db::Layout ();
layout->dbu (0.001);
@ -850,7 +951,7 @@ PolygonGraph::to_layout (bool decompose_by_id) const
}
void
PolygonGraph::dump (const std::string &path, bool decompose_by_id) const
Graph::dump (const std::string &path, bool decompose_by_id) const
{
std::unique_ptr<db::Layout> ly (to_layout (decompose_by_id));
@ -860,36 +961,19 @@ PolygonGraph::dump (const std::string &path, bool decompose_by_id) const
db::Writer writer (opt);
writer.write (*ly, stream);
tl::info << "PolygonGraph written to " << path;
tl::info << "Graph written to " << path;
}
void
PolygonGraph::clear ()
Graph::clear ()
{
mp_polygons.clear ();
m_edges_heap.clear ();
m_vertex_heap.clear ();
m_returned_edges.clear ();
// @@@m_is_constrained = false;
// @@@m_level = 0;
m_id = 0;
}
template<class Poly, class Trans>
void
PolygonGraph::make_contours (const Poly &poly, const Trans &trans, std::vector<std::vector<db::GVertex *> > &edge_contours)
{
edge_contours.push_back (std::vector<db::GVertex *> ());
for (auto pt = poly.begin_hull (); pt != poly.end_hull (); ++pt) {
edge_contours.back ().push_back (insert_point (trans * *pt));
}
} // namespace plc
for (unsigned int h = 0; h < poly.holes (); ++h) {
edge_contours.push_back (std::vector<db::GVertex *> ());
for (auto pt = poly.begin_hole (h); pt != poly.end_hole (h); ++pt) {
edge_contours.back ().push_back (insert_point (trans * *pt));
}
}
}
}
} // namespace db

View File

@ -20,8 +20,8 @@
*/
#ifndef HDR_dbPolygonGraph
#define HDR_dbPolygonGraph
#ifndef HDR_dbPLC
#define HDR_dbPLC
#include "dbCommon.h"
#include "dbTriangle.h"
@ -41,8 +41,38 @@ namespace db
class Layout;
class GPolygon;
class GPolygonEdge;
namespace plc
{
/**
* @brief A framework for piecewise linear curves
*
* This framework implements classes for dealing with piecewise linear
* curves. It is the basis for triangulation and polygon decomposition
* algorithms.
*
* The core class is the PLCGraph which is a collection of vertices,
* edges and edge loops (polygons). Vertices, edges and polygons form
* graphs.
*
* A "vertex" (db::plc::Vertex) is a point. A point connects two or
* more edges.
* A vertex has some attributes:
* * 'precious': if set, the vertex is not removed during triangulation
* for example.
*
* An "edge" (db::plc::Edge) is a line connecting two vertexes. The
* edge runs from vertex v1 to vertex v2. An edge separates two
* polygons (left and right, as seen in the run direction).
*
* A "segment" as an edge that is part of an original polygon outline.
*
* A "polygon" (db::plc::Polygon) is a loop of edges.
*/
class Polygon;
class Edge;
class Graph;
/**
* @brief A class representing a vertex in a Delaunay triangulation graph
@ -51,40 +81,71 @@ class GPolygonEdge;
* an integer value that can be used in traversal algorithms
* ("level")
*/
class DB_PUBLIC GVertex
class DB_PUBLIC Vertex
: public db::DPoint
{
public:
typedef std::list<GPolygonEdge *> edges_type;
typedef std::list<Edge *> edges_type;
typedef edges_type::const_iterator edges_iterator;
typedef edges_type::iterator edges_iterator_non_const;
GVertex ();
GVertex (const DPoint &p);
GVertex (const GVertex &v);
GVertex (db::DCoord x, db::DCoord y);
Vertex (const Vertex &v);
Vertex &operator= (const Vertex &v);
GVertex &operator= (const GVertex &v);
#if 0 // @@@
/**
* @brief Gets a value indicating whether any of the attached edges is "outside"
*/
bool is_outside () const;
#endif
std::vector<db::GPolygon *> polygons () const;
/**
* @brief Gets a list of polygons that are attached to this vertex
*/
std::vector<Polygon *> polygons() const;
/**
* @brief Gets the graph object this vertex belongs to
*/
Graph *graph () const { return mp_graph; }
/**
* @brief Iterates the edges on this vertex (begin)
*/
edges_iterator begin_edges () const { return mp_edges.begin (); }
/**
* @brief Iterates the edges on this vertex (end)
*/
edges_iterator end_edges () const { return mp_edges.end (); }
/**
* @brief Returns the number of edges attached to this vertex
*/
size_t num_edges (int max_count = -1) const;
bool has_edge (const GPolygonEdge *edge) const;
/**
* @brief Returns a value indicating whether the given edge is attached to this vertex
*/
bool has_edge (const Edge *edge) const;
/**
* @brief Sets a valid indicating whether the vertex is precious
*
* "precious" vertexes are not removed during triangulation for example.
*/
void set_is_precious (bool f) { m_is_precious = f; }
/**
* @brief Gets a valid indicating whether the vertex is precious
*/
bool is_precious () const { return m_is_precious; }
/**
* @brief Returns a string representation of the vertex
*/
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);
@ -97,13 +158,20 @@ public:
}
private:
friend class GPolygonEdge;
friend class Edge;
friend class Graph;
Vertex (Graph *graph);
Vertex (Graph *graph, const DPoint &p);
Vertex (Graph *graph, const Vertex &v);
Vertex (Graph *graph, db::DCoord x, db::DCoord y);
void remove_edge (const edges_iterator_non_const &ec)
{
mp_edges.erase (ec);
}
Graph *mp_graph;
edges_type mp_edges;
bool m_is_precious;
};
@ -111,15 +179,15 @@ private:
/**
* @brief A class representing an edge in the Delaunay triangulation graph
*/
class DB_PUBLIC GPolygonEdge
class DB_PUBLIC Edge
{
public:
class GPolygonIterator
class PolygonIterator
{
public:
typedef GPolygon value_type;
typedef GPolygon &reference;
typedef GPolygon *pointer;
typedef Polygon value_type;
typedef Polygon &reference;
typedef Polygon *pointer;
reference operator*() const
{
@ -131,17 +199,17 @@ public:
return m_index ? mp_edge->right () : mp_edge->left ();
}
bool operator== (const GPolygonIterator &other) const
bool operator== (const PolygonIterator &other) const
{
return m_index == other.m_index;
}
bool operator!= (const GPolygonIterator &other) const
bool operator!= (const PolygonIterator &other) const
{
return !operator== (other);
}
GPolygonIterator &operator++ ()
PolygonIterator &operator++ ()
{
while (++m_index < 2 && operator-> () == 0)
;
@ -149,9 +217,9 @@ public:
}
private:
friend class GPolygonEdge;
friend class Edge;
GPolygonIterator (const GPolygonEdge *edge)
PolygonIterator (const Edge *edge)
: mp_edge (edge), m_index (0)
{
if (! edge) {
@ -162,48 +230,72 @@ public:
}
}
const GPolygonEdge *mp_edge;
const Edge *mp_edge;
unsigned int m_index;
};
GPolygonEdge ();
GPolygonEdge (GVertex *v1, GVertex *v2);
/**
* @brief Gets the first vertex ("from")
*/
Vertex *v1 () const { return mp_v1; }
GVertex *v1 () const { return mp_v1; }
GVertex *v2 () const { return mp_v2; }
/**
* @brief Gets the first vertex ("to")
*/
Vertex *v2 () const { return mp_v2; }
/**
* @brief Reverses the edge
*/
void reverse ()
{
std::swap (mp_v1, mp_v2);
std::swap (mp_left, mp_right);
}
GPolygon *left () const { return mp_left; }
GPolygon *right () const { return mp_right; }
/**
* @brief Gets the polygon on the left side (can be null)
*/
Polygon *left () const { return mp_left; }
GPolygonIterator begin_polygons () const
/**
* @brief Gets the polygon on the right side (can be null)
*/
Polygon *right () const { return mp_right; }
/**
* @brief Iterates the polygons (one or two, begin iterator)
*/
PolygonIterator begin_polygons () const
{
return GPolygonIterator (this);
return PolygonIterator (this);
}
GPolygonIterator end_polygons () const
/**
* @brief Iterates the polygons (end iterator)
*/
PolygonIterator end_polygons () const
{
return GPolygonIterator (0);
return PolygonIterator (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; }
/**
* @brief Gets a value indicating whether the edge is a segment
*/
bool is_segment () const { return m_is_segment; }
/**
* @brief Gets the edge ID (a unique identifier)
*/
size_t id () const { return m_id; }
/**
* @brief Gets a string representation of the edge
*/
std::string to_string (bool with_id = false) const;
/**
* @brief Converts to an db::DEdge
* @brief Converts to a db::DEdge
*/
db::DEdge edge () const
{
@ -254,7 +346,7 @@ public:
* "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::GPolygonEdge &other) const
bool crosses (const Edge &other) const
{
return crosses (edge (), other.edge ());
}
@ -279,7 +371,7 @@ public:
* @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::GPolygonEdge &other) const
bool crosses_including (const Edge &other) const
{
return crosses_including (edge (), other.edge ());
}
@ -301,7 +393,7 @@ public:
/**
* @brief Gets the intersection point
*/
db::DPoint intersection_point (const GPolygonEdge &other) const
db::DPoint intersection_point (const Edge &other) const
{
return intersection_point (edge (), other.edge ());
}
@ -353,24 +445,23 @@ public:
/**
* @brief Gets the other triangle for the given one
*/
GPolygon *other (const GPolygon *) const;
Polygon *other (const Polygon *) const;
/**
* @brief Gets the other vertex for the given one
*/
GVertex *other (const GVertex *) const;
Vertex *other (const Vertex *) const;
/**
* @brief Gets a value indicating whether the edge has the given vertex
*/
bool has_vertex (const GVertex *) const;
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
*/
GVertex *common_vertex (const GPolygonEdge *other) const;
Vertex *common_vertex (const Edge *other) const;
#if 0 // @@@
/**
* @brief Returns a value indicating whether this edge can be flipped
*/
@ -379,81 +470,93 @@ public:
/**
* @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 GVertex *vertex) const;
bool can_join_via (const Vertex *vertex) 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 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;
#endif // @@@
/**
* @brief Returns a value indicating whether t is attached to this edge
*/
bool has_polygon (const GPolygon *t) const;
bool has_polygon (const Polygon *t) const;
protected:
void unlink ();
void link ();
private:
friend class GPolygon;
friend class PolygonGraph;
friend class Polygon;
friend class Graph;
friend class Triangulation;
GVertex *mp_v1, *mp_v2;
GPolygon *mp_left, *mp_right;
GVertex::edges_iterator_non_const m_ec_v1, m_ec_v2;
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; }
void set_is_segment (bool is_seg) { m_is_segment = is_seg; }
Edge (Graph *graph);
Edge (Graph *graph, Vertex *v1, Vertex *v2);
Graph *mp_graph;
Vertex *mp_v1, *mp_v2;
Polygon *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 (GPolygon *t);
void set_right (GPolygon *t);
void set_left (Polygon *t);
void set_right (Polygon *t);
};
/**
* @brief A compare function that compares triangles by ID
* @brief A compare function that compares edges by ID
*
* The ID acts as a more predicable unique ID for the object in sets and maps.
*/
struct GPolygonEdgeLessFunc
struct EdgeLessFunc
{
bool operator () (GPolygonEdge *a, GPolygonEdge *b) const
bool operator () (Edge *a, Edge *b) const
{
return a->id () < b->id ();
}
};
/**
* @brief A class representing a triangle
* @brief A class representing a polygon
*/
class DB_PUBLIC GPolygon
: public tl::list_node<GPolygon>, public tl::Object
class DB_PUBLIC Polygon
: public tl::list_node<Polygon>, public tl::Object
{
public:
GPolygon ();
Polygon (Graph *graph);
Polygon (Graph *graph, Edge *e1, Edge *e2, Edge *e3);
template<class Iter>
GPolygon (Iter from, Iter to)
: mp_e (from, to)
Polygon (Graph *graph, Iter from, Iter to)
: mp_graph (graph), mp_e (from, to)
{
init ();
}
~GPolygon ();
~Polygon ();
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; }
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;
@ -469,7 +572,7 @@ public:
* @brief Gets the nth vertex (n wraps around and can be negative)
* The vertexes are oriented clockwise.
*/
inline GVertex *vertex (int n) const
inline Vertex *vertex (int n) const
{
size_t sz = size ();
tl_assert (sz > 0);
@ -483,7 +586,7 @@ public:
/**
* @brief Gets the nth edge (n wraps around and can be negative)
*/
inline GPolygonEdge *edge (int n) const
inline Edge *edge (int n) const
{
size_t sz = size ();
tl_assert (sz > 0);
@ -504,46 +607,50 @@ public:
*/
db::DBox bbox () const;
#if 0 // @@@
/**
* @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).
*
* This method only applies to triangles.
*/
std::pair<db::DPoint, double> circumcircle (bool *ok = 0) const;
/**
* @brief Gets the vertex opposite of the given edge
*
* This method only applies to triangles.
*/
GVertex *opposite (const GPolygonEdge *edge) const;
Vertex *opposite (const Edge *edge) const;
/**
* @brief Gets the edge opposite of the given vertex
*
* This method only applies to triangles.
*/
GPolygonEdge *opposite (const GVertex *vertex) const;
#endif
Edge *opposite (const Vertex *vertex) const;
/**
* @brief Gets the edge with the given vertexes
*/
GPolygonEdge *find_edge_with (const GVertex *v1, const GVertex *v2) const;
Edge *find_edge_with (const Vertex *v1, const Vertex *v2) const;
/**
* @brief Finds the common edge for both polygons
*/
GPolygonEdge *common_edge (const GPolygon *other) const;
Edge *common_edge (const Polygon *other) const;
#if 0 // @@@
/**
* @brief Returns a value indicating whether the point is inside (1), on the polygon (0) or outside (-1)
*
* This method only applies to triangles currently.
*/
int contains (const db::DPoint &point) const;
#endif
/**
* @brief Gets a value indicating whether the triangle has the given vertex
*/
inline bool has_vertex (const db::GVertex *v) const
inline bool has_vertex (const Vertex *v) const
{
for (auto i = mp_v.begin (); i != mp_v.end (); ++i) {
if (*i == v) {
@ -556,7 +663,7 @@ public:
/**
* @brief Gets a value indicating whether the triangle has the given edge
*/
inline bool has_edge (const db::GPolygonEdge *e) const
inline bool has_edge (const Edge *e) const
{
for (auto i = mp_e.begin (); i != mp_e.end (); ++i) {
if (*i == e) {
@ -571,12 +678,12 @@ public:
*/
double min_edge_length () const;
#if 0 // @@@
/**
* @brief Returns the min edge length to circumcircle radius ratio
*
* This method only applies to triangles currently.
*/
double b () const;
#endif
/**
* @brief Returns a value indicating whether the polygon borders to a segment
@ -589,16 +696,17 @@ public:
unsigned int num_segments () const;
private:
// @@@ bool m_is_outside;
std::vector<GPolygonEdge *> mp_e;
std::vector<db::GVertex *> mp_v;
Graph *mp_graph;
bool m_is_outside;
std::vector<Edge *> mp_e;
std::vector<Vertex *> mp_v;
size_t m_id;
void init ();
// no copying
GPolygon &operator= (const GPolygon &);
GPolygon (const GPolygon &);
Polygon &operator= (const Polygon &);
Polygon (const Polygon &);
};
/**
@ -606,87 +714,29 @@ private:
*
* The ID acts as a more predicable unique ID for the object in sets and maps.
*/
struct GPolygonLessFunc
struct PolygonLessFunc
{
bool operator () (GPolygon *a, GPolygon *b) const
bool operator () (Polygon *a, Polygon *b) const
{
return a->id () < b->id ();
}
};
class DB_PUBLIC PolygonGraph
/**
* @brief A class representing the polygon graph
*
* A polygon graph is the main container, holding vertexes, edges and polygons.
* The graph can be of "triangles" type, in which case it is guaranteed to only
* hold triangles (polygons with 3 vertexes).
*/
class DB_PUBLIC Graph
{
public:
#if 0 // @@@
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<size_t>::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;
};
#endif
typedef tl::list<db::GPolygon> polygons_type;
typedef tl::list<Polygon> polygons_type;
typedef polygons_type::const_iterator polygon_iterator;
PolygonGraph ();
~PolygonGraph ();
/**
* @brief Creates a convex decomposition for the given polygon
*/
void convex_decompose (const DPolygon &poly);
Graph ();
~Graph ();
/**
* @brief Returns a string representation of the polygon graph.
@ -718,15 +768,6 @@ public:
*/
void clear ();
protected:
#if 0 // @@@
/**
* @brief Checks the polygon graph for consistency
* This method is for testing purposes mainly.
*/
bool check (bool check_delaunay = true) const;
#endif
/**
* @brief Dumps the polygon graph to a GDS file at the given path
* This method is for testing purposes mainly.
@ -743,35 +784,43 @@ protected:
*/
db::Layout *to_layout (bool decompose_by_id = false) const;
private:
tl::list<db::GPolygon> mp_polygons;
tl::stable_vector<db::GPolygonEdge> m_edges_heap;
std::vector<db::GPolygonEdge *> m_returned_edges;
tl::stable_vector<db::GVertex> m_vertex_heap;
// @@@ bool m_is_constrained;
// @@@ size_t m_level;
size_t m_id;
// @@@ size_t m_flips, m_hops;
db::GVertex *create_vertex (double x, double y);
db::GVertex *create_vertex (const db::DPoint &pt);
db::GPolygonEdge *create_edge (db::GVertex *v1, db::GVertex *v2);
protected:
Vertex *create_vertex (double x, double y);
Vertex *create_vertex (const db::DPoint &pt);
Edge *create_edge (Vertex *v1, Vertex *v2);
template <class Iter>
db::GPolygon *
Polygon *
create_polygon (Iter from, Iter to)
{
db::GPolygon *res = new db::GPolygon (from ,to);
Polygon *res = new Polygon (this, from ,to);
res->set_id (++m_id);
mp_polygons.push_back (res);
return res;
}
void remove_polygon (db::GPolygon *tri);
template<class Poly, class Trans> void make_contours (const Poly &poly, const Trans &trans, std::vector<std::vector<db::GVertex *> > &contours);
Polygon *create_triangle (Edge *e1, Edge *e2, Edge *e3);
void remove_polygon (Polygon *p);
private:
friend class Triangulation;
friend class ConvexDecomposition;
tl::list<Polygon> mp_polygons;
tl::stable_vector<Edge> m_edges_heap;
std::vector<Edge *> m_returned_edges;
tl::stable_vector<Vertex> m_vertex_heap;
size_t m_id;
tl::list<Polygon> &polygons () { return mp_polygons; }
tl::stable_vector<Edge> &edges () { return m_edges_heap; }
tl::stable_vector<Vertex> &vertexes () { return m_vertex_heap; }
};
}
} // namespace plc
} // namespace db
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,306 @@
/*
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_dbPLCTriangulation
#define HDR_dbPLCTriangulation
#include "dbCommon.h"
#include "dbPLC.h"
#include <limits>
#include <list>
#include <vector>
#include <algorithm>
namespace db
{
namespace plc
{
struct DB_PUBLIC TriangulationParameters
{
TriangulationParameters ()
: min_b (1.0),
min_length (0.0),
max_area (0.0),
max_area_border (0.0),
max_iterations (std::numeric_limits<size_t>::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;
};
/**
* @brief A Triangulation algorithm
*
* This class implements a constrained refined Delaunay triangulation using Chew's algorithm.
*/
class DB_PUBLIC Triangulation
{
public:
/**
* @brief The constructor
*
* The graph will be one filled by the triangulation.
*/
Triangulation (Graph *graph);
/**
* @brief Clears the triangulation
*/
void clear ();
/**
* @brief Initializes the triangle collection with a box
* Two triangles will be created.
*/
void init_box (const db::DBox &box);
/**
* @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 &region, const TriangulationParameters &parameters, double dbu = 1.0);
// more versions
void triangulate (const db::Region &region, const TriangulationParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
void triangulate (const db::Polygon &poly, const TriangulationParameters &parameters, double dbu = 1.0);
void triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulationParameters &parameters, double dbu = 1.0);
void triangulate (const db::Polygon &poly, const TriangulationParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
void triangulate (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const TriangulationParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
/**
* @brief Triangulates a floating-point polygon
*/
void triangulate (const db::DPolygon &poly, const TriangulationParameters &parameters, const db::DCplxTrans &trans = db::DCplxTrans ());
void triangulate (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const TriangulationParameters &parameters, 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.
*/
Vertex *insert_point (const db::DPoint &point, std::list<tl::weak_ptr<Polygon> > *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 polygon graph for consistency
* This method is for testing purposes mainly.
*/
bool check (bool check_delaunay = true) const;
/**
* @brief Finds the points within (not "on") a circle of radius "radius" around the given vertex.
*/
std::vector<Vertex *> 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.
*/
Vertex *insert_point (db::DCoord x, db::DCoord y, std::list<tl::weak_ptr<Polygon> > *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 (Vertex *vertex, std::list<tl::weak_ptr<Polygon> > *new_triangles = 0);
/**
* @brief Flips the given edge
*/
std::pair<std::pair<Polygon *, Polygon *>, Edge *> flip (Edge *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<Edge *> search_edges_crossing (Vertex *from, Vertex *to);
/**
* @brief Finds the edge for two given points
*/
Edge *find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2);
/**
* @brief Finds the vertex for a point
*/
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<Edge *> ensure_edge (Vertex *from, 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<std::vector<Vertex *> > &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 &region, 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<db::Point> &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<DPoint> &vertexes, const DCplxTrans &trans);
/**
* @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion)
*/
static bool is_illegal_edge (Edge *edge);
// NOTE: these functions are SLOW and intended to test purposes only
std::vector<Vertex *> find_touching (const db::DBox &box) const;
std::vector<Vertex *> find_inside_circle (const db::DPoint &center, double radius) const;
private:
Graph *mp_graph;
bool m_is_constrained;
size_t m_level;
size_t m_id;
size_t m_flips, m_hops;
template<class Poly, class Trans> void make_contours (const Poly &poly, const Trans &trans, std::vector<std::vector<Vertex *> > &contours);
void remove_outside_vertex (Vertex *vertex, std::list<tl::weak_ptr<Polygon> > *new_triangles = 0);
void remove_inside_vertex (Vertex *vertex, std::list<tl::weak_ptr<Polygon> > *new_triangles_out = 0);
std::vector<Polygon *> fill_concave_corners (const std::vector<Edge *> &edges);
void fix_triangles (const std::vector<Polygon *> &tris, const std::vector<Edge *> &fixed_edges, std::list<tl::weak_ptr<Polygon> > *new_triangles);
std::vector<Polygon *> find_triangle_for_point (const db::DPoint &point);
Edge *find_closest_edge (const db::DPoint &p, Vertex *vstart = 0, bool inside_only = false);
Vertex *insert (Vertex *vertex, std::list<tl::weak_ptr<Polygon> > *new_triangles = 0);
void split_triangle (Polygon *t, Vertex *vertex, std::list<tl::weak_ptr<Polygon> > *new_triangles_out);
void split_triangles_on_edge (Vertex *vertex, Edge *split_edge, std::list<tl::weak_ptr<Polygon> > *new_triangles_out);
void add_more_triangles (std::vector<Polygon *> &new_triangles,
Edge *incoming_edge,
Vertex *from_vertex, Vertex *to_vertex,
Edge *conn_edge);
void insert_new_vertex(Vertex *vertex, std::list<tl::weak_ptr<Polygon> > *new_triangles_out);
std::vector<Edge *> ensure_edge_inner (Vertex *from, Vertex *to);
void join_edges (std::vector<Edge *> &edges);
void refine (const TriangulationParameters &param);
};
} // namespace plc
} // namespace db
#endif

View File

@ -21,31 +21,21 @@
*/
#include "dbPolygonGraph.h"
#include "dbPLC.h"
#include "tlUnitTest.h"
#include <list>
#include <memory>
class TestablePolygonGraph
: public db::PolygonGraph
{
public:
using db::PolygonGraph::PolygonGraph;
// @@@ using db::PolygonGraph::check;
using db::PolygonGraph::dump;
};
TEST(basic)
{
db::DBox box (0, 0, 100.0, 200.0);
TestablePolygonGraph pg;
db::plc::Graph plc;
// @@@ pg.insert_polygon (db::DSimplePolygon (box));
// @@@
tl::info << pg.to_string ();
pg.dump ("debug.gds"); // @@@
tl::info << plc.to_string ();
plc.dump ("debug.gds"); // @@@
}

File diff suppressed because it is too large Load Diff

View File

@ -461,7 +461,7 @@ TEST(ensure_edge)
EXPECT_EQ (tl::to_string (area_in), "0.25");
}
bool safe_inside (const db::DBox &b1, const db::DBox &b2)
static bool safe_inside (const db::DBox &b1, const db::DBox &b2)
{
typedef db::coord_traits<db::DBox::coord_type> ct;

View File

@ -12,7 +12,8 @@ SOURCES = \
dbFillToolTests.cc \
dbLogTests.cc \
dbObjectWithPropertiesTests.cc \
dbPolygonGraphTests.cc \
dbPLCGraphTest.cc \
dbPLCTriangulationTests.cc \
dbPolygonNeighborhoodTests.cc \
dbPropertiesFilterTests.cc \
dbQuadTreeTests.cc \