mirror of https://github.com/KLayout/klayout.git
Porting HM decomposition to new PLC framework
This commit is contained in:
parent
76e039bd2a
commit
60d1fb0685
|
|
@ -71,6 +71,7 @@ SOURCES = \
|
||||||
dbObject.cc \
|
dbObject.cc \
|
||||||
dbObjectWithProperties.cc \
|
dbObjectWithProperties.cc \
|
||||||
dbPLC.cc \
|
dbPLC.cc \
|
||||||
|
dbPLCConvexDecomposition.cc \
|
||||||
dbPLCTriangulation.cc \
|
dbPLCTriangulation.cc \
|
||||||
dbPath.cc \
|
dbPath.cc \
|
||||||
dbPCellDeclaration.cc \
|
dbPCellDeclaration.cc \
|
||||||
|
|
@ -309,6 +310,7 @@ HEADERS = \
|
||||||
dbObjectTag.h \
|
dbObjectTag.h \
|
||||||
dbObjectWithProperties.h \
|
dbObjectWithProperties.h \
|
||||||
dbPLC.h \
|
dbPLC.h \
|
||||||
|
dbPLCConvexDecomposition.h \
|
||||||
dbPLCTriangulation.h \
|
dbPLCTriangulation.h \
|
||||||
dbPath.h \
|
dbPath.h \
|
||||||
dbPCellDeclaration.h \
|
dbPCellDeclaration.h \
|
||||||
|
|
|
||||||
|
|
@ -804,105 +804,6 @@ Graph::bbox () const
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 // @@@
|
|
||||||
bool
|
|
||||||
Graph::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;
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
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 *
|
db::Layout *
|
||||||
Graph::to_layout (bool decompose_by_id) const
|
Graph::to_layout (bool decompose_by_id) const
|
||||||
{
|
{
|
||||||
|
|
@ -913,7 +814,7 @@ Graph::to_layout (bool decompose_by_id) const
|
||||||
|
|
||||||
db::Cell &top = layout->cell (layout->add_cell ("DUMP"));
|
db::Cell &top = layout->cell (layout->add_cell ("DUMP"));
|
||||||
unsigned int l1 = layout->insert_layer (db::LayerProperties (1, 0));
|
unsigned int l1 = layout->insert_layer (db::LayerProperties (1, 0));
|
||||||
// @@@ unsigned int l2 = layout->insert_layer (db::LayerProperties (2, 0));
|
unsigned int l2 = layout->insert_layer (db::LayerProperties (2, 0));
|
||||||
unsigned int l10 = layout->insert_layer (db::LayerProperties (10, 0));
|
unsigned int l10 = layout->insert_layer (db::LayerProperties (10, 0));
|
||||||
unsigned int l20 = layout->insert_layer (db::LayerProperties (20, 0));
|
unsigned int l20 = layout->insert_layer (db::LayerProperties (20, 0));
|
||||||
unsigned int l21 = layout->insert_layer (db::LayerProperties (21, 0));
|
unsigned int l21 = layout->insert_layer (db::LayerProperties (21, 0));
|
||||||
|
|
@ -927,7 +828,7 @@ Graph::to_layout (bool decompose_by_id) const
|
||||||
}
|
}
|
||||||
db::DPolygon poly;
|
db::DPolygon poly;
|
||||||
poly.assign_hull (pts.begin (), pts.end ());
|
poly.assign_hull (pts.begin (), pts.end ());
|
||||||
top.shapes (/*@@@t->is_outside () ? l2 :*/ l1).insert (dbu_trans * poly);
|
top.shapes (t->is_outside () ? l2 : l1).insert (dbu_trans * poly);
|
||||||
if (decompose_by_id) {
|
if (decompose_by_id) {
|
||||||
if ((t->id () & 1) != 0) {
|
if ((t->id () & 1) != 0) {
|
||||||
top.shapes (l20).insert (dbu_trans * poly);
|
top.shapes (l20).insert (dbu_trans * poly);
|
||||||
|
|
|
||||||
|
|
@ -504,6 +504,7 @@ private:
|
||||||
friend class Polygon;
|
friend class Polygon;
|
||||||
friend class Graph;
|
friend class Graph;
|
||||||
friend class Triangulation;
|
friend class Triangulation;
|
||||||
|
friend class ConvexDecomposition;
|
||||||
|
|
||||||
Graph *mp_graph;
|
Graph *mp_graph;
|
||||||
Vertex *mp_v1, *mp_v2;
|
Vertex *mp_v1, *mp_v2;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,530 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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 "dbPLCConvexDecomposition.h"
|
||||||
|
#include "dbPLCTriangulation.h"
|
||||||
|
#include "tlLog.h"
|
||||||
|
#include "tlTimer.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace db
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace plc
|
||||||
|
{
|
||||||
|
|
||||||
|
ConvexDecomposition::ConvexDecomposition (Graph *graph)
|
||||||
|
{
|
||||||
|
mp_graph = graph;
|
||||||
|
clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::clear ()
|
||||||
|
{
|
||||||
|
mp_graph->clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SortAngleAndEdgesByEdgeLength
|
||||||
|
{
|
||||||
|
typedef std::list<std::pair<double, const Edge *> > angle_and_edges_list;
|
||||||
|
|
||||||
|
bool operator() (const angle_and_edges_list::iterator &a, const angle_and_edges_list::iterator &b) const
|
||||||
|
{
|
||||||
|
double la = a->second->edge ().double_sq_length ();
|
||||||
|
double lb = b->second->edge ().double_sq_length ();
|
||||||
|
if (fabs (la - lb) > db::epsilon) {
|
||||||
|
return la < lb;
|
||||||
|
} else {
|
||||||
|
return a->second->edge ().less (b->second->edge ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: move to some generic header
|
||||||
|
template <class T>
|
||||||
|
struct less_compare_func
|
||||||
|
{
|
||||||
|
bool operator() (const T &a, const T &b) const
|
||||||
|
{
|
||||||
|
return a.less (b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: move to some generic header
|
||||||
|
template <class T>
|
||||||
|
struct equal_compare_func
|
||||||
|
{
|
||||||
|
bool operator() (const T &a, const T &b) const
|
||||||
|
{
|
||||||
|
return a.equal (b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConcaveCorner
|
||||||
|
{
|
||||||
|
ConcaveCorner ()
|
||||||
|
: corner (0), incoming (0), outgoing (0)
|
||||||
|
{
|
||||||
|
// .. nothing yet ..
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcaveCorner (Vertex *_corner, Edge *_incoming, Edge *_outgoing)
|
||||||
|
: corner (_corner), incoming (_incoming), outgoing (_outgoing)
|
||||||
|
{
|
||||||
|
// .. nothing yet ..
|
||||||
|
}
|
||||||
|
|
||||||
|
Vertex *corner;
|
||||||
|
Edge *incoming, *outgoing;
|
||||||
|
};
|
||||||
|
|
||||||
|
Edge *find_outgoing_segment (Vertex *vertex, Edge *incoming, int &vp_max_sign)
|
||||||
|
{
|
||||||
|
Vertex *vfrom = incoming->other (vertex);
|
||||||
|
db::DEdge e1 (*vfrom, *vertex);
|
||||||
|
|
||||||
|
double vp_max = 0.0;
|
||||||
|
vp_max_sign = 0;
|
||||||
|
Edge *outgoing = 0;
|
||||||
|
|
||||||
|
// Look for the outgoing edge. We pick the one which bends "most", favoring
|
||||||
|
// convex corners. Multiple edges per vertex are possible is corner cases such as the
|
||||||
|
// "hourglass" configuration.
|
||||||
|
|
||||||
|
for (auto e = vertex->begin_edges (); e != vertex->end_edges (); ++e) {
|
||||||
|
|
||||||
|
Edge *en = *e;
|
||||||
|
if (en != incoming && en->is_segment ()) {
|
||||||
|
|
||||||
|
Vertex *v = en->other (vertex);
|
||||||
|
db::DEdge e2 (*vertex, *v);
|
||||||
|
double vp = double (db::vprod (e1, e2)) / (e1.double_length () * e2.double_length ());
|
||||||
|
|
||||||
|
// vp > 0: concave, vp < 0: convex
|
||||||
|
|
||||||
|
if (! outgoing || vp > vp_max) {
|
||||||
|
vp_max_sign = db::vprod_sign (e1, e2);
|
||||||
|
vp_max = vp;
|
||||||
|
outgoing = en;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tl_assert (outgoing != 0);
|
||||||
|
return outgoing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void collect_concave_vertexes (Graph &tris, std::vector<ConcaveCorner> &concave_vertexes)
|
||||||
|
{
|
||||||
|
concave_vertexes.clear ();
|
||||||
|
|
||||||
|
// @@@ use edge "level"
|
||||||
|
// @@@ use edges from heap
|
||||||
|
std::unordered_set<Edge *> left;
|
||||||
|
|
||||||
|
for (auto it = tris.begin (); it != tris.end (); ++it) {
|
||||||
|
for (unsigned int i = 0; i < 3; ++i) {
|
||||||
|
Edge *e = it->edge (i);
|
||||||
|
if (e->is_segment ()) {
|
||||||
|
left.insert (e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (! left.empty ()) {
|
||||||
|
|
||||||
|
// First segment for a new loop
|
||||||
|
Edge *segment = *left.begin ();
|
||||||
|
|
||||||
|
// walk along the segments in clockwise direction. Find concave
|
||||||
|
// vertexes and create new vertexes perpendicular to the incoming
|
||||||
|
// and outgoing edge.
|
||||||
|
|
||||||
|
Edge *start_segment = segment;
|
||||||
|
Vertex *vto = segment->right () ? segment->v2 () : segment->v1 ();
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
left.erase (segment);
|
||||||
|
|
||||||
|
Edge *prev_segment = segment;
|
||||||
|
|
||||||
|
int vp_sign = 0;
|
||||||
|
segment = find_outgoing_segment (vto, prev_segment, vp_sign);
|
||||||
|
|
||||||
|
if (vp_sign > 0) {
|
||||||
|
concave_vertexes.push_back (ConcaveCorner (vto, prev_segment, segment));
|
||||||
|
}
|
||||||
|
|
||||||
|
vto = segment->other (vto);
|
||||||
|
|
||||||
|
} while (segment != start_segment);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<bool, db::DPoint>
|
||||||
|
search_crossing_with_next_segment (const Vertex *v0, const db::DVector &direction)
|
||||||
|
{
|
||||||
|
auto vtri = v0->polygons (); // TODO: slow?
|
||||||
|
std::vector<const Vertex *> nvv, nvv_next;
|
||||||
|
|
||||||
|
for (auto it = vtri.begin (); it != vtri.end (); ++it) {
|
||||||
|
|
||||||
|
// Search for a segment in the direction perpendicular to the edge
|
||||||
|
nvv.clear ();
|
||||||
|
nvv.push_back (v0);
|
||||||
|
const Polygon *t = *it;
|
||||||
|
|
||||||
|
while (! nvv.empty ()) {
|
||||||
|
|
||||||
|
nvv_next.clear ();
|
||||||
|
|
||||||
|
for (auto iv = nvv.begin (); iv != nvv.end (); ++iv) {
|
||||||
|
|
||||||
|
const Vertex *v = *iv;
|
||||||
|
const Edge *oe = t->opposite (v);
|
||||||
|
const Polygon *tt = oe->other (t);
|
||||||
|
const Vertex *v1 = oe->v1 ();
|
||||||
|
const Vertex *v2 = oe->v2 ();
|
||||||
|
|
||||||
|
if (db::sprod_sign (*v2 - *v, direction) >= 0 && db::sprod_sign (*v1 - *v, direction) >= 0 &&
|
||||||
|
db::vprod_sign (*v2 - *v, direction) * db::vprod_sign (*v1 - *v, direction) < 0) {
|
||||||
|
|
||||||
|
// this triangle covers the normal vector of e1 -> stop here or continue searching in that direction
|
||||||
|
if (oe->is_segment ()) {
|
||||||
|
auto cp = oe->edge ().cut_point (db::DEdge (*v0, *v0 + direction));
|
||||||
|
if (cp.first) {
|
||||||
|
return std::make_pair (true, cp.second);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// continue searching in that direction
|
||||||
|
nvv_next.push_back (v1);
|
||||||
|
nvv_next.push_back (v2);
|
||||||
|
t = tt;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nvv.swap (nvv_next);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair (false, db::DPoint ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters ¶m)
|
||||||
|
{
|
||||||
|
bool with_segments = param.with_segments;
|
||||||
|
bool split_edges = param.split_edges;
|
||||||
|
|
||||||
|
std::vector<ConcaveCorner> concave_vertexes;
|
||||||
|
collect_concave_vertexes (*mp_graph, concave_vertexes);
|
||||||
|
|
||||||
|
// @@@ return if no concave corners
|
||||||
|
|
||||||
|
// @@@ sort concave vertexes
|
||||||
|
|
||||||
|
std::vector<db::DPoint> new_points;
|
||||||
|
|
||||||
|
if (with_segments) {
|
||||||
|
|
||||||
|
// Create internal segments cutting off pieces orthogonal to the edges
|
||||||
|
// connecting the concave vertex.
|
||||||
|
|
||||||
|
for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) {
|
||||||
|
|
||||||
|
for (unsigned int ei = 0; ei < 2; ++ei) {
|
||||||
|
|
||||||
|
db::DEdge ee;
|
||||||
|
const Vertex *v0 = cc->corner;
|
||||||
|
if (ei == 0) {
|
||||||
|
ee = db::DEdge (*cc->incoming->other (v0), *v0);
|
||||||
|
} else {
|
||||||
|
ee = db::DEdge (*v0, *cc->outgoing->other (v0));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cp = search_crossing_with_next_segment (v0, db::DVector (ee.dy (), -ee.dx ()));
|
||||||
|
if (cp.first) {
|
||||||
|
new_points.push_back (cp.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// eliminate duplicates and put the new points in some order
|
||||||
|
|
||||||
|
if (! new_points.empty ()) {
|
||||||
|
|
||||||
|
std::sort (new_points.begin (), new_points.end (), less_compare_func<db::DPoint> ());
|
||||||
|
new_points.erase (std::unique (new_points.begin (), new_points.end (), equal_compare_func<db::DPoint> ()), new_points.end ());
|
||||||
|
|
||||||
|
// Insert the new points and make connections
|
||||||
|
for (auto p = new_points.begin (); p != new_points.end (); ++p) {
|
||||||
|
tris.insert_point (*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// As the insertion invalidates the edges, we need to collect the concave vertexes again
|
||||||
|
collect_concave_vertexes (*mp_graph, concave_vertexes);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect essential edges
|
||||||
|
// Every concave vertex can have up to two essential edges.
|
||||||
|
// Other then suggested by Hertel-Mehlhorn we don't pick
|
||||||
|
// them one-by-one, but using them in length order, from the
|
||||||
|
|
||||||
|
std::unordered_set<const Edge *> essential_edges;
|
||||||
|
|
||||||
|
typedef std::list<std::pair<double, const Edge *> > angles_and_edges_list;
|
||||||
|
angles_and_edges_list angles_and_edges;
|
||||||
|
std::vector<angles_and_edges_list::iterator> sorted_edges;
|
||||||
|
|
||||||
|
for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) {
|
||||||
|
|
||||||
|
angles_and_edges.clear ();
|
||||||
|
const Vertex *v0 = cc->corner;
|
||||||
|
|
||||||
|
const Edge *e = cc->incoming;
|
||||||
|
while (e) {
|
||||||
|
|
||||||
|
const Polygon *t = e->v2 () == v0 ? e->right () : e->left ();
|
||||||
|
tl_assert (t != 0);
|
||||||
|
|
||||||
|
// @@@ make a method of Polygon -> next_edge(Edge *e, Vertex *at)
|
||||||
|
const Edge *en = 0;
|
||||||
|
for (unsigned int i = 0; i < 3; ++i) {
|
||||||
|
const Edge *ee = t->edge (i);
|
||||||
|
if (ee != e && (ee->v1 () == v0 || ee->v2 () == v0)) {
|
||||||
|
en = ee;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db::DVector v1 = e->edge ().d () * (e->v1 () == v0 ? 1.0 : -1.0);
|
||||||
|
db::DVector v2 = en->edge ().d () * (en->v1 () == v0 ? 1.0 : -1.0);
|
||||||
|
|
||||||
|
double angle = atan2 (db::vprod (v1, v2), db::sprod (v1, v2));
|
||||||
|
|
||||||
|
e = (en == cc->outgoing) ? 0 : en;
|
||||||
|
angles_and_edges.push_back (std::make_pair (angle, e));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sorted_edges.clear ();
|
||||||
|
|
||||||
|
for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) {
|
||||||
|
if (i->second) {
|
||||||
|
sorted_edges.push_back (i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort (sorted_edges.begin (), sorted_edges.end (), SortAngleAndEdgesByEdgeLength ());
|
||||||
|
|
||||||
|
for (auto i = sorted_edges.end (); i != sorted_edges.begin (); ) {
|
||||||
|
--i;
|
||||||
|
angles_and_edges_list::iterator ii = *i;
|
||||||
|
angles_and_edges_list::iterator iin = ii;
|
||||||
|
++iin;
|
||||||
|
if (ii->first + iin->first < (split_edges ? M_PI + db::epsilon : M_PI - db::epsilon)) {
|
||||||
|
// not an essential edge -> remove
|
||||||
|
iin->first += ii->first;
|
||||||
|
angles_and_edges.erase (ii);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) {
|
||||||
|
essential_edges.insert (i->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine triangles, but don't cross essential edges
|
||||||
|
|
||||||
|
std::unordered_set<const Polygon *> left_triangles;
|
||||||
|
for (auto it = mp_graph->begin (); it != mp_graph->end (); ++it) {
|
||||||
|
left_triangles.insert (it.operator-> ());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<std::vector<Edge *> > polygons;
|
||||||
|
|
||||||
|
while (! left_triangles.empty ()) {
|
||||||
|
|
||||||
|
polygons.push_back (std::vector<Edge *> ());
|
||||||
|
std::vector<Edge *> &edges = polygons.back ();
|
||||||
|
|
||||||
|
const Polygon *tri = *left_triangles.begin ();
|
||||||
|
std::vector<const Polygon *> queue, next_queue;
|
||||||
|
queue.push_back (tri);
|
||||||
|
|
||||||
|
while (! queue.empty ()) {
|
||||||
|
|
||||||
|
next_queue.clear ();
|
||||||
|
|
||||||
|
for (auto q = queue.begin (); q != queue.end (); ++q) {
|
||||||
|
|
||||||
|
left_triangles.erase (*q);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < 3; ++i) {
|
||||||
|
|
||||||
|
const Edge *e = (*q)->edge (i);
|
||||||
|
const Polygon *qq = e->other (*q);
|
||||||
|
|
||||||
|
if (! qq || essential_edges.find (e) != essential_edges.end ()) {
|
||||||
|
edges.push_back (const_cast<Edge *> (e)); // @@@ const_cast
|
||||||
|
} else if (left_triangles.find (qq) != left_triangles.end ()) {
|
||||||
|
next_queue.push_back (qq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.swap (next_queue);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the triangles
|
||||||
|
|
||||||
|
while (mp_graph->begin () != mp_graph->end ()) {
|
||||||
|
delete mp_graph->begin ().operator-> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the polygons
|
||||||
|
|
||||||
|
for (auto p = polygons.begin (); p != polygons.end (); ++p) {
|
||||||
|
mp_graph->create_polygon (p->begin (), p->end ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, double dbu)
|
||||||
|
{
|
||||||
|
decompose (poly, parameters, db::CplxTrans (dbu));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters ¶meters, double dbu)
|
||||||
|
{
|
||||||
|
decompose (poly, vertexes, parameters, db::CplxTrans (dbu));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans)
|
||||||
|
{
|
||||||
|
// start with a triangulation
|
||||||
|
TriangulationParameters param;
|
||||||
|
param.max_area = 0.0;
|
||||||
|
param.min_b = 0.0;
|
||||||
|
|
||||||
|
Triangulation tri (mp_graph);
|
||||||
|
tri.triangulate (poly, param, trans);
|
||||||
|
|
||||||
|
hertel_mehlhorn_decomposition (tri, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans)
|
||||||
|
{
|
||||||
|
// start with a triangulation
|
||||||
|
TriangulationParameters param;
|
||||||
|
param.max_area = 0.0;
|
||||||
|
param.min_b = 0.0;
|
||||||
|
|
||||||
|
Triangulation tri (mp_graph);
|
||||||
|
tri.triangulate (poly, vertexes, param, trans);
|
||||||
|
|
||||||
|
// @@@ consider vertexes
|
||||||
|
hertel_mehlhorn_decomposition (tri, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::decompose (const db::DPolygon &poly, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans)
|
||||||
|
{
|
||||||
|
// start with a triangulation
|
||||||
|
TriangulationParameters param;
|
||||||
|
param.max_area = 0.0;
|
||||||
|
param.min_b = 0.0;
|
||||||
|
|
||||||
|
Triangulation tri (mp_graph);
|
||||||
|
tri.triangulate (poly, param, trans);
|
||||||
|
|
||||||
|
hertel_mehlhorn_decomposition (tri, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::decompose (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans)
|
||||||
|
{
|
||||||
|
// start with a triangulation
|
||||||
|
TriangulationParameters param;
|
||||||
|
param.max_area = 0.0;
|
||||||
|
param.min_b = 0.0;
|
||||||
|
|
||||||
|
Triangulation tri (mp_graph);
|
||||||
|
tri.triangulate (poly, vertexes, param, trans);
|
||||||
|
|
||||||
|
// @@@ consider vertexes
|
||||||
|
hertel_mehlhorn_decomposition (tri, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, double dbu)
|
||||||
|
{
|
||||||
|
decompose (region, parameters, db::CplxTrans (dbu));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConvexDecomposition::decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans)
|
||||||
|
{
|
||||||
|
// start with a triangulation
|
||||||
|
TriangulationParameters param;
|
||||||
|
param.max_area = 0.0;
|
||||||
|
param.min_b = 0.0;
|
||||||
|
|
||||||
|
Triangulation tri (mp_graph);
|
||||||
|
tri.triangulate (region, param, trans);
|
||||||
|
|
||||||
|
hertel_mehlhorn_decomposition (tri, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace plc
|
||||||
|
|
||||||
|
} // namespace db
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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_dbPLCConvexDecomposition
|
||||||
|
#define HDR_dbPLCConvexDecomposition
|
||||||
|
|
||||||
|
#include "dbCommon.h"
|
||||||
|
#include "dbPLC.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace db
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace plc
|
||||||
|
{
|
||||||
|
|
||||||
|
class Triangulation;
|
||||||
|
|
||||||
|
struct DB_PUBLIC ConvexDecompositionParameters
|
||||||
|
{
|
||||||
|
ConvexDecompositionParameters ()
|
||||||
|
: with_segments (false),
|
||||||
|
split_edges (false),
|
||||||
|
base_verbosity (30)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Introduce new segments
|
||||||
|
*
|
||||||
|
* If true, new segments will be introduced.
|
||||||
|
* New segments are constructed perpendicular to the edges forming
|
||||||
|
* a concave corner.
|
||||||
|
*/
|
||||||
|
bool with_segments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Split edges
|
||||||
|
*
|
||||||
|
* If true, edges in the resulting polygons may be split.
|
||||||
|
* This will produce edge sections that correlate with
|
||||||
|
* other polygon edges, but may be collinear with neighbor
|
||||||
|
* edges.
|
||||||
|
*/
|
||||||
|
double split_edges;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The verbosity level above which triangulation reports details
|
||||||
|
*/
|
||||||
|
int base_verbosity;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A convex decomposition algorithm
|
||||||
|
*
|
||||||
|
* This class implements a variant of the Hertel-Mehlhorn decomposition.
|
||||||
|
*/
|
||||||
|
class DB_PUBLIC ConvexDecomposition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief The constructor
|
||||||
|
*
|
||||||
|
* The graph will be one filled by the decomposition.
|
||||||
|
*/
|
||||||
|
ConvexDecomposition (Graph *graph);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears the triangulation
|
||||||
|
*/
|
||||||
|
void clear ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a decomposition 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 decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, double dbu = 1.0);
|
||||||
|
|
||||||
|
// more versions
|
||||||
|
void decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||||
|
void decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, double dbu = 1.0);
|
||||||
|
void decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters ¶meters, double dbu = 1.0);
|
||||||
|
void decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||||
|
void decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decomposes a floating-point polygon
|
||||||
|
*/
|
||||||
|
void decompose (const db::DPolygon &poly, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ());
|
||||||
|
void decompose (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ());
|
||||||
|
|
||||||
|
private:
|
||||||
|
Graph *mp_graph;
|
||||||
|
|
||||||
|
void hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters ¶m);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace plc
|
||||||
|
|
||||||
|
} // namespace db
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -22,9 +22,6 @@
|
||||||
|
|
||||||
|
|
||||||
#include "dbPLCTriangulation.h"
|
#include "dbPLCTriangulation.h"
|
||||||
#include "dbLayout.h"
|
|
||||||
#include "dbWriter.h"
|
|
||||||
#include "tlStream.h"
|
|
||||||
#include "tlLog.h"
|
#include "tlLog.h"
|
||||||
#include "tlTimer.h"
|
#include "tlTimer.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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 "dbPLCConvexDecomposition.h"
|
||||||
|
#include "dbWriter.h"
|
||||||
|
#include "dbRegionProcessors.h"
|
||||||
|
#include "dbTestSupport.h"
|
||||||
|
#include "tlUnitTest.h"
|
||||||
|
#include "tlStream.h"
|
||||||
|
#include "tlFileUtils.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
class TestableConvexDecomposition
|
||||||
|
: public db::plc::ConvexDecomposition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using db::plc::ConvexDecomposition::ConvexDecomposition;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(basic)
|
||||||
|
{
|
||||||
|
db::plc::Graph plc;
|
||||||
|
TestableConvexDecomposition decomp (&plc);
|
||||||
|
|
||||||
|
db::Point contour[] = {
|
||||||
|
db::Point (0, 0),
|
||||||
|
db::Point (0, 100),
|
||||||
|
db::Point (1000, 100),
|
||||||
|
db::Point (1000, 500),
|
||||||
|
db::Point (1100, 500),
|
||||||
|
db::Point (1100, 100),
|
||||||
|
db::Point (2100, 100),
|
||||||
|
db::Point (2100, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
db::Point contour2[] = {
|
||||||
|
db::Point (4000, 0),
|
||||||
|
db::Point (4000, 100),
|
||||||
|
db::Point (5000, 100),
|
||||||
|
db::Point (5000, 500),
|
||||||
|
db::Point (5100, 500),
|
||||||
|
db::Point (5100, 100),
|
||||||
|
db::Point (6100, 100),
|
||||||
|
db::Point (6100, -1000),
|
||||||
|
db::Point (4150, -1000),
|
||||||
|
db::Point (4150, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
db::Region region;
|
||||||
|
|
||||||
|
db::Polygon poly;
|
||||||
|
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
|
||||||
|
region.insert (poly);
|
||||||
|
|
||||||
|
poly.clear ();
|
||||||
|
poly.assign_hull (contour2 + 0, contour2 + sizeof (contour2) / sizeof (contour2[0]));
|
||||||
|
region.insert (poly);
|
||||||
|
|
||||||
|
double dbu = 0.001;
|
||||||
|
|
||||||
|
db::plc::ConvexDecompositionParameters param;
|
||||||
|
decomp.decompose (region, param, dbu);
|
||||||
|
|
||||||
|
std::unique_ptr<db::Layout> ly (plc.to_layout ());
|
||||||
|
db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au1.gds");
|
||||||
|
|
||||||
|
param.with_segments = true;
|
||||||
|
param.split_edges = false;
|
||||||
|
decomp.decompose (region, param, dbu);
|
||||||
|
|
||||||
|
ly.reset (plc.to_layout ());
|
||||||
|
db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au2.gds");
|
||||||
|
|
||||||
|
param.with_segments = false;
|
||||||
|
param.split_edges = true;
|
||||||
|
decomp.decompose (region, param, dbu);
|
||||||
|
|
||||||
|
ly.reset (plc.to_layout ());
|
||||||
|
db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au3.gds");
|
||||||
|
|
||||||
|
param.with_segments = true;
|
||||||
|
param.split_edges = true;
|
||||||
|
decomp.decompose (region, param, dbu);
|
||||||
|
|
||||||
|
ly.reset (plc.to_layout ());
|
||||||
|
db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au4.gds");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -12,6 +12,7 @@ SOURCES = \
|
||||||
dbFillToolTests.cc \
|
dbFillToolTests.cc \
|
||||||
dbLogTests.cc \
|
dbLogTests.cc \
|
||||||
dbObjectWithPropertiesTests.cc \
|
dbObjectWithPropertiesTests.cc \
|
||||||
|
dbPLCConvexDecompositionTests.cc \
|
||||||
dbPLCGraphTests.cc \
|
dbPLCGraphTests.cc \
|
||||||
dbPLCTests.cc \
|
dbPLCTests.cc \
|
||||||
dbPLCTriangulationTests.cc \
|
dbPLCTriangulationTests.cc \
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue