mirror of https://github.com/KLayout/klayout.git
WIP
This commit is contained in:
parent
e6ff30adee
commit
b0f05b5327
|
|
@ -76,6 +76,7 @@ SOURCES = \
|
|||
dbPCellVariant.cc \
|
||||
dbPoint.cc \
|
||||
dbPolygon.cc \
|
||||
dbPolygonGraph.cc \
|
||||
dbPolygonNeighborhood.cc \
|
||||
dbPolygonTools.cc \
|
||||
dbPolygonGenerators.cc \
|
||||
|
|
@ -314,6 +315,7 @@ HEADERS = \
|
|||
dbPCellVariant.h \
|
||||
dbPoint.h \
|
||||
dbPolygon.h \
|
||||
dbPolygonGraph.h \
|
||||
dbPolygonNeighborhood.h \
|
||||
dbPolygonTools.h \
|
||||
dbPolygonGenerators.h \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,846 @@
|
|||
|
||||
/*
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbPolygonGraph.h"
|
||||
#include "dbLayout.h"
|
||||
#include "dbWriter.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// GVertex implementation
|
||||
|
||||
GVertex::GVertex ()
|
||||
: DPoint (), m_is_precious (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
GVertex::GVertex (const db::DPoint &p)
|
||||
: DPoint (p), m_is_precious (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
GVertex::GVertex (const GVertex &v)
|
||||
: DPoint (), m_is_precious (false)
|
||||
{
|
||||
operator= (v);
|
||||
}
|
||||
|
||||
GVertex &GVertex::operator= (const GVertex &v)
|
||||
{
|
||||
if (this != &v) {
|
||||
// NOTE: edges are not copied!
|
||||
db::DPoint::operator= (v);
|
||||
m_is_precious = v.m_is_precious;
|
||||
}
|
||||
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
|
||||
{
|
||||
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
|
||||
if ((*e)->is_outside ()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<db::GPolygon *>
|
||||
GVertex::polygons () const
|
||||
{
|
||||
std::set<db::GPolygon *> seen;
|
||||
std::vector<db::GPolygon *> 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) {
|
||||
res.push_back (t.operator-> ());
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
GVertex::has_edge (const GPolygonEdge *edge) const
|
||||
{
|
||||
for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) {
|
||||
if (*e == edge) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
GVertex::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
|
||||
return mp_edges.size ();
|
||||
} else {
|
||||
size_t n = 0;
|
||||
for (auto i = mp_edges.begin (); i != mp_edges.end () && --max_count >= 0; ++i) {
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
GVertex::to_string (bool with_id) const
|
||||
{
|
||||
std::string res = tl::sprintf ("(%.12g, %.12g)", x (), y());
|
||||
if (with_id) {
|
||||
res += tl::sprintf ("[%x]", (size_t)this);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
GVertex::in_circle (const DPoint &point, const DPoint ¢er, double radius)
|
||||
{
|
||||
double dx = point.x () - center.x ();
|
||||
double dy = point.y () - center.y ();
|
||||
double d2 = dx * dx + dy * dy;
|
||||
double r2 = radius * radius;
|
||||
double delta = fabs (d2 + r2) * db::epsilon;
|
||||
if (d2 < r2 - delta) {
|
||||
return 1;
|
||||
} else if (d2 < r2 + delta) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// GPolygonEdge implementation
|
||||
|
||||
GPolygonEdge::GPolygonEdge ()
|
||||
: 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)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
GPolygonEdge::set_left (GPolygon *t)
|
||||
{
|
||||
mp_left = t;
|
||||
}
|
||||
|
||||
void
|
||||
GPolygonEdge::set_right (GPolygon *t)
|
||||
{
|
||||
mp_right = t;
|
||||
}
|
||||
|
||||
void
|
||||
GPolygonEdge::link ()
|
||||
{
|
||||
mp_v1->mp_edges.push_back (this);
|
||||
m_ec_v1 = --mp_v1->mp_edges.end ();
|
||||
|
||||
mp_v2->mp_edges.push_back (this);
|
||||
m_ec_v2 = --mp_v2->mp_edges.end ();
|
||||
}
|
||||
|
||||
void
|
||||
GPolygonEdge::unlink ()
|
||||
{
|
||||
if (mp_v1) {
|
||||
mp_v1->remove_edge (m_ec_v1);
|
||||
}
|
||||
if (mp_v2) {
|
||||
mp_v2->remove_edge (m_ec_v2);
|
||||
}
|
||||
mp_v1 = mp_v2 = 0;
|
||||
}
|
||||
|
||||
GPolygon *
|
||||
GPolygonEdge::other (const GPolygon *t) const
|
||||
{
|
||||
if (t == mp_left) {
|
||||
return mp_right;
|
||||
}
|
||||
if (t == mp_right) {
|
||||
return mp_left;
|
||||
}
|
||||
tl_assert (false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GVertex *
|
||||
GPolygonEdge::other (const GVertex *t) const
|
||||
{
|
||||
if (t == mp_v1) {
|
||||
return mp_v2;
|
||||
}
|
||||
if (t == mp_v2) {
|
||||
return mp_v1;
|
||||
}
|
||||
tl_assert (false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
GPolygonEdge::has_vertex (const GVertex *v) const
|
||||
{
|
||||
return mp_v1 == v || mp_v2 == v;
|
||||
}
|
||||
|
||||
GVertex *
|
||||
GPolygonEdge::common_vertex (const GPolygonEdge *other) const
|
||||
{
|
||||
if (has_vertex (other->v1 ())) {
|
||||
return (other->v1 ());
|
||||
}
|
||||
if (has_vertex (other->v2 ())) {
|
||||
return (other->v2 ());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string
|
||||
GPolygonEdge::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 ("[%x]", (size_t)this);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
double
|
||||
GPolygonEdge::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;
|
||||
if (l <= 0.0) {
|
||||
pp = e.p1 ();
|
||||
} else if (l >= 1.0) {
|
||||
pp = e.p2 ();
|
||||
} else {
|
||||
pp = e.p1 () + e.d () * l;
|
||||
}
|
||||
return (p - pp).length ();
|
||||
}
|
||||
|
||||
bool
|
||||
GPolygonEdge::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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return e.intersect_point (other).second;
|
||||
}
|
||||
|
||||
bool
|
||||
GPolygonEdge::point_on (const db::DEdge &edge, const db::DPoint &point)
|
||||
{
|
||||
if (edge.side_of (point) != 0) {
|
||||
return false;
|
||||
} else {
|
||||
return db::sprod_sign (point - edge.p1 (), edge.d ()) * db::sprod_sign(point - edge.p2 (), edge.d ()) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // @@@
|
||||
bool
|
||||
GPolygonEdge::is_for_outside_polygons () const
|
||||
{
|
||||
return (left () && left ()->is_outside ()) || (right () && right ()->is_outside ());
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
GPolygonEdge::has_polygon (const GPolygon *t) const
|
||||
{
|
||||
return t != 0 && (left () == t || right () == t);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// GPolygon implementation
|
||||
|
||||
GPolygon::GPolygon ()
|
||||
: m_id (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
GPolygon::init ()
|
||||
{
|
||||
m_id = 0;
|
||||
|
||||
if (mp_e.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<GPolygonEdge *> e;
|
||||
e.swap (mp_e);
|
||||
|
||||
std::multimap<db::GVertex *, GPolygonEdge *> v2e;
|
||||
|
||||
for (auto i = e.begin (); i != e.end (); ++i) {
|
||||
if (i != e.begin ()) {
|
||||
v2e.insert (std::make_pair ((*i)->v1 (), *i));
|
||||
v2e.insert (std::make_pair ((*i)->v2 (), *i));
|
||||
}
|
||||
}
|
||||
|
||||
mp_e.reserve (e.size ());
|
||||
mp_e.push_back (e.front ());
|
||||
// NOTE: we assume the edges follow the clockwise orientation
|
||||
mp_e.back ()->set_right (this);
|
||||
|
||||
mp_v.reserve (e.size ());
|
||||
mp_v.push_back (mp_e.back ()->v1 ());
|
||||
|
||||
auto v = mp_e.back ()->v2 ();
|
||||
|
||||
// join the edges in the order of the polygon
|
||||
while (! v2e.empty ()) {
|
||||
|
||||
mp_v.push_back (v);
|
||||
|
||||
auto i = v2e.find (v);
|
||||
tl_assert (i != v2e.end () && i->first == v && i->second != mp_e.back ());
|
||||
v2e.erase (i);
|
||||
mp_e.push_back (i->second);
|
||||
// NOTE: we assume the edges follow the clockwise orientation
|
||||
mp_e.back ()->set_right (this);
|
||||
|
||||
v = i->second->other (v);
|
||||
i = v2e.find (v);
|
||||
while (i != v2e.end () && i->first == v) {
|
||||
if (i->second == mp_e.back ()) {
|
||||
v2e.erase (i);
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
GPolygon::~GPolygon ()
|
||||
{
|
||||
unlink ();
|
||||
}
|
||||
|
||||
void
|
||||
GPolygon::unlink ()
|
||||
{
|
||||
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
|
||||
if ((*e)->left () == this) {
|
||||
(*e)->set_left (0);
|
||||
}
|
||||
if ((*e)->right () == this) {
|
||||
(*e)->set_right (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
GPolygon::to_string (bool with_id) const
|
||||
{
|
||||
std::string res = "(";
|
||||
for (int i = 0; i < int (size ()); ++i) {
|
||||
if (i > 0) {
|
||||
res += ", ";
|
||||
}
|
||||
if (vertex (i)) {
|
||||
res += vertex (i)->to_string (with_id);
|
||||
} else {
|
||||
res += "(null)";
|
||||
}
|
||||
}
|
||||
res += ")";
|
||||
return res;
|
||||
}
|
||||
|
||||
double
|
||||
GPolygon::area () const
|
||||
{
|
||||
return fabs (db::vprod (mp_e[0]->d (), mp_e[1]->d ())) * 0.5;
|
||||
}
|
||||
|
||||
db::DBox
|
||||
GPolygon::bbox () const
|
||||
{
|
||||
db::DBox box;
|
||||
for (auto i = mp_v.begin (); i != mp_v.end (); ++i) {
|
||||
box += **i;
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
GPolygonEdge *
|
||||
GPolygon::find_edge_with (const GVertex *v1, const GVertex *v2) const
|
||||
{
|
||||
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
|
||||
if ((*e)->has_vertex (v1) && (*e)->has_vertex (v2)) {
|
||||
return *e;
|
||||
}
|
||||
}
|
||||
tl_assert (false);
|
||||
}
|
||||
|
||||
GPolygonEdge *
|
||||
GPolygon::common_edge (const GPolygon *other) const
|
||||
{
|
||||
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
|
||||
if ((*e)->other (this) == other) {
|
||||
return *e;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 // @@@
|
||||
int
|
||||
GPolygon::contains (const db::DPoint &point) const
|
||||
{
|
||||
auto c = *mp_v[2] - *mp_v[0];
|
||||
auto b = *mp_v[1] - *mp_v[0];
|
||||
|
||||
int vps = db::vprod_sign (c, b);
|
||||
if (vps == 0) {
|
||||
return db::vprod_sign (point - *mp_v[0], b) == 0 && db::vprod_sign (point - *mp_v[0], c) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int res = 1;
|
||||
|
||||
const GVertex *vl = mp_v[2];
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const GVertex *v = mp_v[i];
|
||||
int n = db::vprod_sign (point - *vl, *v - *vl) * vps;
|
||||
if (n < 0) {
|
||||
return -1;
|
||||
} else if (n == 0) {
|
||||
res = 0;
|
||||
}
|
||||
vl = v;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
double
|
||||
GPolygon::min_edge_length () const
|
||||
{
|
||||
double lmin = mp_e[0]->d ().length ();
|
||||
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
|
||||
lmin = std::min (lmin, (*e)->d ().length ());
|
||||
}
|
||||
return lmin;
|
||||
}
|
||||
|
||||
#if 0 // @@@
|
||||
double
|
||||
GPolygon::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
|
||||
{
|
||||
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
|
||||
if ((*e)->is_segment ()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
GPolygon::num_segments () const
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for (auto e = mp_e.begin (); e != mp_e.end (); ++e) {
|
||||
if ((*e)->is_segment ()) {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
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 ()
|
||||
: m_id (0)
|
||||
// @@@: m_is_constrained (false), m_level (0), m_id (0), m_flips (0), m_hops (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
PolygonGraph::~PolygonGraph ()
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
db::GVertex *
|
||||
PolygonGraph::create_vertex (double x, double y)
|
||||
{
|
||||
m_vertex_heap.push_back (db::GVertex (x, y));
|
||||
return &m_vertex_heap.back ();
|
||||
}
|
||||
|
||||
db::GVertex *
|
||||
PolygonGraph::create_vertex (const db::DPoint &pt)
|
||||
{
|
||||
m_vertex_heap.push_back (pt);
|
||||
return &m_vertex_heap.back ();
|
||||
}
|
||||
|
||||
db::GPolygonEdge *
|
||||
PolygonGraph::create_edge (db::GVertex *v1, db::GVertex *v2)
|
||||
{
|
||||
db::GPolygonEdge *edge = 0;
|
||||
|
||||
if (! m_returned_edges.empty ()) {
|
||||
edge = m_returned_edges.back ();
|
||||
m_returned_edges.pop_back ();
|
||||
*edge = db::GPolygonEdge (v1, v2);
|
||||
} else {
|
||||
m_edges_heap.push_back (db::GPolygonEdge (v1, v2));
|
||||
edge = &m_edges_heap.back ();
|
||||
}
|
||||
|
||||
edge->link ();
|
||||
edge->set_id (++m_id);
|
||||
return edge;
|
||||
}
|
||||
|
||||
void
|
||||
PolygonGraph::remove_polygon (db::GPolygon *poly)
|
||||
{
|
||||
std::vector<db::GPolygonEdge *> edges;
|
||||
edges.reserve (poly->size ());
|
||||
for (int i = 0; i < int (poly->size ()); ++i) {
|
||||
edges [i] = poly->edge (i);
|
||||
}
|
||||
|
||||
delete poly;
|
||||
|
||||
// clean up edges we do no longer need
|
||||
for (auto e = edges.begin (); e != edges.end (); ++e) {
|
||||
if ((*e) && (*e)->left () == 0 && (*e)->right () == 0 && (*e)->v1 ()) {
|
||||
(*e)->unlink ();
|
||||
m_returned_edges.push_back (*e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PolygonGraph::insert_polygon (const db::DPolygon &polygon)
|
||||
{
|
||||
if (polygon.begin_edge ().at_end ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<db::GPolygonEdge *> edges;
|
||||
|
||||
for (unsigned int c = 0; c < polygon.holes () + 1; ++c) {
|
||||
const db::DPolygon::contour_type &ctr = polygon.contour (c);
|
||||
db::GVertex *v0 = 0, *vv, *v;
|
||||
for (auto p = ctr.begin (); p != ctr.end (); ++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));
|
||||
}
|
||||
}
|
||||
|
||||
create_polygon (edges.begin (), edges.end ());
|
||||
}
|
||||
|
||||
std::string
|
||||
PolygonGraph::to_string ()
|
||||
{
|
||||
std::string res;
|
||||
for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) {
|
||||
if (! res.empty ()) {
|
||||
res += ", ";
|
||||
}
|
||||
res += t->to_string ();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
db::DBox
|
||||
PolygonGraph::bbox () const
|
||||
{
|
||||
db::DBox box;
|
||||
for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) {
|
||||
box += t->bbox ();
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
#if 0 // @@@
|
||||
bool
|
||||
PolygonGraph::check (bool check_delaunay) const
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
if (check_delaunay) {
|
||||
for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) {
|
||||
auto cp = t->circumcircle ();
|
||||
auto vi = find_inside_circle (cp.first, cp.second);
|
||||
if (! vi.empty ()) {
|
||||
res = false;
|
||||
tl::error << "(check error) polygon does not meet Delaunay criterion: " << t->to_string ();
|
||||
for (auto v = vi.begin (); v != vi.end (); ++v) {
|
||||
tl::error << " vertex inside circumcircle: " << (*v)->to_string (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (! t->edge (i)->has_polygon (t.operator-> ())) {
|
||||
tl::error << "(check error) edges " << t->edge (i)->to_string (true)
|
||||
<< " attached to polygon " << t->to_string (true) << " does not refer to this polygon";
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ()) {
|
||||
tl::error << "(check error) edge " << e->to_string (true) << " splits an outside and inside polygon, but is not a segment";
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto t = e->begin_polygons (); t != e->end_polygons (); ++t) {
|
||||
if (! t->has_edge (e.operator-> ())) {
|
||||
tl::error << "(check error) edge " << e->to_string (true) << " not found in adjacent polygon " << t->to_string (true);
|
||||
res = false;
|
||||
}
|
||||
if (! t->has_vertex (e->v1 ())) {
|
||||
tl::error << "(check error) edges " << e->to_string (true) << " vertex 1 not found in adjacent polygon " << t->to_string (true);
|
||||
res = false;
|
||||
}
|
||||
if (! t->has_vertex (e->v2 ())) {
|
||||
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-> ());
|
||||
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) {
|
||||
const char * side_str = sgn > 0.0 ? "left" : "right";
|
||||
tl::error << "(check error) external point " << vopp->to_string (true) << " not on " << side_str << " side of edge " << e->to_string (true);
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! e->v1 ()->has_edge (e.operator-> ())) {
|
||||
tl::error << "(check error) edge " << e->to_string (true) << " vertex 1 does not list this edge";
|
||||
res = false;
|
||||
}
|
||||
if (! e->v2 ()->has_edge (e.operator-> ())) {
|
||||
tl::error << "(check error) edge " << e->to_string (true) << " vertex 2 does not list this edge";
|
||||
res = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 ()) {
|
||||
++num_outside_edges;
|
||||
}
|
||||
}
|
||||
if (num_outside_edges > 0 && num_outside_edges != 2) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
db::Layout *
|
||||
PolygonGraph::to_layout (bool decompose_by_id) const
|
||||
{
|
||||
db::Layout *layout = new db::Layout ();
|
||||
layout->dbu (0.001);
|
||||
|
||||
auto dbu_trans = db::CplxTrans (layout->dbu ()).inverted ();
|
||||
|
||||
db::Cell &top = layout->cell (layout->add_cell ("DUMP"));
|
||||
unsigned int l1 = layout->insert_layer (db::LayerProperties (1, 0));
|
||||
// @@@ unsigned int l2 = layout->insert_layer (db::LayerProperties (2, 0));
|
||||
unsigned int l10 = layout->insert_layer (db::LayerProperties (10, 0));
|
||||
unsigned int l20 = layout->insert_layer (db::LayerProperties (20, 0));
|
||||
unsigned int l21 = layout->insert_layer (db::LayerProperties (21, 0));
|
||||
unsigned int l22 = layout->insert_layer (db::LayerProperties (22, 0));
|
||||
|
||||
std::vector<db::DPoint> pts;
|
||||
for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) {
|
||||
pts.clear ();
|
||||
for (int i = 0; i < int (t->size ()); ++i) {
|
||||
pts.push_back (*t->vertex (i));
|
||||
}
|
||||
db::DPolygon poly;
|
||||
poly.assign_hull (pts.begin (), pts.end ());
|
||||
top.shapes (/*@@@t->is_outside () ? l2 :*/ l1).insert (dbu_trans * poly);
|
||||
if (decompose_by_id) {
|
||||
if ((t->id () & 1) != 0) {
|
||||
top.shapes (l20).insert (dbu_trans * poly);
|
||||
}
|
||||
if ((t->id () & 2) != 0) {
|
||||
top.shapes (l21).insert (dbu_trans * poly);
|
||||
}
|
||||
if ((t->id () & 4) != 0) {
|
||||
top.shapes (l22).insert (dbu_trans * poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ());
|
||||
}
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
void
|
||||
PolygonGraph::dump (const std::string &path, bool decompose_by_id) const
|
||||
{
|
||||
std::unique_ptr<db::Layout> ly (to_layout (decompose_by_id));
|
||||
|
||||
tl::OutputStream stream (path);
|
||||
|
||||
db::SaveLayoutOptions opt;
|
||||
db::Writer writer (opt);
|
||||
writer.write (*ly, stream);
|
||||
|
||||
tl::info << "PolygonGraph written to " << path;
|
||||
}
|
||||
|
||||
void
|
||||
PolygonGraph::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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,777 @@
|
|||
|
||||
/*
|
||||
|
||||
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_dbPolygonGraph
|
||||
#define HDR_dbPolygonGraph
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbTriangle.h"
|
||||
#include "dbBox.h"
|
||||
#include "dbRegion.h"
|
||||
|
||||
#include "tlObjectCollection.h"
|
||||
#include "tlStableVector.h"
|
||||
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Layout;
|
||||
|
||||
class GPolygon;
|
||||
class GPolygonEdge;
|
||||
|
||||
/**
|
||||
* @brief A class representing a vertex in a Delaunay triangulation graph
|
||||
*
|
||||
* The vertex carries information about the connected edges and
|
||||
* an integer value that can be used in traversal algorithms
|
||||
* ("level")
|
||||
*/
|
||||
class DB_PUBLIC GVertex
|
||||
: public db::DPoint
|
||||
{
|
||||
public:
|
||||
typedef std::list<GPolygonEdge *> 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);
|
||||
|
||||
GVertex &operator= (const GVertex &v);
|
||||
|
||||
#if 0 // @@@
|
||||
bool is_outside () const;
|
||||
#endif
|
||||
std::vector<db::GPolygon *> polygons () const;
|
||||
|
||||
edges_iterator begin_edges () const { return mp_edges.begin (); }
|
||||
edges_iterator end_edges () const { return mp_edges.end (); }
|
||||
size_t num_edges (int max_count = -1) const;
|
||||
|
||||
bool has_edge (const GPolygonEdge *edge) const;
|
||||
|
||||
void set_is_precious (bool f) { m_is_precious = f; }
|
||||
bool is_precious () const { return m_is_precious; }
|
||||
|
||||
std::string to_string (bool with_id = false) const;
|
||||
|
||||
/**
|
||||
* @brief Returns 1 is the point is inside the circle, 0 if on the circle and -1 if outside
|
||||
* TODO: Move to db::DPoint
|
||||
*/
|
||||
static int in_circle (const db::DPoint &point, const db::DPoint ¢er, double radius);
|
||||
|
||||
/**
|
||||
* @brief Returns 1 is this point is inside the circle, 0 if on the circle and -1 if outside
|
||||
*/
|
||||
int in_circle (const db::DPoint ¢er, double radius) const
|
||||
{
|
||||
return in_circle (*this, center, radius);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class GPolygonEdge;
|
||||
|
||||
void remove_edge (const edges_iterator_non_const &ec)
|
||||
{
|
||||
mp_edges.erase (ec);
|
||||
}
|
||||
|
||||
edges_type mp_edges;
|
||||
bool m_is_precious;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class representing an edge in the Delaunay triangulation graph
|
||||
*/
|
||||
class DB_PUBLIC GPolygonEdge
|
||||
{
|
||||
public:
|
||||
class GPolygonIterator
|
||||
{
|
||||
public:
|
||||
typedef GPolygon value_type;
|
||||
typedef GPolygon &reference;
|
||||
typedef GPolygon *pointer;
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
return *operator-> ();
|
||||
}
|
||||
|
||||
pointer operator->() const
|
||||
{
|
||||
return m_index ? mp_edge->right () : mp_edge->left ();
|
||||
}
|
||||
|
||||
bool operator== (const GPolygonIterator &other) const
|
||||
{
|
||||
return m_index == other.m_index;
|
||||
}
|
||||
|
||||
bool operator!= (const GPolygonIterator &other) const
|
||||
{
|
||||
return !operator== (other);
|
||||
}
|
||||
|
||||
GPolygonIterator &operator++ ()
|
||||
{
|
||||
while (++m_index < 2 && operator-> () == 0)
|
||||
;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class GPolygonEdge;
|
||||
|
||||
GPolygonIterator (const GPolygonEdge *edge)
|
||||
: mp_edge (edge), m_index (0)
|
||||
{
|
||||
if (! edge) {
|
||||
m_index = 2;
|
||||
} else {
|
||||
--m_index;
|
||||
operator++ ();
|
||||
}
|
||||
}
|
||||
|
||||
const GPolygonEdge *mp_edge;
|
||||
unsigned int m_index;
|
||||
};
|
||||
|
||||
GPolygonEdge ();
|
||||
GPolygonEdge (GVertex *v1, GVertex *v2);
|
||||
|
||||
GVertex *v1 () const { return mp_v1; }
|
||||
GVertex *v2 () const { return mp_v2; }
|
||||
|
||||
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; }
|
||||
|
||||
GPolygonIterator begin_polygons () const
|
||||
{
|
||||
return GPolygonIterator (this);
|
||||
}
|
||||
|
||||
GPolygonIterator end_polygons () const
|
||||
{
|
||||
return GPolygonIterator (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; }
|
||||
bool is_segment () const { return m_is_segment; }
|
||||
|
||||
std::string to_string (bool with_id = false) const;
|
||||
|
||||
/**
|
||||
* @brief Converts to an db::DEdge
|
||||
*/
|
||||
db::DEdge edge () const
|
||||
{
|
||||
return db::DEdge (*mp_v1, *mp_v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the distance of the given point to the edge
|
||||
*
|
||||
* The distance is the minimum distance of the point to one point from the edge.
|
||||
* TODO: Move to db::DEdge
|
||||
*/
|
||||
static double distance (const db::DEdge &e, const db::DPoint &p);
|
||||
|
||||
/**
|
||||
* @brief Returns the distance of the given point to the edge
|
||||
*
|
||||
* The distance is the minimum distance of the point to one point from the edge.
|
||||
*/
|
||||
double distance (const db::DPoint &p) const
|
||||
{
|
||||
return distance (edge (), p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* TODO: Move to db::DEdge
|
||||
*/
|
||||
static bool crosses (const db::DEdge &e, const db::DEdge &other);
|
||||
|
||||
/**
|
||||
* @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::DEdge &other) const
|
||||
{
|
||||
return crosses (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 which is not an endpoint
|
||||
* of one of the edges.
|
||||
*/
|
||||
bool crosses (const db::GPolygonEdge &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 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
|
||||
{
|
||||
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::GPolygonEdge &other) const
|
||||
{
|
||||
return crosses_including (edge (), other.edge ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the intersection point
|
||||
* TODO: Move to db::DEdge
|
||||
*/
|
||||
static db::DPoint intersection_point (const db::DEdge &e, const DEdge &other);
|
||||
|
||||
/**
|
||||
* @brief Gets the intersection point
|
||||
*/
|
||||
db::DPoint intersection_point (const db::DEdge &other) const
|
||||
{
|
||||
return intersection_point (edge (), other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the intersection point
|
||||
*/
|
||||
db::DPoint intersection_point (const GPolygonEdge &other) const
|
||||
{
|
||||
return intersection_point (edge (), other.edge ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the point is on the edge
|
||||
* TODO: Move to db::DEdge
|
||||
*/
|
||||
static bool point_on (const db::DEdge &edge, const db::DPoint &point);
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the point is on the edge
|
||||
*/
|
||||
bool point_on (const db::DPoint &point) const
|
||||
{
|
||||
return point_on (edge (), 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)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the distance vector
|
||||
*/
|
||||
db::DVector d () const
|
||||
{
|
||||
return *mp_v2 - *mp_v1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the other triangle for the given one
|
||||
*/
|
||||
GPolygon *other (const GPolygon *) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the other vertex for the given one
|
||||
*/
|
||||
GVertex *other (const GVertex *) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the edge has the given vertex
|
||||
*/
|
||||
bool has_vertex (const GVertex *) 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;
|
||||
|
||||
#if 0 // @@@
|
||||
/**
|
||||
* @brief Returns a value indicating whether this edge can be flipped
|
||||
*/
|
||||
bool can_flip () const;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
protected:
|
||||
void unlink ();
|
||||
void link ();
|
||||
|
||||
private:
|
||||
friend class GPolygon;
|
||||
friend class PolygonGraph;
|
||||
|
||||
GVertex *mp_v1, *mp_v2;
|
||||
GPolygon *mp_left, *mp_right;
|
||||
GVertex::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);
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 GPolygonEdgeLessFunc
|
||||
{
|
||||
bool operator () (GPolygonEdge *a, GPolygonEdge *b) const
|
||||
{
|
||||
return a->id () < b->id ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class representing a triangle
|
||||
*/
|
||||
class DB_PUBLIC GPolygon
|
||||
: public tl::list_node<GPolygon>, public tl::Object
|
||||
{
|
||||
public:
|
||||
GPolygon ();
|
||||
|
||||
template<class Iter>
|
||||
GPolygon (Iter from, Iter to)
|
||||
: mp_e (from, to)
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
~GPolygon ();
|
||||
|
||||
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; }
|
||||
|
||||
std::string to_string (bool with_id = false) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the number of vertexes
|
||||
*/
|
||||
size_t size () const
|
||||
{
|
||||
return mp_v.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the nth vertex (n wraps around and can be negative)
|
||||
* The vertexes are oriented clockwise.
|
||||
*/
|
||||
inline GVertex *vertex (int n) const
|
||||
{
|
||||
size_t sz = size ();
|
||||
tl_assert (sz > 0);
|
||||
if (n >= 0 && size_t (n) < sz) {
|
||||
return mp_v[n];
|
||||
} else {
|
||||
return mp_v[(n + sz) % sz];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the nth edge (n wraps around and can be negative)
|
||||
*/
|
||||
inline GPolygonEdge *edge (int n) const
|
||||
{
|
||||
size_t sz = size ();
|
||||
tl_assert (sz > 0);
|
||||
if (n >= 0 && size_t (n) < sz) {
|
||||
return mp_e[n];
|
||||
} else {
|
||||
return mp_e[(n + sz) % sz];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the area
|
||||
*/
|
||||
double area () const;
|
||||
|
||||
/**
|
||||
* @brief Returns the bounding box of the triangle
|
||||
*/
|
||||
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).
|
||||
*/
|
||||
std::pair<db::DPoint, double> circumcircle (bool *ok = 0) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the vertex opposite of the given edge
|
||||
*/
|
||||
GVertex *opposite (const GPolygonEdge *edge) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the edge opposite of the given vertex
|
||||
*/
|
||||
GPolygonEdge *opposite (const GVertex *vertex) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Gets the edge with the given vertexes
|
||||
*/
|
||||
GPolygonEdge *find_edge_with (const GVertex *v1, const GVertex *v2) const;
|
||||
|
||||
/**
|
||||
* @brief Finds the common edge for both polygons
|
||||
*/
|
||||
GPolygonEdge *common_edge (const GPolygon *other) const;
|
||||
|
||||
#if 0 // @@@
|
||||
/**
|
||||
* @brief Returns a value indicating whether the point is inside (1), on the polygon (0) or outside (-1)
|
||||
*/
|
||||
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
|
||||
{
|
||||
for (auto i = mp_v.begin (); i != mp_v.end (); ++i) {
|
||||
if (*i == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the triangle has the given edge
|
||||
*/
|
||||
inline bool has_edge (const db::GPolygonEdge *e) const
|
||||
{
|
||||
for (auto i = mp_e.begin (); i != mp_e.end (); ++i) {
|
||||
if (*i == e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the minimum edge length
|
||||
*/
|
||||
double min_edge_length () const;
|
||||
|
||||
#if 0 // @@@
|
||||
/**
|
||||
* @brief Returns the min edge length to circumcircle radius ratio
|
||||
*/
|
||||
double b () const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Returns a value indicating whether the polygon borders to a segment
|
||||
*/
|
||||
bool has_segment () const;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of segments the polygon borders to
|
||||
*/
|
||||
unsigned int num_segments () const;
|
||||
|
||||
private:
|
||||
// @@@ bool m_is_outside;
|
||||
std::vector<GPolygonEdge *> mp_e;
|
||||
std::vector<db::GVertex *> mp_v;
|
||||
size_t m_id;
|
||||
|
||||
void init ();
|
||||
|
||||
// no copying
|
||||
GPolygon &operator= (const GPolygon &);
|
||||
GPolygon (const GPolygon &);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A compare function that compares polygons by ID
|
||||
*
|
||||
* The ID acts as a more predicable unique ID for the object in sets and maps.
|
||||
*/
|
||||
struct GPolygonLessFunc
|
||||
{
|
||||
bool operator () (GPolygon *a, GPolygon *b) const
|
||||
{
|
||||
return a->id () < b->id ();
|
||||
}
|
||||
};
|
||||
|
||||
class DB_PUBLIC PolygonGraph
|
||||
{
|
||||
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 polygons_type::const_iterator polygon_iterator;
|
||||
|
||||
PolygonGraph ();
|
||||
~PolygonGraph ();
|
||||
|
||||
/**
|
||||
* @brief Inserts the given polygon
|
||||
*/
|
||||
void insert_polygon (const db::DPolygon &box);
|
||||
|
||||
/**
|
||||
* @brief Returns a string representation of the polygon graph.
|
||||
*/
|
||||
std::string to_string ();
|
||||
|
||||
/**
|
||||
* @brief Returns the bounding box of the polygon graph.
|
||||
*/
|
||||
db::DBox bbox () const;
|
||||
|
||||
/**
|
||||
* @brief Iterates the polygons in the graph (begin iterator)
|
||||
*/
|
||||
polygon_iterator begin () const { return mp_polygons.begin (); }
|
||||
|
||||
/**
|
||||
* @brief Iterates the polygons in the graph (end iterator)
|
||||
*/
|
||||
polygon_iterator end () const { return mp_polygons.end (); }
|
||||
|
||||
/**
|
||||
* @brief Returns the number of polygons in the graph
|
||||
*/
|
||||
size_t num_polygons () const { return mp_polygons.size (); }
|
||||
|
||||
/**
|
||||
* @brief Clears the polygon set
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* "decompose_id" will map polygons to layer 20, 21 and 22.
|
||||
* according to bit 0, 1 and 2 of the ID (useful with the 'mark_polygons'
|
||||
* flat in TriangulateParameters).
|
||||
*/
|
||||
void dump (const std::string &path, bool decompose_by_id = false) const;
|
||||
|
||||
/**
|
||||
* @brief Creates a new layout object representing the polygon graph
|
||||
* This method is for testing purposes mainly.
|
||||
*/
|
||||
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);
|
||||
|
||||
template <class Iter>
|
||||
db::GPolygon *
|
||||
create_polygon (Iter from, Iter to)
|
||||
{
|
||||
db::GPolygon *res = new db::GPolygon (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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
/*
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbPolygonGraph.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;
|
||||
pg.insert_polygon (db::DPolygon (box));
|
||||
|
||||
// @@@
|
||||
tl::info << pg.to_string ();
|
||||
pg.dump ("debug.gds"); // @@@
|
||||
}
|
||||
|
||||
|
|
@ -12,6 +12,7 @@ SOURCES = \
|
|||
dbFillToolTests.cc \
|
||||
dbLogTests.cc \
|
||||
dbObjectWithPropertiesTests.cc \
|
||||
dbPolygonGraphTests.cc \
|
||||
dbPolygonNeighborhoodTests.cc \
|
||||
dbPropertiesFilterTests.cc \
|
||||
dbQuadTreeTests.cc \
|
||||
|
|
|
|||
Loading…
Reference in New Issue