Hierarchical implementation of extended method for edges

This commit is contained in:
Matthias Koefferlein 2019-02-17 17:34:31 +01:00
parent ae783a2245
commit 74006b6208
7 changed files with 349 additions and 168 deletions

View File

@ -27,17 +27,141 @@
#include "dbEdges.h"
#include "dbEdgeBoolean.h"
#include "dbBoxConvert.h"
#include "dbBoxScanner.h"
#include "dbRegion.h"
#include "dbFlatRegion.h"
#include "dbPolygonTools.h"
#include "dbShapeProcessor.h"
#include "dbEdgeProcessor.h"
#include "dbPolygonGenerators.h"
#include "dbPolygon.h"
#include "dbPath.h"
#include <sstream>
namespace db
{
// -------------------------------------------------------------------------------------------------------------
// JoinEdgesCluster implementation
JoinEdgesCluster::JoinEdgesCluster (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i)
: mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i)
{
// .. nothing yet ..
}
void
JoinEdgesCluster::finish ()
{
std::multimap<db::Point, iterator> objects_by_p1;
std::multimap<db::Point, iterator> objects_by_p2;
for (iterator o = begin (); o != end (); ++o) {
if (o->first->p1 () != o->first->p2 ()) {
objects_by_p1.insert (std::make_pair (o->first->p1 (), o));
objects_by_p2.insert (std::make_pair (o->first->p2 (), o));
}
}
while (! objects_by_p2.empty ()) {
tl_assert (! objects_by_p1.empty ());
// Find the beginning of a new sequence
std::multimap<db::Point, iterator>::iterator j0 = objects_by_p1.begin ();
std::multimap<db::Point, iterator>::iterator j = j0;
do {
std::multimap<db::Point, iterator>::iterator jj = objects_by_p2.find (j->first);
if (jj == objects_by_p2.end ()) {
break;
} else {
j = objects_by_p1.find (jj->second->first->p1 ());
tl_assert (j != objects_by_p1.end ());
}
} while (j != j0);
iterator i = j->second;
// determine a sequence
// TODO: this chooses any solution in case of forks. Choose a specific one?
std::vector<db::Point> pts;
pts.push_back (i->first->p1 ());
do {
// record the next point
pts.push_back (i->first->p2 ());
// remove the edge as it's taken
std::multimap<db::Point, iterator>::iterator jj;
for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) {
if (jj->second == i) {
break;
}
}
tl_assert (jj != objects_by_p2.end () && jj->second == i);
objects_by_p2.erase (jj);
objects_by_p1.erase (j);
// process along the edge to the next one
// TODO: this chooses any solution in case of forks. Choose a specific one?
j = objects_by_p1.find (i->first->p2 ());
if (j != objects_by_p1.end ()) {
i = j->second;
} else {
break;
}
} while (true);
bool cyclic = (pts.back () == pts.front ());
if (! cyclic) {
// non-cyclic sequence
db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false);
std::vector<db::Point> hull;
path.hull (hull, m_ext_o, m_ext_i);
db::Polygon poly;
poly.assign_hull (hull.begin (), hull.end ());
mp_output->put (poly);
} else {
// we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole
db::Polygon poly;
poly.assign_hull (pts.begin (), pts.end ());
db::EdgeProcessor ep;
db::PolygonGenerator pg (*mp_output, false, true);
int mode_a = -1, mode_b = -1;
if (m_ext_o == 0) {
ep.insert (poly, 0);
} else {
db::Polygon sized_poly (poly);
sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/);
ep.insert (sized_poly, 0);
mode_a = 1;
}
if (m_ext_i == 0) {
ep.insert (poly, 1);
} else {
db::Polygon sized_poly (poly);
sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/);
ep.insert (sized_poly, 1);
mode_b = 1;
}
db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b);
ep.process (pg, op);
}
}
}
// -------------------------------------------------------------------------------------------------------------
// AsIfFlagEdges implementation
@ -219,148 +343,13 @@ AsIfFlatEdges::selected_not_interacting (const Region &other) const
namespace
{
template <class OutputContainer>
struct JoinEdgesCluster
: public db::cluster<db::Edge, size_t>,
public db::PolygonSink
{
typedef db::Edge::coord_type coord_type;
JoinEdgesCluster (OutputContainer *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i)
: mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i)
{
// .. nothing yet ..
}
void finish ()
{
std::multimap<db::Point, iterator> objects_by_p1;
std::multimap<db::Point, iterator> objects_by_p2;
for (iterator o = begin (); o != end (); ++o) {
if (o->first->p1 () != o->first->p2 ()) {
objects_by_p1.insert (std::make_pair (o->first->p1 (), o));
objects_by_p2.insert (std::make_pair (o->first->p2 (), o));
}
}
while (! objects_by_p2.empty ()) {
tl_assert (! objects_by_p1.empty ());
// Find the beginning of a new sequence
std::multimap<db::Point, iterator>::iterator j0 = objects_by_p1.begin ();
std::multimap<db::Point, iterator>::iterator j = j0;
do {
std::multimap<db::Point, iterator>::iterator jj = objects_by_p2.find (j->first);
if (jj == objects_by_p2.end ()) {
break;
} else {
j = objects_by_p1.find (jj->second->first->p1 ());
tl_assert (j != objects_by_p1.end ());
}
} while (j != j0);
iterator i = j->second;
// determine a sequence
// TODO: this chooses any solution in case of forks. Choose a specific one?
std::vector<db::Point> pts;
pts.push_back (i->first->p1 ());
do {
// record the next point
pts.push_back (i->first->p2 ());
// remove the edge as it's taken
std::multimap<db::Point, iterator>::iterator jj;
for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) {
if (jj->second == i) {
break;
}
}
tl_assert (jj != objects_by_p2.end () && jj->second == i);
objects_by_p2.erase (jj);
objects_by_p1.erase (j);
// process along the edge to the next one
// TODO: this chooses any solution in case of forks. Choose a specific one?
j = objects_by_p1.find (i->first->p2 ());
if (j != objects_by_p1.end ()) {
i = j->second;
} else {
break;
}
} while (true);
bool cyclic = (pts.back () == pts.front ());
if (! cyclic) {
// non-cyclic sequence
db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false);
std::vector<db::Point> hull;
path.hull (hull, m_ext_o, m_ext_i);
db::Polygon poly;
poly.assign_hull (hull.begin (), hull.end ());
put (poly);
} else {
// we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole
db::Polygon poly;
poly.assign_hull (pts.begin (), pts.end ());
db::EdgeProcessor ep;
db::PolygonGenerator pg (*this, false, true);
int mode_a = -1, mode_b = -1;
if (m_ext_o == 0) {
ep.insert (poly, 0);
} else {
db::Polygon sized_poly (poly);
sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/);
ep.insert (sized_poly, 0);
mode_a = 1;
}
if (m_ext_i == 0) {
ep.insert (poly, 1);
} else {
db::Polygon sized_poly (poly);
sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/);
ep.insert (sized_poly, 1);
mode_b = 1;
}
db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b);
ep.process (pg, op);
}
}
}
virtual void put (const db::Polygon &polygon)
{
mp_output->insert (polygon);
}
private:
OutputContainer *mp_output;
coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i;
};
template <class OutputContainer>
struct JoinEdgesClusterCollector
: public db::cluster_collector<db::Edge, size_t, JoinEdgesCluster<OutputContainer> >
: public db::cluster_collector<db::Edge, size_t, JoinEdgesCluster>
{
typedef db::Edge::coord_type coord_type;
JoinEdgesClusterCollector (OutputContainer *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i)
: db::cluster_collector<db::Edge, size_t, JoinEdgesCluster<OutputContainer> > (JoinEdgesCluster<OutputContainer> (output, ext_b, ext_e, ext_o, ext_i), true)
JoinEdgesClusterCollector (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i)
: db::cluster_collector<db::Edge, size_t, JoinEdgesCluster> (JoinEdgesCluster (output, ext_b, ext_e, ext_o, ext_i), true)
{
// .. nothing yet ..
}
@ -368,20 +357,45 @@ struct JoinEdgesClusterCollector
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
if (o1->p2 () == o2->p1 () || o1->p1 () == o2->p2 ()) {
db::cluster_collector<db::Edge, size_t, JoinEdgesCluster<OutputContainer> >::add (o1, p1, o2, p2);
db::cluster_collector<db::Edge, size_t, JoinEdgesCluster>::add (o1, p1, o2, p2);
}
}
};
}
db::Polygon
AsIfFlatEdges::extended_edge (const db::Edge &edge, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i)
{
db::DVector d;
if (edge.is_degenerate ()) {
d = db::DVector (1.0, 0.0);
} else {
d = db::DVector (edge.d ()) * (1.0 / edge.double_length ());
}
db::DVector n (-d.y (), d.x ());
db::Point pts[4] = {
db::Point (db::DPoint (edge.p1 ()) - d * double (ext_b) + n * double (ext_o)),
db::Point (db::DPoint (edge.p2 ()) + d * double (ext_e) + n * double (ext_o)),
db::Point (db::DPoint (edge.p2 ()) + d * double (ext_e) - n * double (ext_i)),
db::Point (db::DPoint (edge.p1 ()) - d * double (ext_b) - n * double (ext_i)),
};
db::Polygon poly;
poly.assign_hull (pts + 0, pts + 4);
return poly;
}
RegionDelegate *
AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const
{
if (join) {
std::auto_ptr<FlatRegion> output (new FlatRegion ());
JoinEdgesClusterCollector<FlatRegion> cluster_collector (output.get (), ext_b, ext_e, ext_o, ext_i);
db::ShapeGenerator sg (output->raw_polygons (), false);
JoinEdgesClusterCollector cluster_collector (&sg, ext_b, ext_e, ext_o, ext_i);
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
scanner.reserve (size ());
@ -401,29 +415,8 @@ AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, c
} else {
std::auto_ptr<FlatRegion> output (new FlatRegion ());
for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) {
db::DVector d;
if (e->is_degenerate ()) {
d = db::DVector (1.0, 0.0);
} else {
d = db::DVector (e->d ()) * (1.0 / e->double_length ());
}
db::DVector n (-d.y (), d.x ());
db::Point pts[4] = {
db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) + n * double (ext_o)),
db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) + n * double (ext_o)),
db::Point (db::DPoint (e->p2 ()) + d * double (ext_e) - n * double (ext_i)),
db::Point (db::DPoint (e->p1 ()) - d * double (ext_b) - n * double (ext_i)),
};
db::Polygon poly;
poly.assign_hull (pts + 0, pts + 4);
output->insert (poly);
output->insert (extended_edge (*e, ext_b, ext_e, ext_o, ext_i));
}
return output.release ();

View File

@ -25,11 +25,35 @@
#define HDR_dbAsIfFlatEdges
#include "dbCommon.h"
#include "dbBoxScanner.h"
#include "dbEdgesDelegate.h"
#include <map>
#include <vector>
namespace db {
class PolygonSink;
/**
* @brief A helper class to turn joined edge sequences into polygons
*
* This object is an edge cluster so it can connect to a cluster collector
* driven by a box scanner.
*/
struct JoinEdgesCluster
: public db::cluster<db::Edge, size_t>
{
typedef db::Edge::coord_type coord_type;
JoinEdgesCluster (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i);
void finish ();
private:
db::PolygonSink *mp_output;
coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i;
};
/**
* @brief Provides default flat implementations
*/
@ -160,6 +184,7 @@ protected:
void update_bbox (const db::Box &box);
void invalidate_bbox ();
EdgePairs run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const;
static db::Polygon extended_edge (const db::Edge &edge, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i);
private:
AsIfFlatEdges &operator= (const AsIfFlatEdges &other);

View File

@ -31,6 +31,7 @@
#include "dbCellMapping.h"
#include "dbLayoutUtils.h"
#include "dbLocalOperation.h"
#include "dbLocalOperationUtils.h"
#include "dbHierProcessor.h"
#include "dbEmptyEdges.h"
@ -807,8 +808,96 @@ EdgesDelegate *DeepEdges::outside_part (const Region &other) const
RegionDelegate *DeepEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const
{
// TODO: implement
return AsIfFlatEdges::extended (ext_b, ext_e, ext_o, ext_i, join);
ensure_merged_edges_valid ();
std::auto_ptr<db::DeepRegion> res (new db::DeepRegion (m_merged_edges.derived ()));
db::Layout &layout = const_cast<db::Layout &> (m_merged_edges.layout ());
db::Cell &top_cell = const_cast<db::Cell &> (m_merged_edges.initial_cell ());
db::MagnificationReducer red;
db::cell_variants_collector<db::MagnificationReducer> vars (red);
vars.collect (m_merged_edges.layout (), m_merged_edges.initial_cell ());
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> > to_commit;
if (join) {
// In joined mode we need to create a special cluster which connects all joined edges
db::DeepLayer joined = m_merged_edges.derived ();
db::hier_clusters<db::Edge> hc;
db::Connectivity conn (db::Connectivity::EdgesConnectByPoints);
conn.connect (m_merged_edges);
// TODO: this uses the wrong verbosity inside ...
hc.build (layout, m_merged_edges.initial_cell (), db::ShapeIterator::Edges, conn);
// TODO: iterate only over the called cells?
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
const std::map<db::ICplxTrans, size_t> &vv = vars.variants (c->cell_index ());
for (std::map<db::ICplxTrans, size_t>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
db::Shapes *out;
if (vv.size () == 1) {
out = & c->shapes (res->deep_layer ().layer ());
} else {
out = & to_commit [c->cell_index ()][v->first];
}
const db::connected_clusters<db::Edge> &cc = hc.clusters_per_cell (c->cell_index ());
for (db::connected_clusters<db::Edge>::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) {
if (cc.is_root (*cl)) {
PolygonRefToShapesGenerator prgen (&layout, out);
polygon_transformation_filter<db::ICplxTrans> ptrans (&prgen, v->first.inverted ());
JoinEdgesCluster jec (&ptrans, ext_b, ext_e, ext_o, ext_i);
std::list<db::Edge> heap;
for (db::recursive_cluster_shape_iterator<db::Edge> rcsi (hc, m_merged_edges.layer (), c->cell_index (), *cl); ! rcsi.at_end (); ++rcsi) {
heap.push_back (rcsi->transformed (v->first * rcsi.trans ()));
jec.add (&heap.back (), 0);
}
jec.finish ();
}
}
}
}
} else {
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
const std::map<db::ICplxTrans, size_t> &vv = vars.variants (c->cell_index ());
for (std::map<db::ICplxTrans, size_t>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
db::Shapes *out;
if (vv.size () == 1) {
out = & c->shapes (res->deep_layer ().layer ());
} else {
out = & to_commit [c->cell_index ()][v->first];
}
for (db::Shapes::shape_iterator si = c->shapes (m_merged_edges.layer ()).begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) {
out->insert (extended_edge (si->edge ().transformed (v->first), ext_b, ext_e, ext_o, ext_i).transformed (v->first.inverted ()));
}
}
}
}
// propagate results from variants
vars.commit_shapes (layout, top_cell, res->deep_layer ().layer (), to_commit);
return res.release ();
}
EdgesDelegate *DeepEdges::start_segments (length_type length, double fraction) const

View File

@ -175,6 +175,8 @@ public:
}
}
db::Shapes &raw_polygons () { return m_polygons; }
protected:
virtual void merged_semantics_changed ();
virtual Box compute_bbox () const;
@ -185,8 +187,6 @@ private:
friend class AsIfFlatRegion;
friend class Region;
db::Shapes &raw_polygons () { return m_polygons; }
FlatRegion &operator= (const FlatRegion &other);
bool m_is_merged;

View File

@ -36,6 +36,33 @@
namespace db
{
template <class Trans>
class polygon_transformation_filter
: public PolygonSink
{
public:
/**
* @brief Constructor specifying an external vector for storing the polygons
*/
polygon_transformation_filter (PolygonSink *output, const Trans &tr)
: mp_output (output), m_trans (tr)
{
// .. nothing yet ..
}
/**
* @brief Implementation of the PolygonSink interface
*/
virtual void put (const db::Polygon &polygon)
{
mp_output->put (polygon.transformed (m_trans));
}
private:
db::PolygonSink *mp_output;
const Trans m_trans;
};
class PolygonRefGenerator
: public PolygonSink
{

View File

@ -247,3 +247,50 @@ TEST(5_Filters)
}
}
TEST(6_Extended)
{
db::Layout ly;
{
std::string fn (tl::testsrc ());
fn += "/testdata/algo/deep_region_area_peri_l1.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
db::DeepShapeStore dss;
unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0));
db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
db::Edges e2 = r2.edges ();
db::Layout target;
unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2);
db::EdgeLengthFilter elf1 (0, 40000, false);
db::Edges e2f = e2.filtered (elf1);
db::Region e2e1;
e2.extended (e2e1, 100, 200, 300, 50);
db::Region e2e2;
e2f.extended (e2e2, 0, 0, 300, 0);
db::Region e2e3;
e2.extended (e2e3, 100, 200, 300, 50, true);
db::Region e2e4;
e2f.extended (e2e4, 0, 0, 300, 0, true);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2e1);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2e2);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), e2e3);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), e2e4);
CHECKPOINT();
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_edges_au6.gds");
}

BIN
testdata/algo/deep_edges_au6.gds vendored Normal file

Binary file not shown.