Refactoring of edges processor.

This commit is contained in:
Matthias Koefferlein 2019-02-19 23:10:14 +01:00
parent 496b695ef0
commit 3dd1ed4c4d
13 changed files with 583 additions and 430 deletions

View File

@ -25,6 +25,7 @@
#include "dbFlatEdges.h"
#include "dbEmptyEdges.h"
#include "dbEdges.h"
#include "dbEdgesUtils.h"
#include "dbEdgeBoolean.h"
#include "dbBoxConvert.h"
#include "dbRegion.h"
@ -41,127 +42,6 @@
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
@ -277,30 +157,6 @@ struct JoinEdgesClusterCollector
}
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
{
@ -337,65 +193,6 @@ AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, c
}
}
db::Edge
AsIfFlatEdges::compute_partial (const db::Edge &edge, int mode, length_type length, double fraction)
{
double l = std::max (edge.double_length () * fraction, double (length));
if (mode < 0) {
return db::Edge (edge.p1 (), db::Point (db::DPoint (edge.p1 ()) + db::DVector (edge.d ()) * (l / edge.double_length ())));
} else if (mode > 0) {
return db::Edge (db::Point (db::DPoint (edge.p2 ()) - db::DVector (edge.d ()) * (l / edge.double_length ())), edge.p2 ());
} else {
db::DVector dl = db::DVector (edge.d ()) * (0.5 * l / edge.double_length ());
db::DPoint center = db::DPoint (edge.p1 ()) + db::DVector (edge.p2 () - edge.p1 ()) * 0.5;
return db::Edge (db::Point (center - dl), db::Point (center + dl));
}
}
EdgesDelegate *
AsIfFlatEdges::segments (int mode, length_type length, double fraction) const
{
std::auto_ptr<FlatEdges> edges (new FlatEdges ());
edges->reserve (size ());
// zero-length edges would vanish in merged sematics, so we don't set it now
if (length == 0) {
edges->set_merged_semantics (false);
}
for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) {
edges->insert (compute_partial (*e, mode, length, fraction));
}
return edges.release ();
}
EdgesDelegate *
AsIfFlatEdges::start_segments (length_type length, double fraction) const
{
return segments (-1, length, fraction);
}
EdgesDelegate *
AsIfFlatEdges::end_segments (length_type length, double fraction) const
{
return segments (1, length, fraction);
}
EdgesDelegate *
AsIfFlatEdges::centers (length_type length, double fraction) const
{
return segments (0, length, fraction);
}
EdgesDelegate *
AsIfFlatEdges::selected_interacting_generic (const Edges &edges, bool inverse) const
{
@ -543,6 +340,28 @@ void AsIfFlatEdges::invalidate_bbox ()
m_bbox_valid = false;
}
EdgesDelegate *
AsIfFlatEdges::processed (const EdgeProcessorBase &filter) const
{
std::auto_ptr<FlatEdges> edges (new FlatEdges ());
if (filter.result_must_not_be_merged ()) {
edges->set_merged_semantics (false);
}
std::vector<db::Edge> res_edges;
for (EdgesIterator e (filter.requires_raw_input () ? begin () : begin_merged ()); ! e.at_end (); ++e) {
res_edges.clear ();
filter.process (*e, res_edges);
for (std::vector<db::Edge>::const_iterator er = res_edges.begin (); er != res_edges.end (); ++er) {
edges->insert (*er);
}
}
return edges.release ();
}
EdgesDelegate *
AsIfFlatEdges::filtered (const EdgeFilterBase &filter) const
{

View File

@ -37,133 +37,6 @@ namespace db {
class PolygonSink;
/**
* @brief A helper class for the edge interaction functionality which acts as an edge pair receiver
*/
template <class OutputContainer>
class edge_interaction_filter
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
edge_interaction_filter (OutputContainer &output)
: mp_output (&output)
{
// .. nothing yet ..
}
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
// Select the edges which intersect
if (p1 != p2) {
const db::Edge *o = p1 > p2 ? o2 : o1;
const db::Edge *oo = p1 > p2 ? o1 : o2;
if (o->intersect (*oo)) {
if (m_seen.insert (o).second) {
mp_output->insert (*o);
}
}
}
}
private:
OutputContainer *mp_output;
std::set<const db::Edge *> m_seen;
};
/**
* @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver
*
* Note: This special scanner uses pointers to two different objects: edges and polygons.
* It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate
* pointers to edges.
*
* There is a special box converter which is able to sort that out as well.
*/
template <class OutputContainer>
class edge_to_region_interaction_filter
: public db::box_scanner_receiver2<db::Edge, size_t, db::Polygon, size_t>
{
public:
edge_to_region_interaction_filter (OutputContainer &output)
: mp_output (&output)
{
// .. nothing yet ..
}
void add (const db::Edge *e, size_t, const db::Polygon *p, size_t)
{
if (m_seen.find (e) == m_seen.end ()) {
if (db::interact (*p, *e)) {
m_seen.insert (e);
mp_output->insert (*e);
}
}
}
private:
OutputContainer *mp_output;
std::set<const db::Edge *> m_seen;
};
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*
* If will perform a edge by edge check using the provided EdgeRelationFilter
*/
template <class Output>
class edge2edge_check_for_edges
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
edge2edge_check_for_edges (const EdgeRelationFilter &check, Output &output, bool requires_different_layers)
: mp_check (&check), mp_output (&output)
{
m_requires_different_layers = requires_different_layers;
}
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
// Overlap or inside checks require input from different layers
if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
db::EdgePair ep;
if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) {
mp_output->insert (ep);
}
}
}
private:
const EdgeRelationFilter *mp_check;
Output *mp_output;
bool m_requires_different_layers;
};
/**
* @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
*/
@ -209,6 +82,13 @@ public:
return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
}
virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &filter)
{
return processed (filter);
}
virtual EdgesDelegate *processed (const EdgeProcessorBase &filter) const;
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter)
{
return filtered (filter);
@ -274,9 +154,6 @@ public:
}
virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const;
virtual EdgesDelegate *start_segments (length_type length, double fraction) const;
virtual EdgesDelegate *end_segments (length_type length, double fraction) const;
virtual EdgesDelegate *centers (length_type length, double fraction) const;
virtual EdgesDelegate *selected_interacting (const Edges &) const;
virtual EdgesDelegate *selected_not_interacting (const Edges &) const;
@ -294,8 +171,6 @@ 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);
static db::Edge compute_partial (const db::Edge &edge, int mode, length_type length, double fraction);
virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, bool inverse) const;
virtual EdgesDelegate *selected_interacting_generic (const Region &region, bool inverse) const;
@ -308,7 +183,6 @@ private:
virtual db::Box compute_bbox () const;
EdgesDelegate *boolean (const Edges *other, EdgeBoolOp op) const;
EdgesDelegate *edge_region_op (const Region &other, bool outside, bool include_borders) const;
EdgesDelegate *segments (int mode, length_type length, double fraction) const;
};
}

View File

@ -21,6 +21,7 @@
*/
#include "dbEdges.h"
#include "dbEdgesUtils.h"
#include "dbRegion.h"
#include "dbDeepEdges.h"
#include "dbDeepRegion.h"
@ -476,6 +477,77 @@ std::string DeepEdges::to_string (size_t nmax) const
return db::AsIfFlatEdges::to_string (nmax);
}
EdgesDelegate *DeepEdges::process_in_place (const EdgeProcessorBase &filter)
{
// TODO: implement to be really in-place
return processed (filter);
}
EdgesDelegate *DeepEdges::processed (const EdgeProcessorBase &filter) const
{
ensure_merged_edges_valid ();
std::auto_ptr<VariantsCollectorBase> vars;
if (filter.vars ()) {
vars.reset (new db::VariantsCollectorBase (filter.vars ()));
vars->collect (m_merged_edges.layout (), m_merged_edges.initial_cell ());
// NOTE: m_merged_polygons is mutable, so why is the const_cast needed?
const_cast<db::DeepLayer &> (m_merged_edges).separate_variants (*vars);
}
db::Layout &layout = m_merged_edges.layout ();
std::vector<db::Edge> res_edges;
std::auto_ptr<db::DeepEdges> res (new db::DeepEdges (m_merged_edges.derived ()));
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
if (vars.get ()) {
const std::map<db::ICplxTrans, size_t> &v = vars->variants (c->cell_index ());
tl_assert (v.size () == size_t (1));
const db::ICplxTrans &tr = v.begin ()->first;
db::ICplxTrans trinv = tr.inverted ();
const db::Shapes &s = c->shapes (filter.requires_raw_input () ? m_deep_layer.layer () : m_merged_edges.layer ());
db::Shapes &st = c->shapes (res->deep_layer ().layer ());
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) {
res_edges.clear ();
filter.process (si->edge ().transformed (tr), res_edges);
for (std::vector<db::Edge>::const_iterator er = res_edges.begin (); er != res_edges.end (); ++er) {
st.insert (er->transformed (trinv));
}
}
} else {
const db::Shapes &s = c->shapes (filter.requires_raw_input () ? m_deep_layer.layer () : m_merged_edges.layer ());
db::Shapes &st = c->shapes (res->deep_layer ().layer ());
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) {
res_edges.clear ();
filter.process (si->edge (), res_edges);
for (std::vector<db::Edge>::const_iterator er = res_edges.begin (); er != res_edges.end (); ++er) {
st.insert (*er);
}
}
}
}
if (filter.result_is_merged ()) {
res->set_is_merged (true);
}
return res.release ();
}
EdgesDelegate *DeepEdges::filter_in_place (const EdgeFilterBase &filter)
{
// TODO: implement to be really in-place
@ -917,62 +989,6 @@ RegionDelegate *DeepEdges::extended (coord_type ext_b, coord_type ext_e, coord_t
return res.release ();
}
EdgesDelegate *DeepEdges::segments (int mode, length_type length, double fraction) const
{
ensure_merged_edges_valid ();
std::auto_ptr<db::DeepEdges> res (new db::DeepEdges (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;
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 (compute_partial (si->edge ().transformed (v->first), mode, length, fraction).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
{
return segments (-1, length, fraction);
}
EdgesDelegate *DeepEdges::end_segments (length_type length, double fraction) const
{
return segments (1, length, fraction);
}
EdgesDelegate *DeepEdges::centers (length_type length, double fraction) const
{
return segments (0, length, fraction);
}
namespace
{

View File

@ -110,6 +110,8 @@ public:
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter);
virtual EdgesDelegate *filtered (const EdgeFilterBase &) const;
virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &);
virtual EdgesDelegate *processed (const EdgeProcessorBase &) const;
virtual EdgesDelegate *merged_in_place ();
virtual EdgesDelegate *merged () const;
@ -131,9 +133,6 @@ public:
virtual EdgesDelegate *outside_part (const Region &other) const;
virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const;
virtual EdgesDelegate *start_segments (length_type length, double fraction) const;
virtual EdgesDelegate *end_segments (length_type length, double fraction) const;
virtual EdgesDelegate *centers (length_type length, double fraction) const;
virtual EdgesDelegate *selected_interacting (const Edges &) const;
virtual EdgesDelegate *selected_not_interacting (const Edges &) const;
@ -173,7 +172,6 @@ private:
DeepLayer and_or_not_with(const DeepEdges *other, bool and_op) const;
DeepLayer edge_region_op (const DeepRegion *other, bool outside, bool include_borders) const;
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;
EdgesDelegate *segments (int mode, length_type length, double fraction) const;
EdgesDelegate *selected_interacting_generic (const Edges &edges, bool invert) const;
EdgesDelegate *selected_interacting_generic (const Region &region, bool invert) const;
};

View File

@ -30,6 +30,56 @@
namespace db
{
namespace
{
// -------------------------------------------------------------------------------------------------------------
// Smoothing processor
class EdgeSegmentSelector
: public EdgeProcessorBase
{
public:
EdgeSegmentSelector (int mode, db::Edges::length_type length, double fraction)
: m_mode (mode), m_length (length), m_fraction (fraction)
{ }
virtual void process (const db::Edge &edge, std::vector<db::Edge> &res) const
{
double l = std::max (edge.double_length () * m_fraction, double (m_length));
if (m_mode < 0) {
res.push_back (db::Edge (edge.p1 (), db::Point (db::DPoint (edge.p1 ()) + db::DVector (edge.d ()) * (l / edge.double_length ()))));
} else if (m_mode > 0) {
res.push_back (db::Edge (db::Point (db::DPoint (edge.p2 ()) - db::DVector (edge.d ()) * (l / edge.double_length ())), edge.p2 ()));
} else {
db::DVector dl = db::DVector (edge.d ()) * (0.5 * l / edge.double_length ());
db::DPoint center = db::DPoint (edge.p1 ()) + db::DVector (edge.p2 () - edge.p1 ()) * 0.5;
res.push_back (db::Edge (db::Point (center - dl), db::Point (center + dl)));
}
}
virtual const TransformationReducer *vars () const { return &m_vars; }
virtual bool result_is_merged () const { return false; }
virtual bool requires_raw_input () const { return false; }
virtual bool result_must_not_be_merged () const { return m_length <= 0; }
private:
int m_mode;
db::Edges::length_type m_length;
double m_fraction;
db::MagnificationReducer m_vars;
};
}
// -------------------------------------------------------------------------------------------------------------
// Edges implementation
@ -140,6 +190,21 @@ void Edges::extended (Region &output, coord_type ext_b, coord_type ext_e, coord_
output.set_delegate (mp_delegate->extended (ext_b, ext_e, ext_o, ext_i, join));
}
Edges Edges::start_segments (length_type length, double fraction) const
{
return Edges (mp_delegate->processed (EdgeSegmentSelector (-1, length, fraction)));
}
Edges Edges::end_segments (length_type length, double fraction) const
{
return Edges (mp_delegate->processed (EdgeSegmentSelector (1, length, fraction)));
}
Edges Edges::centers (length_type length, double fraction) const
{
return Edges (mp_delegate->processed (EdgeSegmentSelector (0, length, fraction)));
}
template <class T>
Edges &Edges::transform (const T &trans)
{

View File

@ -200,19 +200,6 @@ private:
class Edges;
/**
* @brief A base class for edge filters
*/
class DB_PUBLIC EdgeFilterBase
{
public:
EdgeFilterBase () { }
virtual ~EdgeFilterBase () { }
virtual bool selected (const db::Edge &edge) const = 0;
virtual const TransformationReducer *vars () const = 0;
};
/**
* @brief An edge set
*
@ -603,6 +590,39 @@ public:
return Edges (mp_delegate->filtered (filter));
}
/**
* @brief Processes the (merged) edges
*
* This method will keep all edges which the processor returns.
* The processing filter can apply modifications too. These modifications will be
* kept in the output edge collection.
*
* Merged semantics applies. In merged semantics, the filter will run over
* all merged edges.
*/
Edges &process (const EdgeProcessorBase &filter)
{
set_delegate (mp_delegate->process_in_place (filter));
return *this;
}
/**
* @brief Returns the processed edges
*
* This method will keep all edges which the processor returns.
* The processing filter can apply modifications too. These modifications will be
* kept in the output edge collection.
*
* Merged semantics applies. In merged semantics, the filter will run over
* all merged edges.
*
* This method will return a new edge collection with the modified and filtered edges.
*/
Edges processed (const EdgeProcessorBase &filter) const
{
return Edges (mp_delegate->processed (filter));
}
/**
* @brief Applies a width check and returns EdgePairs which correspond to violation markers
*
@ -947,10 +967,7 @@ public:
*
* Merged semantics applies.
*/
Edges start_segments (length_type length, double fraction) const
{
return Edges (mp_delegate->start_segments (length, fraction));
}
Edges start_segments (length_type length, double fraction) const;
/**
* @brief Returns edges (point-like) representing the end part of the edges
@ -960,10 +977,7 @@ public:
*
* Merged semantics applies.
*/
Edges end_segments (length_type length, double fraction) const
{
return Edges (mp_delegate->end_segments (length, fraction));
}
Edges end_segments (length_type length, double fraction) const;
/**
* @brief Returns edges (point-like) representing the center of the edges
@ -973,10 +987,7 @@ public:
*
* Merged semantics applies.
*/
Edges centers (length_type length, double fraction) const
{
return Edges (mp_delegate->centers (length, fraction));
}
Edges centers (length_type length, double fraction) const;
/**
* @brief Select the edges inside the given region

View File

@ -34,6 +34,35 @@
namespace db {
/**
* @brief A base class for edge filters
*/
class DB_PUBLIC EdgeFilterBase
{
public:
EdgeFilterBase () { }
virtual ~EdgeFilterBase () { }
virtual bool selected (const db::Edge &edge) const = 0;
virtual const TransformationReducer *vars () const = 0;
};
/**
* @brief A base class for polygon processors
*/
class DB_PUBLIC EdgeProcessorBase
{
public:
EdgeProcessorBase () { }
virtual ~EdgeProcessorBase () { }
virtual void process (const db::Edge &polygon, std::vector<db::Edge> &res) const = 0;
virtual const TransformationReducer *vars () const = 0;
virtual bool result_is_merged () const = 0;
virtual bool requires_raw_input () const = 0;
virtual bool result_must_not_be_merged () const = 0;
};
/**
* @brief A common definition for the boolean operations available on edges
*/
@ -128,6 +157,8 @@ public:
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter) = 0;
virtual EdgesDelegate *filtered (const EdgeFilterBase &filter) const = 0;
virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &filter) = 0;
virtual EdgesDelegate *processed (const EdgeProcessorBase &filter) const = 0;
virtual EdgesDelegate *merged_in_place () = 0;
virtual EdgesDelegate *merged () const = 0;
@ -142,9 +173,6 @@ public:
virtual EdgesDelegate *add (const Edges &other) const = 0;
virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const = 0;
virtual EdgesDelegate *start_segments (length_type length, double fraction) const = 0;
virtual EdgesDelegate *end_segments (length_type length, double fraction) const = 0;
virtual EdgesDelegate *centers (length_type length, double fraction) const = 0;
virtual EdgesDelegate *inside_part (const Region &other) const = 0;
virtual EdgesDelegate *outside_part (const Region &other) const = 0;

View File

@ -0,0 +1,177 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbEdgesUtils.h"
#include "dbRegion.h"
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);
}
}
}
// -------------------------------------------------------------------------------------------------------------
// extended_edge implementation
db::Polygon
extended_edge (const db::Edge &edge, db::Coord ext_b, db::Coord ext_e, db::Coord ext_o, db::Coord 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;
}
}

View File

@ -25,9 +25,13 @@
#include "dbCommon.h"
#include "dbEdges.h"
#include "dbBoxScanner.h"
#include "dbPolygonTools.h"
namespace db {
class PolygonSink;
/**
* @brief An edge length filter for use with Edges::filter or Edges::filtered
*
@ -164,6 +168,138 @@ private:
db::MagnificationAndOrientationReducer m_vars;
};
/**
* @brief A helper class for the edge interaction functionality which acts as an edge pair receiver
*/
template <class OutputContainer>
class edge_interaction_filter
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
edge_interaction_filter (OutputContainer &output)
: mp_output (&output)
{
// .. nothing yet ..
}
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
// Select the edges which intersect
if (p1 != p2) {
const db::Edge *o = p1 > p2 ? o2 : o1;
const db::Edge *oo = p1 > p2 ? o1 : o2;
if (o->intersect (*oo)) {
if (m_seen.insert (o).second) {
mp_output->insert (*o);
}
}
}
}
private:
OutputContainer *mp_output;
std::set<const db::Edge *> m_seen;
};
/**
* @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver
*
* Note: This special scanner uses pointers to two different objects: edges and polygons.
* It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate
* pointers to edges.
*
* There is a special box converter which is able to sort that out as well.
*/
template <class OutputContainer>
class edge_to_region_interaction_filter
: public db::box_scanner_receiver2<db::Edge, size_t, db::Polygon, size_t>
{
public:
edge_to_region_interaction_filter (OutputContainer &output)
: mp_output (&output)
{
// .. nothing yet ..
}
void add (const db::Edge *e, size_t, const db::Polygon *p, size_t)
{
if (m_seen.find (e) == m_seen.end ()) {
if (db::interact (*p, *e)) {
m_seen.insert (e);
mp_output->insert (*e);
}
}
}
private:
OutputContainer *mp_output;
std::set<const db::Edge *> m_seen;
};
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*
* If will perform a edge by edge check using the provided EdgeRelationFilter
*/
template <class Output>
class edge2edge_check_for_edges
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
edge2edge_check_for_edges (const EdgeRelationFilter &check, Output &output, bool requires_different_layers)
: mp_check (&check), mp_output (&output)
{
m_requires_different_layers = requires_different_layers;
}
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
// Overlap or inside checks require input from different layers
if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
db::EdgePair ep;
if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) {
mp_output->insert (ep);
}
}
}
private:
const EdgeRelationFilter *mp_check;
Output *mp_output;
bool m_requires_different_layers;
};
/**
* @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 Implements the extension algorithm to turn an edge into a polygon
*/
db::Polygon extended_edge (const db::Edge &edge, db::Coord ext_b, db::Coord ext_e, db::Coord ext_o, db::Coord ext_i);
} // namespace db
#endif

View File

@ -65,6 +65,8 @@ public:
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &) { return this; }
virtual EdgesDelegate *filtered (const EdgeFilterBase &) const { return new EmptyEdges (); }
virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &) { return this; }
virtual EdgesDelegate *processed (const EdgeProcessorBase &) const { return new EmptyEdges (); }
virtual EdgesDelegate *merged_in_place () { return this; }
virtual EdgesDelegate *merged () const { return new EmptyEdges (); }
@ -79,9 +81,6 @@ public:
virtual EdgesDelegate *add (const Edges &other) const;
virtual RegionDelegate *extended (coord_type, coord_type, coord_type, coord_type, bool) const;
virtual EdgesDelegate *start_segments (length_type, double) const { return new EmptyEdges (); }
virtual EdgesDelegate *end_segments (length_type, double) const { return new EmptyEdges (); }
virtual EdgesDelegate *centers (length_type, double) const { return new EmptyEdges (); }
virtual EdgesDelegate *inside_part (const Region &) const { return new EmptyEdges (); }
virtual EdgesDelegate *outside_part (const Region &) const { return new EmptyEdges (); }

View File

@ -182,6 +182,35 @@ Box FlatEdges::compute_bbox () const
return m_edges.bbox ();
}
EdgesDelegate *
FlatEdges::processed_in_place (const EdgeProcessorBase &filter)
{
std::vector<db::Edge> edge_res;
edge_iterator_type pw = m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().begin ();
for (EdgesIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) {
edge_res.clear ();
filter.process (*p, edge_res);
for (std::vector<db::Edge>::const_iterator pr = edge_res.begin (); pr != edge_res.end (); ++pr) {
if (pw == m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ()) {
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().insert (*pr);
pw = m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ();
} else {
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().replace (pw++, *pr);
}
}
}
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().erase (pw, m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ());
m_merged_edges.clear ();
m_is_merged = filter.result_is_merged () && merged_semantics ();
return this;
}
EdgesDelegate *
FlatEdges::filter_in_place (const EdgeFilterBase &filter)
{

View File

@ -111,6 +111,7 @@ public:
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
virtual EdgesDelegate *processed_in_place (const EdgeProcessorBase &filter);
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter);
virtual EdgesDelegate *add_in_place (const Edges &other);

View File

@ -630,7 +630,7 @@ public:
/**
* @brief Processes the (merged) polygons
*
* This method will keep all polygons for which the processing filter returns true.
* This method will keep all polygons which the processor returns.
* The processing filter can apply modifications too. These modifications will be
* kept in the output region.
*
@ -646,7 +646,7 @@ public:
/**
* @brief Returns the processed polygons
*
* This method will keep all polygons for which the processing filter returns true.
* This method will keep all polygons which the processor returns.
* The processing filter can apply modifications too. These modifications will be
* kept in the output region.
*