Porting HM decomposition to new PLC framework

This commit is contained in:
Matthias Koefferlein 2025-04-16 00:02:25 +02:00
parent 76e039bd2a
commit 60d1fb0685
12 changed files with 780 additions and 104 deletions

View File

@ -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 \

View File

@ -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);

View File

@ -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;

View File

@ -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 &param)
{
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 &parameters, double dbu)
{
decompose (poly, parameters, db::CplxTrans (dbu));
}
void
ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters &parameters, double dbu)
{
decompose (poly, vertexes, parameters, db::CplxTrans (dbu));
}
void
ConvexDecomposition::decompose (const db::Polygon &poly, const ConvexDecompositionParameters &parameters, 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 &parameters, 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 &parameters, 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 &parameters, 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 &region, const ConvexDecompositionParameters &parameters, double dbu)
{
decompose (region, parameters, db::CplxTrans (dbu));
}
void
ConvexDecomposition::decompose (const db::Region &region, const ConvexDecompositionParameters &parameters, 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

View File

@ -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 &region, const ConvexDecompositionParameters &parameters, double dbu = 1.0);
// more versions
void decompose (const db::Region &region, const ConvexDecompositionParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
void decompose (const db::Polygon &poly, const ConvexDecompositionParameters &parameters, double dbu = 1.0);
void decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters &parameters, double dbu = 1.0);
void decompose (const db::Polygon &poly, const ConvexDecompositionParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
void decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters &parameters, const db::CplxTrans &trans = db::CplxTrans ());
/**
* @brief Decomposes a floating-point polygon
*/
void decompose (const db::DPolygon &poly, const ConvexDecompositionParameters &parameters, const db::DCplxTrans &trans = db::DCplxTrans ());
void decompose (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const ConvexDecompositionParameters &parameters, const db::DCplxTrans &trans = db::DCplxTrans ());
private:
Graph *mp_graph;
void hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters &param);
};
} // namespace plc
} // namespace db
#endif

View File

@ -22,9 +22,6 @@
#include "dbPLCTriangulation.h"
#include "dbLayout.h"
#include "dbWriter.h"
#include "tlStream.h"
#include "tlLog.h"
#include "tlTimer.h"

View File

@ -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");
}

View File

@ -12,6 +12,7 @@ SOURCES = \
dbFillToolTests.cc \
dbLogTests.cc \
dbObjectWithPropertiesTests.cc \
dbPLCConvexDecompositionTests.cc \
dbPLCGraphTests.cc \
dbPLCTests.cc \
dbPLCTriangulationTests.cc \

BIN
testdata/algo/hm_decomposition_au1.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/hm_decomposition_au2.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/hm_decomposition_au3.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/hm_decomposition_au4.gds vendored Normal file

Binary file not shown.