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 \
|
||||
dbObjectWithProperties.cc \
|
||||
dbPLC.cc \
|
||||
dbPLCConvexDecomposition.cc \
|
||||
dbPLCTriangulation.cc \
|
||||
dbPath.cc \
|
||||
dbPCellDeclaration.cc \
|
||||
|
|
@ -309,6 +310,7 @@ HEADERS = \
|
|||
dbObjectTag.h \
|
||||
dbObjectWithProperties.h \
|
||||
dbPLC.h \
|
||||
dbPLCConvexDecomposition.h \
|
||||
dbPLCTriangulation.h \
|
||||
dbPath.h \
|
||||
dbPCellDeclaration.h \
|
||||
|
|
|
|||
|
|
@ -804,105 +804,6 @@ Graph::bbox () const
|
|||
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 *
|
||||
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"));
|
||||
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 l20 = layout->insert_layer (db::LayerProperties (20, 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;
|
||||
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 ((t->id () & 1) != 0) {
|
||||
top.shapes (l20).insert (dbu_trans * poly);
|
||||
|
|
|
|||
|
|
@ -504,6 +504,7 @@ private:
|
|||
friend class Polygon;
|
||||
friend class Graph;
|
||||
friend class Triangulation;
|
||||
friend class ConvexDecomposition;
|
||||
|
||||
Graph *mp_graph;
|
||||
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 "dbLayout.h"
|
||||
#include "dbWriter.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlLog.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 \
|
||||
dbLogTests.cc \
|
||||
dbObjectWithPropertiesTests.cc \
|
||||
dbPLCConvexDecompositionTests.cc \
|
||||
dbPLCGraphTests.cc \
|
||||
dbPLCTests.cc \
|
||||
dbPLCTriangulationTests.cc \
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue