Hierarchical implementation of polygon vs. edge interact

This commit is contained in:
Matthias Koefferlein 2019-02-15 23:43:45 +01:00
parent 78617930dd
commit 6e35e80963
7 changed files with 302 additions and 158 deletions

View File

@ -300,117 +300,6 @@ AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const
return new_region.release ();
}
namespace
{
/**
* @brief A helper class for the region to edge interaction functionality
*
* 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 region_to_edge_interaction_filter
: public db::box_scanner_receiver<char, size_t>
{
public:
region_to_edge_interaction_filter (OutputContainer &output)
: mp_output (&output), m_inverse (false)
{
// .. nothing yet ..
}
region_to_edge_interaction_filter (OutputContainer &output, const db::RegionIterator &polygons)
: mp_output (&output), m_inverse (true)
{
for (db::RegionIterator p = polygons; ! p.at_end (); ++p) {
m_seen.insert (&*p);
}
}
void add (const char *o1, size_t p1, const char *o2, size_t p2)
{
const db::Edge *e = 0;
const db::Polygon *p = 0;
// Note: edges have property 0 and have even-valued pointers.
// Polygons have property 1 and odd-valued pointers.
if (p1 == 0 && p2 == 1) {
e = reinterpret_cast<const db::Edge *> (o1);
p = reinterpret_cast<const db::Polygon *> (o2 - 1);
} else if (p1 == 1 && p2 == 0) {
e = reinterpret_cast<const db::Edge *> (o2);
p = reinterpret_cast<const db::Polygon *> (o1 - 1);
}
if (e && p && (m_seen.find (p) == m_seen.end ()) != m_inverse) {
// A polygon and an edge interact if the edge is either inside completely
// of at least one edge of the polygon intersects with the edge
bool interacts = false;
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
interacts = true;
} else {
for (db::Polygon::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
if ((*pe).intersect (*e)) {
interacts = true;
}
}
}
if (interacts) {
if (m_inverse) {
m_seen.erase (p);
} else {
m_seen.insert (p);
mp_output->insert (*p);
}
}
}
}
void fill_output ()
{
for (std::set<const db::Polygon *>::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) {
mp_output->insert (**p);
}
}
private:
OutputContainer *mp_output;
std::set<const db::Polygon *> m_seen;
bool m_inverse;
};
/**
* @brief A special box converter that splits the pointers into polygon and edge pointers
*/
struct EdgeOrRegionBoxConverter
{
typedef db::Box box_type;
db::Box operator() (const char &c) const
{
// Note: edges have property 0 and have even-valued pointers.
// Polygons have property 1 and odd-valued pointers.
const char *cp = &c;
if ((size_t (cp) & 1) == 1) {
// it's a polygon
return (reinterpret_cast<const db::Polygon *> (cp - 1))->box ();
} else {
// it's an edge
const db::Edge *e = reinterpret_cast<const db::Edge *> (cp);
return db::Box (e->p1 (), e->p2 ());
}
}
};
}
RegionDelegate *
AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) const
{
@ -424,30 +313,31 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse)
return clone ();
}
db::box_scanner<char, size_t> scanner (report_progress (), progress_desc ());
scanner.reserve (size () + other.size ());
db::box_scanner2<db::Polygon, size_t, db::Edge, size_t> scanner (report_progress (), progress_desc ());
scanner.reserve1 (size ());
scanner.reserve2 (other.size ());
std::auto_ptr<FlatRegion> output (new FlatRegion (false));
region_to_edge_interaction_filter<Shapes> filter (output->raw_polygons (), inverse);
AddressablePolygonDelivery p (begin_merged (), has_valid_merged_polygons ());
for ( ; ! p.at_end (); ++p) {
scanner.insert ((char *) p.operator-> () + 1, 1);
scanner.insert1 (p.operator-> (), 0);
if (inverse) {
filter.preset (p.operator-> ());
}
}
AddressableEdgeDelivery e (other.addressable_edges ());
for ( ; ! e.at_end (); ++e) {
scanner.insert ((char *) e.operator-> (), 0);
scanner.insert2 (e.operator-> (), 0);
}
std::auto_ptr<FlatRegion> output (new FlatRegion (false));
EdgeOrRegionBoxConverter bc;
scanner.process (filter, 1, db::box_convert<db::Polygon> (), db::box_convert<db::Edge> ());
if (! inverse) {
region_to_edge_interaction_filter<Shapes> filter (output->raw_polygons ());
scanner.process (filter, 1, bc);
} else {
region_to_edge_interaction_filter<Shapes> filter (output->raw_polygons (), RegionIterator (begin_merged ()));
scanner.process (filter, 1, bc);
if (inverse) {
filter.fill_output ();
}

View File

@ -27,9 +27,75 @@
#include "dbCommon.h"
#include "dbRegionDelegate.h"
#include "dbPolygon.h"
#include "dbEdge.h"
#include "dbBoxScanner.h"
#include <set>
namespace db {
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class OutputContainer>
class DB_PUBLIC region_to_edge_interaction_filter
: public db::box_scanner_receiver2<db::Polygon, size_t, db::Edge, size_t>
{
public:
region_to_edge_interaction_filter (OutputContainer &output, bool inverse)
: mp_output (&output), m_inverse (inverse)
{
// .. nothing yet ..
}
void preset (const db::Polygon *poly)
{
m_seen.insert (poly);
}
void add (const db::Polygon *p, size_t, const db::Edge *e, size_t)
{
if ((m_seen.find (p) == m_seen.end ()) != m_inverse) {
// A polygon and an edge interact if the edge is either inside completely
// of at least one edge of the polygon intersects with the edge
bool interacts = false;
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
interacts = true;
} else {
for (db::Polygon::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
if ((*pe).intersect (*e)) {
interacts = true;
}
}
}
if (interacts) {
if (m_inverse) {
m_seen.erase (p);
} else {
m_seen.insert (p);
mp_output->insert (*p);
}
}
}
}
void fill_output ()
{
for (std::set<const db::Polygon *>::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) {
mp_output->insert (**p);
}
}
private:
OutputContainer *mp_output;
std::set<const db::Polygon *> m_seen;
bool m_inverse;
};
/**
* @brief Provides default flat implementations
*/

View File

@ -1328,11 +1328,93 @@ private:
mutable db::EdgeProcessor m_ep;
};
struct ResultInserter
{
ResultInserter (db::Layout *layout, std::unordered_set<db::PolygonRef> &result)
: mp_layout (layout), mp_result (&result)
{
// .. nothing yet ..
}
void insert (const db::Polygon &p)
{
(*mp_result).insert (db::PolygonRef (p, mp_layout->shape_repository ()));
}
private:
db::Layout *mp_layout;
std::unordered_set<db::PolygonRef> *mp_result;
};
class InteractingWithEdgeLocalOperation
: public local_operation<db::PolygonRef, db::Edge, db::PolygonRef>
{
public:
InteractingWithEdgeLocalOperation (bool inverse)
: m_inverse (inverse)
{
// .. nothing yet ..
}
virtual void compute_local (db::Layout *layout, const shape_interactions<db::PolygonRef, db::Edge> &interactions, std::unordered_set<db::PolygonRef> &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const
{
m_scanner.clear ();
ResultInserter inserter (layout, result);
region_to_edge_interaction_filter<ResultInserter> filter (inserter, m_inverse);
for (shape_interactions<db::PolygonRef, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
for (shape_interactions<db::PolygonRef, db::Edge>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
m_scanner.insert2 (& interactions.intruder_shape (*j), 0);
}
}
std::list<db::Polygon> heap;
for (shape_interactions<db::PolygonRef, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
const db::PolygonRef &subject = interactions.subject_shape (i->first);
heap.push_back (subject.obj ().transformed (subject.trans ()));
m_scanner.insert1 (&heap.back (), 0);
if (m_inverse) {
filter.preset (&heap.back ());
}
}
m_scanner.process (filter, 1, db::box_convert<db::Polygon> (), db::box_convert<db::Edge> ());
if (m_inverse) {
filter.fill_output ();
}
}
virtual on_empty_intruder_mode on_empty_intruder_hint () const
{
if (!m_inverse) {
return Drop;
} else {
return Copy;
}
}
virtual std::string description () const
{
return tl::to_string (tr ("Select regions by their geometric relation (interacting, inside, outside ..)"));
}
private:
bool m_inverse;
mutable db::box_scanner2<db::Polygon, size_t, db::Edge, size_t> m_scanner;
};
}
RegionDelegate *
DeepRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const
{
// with these flag set to true, the resulting polygons are broken again.
bool split_after = false;
const db::DeepRegion *other_deep = dynamic_cast<const db::DeepRegion *> (other.delegate ());
if (! other_deep) {
return db::AsIfFlatRegion::selected_interacting_generic (other, mode, touching, inverse);
@ -1347,24 +1429,52 @@ DeepRegion::selected_interacting_generic (const Region &other, int mode, bool to
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&m_deep_layer.layout ()), const_cast<db::Cell *> (&m_deep_layer.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (m_deep_layer.store ()->threads ());
#if defined(SPLIT )
// with these settings, the resulting polygons are broken again ...
proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ());
proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ());
#endif
if (split_after) {
proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ());
proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ());
}
proc.run (&op, m_merged_polygons.layer (), other_deep->merged_deep_layer ().layer (), dl_out.layer ());
proc.run (&op, m_merged_polygons.layer (), other_deep->deep_layer ().layer (), dl_out.layer ());
db::DeepRegion *res = new db::DeepRegion (dl_out);
res->set_is_merged (true);
if (! split_after) {
res->set_is_merged (true);
}
return res;
}
RegionDelegate *
DeepRegion::selected_interacting_generic (const Edges &other, bool inverse) const
{
// TODO: implement hierarchically
return db::AsIfFlatRegion::selected_interacting_generic (other, inverse);
// with these flag set to true, the resulting polygons are broken again.
bool split_after = false;
const db::DeepEdges *other_deep = dynamic_cast<const db::DeepEdges *> (other.delegate ());
if (! other_deep) {
return db::AsIfFlatRegion::selected_interacting_generic (other, inverse);
}
ensure_merged_polygons_valid ();
DeepLayer dl_out (m_deep_layer.derived ());
db::InteractingWithEdgeLocalOperation op (inverse);
db::local_processor<db::PolygonRef, db::Edge, db::PolygonRef> proc (const_cast<db::Layout *> (&m_deep_layer.layout ()), const_cast<db::Cell *> (&m_deep_layer.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (m_deep_layer.store ()->threads ());
if (split_after) {
proc.set_area_ratio (m_deep_layer.store ()->max_area_ratio ());
proc.set_max_vertex_count (m_deep_layer.store ()->max_vertex_count ());
}
proc.run (&op, m_merged_polygons.layer (), other_deep->deep_layer ().layer (), dl_out.layer ());
db::DeepRegion *res = new db::DeepRegion (dl_out);
if (! split_after) {
res->set_is_merged (true);
}
return res;
}
}

View File

@ -237,6 +237,30 @@ private:
Trans m_trans;
};
// ---------------------------------------------------------------------------------------------
/**
* @brief Safe enlargement of a box
* Boxes must not vanish when augmented for overlapping queries. Hence we must not make
* the boxes shrinked too much on enlarge.
*/
db::Box safe_box_enlarged (const db::Box &box, db::Coord dx, db::Coord dy)
{
if (box.empty ()) {
return box;
} else {
db::Coord w2 = db::Coord (box.width () / 2);
db::Coord h2 = db::Coord (box.height () / 2);
if (dx + w2 < 0) {
dx = -w2;
}
if (dy + h2 < 0) {
dy = -h2;
}
return box.enlarged (db::Vector (dx, dy));
}
}
// ---------------------------------------------------------------------------------------------
// LocalProcessorCellContext implementation
@ -723,7 +747,7 @@ public:
// Find all instance array members that potentially interact with the shape and use
// add_shapes_from_intruder_inst on them
db::Box ref_box = db::box_convert<TS> () (*ref);
for (db::CellInstArray::iterator n = inst->begin_touching (ref_box.enlarged (db::Vector (m_dist - 1, m_dist - 1)), inst_bc); !n.at_end (); ++n) {
for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (ref_box, m_dist - 1, m_dist - 1), inst_bc); !n.at_end (); ++n) {
db::ICplxTrans tn = inst->complex_trans (*n);
db::Box region = ref_box.transformed (tn.inverted ()).enlarged (db::Vector (m_dist, m_dist)) & intruder_cell.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
if (! region.empty ()) {
@ -794,7 +818,7 @@ instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, u
// TODO: in some cases, it may be possible to optimize this for arrays
for (db::CellInstArray::iterator k = inst2->begin_touching (ibox1.enlarged (db::Vector (-1, -1)), inst2_bc); ! k.at_end (); ++k) {
for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) {
if (inst1 == inst2 && *n == *k) {
// skip self-interactions - this is handled inside the cell
@ -887,7 +911,7 @@ instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *ins
db::box_convert <db::CellInst, true> inst_bc (*layout, layer);
db::Box rbox = db::box_convert<T> () (ref);
for (db::CellInstArray::iterator n = inst->begin_touching (rbox.enlarged (db::Vector (dist - 1, dist - 1)), inst_bc); ! n.at_end (); ++n) {
for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
db::ICplxTrans tn = inst->complex_trans (*n);
db::Box cbox = (tn * cell.bbox (layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
@ -1293,7 +1317,7 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
// TODO: in some cases, it may be possible to optimize this for arrays
for (std::unordered_set<const db::CellInstArray *>::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) {
for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) {
for (db::CellInstArray::iterator k = (*j)->begin_touching (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
db::ICplxTrans tk = (*j)->complex_trans (*k);
// NOTE: no self-interactions
if (i->first != *j || tn != tk) {
@ -1568,7 +1592,20 @@ local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_conte
}
op->compute_local (mp_subject_layout, interactions, result, m_max_vertex_count, m_area_ratio);
if (interactions.begin () != interactions.end ()) {
if (interactions.begin_intruders () == interactions.end_intruders ()) {
typename local_operation<TS, TI, TR>::on_empty_intruder_mode eh = op->on_empty_intruder_hint ();
if (eh == local_operation<TS, TI, TR>::Drop) {
return;
}
}
op->compute_local (mp_subject_layout, interactions, result, m_max_vertex_count, m_area_ratio);
}
}
template class DB_PUBLIC local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef>;

View File

@ -54,6 +54,8 @@ public:
typedef std::unordered_map<unsigned int, std::vector<unsigned int> > container;
typedef container::const_iterator iterator;
typedef container::value_type::second_type::const_iterator iterator2;
typedef typename std::unordered_map<unsigned int, TS>::const_iterator subject_iterator;
typedef typename std::unordered_map<unsigned int, TI>::const_iterator intruder_iterator;
shape_interactions ();
@ -67,6 +69,26 @@ public:
return m_interactions.end ();
}
subject_iterator begin_subjects () const
{
return m_subject_shapes.begin ();
}
subject_iterator end_subjects () const
{
return m_subject_shapes.end ();
}
intruder_iterator begin_intruders () const
{
return m_intruder_shapes.begin ();
}
intruder_iterator end_intruders () const
{
return m_intruder_shapes.end ();
}
bool has_intruder_shape_id (unsigned int id) const;
bool has_subject_shape_id (unsigned int id) const;
void add_intruder_shape (unsigned int id, const TI &shape);

View File

@ -781,31 +781,50 @@ TEST(14_Interacting)
db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
db::Region r6 (db::RecursiveShapeIterator (ly, top_cell, l6), dss);
db::Layout target;
unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index));
{
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 (10, 0)), r2.selected_interacting (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.selected_not_interacting (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2.selected_inside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r2.selected_not_inside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r2.selected_outside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r2.selected_not_outside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (16, 0)), r2.selected_overlapping (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (17, 0)), r2.selected_not_overlapping (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r2.selected_interacting (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r2.selected_not_interacting (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2.selected_inside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r2.selected_not_inside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r2.selected_outside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (15, 0)), r2.selected_not_outside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (16, 0)), r2.selected_overlapping (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (17, 0)), r2.selected_not_overlapping (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r6.selected_interacting (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r6.selected_not_interacting (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r6.selected_inside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r6.selected_not_inside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r6.selected_outside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r6.selected_not_outside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), r6.selected_overlapping (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), r6.selected_not_overlapping (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r6.selected_interacting (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r6.selected_not_interacting (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), r6.selected_inside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), r6.selected_not_inside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (24, 0)), r6.selected_outside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (25, 0)), r6.selected_not_outside (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (26, 0)), r6.selected_overlapping (r1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (27, 0)), r6.selected_not_overlapping (r1));
EXPECT_EQ (r2.selected_interacting (r1).is_merged (), true);
EXPECT_EQ (r2.selected_interacting (r1).is_merged (), true);
CHECKPOINT();
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au14.gds");
CHECKPOINT();
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au14a.gds");
}
db::Edges r1e = r1.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 (10, 0)), r6);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1e);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r6.selected_interacting (r1e));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r6.selected_not_interacting (r1e));
EXPECT_EQ (r6.selected_interacting (r1e).is_merged (), true);
CHECKPOINT();
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au14b.gds");
}
}
TEST(15_Filtered)