klayout/src/db/db/dbHierProcessor.cc

1963 lines
80 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2026 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 "dbHierProcessor.h"
#include "dbBoxScanner.h"
#include "dbRecursiveShapeIterator.h"
#include "dbBoxConvert.h"
#include "dbEdgeProcessor.h"
#include "dbEdgeBoolean.h"
#include "dbPolygonGenerators.h"
#include "dbLocalOperationUtils.h"
#include "dbShapeFlags.h"
#include "dbCellVariants.h"
#include "dbHierProcessorUtils.h"
#include "tlLog.h"
#include "tlTimer.h"
#include "tlInternational.h"
// ---------------------------------------------------------------------------------------------
// Cronology debugging support (TODO: experimental)
#if defined(HAVE_CRONOLOGY)
#include <cronology/events.hpp>
CRONOLOGY_MAKE_EVENT(event_compute_contexts, "Compute contexts")
CRONOLOGY_MAKE_EVENT(event_compute_contexts_unlocked, "Compute contexts (unlocked)")
cronology::events::event_collection<event_compute_contexts, event_compute_contexts_unlocked> collect_events;
#define CRONOLOGY_COLLECTION_BRACKET(event_name) cronology::EventBracket __bracket##event_name (collect_events.threadwise ()->event<event_name> ().event ());
CRONOLOGY_MAKE_EVENT(event_compute_results, "Compute results")
CRONOLOGY_MAKE_EVENT(event_compute_local_cell, "Compute local cell results")
CRONOLOGY_MAKE_EVENT(event_propagate, "Propagate local cell results")
cronology::events::event_collection<event_compute_results, event_compute_local_cell, event_propagate> compute_events;
#define CRONOLOGY_COMPUTE_BRACKET(event_name) cronology::EventBracket __bracket##event_name (compute_events.threadwise ()->event<event_name> ().event ());
#else
#define CRONOLOGY_COLLECTION_BRACKET(event_name)
#define CRONOLOGY_COMPUTE_BRACKET(event_name)
#endif
namespace db
{
// Heuristic parameter to control the recursion of the cell-to-cell intruder
// detection: do not recurse if the intruder cell's bounding box is smaller
// than the overlap box times this factor.
const double area_ratio_for_recursion = 3.0;
// ---------------------------------------------------------------------------------------------
/**
* @brief a utility to capture insert attempts of the wrong type into a box scanner
* These attempts can happen because the generic nature of the interaction detector code
*/
template <class T, class P>
void safe_insert2_into_box_scanner (db::box_scanner2 <T, P, T, P> &scanner, const T *t, const P &p)
{
scanner.insert2 (t, p);
}
template <class T1, class P1, class T2, class P2>
void safe_insert2_into_box_scanner (db::box_scanner2 <T1, P1, T2, P2> &scanner, const T2 *t, const P2 &p)
{
scanner.insert2 (t, p);
}
template <class T1, class P1, class T2, class P2>
void safe_insert2_into_box_scanner (db::box_scanner2 <T1, P1, T2, P2> &, const T1 *, const P1 &)
{
tl_assert (false);
}
// ---------------------------------------------------------------------------------------------
/**
* @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 if (box == db::Box::world ()) {
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));
}
}
// ---------------------------------------------------------------------------------------------
// Debugging utility: dump the cell contexts
template <class TS, class TI, class TR>
static void dump_cell_contexts (local_processor_contexts<TS, TI, TR> &contexts, const db::Layout *subject_layout, const db::Layout *intruder_layout)
{
for (auto cc = contexts.begin (); cc != contexts.end (); ++cc) {
tl::info << "Cell " << subject_layout->cell_name (cc->first->cell_index ()) << ":";
int i = 0;
for (auto c = cc->second.begin (); c != cc->second.end (); ++c) {
tl::info << " Context #" << ++i;
tl::info << " Instances:";
for (auto i = c->first.first.begin (); i != c->first.first.end (); ++i) {
const db::CellInstArray &ci = *i;
tl::info << " " << intruder_layout->cell_name (ci.object ().cell_index ()) << " @ " << ci.complex_trans (*ci.begin ()).to_string () << " (" << ci.size () << ")";
}
tl::info << " Shapes:";
for (auto i = c->first.second.begin (); i != c->first.second.end (); ++i) {
for (auto s = i->second.begin (); s != i->second.end (); ++s) {
tl::info << " " << intruder_layout->get_properties (i->first).to_string () << ": " << s->to_string ();
}
}
}
}
}
// ---------------------------------------------------------------------------------------------
template <class TS, class TI>
shape_interactions<TS, TI>::shape_interactions ()
: m_id (0)
{
// .. nothing yet ..
}
template <class TS, class TI>
bool
shape_interactions<TS, TI>::has_intruder_shape_id (unsigned int id) const
{
return m_intruder_shapes.find (id) != m_intruder_shapes.end ();
}
template <class TS, class TI>
bool
shape_interactions<TS, TI>::has_subject_shape_id (unsigned int id) const
{
return m_subject_shapes.find (id) != m_subject_shapes.end ();
}
template <class TS, class TI>
void
shape_interactions<TS, TI>::add_intruder_shape (unsigned int id, unsigned int layer, const TI &shape)
{
m_intruder_shapes [id] = std::make_pair (layer, shape);
}
template <class TS, class TI>
void
shape_interactions<TS, TI>::add_subject_shape (unsigned int id, const TS &shape)
{
m_subject_shapes [id] = shape;
}
template <class TS, class TI>
void
shape_interactions<TS, TI>::add_subject (unsigned int id, const TS &shape)
{
m_subject_shapes [id] = shape;
m_interactions.insert (std::make_pair (id, container::value_type::second_type ()));
}
template <class TS, class TI>
void
shape_interactions<TS, TI>::add_interaction (unsigned int subject_id, unsigned int intruder_id)
{
m_interactions [subject_id].push_back (intruder_id);
}
template <class TS, class TI>
const std::vector<unsigned int> &
shape_interactions<TS, TI>::intruders_for (unsigned int subject_id) const
{
iterator i = m_interactions.find (subject_id);
if (i == m_interactions.end ()) {
static std::vector<unsigned int> empty;
return empty;
} else {
return i->second;
}
}
template <class TS, class TI>
const TS &
shape_interactions<TS, TI>::subject_shape (unsigned int id) const
{
typename std::unordered_map<unsigned int, TS>::const_iterator i = m_subject_shapes.find (id);
if (i == m_subject_shapes.end ()) {
static TS s;
return s;
} else {
return i->second;
}
}
template <class TS, class TI>
const std::pair<unsigned int, TI> &
shape_interactions<TS, TI>::intruder_shape (unsigned int id) const
{
typename std::unordered_map<unsigned int, std::pair<unsigned int, TI> >::const_iterator i = m_intruder_shapes.find (id);
if (i == m_intruder_shapes.end ()) {
static std::pair<unsigned int, TI> s;
return s;
} else {
return i->second;
}
}
// ---------------------------------------------------------------------------------------------
// Helper classes for the LocalProcessor
namespace
{
template <class TS, class TI>
struct interaction_registration_shape2shape
: db::box_scanner_receiver2<TS, unsigned int, TI, unsigned int>
{
public:
interaction_registration_shape2shape (db::Layout *layout, shape_interactions<TS, TI> *result, unsigned int intruder_layer_index)
: mp_result (result), mp_layout (layout), m_intruder_layer_index (intruder_layer_index)
{
// nothing yet ..
}
void add (const TS *ref1, unsigned int id1, const TI *ref2, unsigned int id2)
{
if (!mp_result->has_subject_shape_id (id1)) {
mp_result->add_subject_shape (id1, *ref1);
}
if (!mp_result->has_intruder_shape_id (id2)) {
if (mp_layout) {
// In order to guarantee the refs come from the subject layout, we'd need to
// rewrite them
db::shape_reference_translator<TI> rt (mp_layout);
mp_result->add_intruder_shape (id2, m_intruder_layer_index, rt (*ref2));
} else {
mp_result->add_intruder_shape (id2, m_intruder_layer_index, *ref2);
}
}
mp_result->add_interaction (id1, id2);
}
void same (unsigned int, unsigned int)
{
// ignore. Two shapes of a different kind can't be the same.
}
private:
shape_interactions<TS, TI> *mp_result;
db::Layout *mp_layout;
unsigned int m_intruder_layer_index;
};
template <class T>
struct interaction_registration_shape2shape<T, T>
: db::box_scanner_receiver2<T, unsigned int, T, unsigned int>
{
public:
interaction_registration_shape2shape (db::Layout *layout, shape_interactions<T, T> *result, unsigned int intruder_layer_index)
: mp_result (result), mp_layout (layout), m_intruder_layer_index (intruder_layer_index)
{
// nothing yet ..
}
void add (const T *ref1, unsigned int id1, const T *ref2, unsigned int id2)
{
if (! m_same.empty () && (m_same.find (std::make_pair (id1, id2)) != m_same.end () || m_same.find (std::make_pair (id2, id1)) != m_same.end ())) {
// ignore self-interactions
return;
}
if (!mp_result->has_subject_shape_id (id1)) {
mp_result->add_subject_shape (id1, *ref1);
}
if (!mp_result->has_intruder_shape_id (id2)) {
if (mp_layout) {
// In order to guarantee the refs come from the subject layout, we'd need to
// rewrite them
db::shape_reference_translator<T> rt (mp_layout);
mp_result->add_intruder_shape (id2, m_intruder_layer_index, rt (*ref2));
} else {
mp_result->add_intruder_shape (id2, m_intruder_layer_index, *ref2);
}
}
mp_result->add_interaction (id1, id2);
}
void same (unsigned int a, unsigned int b)
{
m_same.insert (std::make_pair (a, b));
}
private:
shape_interactions<T, T> *mp_result;
std::unordered_set<std::pair<unsigned int, unsigned int> > m_same;
db::Layout *mp_layout;
unsigned int m_intruder_layer_index;
};
template <class TS, class TI>
struct interaction_registration_shape1
: db::box_scanner_receiver2<TS, unsigned int, TI, unsigned int>
{
public:
interaction_registration_shape1 (shape_interactions<TS, TI> *result, unsigned int intruder_layer_index)
: mp_result (result), m_intruder_layer_index (intruder_layer_index)
{
// .. nothing yet ..
}
void add (const TS *ref1, unsigned int id1, const TI *ref2, unsigned int id2)
{
if (!mp_result->has_subject_shape_id (id1)) {
mp_result->add_subject_shape (id1, *ref1);
}
if (!mp_result->has_intruder_shape_id (id2)) {
mp_result->add_intruder_shape (id2, m_intruder_layer_index, *ref2);
}
mp_result->add_interaction (id1, id2);
}
private:
shape_interactions<TS, TI> *mp_result;
unsigned int m_intruder_layer_index;
};
template <class T>
struct interaction_registration_shape1<T, T>
: db::box_scanner_receiver<T, unsigned int>
{
public:
interaction_registration_shape1 (shape_interactions<T, T> *result, unsigned int intruder_layer_index)
: mp_result (result), m_intruder_layer_index (intruder_layer_index)
{
// nothing yet ..
}
void add (const T *ref1, unsigned int id1, const T *ref2, unsigned int id2)
{
if (!mp_result->has_subject_shape_id (id1)) {
mp_result->add_subject_shape (id1, *ref1);
}
if (!mp_result->has_intruder_shape_id (id2)) {
mp_result->add_intruder_shape (id2, m_intruder_layer_index, *ref2);
}
mp_result->add_interaction (id1, id2);
}
private:
shape_interactions<T, T> *mp_result;
unsigned int m_intruder_layer_index;
};
template <class TS, class TI>
struct interaction_registration_shape2inst
: db::box_scanner_receiver2<TS, unsigned int, db::CellInstArray, unsigned int>
{
public:
interaction_registration_shape2inst (db::Layout *subject_layout, const db::Layout *intruder_layout, unsigned int intruder_layer, unsigned int intruder_layer_index, db::Coord dist, shape_interactions<TS, TI> *result)
: mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_intruder_layer (intruder_layer), m_intruder_layer_index (intruder_layer_index), m_dist (dist), mp_result (result)
{
// nothing yet ..
}
void add (const TS *ref, unsigned int id1, const db::CellInstArray *inst, unsigned int inst_id)
{
const db::Cell &intruder_cell = mp_intruder_layout->cell (inst->object ().cell_index ());
db::box_convert <db::CellInst, true> inst_bc (*mp_intruder_layout, m_intruder_layer);
mp_result->add_subject_shape (id1, *ref);
// 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 (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.enlarged (db::Vector (m_dist, m_dist)).transformed (tn.inverted ()) & intruder_cell.bbox (m_intruder_layer);
if (! region.empty ()) {
add_shapes_from_intruder_inst (id1, intruder_cell, tn, inst_id, region);
}
}
}
private:
db::Layout *mp_subject_layout;
const db::Layout *mp_intruder_layout;
unsigned int m_intruder_layer, m_intruder_layer_index;
db::Coord m_dist;
shape_interactions<TS, TI> *mp_result;
std::unordered_map<TI, unsigned int> m_inst_shape_ids;
void add_shapes_from_intruder_inst (unsigned int id1, const db::Cell &intruder_cell, const db::ICplxTrans &tn, unsigned int /*inst_id*/, const db::Box &region)
{
db::shape_reference_translator<TI> rt (mp_subject_layout);
db::shape_to_object<TI> s2o;
// Look up all shapes from the intruder instance which interact with the subject shape
// (given through region)
// TODO: should be lighter, cache, handle arrays ..
db::RecursiveShapeIterator si (*mp_intruder_layout, intruder_cell, m_intruder_layer, region);
si.shape_flags (shape_flags<TI> ());
while (! si.at_end ()) {
// NOTE: we intentionally rewrite to the *subject* layout - this way polygon refs in the context come from the
// subject, not from the intruder.
TI ref2 = rt (s2o (si.shape ()), tn * si.trans ());
// reuse the same id for shapes from the same instance -> this avoid duplicates with different IDs on
// the intruder side.
typename std::unordered_map<TI, unsigned int>::const_iterator k = m_inst_shape_ids.find (ref2);
if (k == m_inst_shape_ids.end ()) {
k = m_inst_shape_ids.insert (std::make_pair (ref2, mp_result->next_id ())).first;
mp_result->add_intruder_shape (k->second, m_intruder_layer_index, ref2);
}
mp_result->add_interaction (id1, k->second);
++si;
}
}
};
template <class TS, class TI, class TR>
struct interaction_registration_inst2inst
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, db::CellInstArray, unsigned int>
{
public:
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, db::Coord dist, interactions_type *result)
: mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result), m_foreign (foreign)
{
// nothing yet ..
}
void add (const db::CellInstArray *inst1, unsigned int id1, const db::CellInstArray *inst2, unsigned int id2)
{
// NOTE: self-interactions are possible for arrays: different elements of the
// array may interact which is a cell-external interaction.
if (mp_subject_layout != mp_intruder_layout || id1 != id2 || inst1->size () > 1) {
bool ignore = false;
if (mp_subject_layout == mp_intruder_layout && m_subject_layer == m_intruder_layer && ! m_foreign) {
if (m_interactions.find (std::make_pair (id2, id1)) != m_interactions.end ()) {
// for self interactions ignore the reverse interactions
ignore = true;
} else {
m_interactions.insert (std::make_pair (id1, id2));
}
}
if (! ignore) {
collect_instance_interactions (inst1, inst2);
}
}
}
private:
const db::Layout *mp_subject_layout, *mp_intruder_layout;
unsigned int m_subject_layer, m_intruder_layer;
db::Coord m_dist;
interactions_type *mp_result;
std::unordered_set<std::pair<unsigned int, unsigned int> > m_interactions;
bool m_foreign;
void
collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2)
{
// TODO: this algorithm is not in particular effective for identical arrays or for arrays
// vs. single instances
const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ());
const db::Cell &cell2 = mp_intruder_layout->cell (inst2->object ().cell_index ());
db::box_convert <db::CellInst, true> inst2_bc (*mp_intruder_layout, m_intruder_layer);
db::box_convert <db::CellInst, true> inst1_bc (*mp_subject_layout, m_subject_layer);
db::Box iibox2 = inst2->bbox (inst2_bc).enlarged (db::Vector (m_dist, m_dist));
if (iibox2.empty ()) {
return;
}
std::unordered_map<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > > interactions_cache;
for (db::CellInstArray::iterator n = inst1->begin_touching (safe_box_enlarged (iibox2, -1, -1), inst1_bc); ! n.at_end (); ++n) {
db::ICplxTrans tn1 = inst1->complex_trans (*n);
db::ICplxTrans tni1 = tn1.inverted ();
db::Box ibox1 = (tn1 * cell1.bbox (m_subject_layer)).enlarged (db::Vector (m_dist, m_dist));
std::set<db::CellInstArray> *insts = 0;
if (! ibox1.empty ()) {
// TODO: in some cases, it may be possible to optimize this for arrays
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
continue;
}
db::ICplxTrans tn2 = inst2->complex_trans (*k);
// NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
db::Box ibox2 = (tn2 * cell2.bbox (m_intruder_layer)).enlarged (db::Vector (m_dist, m_dist));
db::Box cbox = ibox1 & ibox2;
if (! cbox.empty () && (cbox == ibox1 || cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1)))) {
db::ICplxTrans tn21 = tni1 * tn2;
std::list<std::pair<db::cell_index_type, db::ICplxTrans> > *interactions = 0;
std::unordered_map<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > >::iterator ic = interactions_cache.find (tn21);
if (ic != interactions_cache.end ()) {
interactions = &ic->second;
} else {
interactions = &interactions_cache [tn21];
collect_intruder_tree_interactions (cell1, cell2, tni1, tn21, cbox, *interactions);
}
for (std::list<std::pair<db::cell_index_type, db::ICplxTrans> >::const_iterator i = interactions->begin (); i != interactions->end (); ++i) {
if (! insts) {
insts = & (*mp_result) [std::make_pair (cell1.cell_index (), tn1)].first;
}
insts->insert (db::CellInstArray (db::CellInst (i->first), i->second));
}
}
}
}
}
}
void collect_intruder_tree_interactions (const db::Cell &subject_cell, const db::Cell &intruder_cell, const db::ICplxTrans &tni1, const db::ICplxTrans &tn21, const db::Box &cbox, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > &interactions)
{
db::ICplxTrans tni2 = tn21.inverted () * tni1;
db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
// do not recurse further if we're overlapping with shapes from the intruder
// or the intruder cell is not much bigger than the region of interest (cbox)
if (intruder_cell.bbox (m_intruder_layer).area () < area_ratio_for_recursion * cbox.area ()
|| ! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
return;
}
for (db::Cell::touching_iterator i = intruder_cell.begin_touching (tbox2); ! i.at_end (); ++i) {
const db::Cell &ic = mp_intruder_layout->cell (i->cell_index ());
for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (tbox2, db::box_convert<db::CellInst> (*mp_intruder_layout, m_intruder_layer)); ! ia.at_end (); ++ia) {
db::ICplxTrans it = i->complex_trans (*ia);
db::Box ibox2 = (tni2.inverted () * it * ic.bbox (m_intruder_layer)).enlarged (db::Vector (m_dist, m_dist));
db::Box ccbox = cbox & ibox2;
if (! ccbox.empty ()) {
collect_intruder_tree_interactions (subject_cell, ic, tni1, tn21 * it, ccbox, interactions);
}
}
}
}
};
template <class TS, class TI, class TR>
struct interaction_registration_inst2shape
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, TI, unsigned int>
{
public:
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result), m_rt (subject_layout)
{
// nothing yet ..
}
void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer)
{
collect_instance_shape_interactions (inst, layer, *ref, m_dist);
}
private:
db::Layout *mp_subject_layout;
unsigned int m_subject_layer;
db::Coord m_dist;
interactions_type *mp_result;
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> m_rt;
void
collect_instance_shape_interactions (const db::CellInstArray *inst, unsigned int layer, const TI &ref, db::Coord dist)
{
const db::Cell &cell = mp_subject_layout->cell (inst->object ().cell_index ());
db::box_convert <db::CellInst, true> inst_bc (*mp_subject_layout, m_subject_layer);
db::Box rbox = db::box_convert<TI> () (ref);
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 (m_subject_layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
if (! cbox.empty ()) {
db::ICplxTrans tni = tn.inverted ();
m_rt.set_trans (tni);
std::set<TI> *shapes = 0;
// not very strong, but already useful: the cells interact if there is a layer in cell
// in the common box
// NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
// dot edges. Instead safe-shrink the search box and use touching mode.
for (db::RecursiveShapeIterator s (*mp_subject_layout, cell, m_subject_layer, safe_box_enlarged (tni * cbox, -1, -1), false); !s.at_end (); ++s) {
if (! shapes) {
shapes = & (*mp_result) [std::make_pair (cell.cell_index (), tn)].second [layer];
}
shapes->insert (m_rt (ref));
}
}
}
}
};
}
// ---------------------------------------------------------------------------------------------
// LocalProcessorContextComputationTask implementation
template <class TS, class TI, class TR>
local_processor_context_computation_task<TS, TI, TR>::local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist)
: tl::Task (),
mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context),
mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst),
mp_intruder_cell (intruder_cell), m_dist (dist)
{
// This is quick, but will take away the intruders from the caller
m_intruders.swap (intruders);
}
template <class TS, class TI, class TR>
void
local_processor_context_computation_task<TS, TI, TR>::perform ()
{
mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist);
}
// ---------------------------------------------------------------------------------------------
// LocalProcessorResultComputationTask implementation
template <class TS, class TI, class TR>
local_processor_result_computation_task<TS, TI, TR>::local_processor_result_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::Cell *cell, local_processor_cell_contexts<TS, TI, TR> *cell_contexts, const local_operation<TS, TI, TR> *op, const std::vector<unsigned int> &output_layers)
: mp_proc (proc), mp_contexts (&contexts), mp_cell (cell), mp_cell_contexts (cell_contexts), mp_op (op), m_output_layers (output_layers)
{
// .. nothing yet ..
}
template <class TS, class TI, class TR>
void
local_processor_result_computation_task<TS, TI, TR>::perform ()
{
mp_cell_contexts->compute_results (*mp_contexts, mp_cell, mp_op, m_output_layers, mp_proc);
{
tl::MutexLocker locker (& mp_contexts->lock ());
#if defined(ENABLE_DB_HP_SANITY_ASSERTIONS)
std::set<const db::local_processor_cell_context<TS, TI, TR> *> td;
for (typename db::local_processor_cell_contexts<TS, TI, TR>::iterator i = mp_cell_contexts->begin (); i != mp_cell_contexts->end (); ++i) {
td.insert (&i->second);
}
for (typename db::local_processor_cell_contexts<TS, TI, TR>::contexts_per_cell_type::iterator pcc = mp_contexts->context_map ().begin (); pcc != mp_contexts->context_map ().end (); ++pcc) {
for (typename db::local_processor_cell_contexts<TS, TI, TR>::iterator i = pcc->second.begin (); i != pcc->second.end (); ++i) {
for (typename db::local_processor_cell_context<TS, TI, TR>::drop_iterator j = i->second.begin_drops (); j != i->second.end_drops (); ++j) {
if (td.find (j->parent_context) != td.end ()) {
tl_assert (false);
}
}
}
}
#endif
// release some memory
auto ctx = mp_contexts->context_map ().find (mp_cell);
tl_assert (ctx != mp_contexts->context_map ().end ());
ctx->second.cleanup ();
}
}
// ---------------------------------------------------------------------------------------------
// LocalProcessorBase implementation
LocalProcessorBase::LocalProcessorBase ()
: m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_top_down (false), m_boolean_core (false),
m_base_verbosity (30), mp_vars (0), mp_current_cell (0)
{
// .. nothing yet ..
}
db::Coord
LocalProcessorBase::dist_for_cell (const db::Cell *cell, db::Coord dist) const
{
return cell ? dist_for_cell (cell->cell_index (), dist) : dist;
}
db::Coord
LocalProcessorBase::dist_for_cell (db::cell_index_type cell_index, db::Coord dist) const
{
if (mp_vars) {
const db::ICplxTrans &tr = mp_vars->single_variant_transformation (cell_index);
double mag = tr.mag ();
return db::coord_traits<db::Coord>::rounded (dist / mag);
} else {
return dist;
}
}
// ---------------------------------------------------------------------------------------------
// LocalProcessor implementation
template <class TS, class TI, class TR>
local_processor<TS, TI, TR>::local_processor (db::Layout *layout, db::Cell *top, const std::set<db::cell_index_type> *breakout_cells)
: mp_subject_layout (layout), mp_intruder_layout (layout),
mp_subject_top (top), mp_intruder_top (top),
mp_subject_breakout_cells (breakout_cells), mp_intruder_breakout_cells (breakout_cells),
m_progress (0), mp_progress (0)
{
set_boolean_core (default_boolean_core<TR> () ());
}
template <class TS, class TI, class TR>
local_processor<TS, TI, TR>::local_processor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_top, const std::set<cell_index_type> *subject_breakout_cells, const std::set<cell_index_type> *intruder_breakout_cells)
: mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout),
mp_subject_top (subject_top), mp_intruder_top (intruder_top),
mp_subject_breakout_cells (subject_breakout_cells), mp_intruder_breakout_cells (intruder_breakout_cells),
m_progress (0), mp_progress (0)
{
set_boolean_core (default_boolean_core<TR> () ());
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::next () const
{
static tl::Mutex s_lock;
tl::MutexLocker locker (&s_lock);
++m_progress;
tl::RelativeProgress *rp = dynamic_cast<tl::RelativeProgress *> (mp_progress);
if (rp) {
rp->set (m_progress);
}
}
template <class TS, class TI, class TR>
size_t local_processor<TS, TI, TR>::get_progress () const
{
size_t p = 0;
{
static tl::Mutex s_lock;
tl::MutexLocker locker (&s_lock);
p = m_progress;
}
return p;
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::run (local_operation<TS, TI, TR> *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer, bool make_variants)
{
std::vector<unsigned int> ol, il;
ol.push_back (output_layer);
il.push_back (intruder_layer);
run (op, subject_layer, il, ol, make_variants);
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::run (local_operation<TS, TI, TR> *op, unsigned int subject_layer, unsigned int intruder_layer, const std::vector<unsigned int> &output_layers, bool make_variants)
{
std::vector<unsigned int> ol, il;
il.push_back (intruder_layer);
run (op, subject_layer, il, output_layers, make_variants);
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::run (local_operation<TS, TI, TR> *op, unsigned int subject_layer, const std::vector<unsigned int> &intruder_layers, unsigned int output_layer, bool make_variants)
{
std::vector<unsigned int> ol;
ol.push_back (output_layer);
run (op, subject_layer, intruder_layers, ol, make_variants);
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::run (local_operation<TS, TI, TR> *op, unsigned int subject_layer, const std::vector<unsigned int> &intruder_layers, const std::vector<unsigned int> &output_layers, bool make_variants)
{
tl::SelfTimer timer (tl::verbosity () > base_verbosity (), tl::to_string (tr ("Executing ")) + description (op));
set_vars_owned (0);
// Prepare cell variants if needed
if (make_variants) {
tl::SelfTimer timer (tl::verbosity () > base_verbosity () + 10, tl::to_string (tr ("Cell variant formation")));
auto op_vars = op->vars ();
if (op_vars) {
db::VariantsCollectorBase *coll = new db::VariantsCollectorBase (op_vars);
set_vars_owned (coll);
coll->collect (mp_subject_layout, mp_subject_top->cell_index ());
coll->separate_variants ();
if (mp_intruder_layout != mp_subject_layout) {
db::VariantsCollectorBase vci (op_vars);
// NOTE: we don't plan to use separate_variants, so the const cast is in order
vci.collect (const_cast<db::Layout *> (mp_intruder_layout), mp_intruder_top->cell_index ());
if (vci.has_variants ()) {
// intruder layout needs to be the same one in that case - we do not want the secondary layout to be modified
throw tl::Exception (tl::to_string (tr ("Can't modify second layout for cell variant formation - this case is not supported as of now")));
}
}
}
}
local_processor_contexts<TS, TI, TR> contexts;
compute_contexts (contexts, op, subject_layer, intruder_layers);
compute_results (contexts, op, output_layers);
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, unsigned int subject_layer, const std::vector<unsigned int> &intruder_layers) const
{
try {
tl::SelfTimer timer (tl::verbosity () > base_verbosity () + 10, tl::to_string (tr ("Computing contexts for ")) + description (op));
if (threads () > 0) {
mp_cc_job.reset (new tl::Job<local_processor_context_computation_worker<TS, TI, TR> > (threads ()));
} else {
mp_cc_job.reset (0);
}
contexts.clear ();
contexts.set_intruder_layers (intruder_layers);
contexts.set_subject_layer (subject_layer);
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type intruders;
issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ());
if (mp_cc_job.get ()) {
mp_cc_job->start ();
mp_cc_job->wait ();
}
} catch (...) {
mp_cc_job.reset (0);
throw;
}
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::issue_compute_contexts (local_processor_contexts<TS, TI, TR> &contexts,
db::local_processor_cell_context<TS, TI, TR> *parent_context,
db::Cell *subject_parent,
db::Cell *subject_cell,
const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell,
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist) const
{
bool is_small_job = subject_cell->begin ().at_end ();
if (! is_small_job && mp_cc_job.get ()) {
mp_cc_job->schedule (new local_processor_context_computation_task<TS, TI, TR> (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist));
} else {
compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist);
}
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS, TI, TR> &contexts,
db::local_processor_cell_context<TS, TI, TR> *parent_context,
db::Cell *subject_parent,
db::Cell *subject_cell,
const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell,
const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist_top) const
{
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
if (tl::verbosity () >= base_verbosity () + 20) {
if (! subject_parent) {
tl::log << tr ("Computing context for top cell ") << mp_subject_layout->cell_name (subject_cell->cell_index ());
} else {
tl::log << tr ("Computing context for ") << mp_subject_layout->cell_name (subject_parent->cell_index ()) << " -> " << mp_subject_layout->cell_name (subject_cell->cell_index ()) << " @" << subject_cell_inst.to_string ();
}
}
db::Coord dist = dist_for_cell (subject_cell->cell_index (), dist_top);
db::local_processor_cell_context<TS, TI, TR> *cell_context = 0;
// prepare a new cell context: this has to happen in a thread-safe way as we share the contexts
// object between threads
{
tl::MutexLocker locker (& contexts.lock ());
db::local_processor_cell_contexts<TS, TI, TR> &cell_contexts = contexts.contexts_per_cell (subject_cell, intruder_cell);
#if defined(ENABLE_DB_HP_SANITY_ASSERTIONS)
if (subject_parent) {
typename db::local_processor_cell_contexts<TS, TI, TR>::contexts_per_cell_type::iterator pcc = contexts.context_map ().find (subject_parent);
if (pcc == contexts.context_map ().end ()) {
tl_assert (false);
}
tl_assert (pcc->first == subject_parent);
bool any = false;
for (typename db::local_processor_cell_contexts<TS, TI, TR>::iterator pcci = pcc->second.begin (); pcci != pcc->second.end () && !any; ++pcci) {
any = (&pcci->second == parent_context);
}
if (!any) {
tl_assert (false);
}
}
#endif
cell_context = cell_contexts.find_context (intruders);
if (cell_context) {
// we already have a context for this intruder scheme
cell_context->add (parent_context, subject_parent, subject_cell_inst);
return;
}
cell_context = cell_contexts.create (intruders);
cell_context->add (parent_context, subject_parent, subject_cell_inst);
}
// perform the actual task ..
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts_unlocked)
std::map<unsigned int, const db::Shapes *> intruder_shapes;
if (intruder_cell) {
for (std::vector<unsigned int>::const_iterator l = contexts.intruder_layers ().begin (); l != contexts.intruder_layers ().end (); ++l) {
const db::Shapes *s = &intruder_cell->shapes (contexts.actual_intruder_layer (*l));
if (! s->empty ()) {
intruder_shapes.insert (std::make_pair (*l, s));
}
}
}
db::box_convert <db::CellInstArray, true> inst_bcs (*mp_subject_layout, contexts.subject_layer ());
// handle top-down interactions (subject instances interacting with intruder shapes)
// and sibling interactions
if (! subject_cell->begin ().at_end ()) {
// Key: single instance given by cell index and transformation
// Value the contexts for the child cell for this instance
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interactions_type interactions;
// insert dummy interactions to handle at least the child cell vs. itself
// - this is important so we will always handle the instances unless they are
// entirely empty in the subject layer
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty ()) {
db::cell_index_type ci = i->cell_inst ().object ().cell_index ();
for (db::CellInstArray::iterator n = i->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tn = i->complex_trans (*n);
interactions.insert (std::make_pair (std::make_pair (ci, tn), interactions_value_type ()));
}
}
}
// in top-down mode we are not interested in cell-to-cell interactions, nor shape-to-instance interactions
// except local ones (shape-to-child cells), hence we skip this part
if (! top_down ()) {
// TODO: can we shortcut this if interactions is empty?
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
db::box_convert <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner;
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
unsigned int id = 0;
if (subject_cell == intruder_cell) {
// Use the same id's for same instances - this way we can easily detect same instances
// and don't make them self-interacting
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
unsigned int iid = ++id;
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), iid);
}
if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) {
scanner.insert2 (&i->cell_inst (), iid);
}
}
} else {
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), ++id);
}
}
if (intruder_cell) {
for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) {
if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) {
scanner.insert2 (&i->cell_inst (), ++id);
}
}
}
}
for (std::set<db::CellInstArray>::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
if (! inst_bci (*i).empty ()) {
scanner.insert2 (i.operator-> (), ++id);
}
}
scanner.process (rec, dist, inst_bcs, inst_bci);
}
if (! intruders.second.empty () || ! intruder_shapes.empty ()) {
db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
db::addressable_object_from_shape<TI> heap;
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), 0);
}
}
for (typename std::map<unsigned int, std::set<TI> >::const_iterator il = intruders.second.begin (); il != intruders.second.end (); ++il) {
for (typename std::set<TI>::const_iterator i = il->second.begin (); i != il->second.end (); ++i) {
scanner.insert2 (i.operator-> (), il->first);
}
}
for (std::map<unsigned int, const db::Shapes *>::const_iterator im = intruder_shapes.begin (); im != intruder_shapes.end (); ++im) {
for (db::Shapes::shape_iterator i = im->second->begin (shape_flags<TI> ()); !i.at_end (); ++i) {
scanner.insert2 (heap (*i), im->first);
}
}
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
}
}
// produce the tasks for computing the next-level interactions
for (typename interactions_type::iterator i = interactions.begin (); i != interactions.end (); ++i) {
db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first);
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist);
}
}
}
template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, const std::vector<unsigned int> &output_layers) const
{
#if 0
// debugging
dump_cell_contexts (contexts, mp_subject_layout, mp_intruder_layout ? mp_intruder_layout : mp_subject_layout);
#endif
tl::SelfTimer timer (tl::verbosity () > base_verbosity () + 10, tl::to_string (tr ("Computing results for ")) + description (op));
// avoids updates while we work on the layout
mp_subject_layout->update ();
db::LayoutLocker layout_update_locker (mp_subject_layout);
// prepare a progress for the computation tasks
size_t comp_effort = 0;
if (report_progress ()) {
for (typename local_processor_contexts<TS, TI, TR>::iterator c = contexts.begin (); c != contexts.end (); ++c) {
comp_effort += c->second.size ();
}
}
tl::RelativeProgress progress (description (op), comp_effort, 1);
m_progress = 0;
mp_progress = 0;
if (threads () > 0) {
std::unique_ptr<tl::Job<local_processor_result_computation_worker<TS, TI, TR> > > rc_job (new tl::Job<local_processor_result_computation_worker<TS, TI, TR> > (threads ()));
// schedule computation jobs in "waves": we need to make sure they are executed
// bottom-up. So we identify a new bunch of cells each time we pass through the cell set
// and proceed until all cells are removed.
std::vector<db::cell_index_type> cells_bu;
cells_bu.reserve (mp_subject_layout->cells ());
for (db::Layout::bottom_up_const_iterator bu = mp_subject_layout->begin_bottom_up (); bu != mp_subject_layout->end_bottom_up (); ++bu) {
cells_bu.push_back (*bu);
}
int iter = 0;
while (true) {
++iter;
tl::SelfTimer timer (tl::verbosity () > base_verbosity () + 10, tl::sprintf (tl::to_string (tr ("Computing results iteration #%d")), iter));
bool any = false;
std::unordered_set<db::cell_index_type> later;
std::vector<db::cell_index_type> next_cells_bu;
next_cells_bu.reserve (cells_bu.size ());
for (std::vector<db::cell_index_type>::const_iterator bu = cells_bu.begin (); bu != cells_bu.end (); ++bu) {
tl::MutexLocker locker (& contexts.lock ());
typename local_processor_contexts<TS, TI, TR>::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu));
if (cpc != contexts.context_map ().end ()) {
if (later.find (*bu) == later.end ()) {
rc_job->schedule (new local_processor_result_computation_task<TS, TI, TR> (this, contexts, cpc->first, &cpc->second, op, output_layers));
any = true;
} else {
next_cells_bu.push_back (*bu);
}
for (db::Cell::parent_cell_iterator pc = cpc->first->begin_parent_cells (); pc != cpc->first->end_parent_cells (); ++pc) {
later.insert (*pc);
}
}
}
cells_bu.swap (next_cells_bu);
if (! any) {
break;
}
if (rc_job.get ()) {
try {
rc_job->start ();
while (! rc_job->wait (10)) {
progress.set (get_progress ());
}
} catch (...) {
rc_job->terminate ();
throw;
}
}
}
} else {
try {
mp_progress = report_progress () ? &progress : 0;
for (db::Layout::bottom_up_const_iterator bu = mp_subject_layout->begin_bottom_up (); bu != mp_subject_layout->end_bottom_up (); ++bu) {
typename local_processor_contexts<TS, TI, TR>::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu));
if (cpc != contexts.context_map ().end ()) {
cpc->second.compute_results (contexts, cpc->first, op, output_layers, this);
cpc->second.cleanup (); // release some memory
}
}
mp_progress = 0;
} catch (...) {
mp_progress = 0;
throw;
}
}
// deliver the results
{
tl::MutexLocker locker (& mp_subject_layout->lock ());
for (auto c = contexts.begin (); c != contexts.end (); ++c) {
db::Cell *cell = c->first;
auto r = c->second.result ().begin ();
auto rend = c->second.result ().end ();
for (auto o = output_layers.begin (); r != rend && o != output_layers.end (); ++o, ++r) {
if (! r->empty ()) {
cell->shapes (*o).insert (r->begin (), r->end ());
}
}
}
}
}
namespace {
template <class TS, class TI>
struct scan_shape2shape_same_layer
{
void
operator () (const db::Shapes *subject_shapes, unsigned int subject_id0, const std::set<TI> &intruders, unsigned int intruder_layer_index, shape_interactions<TS, TI> &interactions, db::Coord dist) const
{
db::box_scanner2<TS, int, TI, int> scanner;
db::addressable_object_from_shape<TS> heap;
interaction_registration_shape1<TS, TI> rec (&interactions, intruder_layer_index);
unsigned int id = subject_id0;
for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags<TS> ()); !i.at_end (); ++i) {
const TS *ref = heap (*i);
scanner.insert1 (ref, id++);
}
// TODO: can we confine this search to the subject's (sized) bounding box?
for (typename std::set<TI>::const_iterator i = intruders.begin (); i != intruders.end (); ++i) {
scanner.insert2 (i.operator-> (), interactions.next_id ());
}
scanner.process (rec, dist, db::box_convert<TS> (), db::box_convert<TI> ());
}
};
template <class T>
struct scan_shape2shape_same_layer<T, T>
{
void
operator () (const db::Shapes *subject_shapes, unsigned int subject_id0, const std::set<T> &intruders, unsigned int intruder_layer, shape_interactions<T, T> &interactions, db::Coord dist) const
{
db::box_scanner<T, int> scanner;
db::addressable_object_from_shape<T> heap;
interaction_registration_shape1<T, T> rec (&interactions, intruder_layer);
unsigned int id = subject_id0;
for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags<T> ()); !i.at_end (); ++i) {
scanner.insert (heap (*i), id++);
}
// TODO: can we confine this search to the subject's (sized) bounding box?
for (typename std::set<T>::const_iterator i = intruders.begin (); i != intruders.end (); ++i) {
scanner.insert (i.operator-> (), interactions.next_id ());
}
scanner.process (rec, dist, db::box_convert<T> ());
}
};
template <class TS, class TI>
struct scan_shape2shape_different_layers
{
void
operator () (db::Layout *layout, const db::Shapes *subject_shapes, const db::Shapes *intruder_shapes, unsigned int subject_id0, const std::set<TI> *intruders, unsigned int intruder_layer_index, shape_interactions<TS, TI> &interactions, db::Coord dist) const
{
db::box_scanner2<TS, int, TI, int> scanner;
db::addressable_object_from_shape<TS> sheap;
db::addressable_object_from_shape<TI> iheap;
interaction_registration_shape2shape<TS, TI> rec (layout, &interactions, intruder_layer_index);
unsigned int id = subject_id0;
for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags<TS> ()); !i.at_end (); ++i, ++id) {
scanner.insert1 (sheap (*i), id);
}
// TODO: can we confine this search to the subject's (sized) bounding box?
if (intruders) {
for (typename std::set<TI>::const_iterator i = intruders->begin (); i != intruders->end (); ++i) {
scanner.insert2 (i.operator-> (), interactions.next_id ());
}
}
if (intruder_shapes == subject_shapes) {
// TODO: can we confine this search to the subject's (sized) bounding box?
// special case of intra-layer interactions ("foreign"): mark identical shapes as same so that shapes are not reported interacting with
// themselves.
unsigned int id = subject_id0;
for (db::Shapes::shape_iterator i = intruder_shapes->begin (shape_flags<TI> ()); !i.at_end (); ++i, ++id) {
unsigned int iid = interactions.next_id ();
scanner.insert2 (iheap (*i), iid);
rec.same (id, iid);
}
} else if (intruder_shapes) {
// TODO: can we confine this search to the subject's (sized) bounding box?
for (db::Shapes::shape_iterator i = intruder_shapes->begin (shape_flags<TI> ()); !i.at_end (); ++i) {
scanner.insert2 (iheap (*i), interactions.next_id ());
}
}
scanner.process (rec, dist, db::box_convert<TS> (), db::box_convert<TI> ());
}
};
}
template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const
{
db::Coord dist = dist_for_cell (subject_cell->cell_index (), op->dist ());
const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ());
db::shape_to_object<TS> s2o;
shape_interactions<TS, TI> interactions;
// insert dummy interactions to accommodate subject vs. nothing and assign an ID
// range for the subject shapes.
unsigned int subject_id0 = 0;
for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags<TS> ()); !i.at_end (); ++i) {
unsigned int id = interactions.next_id ();
if (subject_id0 == 0) {
subject_id0 = id;
}
if (op->on_empty_intruder_hint () != OnEmptyIntruderHint::Drop) {
interactions.add_subject (id, s2o (*i));
}
}
unsigned int il_index = 0;
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il, ++il_index) {
unsigned int ail = contexts.actual_intruder_layer (*il);
bool foreign = contexts.is_foreign (*il);
const db::Shapes *intruder_shapes = 0;
if (intruder_cell) {
intruder_shapes = &intruder_cell->shapes (ail);
if (intruder_shapes->empty ()) {
intruder_shapes = 0;
}
}
db::box_convert<db::CellInstArray, true> inst_bci (*mp_intruder_layout, ail);
typename std::map<unsigned int, std::set<TI> >::const_iterator ipl = intruders.second.find (*il);
static std::set<TI> empty_intruders;
// local shapes vs. local shapes
if (! top_down () && ! subject_shapes->empty () && (intruder_shapes || ipl != intruders.second.end ())) {
if (subject_cell == intruder_cell && contexts.subject_layer () == ail && !foreign) {
scan_shape2shape_same_layer<TS, TI> () (subject_shapes, subject_id0, ipl == intruders.second.end () ? empty_intruders : ipl->second, il_index, interactions, dist);
} else {
db::Layout *target_layout = (mp_subject_layout == mp_intruder_layout ? 0 : mp_subject_layout);
scan_shape2shape_different_layers<TS, TI> () (target_layout, subject_shapes, intruder_shapes, subject_id0, &(ipl == intruders.second.end () ? empty_intruders : ipl->second), il_index, interactions, dist);
}
}
// local shapes vs. child cells
if (! subject_shapes->empty () && ! ((! intruder_cell || intruder_cell->begin ().at_end ()) && intruders.first.empty ())) {
db::box_scanner2<TS, int, db::CellInstArray, int> scanner;
db::addressable_object_from_shape<TS> heap;
interaction_registration_shape2inst<TS, TI> rec (mp_subject_layout, mp_intruder_layout, ail, il_index, dist, &interactions);
unsigned int id = subject_id0;
for (db::Shapes::shape_iterator i = subject_shapes->begin (shape_flags<TS> ()); !i.at_end (); ++i) {
scanner.insert1 (heap (*i), id++);
}
unsigned int inst_id = 0;
if (! top_down () && subject_cell == intruder_cell && contexts.subject_layer () == ail && !foreign) {
// Same cell, same layer -> no shape to child instance interactions because this will be taken care of
// by the instances themselves (and their intruders). This also means, we prefer to deal with
// interactions low in the hierarchy.
} else if (intruder_cell) {
// TODO: can we confine this search to the subject's (sized) bounding box?
for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) {
if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) {
scanner.insert2 (&i->cell_inst (), ++inst_id);
}
}
}
// TODO: can we confine this search to the subject's (sized) bounding box?
for (std::set<db::CellInstArray>::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
if (! inst_bci (*i).empty ()) {
scanner.insert2 (i.operator-> (), ++inst_id);
}
}
scanner.process (rec, dist, db::box_convert<TS> (), inst_bci);
}
}
if (interactions.begin () != interactions.end ()) {
if (interactions.begin_intruders () == interactions.end_intruders ()) {
OnEmptyIntruderHint eh = op->on_empty_intruder_hint ();
if (eh == OnEmptyIntruderHint::Drop) {
return;
}
}
op->compute_local (mp_subject_layout, subject_cell, interactions, result, this);
}
}
template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::run_flat (const db::Shapes *subject_shapes, const db::Shapes *intruders, const local_operation<TS, TI, TR> *op, db::Shapes *result_shapes) const
{
std::vector<generic_shape_iterator<TI> > is;
std::vector<bool> foreign;
if (intruders == subject_idptr () || intruders == foreign_idptr ()) {
is.push_back (generic_shape_iterator<TI> (subject_shapes));
foreign.push_back (intruders == foreign_idptr ());
} else {
is.push_back (generic_shape_iterator<TI> (intruders));
foreign.push_back (false);
}
std::vector<db::Shapes *> os;
os.push_back (result_shapes);
run_flat (generic_shape_iterator<TS> (subject_shapes), is, foreign, op, os);
}
template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::run_flat (const db::Shapes *subject_shapes, const std::vector<const db::Shapes *> &intruders, const local_operation<TS, TI, TR> *op, const std::vector<db::Shapes *> &result_shapes) const
{
std::vector<generic_shape_iterator<TI> > is;
is.reserve (intruders.size ());
std::vector<bool> foreign;
foreign.reserve (intruders.size ());
for (std::vector<const db::Shapes *>::const_iterator i = intruders.begin (); i != intruders.end (); ++i) {
if (*i == subject_idptr () || *i == foreign_idptr ()) {
is.push_back (generic_shape_iterator<TI> (subject_shapes));
foreign.push_back (*i == foreign_idptr ());
} else {
is.push_back (generic_shape_iterator<TI> (*i));
foreign.push_back (false);
}
}
run_flat (generic_shape_iterator<TS> (subject_shapes), is, foreign, op, result_shapes);
}
namespace
{
template <class TS, class TI>
struct interaction_registration_shape1_scanner_combo
{
interaction_registration_shape1_scanner_combo (shape_interactions<TS, TI> *, unsigned int, bool, const std::string &)
{
// can't have self-interactions with different types
tl_assert (false);
}
void insert (const TS *, unsigned int)
{
// nothing here.
}
void process (db::Coord)
{
// nothing here.
}
};
template <class T>
struct interaction_registration_shape1_scanner_combo<T, T>
{
interaction_registration_shape1_scanner_combo (shape_interactions<T, T> *interactions, unsigned int intruder_layer_index, bool report_progress, const std::string &progress_description)
: m_scanner (report_progress, progress_description), m_rec (interactions, intruder_layer_index)
{
// .. nothing yet ..
}
void insert (const T *shape, unsigned int id)
{
m_scanner.insert (shape, id);
}
void process (db::Coord dist)
{
m_scanner.process (m_rec, dist, db::box_convert<T> ());
}
private:
db::box_scanner<T, int> m_scanner;
interaction_registration_shape1<T, T> m_rec;
};
}
template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subjects, const std::vector<generic_shape_iterator<TI> > &intruders, const std::vector<bool> &foreign, const local_operation<TS, TI, TR> *op, const std::vector<db::Shapes *> &result_shapes) const
{
if (subjects.at_end ()) {
return;
}
tl_assert (mp_subject_top == 0);
tl_assert (mp_intruder_top == 0);
std::string process_description, scan_description;
if (report_progress ()) {
process_description = description (op);
if (process_description.empty ()) {
process_description = tl::to_string (tr ("Processing"));
} else {
process_description += tl::to_string (tr (" (processing)"));
}
scan_description = description (op);
if (scan_description.empty ()) {
scan_description = tl::to_string (tr ("Scanning"));
} else {
scan_description += tl::to_string (tr (" (scan)"));
}
}
shape_interactions<TS, TI> interactions;
bool needs_isolated_subjects = (op->on_empty_intruder_hint () != OnEmptyIntruderHint::Drop);
// build the subjects in the intruders list
db::Coord dist = op->dist ();
db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist, dist);
db::Box intruders_box;
for (typename std::vector<generic_shape_iterator<TI> >::const_iterator il = intruders.begin (); il != intruders.end (); ++il) {
intruders_box += il->bbox ();
}
intruders_box = safe_box_enlarged (intruders_box, dist, dist);
db::Box common_box = intruders_box & subjects_box;
if (common_box.empty () || common_box.width () == 0 || common_box.height () == 0) {
if (needs_isolated_subjects) {
for (generic_shape_iterator<TS> is = subjects; ! is.at_end (); ++is) {
// create subject for subject vs. nothing interactions
interactions.add_subject (interactions.next_id (), *is);
}
}
} else {
common_box = safe_box_enlarged (common_box, -1, -1);
if (needs_isolated_subjects) {
unaddressable_shape_delivery<TS> is (subjects);
for ( ; !is.at_end (); ++is) {
unsigned int id = interactions.next_id ();
interactions.add_subject (id, *is);
}
unsigned int il_index = 0;
for (typename std::vector<generic_shape_iterator<TI> >::const_iterator il = intruders.begin (); il != intruders.end (); ++il, ++il_index) {
bool ff = foreign.size () > il_index && foreign [il_index];
if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description);
for (typename shape_interactions<TS, TI>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
scanner.insert (&s->second, s->first);
}
scanner.process (dist);
} else {
db::box_scanner2<TS, unsigned int, TI, unsigned int> scanner (report_progress (), scan_description);
interaction_registration_shape2shape<TS, TI> rec (0 /*layout*/, &interactions, il_index);
for (typename shape_interactions<TS, TI>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
scanner.insert1 (&s->second, s->first);
}
if (*il == subjects) {
// this is the case of intra-layer interactions ("foreign"): we pretend we have two layers and
// reject shape self-interactions by registering them as "same"
for (typename shape_interactions<TS, TI>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
unsigned int iid = interactions.next_id ();
safe_insert2_into_box_scanner (scanner, &s->second, iid);
rec.same (s->first, iid);
}
scanner.process (rec, dist, db::box_convert<TS> (), db::box_convert<TI> ());
} else {
addressable_shape_delivery<TI> ii ((*il).confined (common_box, false));
for (; !ii.at_end (); ++ii) {
scanner.insert2 (ii.operator-> (), interactions.next_id ());
}
scanner.process (rec, dist, db::box_convert<TS> (), db::box_convert<TI> ());
}
}
}
} else {
unsigned int id_first = 0;
{
// allocate a range of IDs for the subjects
generic_shape_iterator<TS> is (subjects);
if (! is.at_end ()) {
id_first = interactions.next_id ();
while (! (++is).at_end ()) {
interactions.next_id ();
}
}
}
unsigned int il_index = 0;
for (typename std::vector<generic_shape_iterator<TI> >::const_iterator il = intruders.begin (); il != intruders.end (); ++il, ++il_index) {
bool ff = foreign.size () > il_index && foreign [il_index];
if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description);
addressable_shape_delivery<TS> is (subjects.confined (common_box, false));
unsigned int id = id_first;
for ( ; ! is.at_end (); ++is, ++id) {
scanner.insert (is.operator-> (), id);
}
scanner.process (dist);
} else {
db::box_scanner2<TS, unsigned int, TI, unsigned int> scanner (report_progress (), scan_description);
interaction_registration_shape2shape<TS, TI> rec (0 /*layout*/, &interactions, il_index);
if (*il == subjects) {
// this is the case of intra-layer interactions ("foreign"): we pretend we have two layers and
// reject shape self-interactions by registering them as "same"
addressable_shape_delivery<TS> is (subjects.confined (common_box, false));
unsigned int id = id_first;
for ( ; ! is.at_end (); ++is, ++id) {
unsigned int iid = interactions.next_id ();
scanner.insert1 (is.operator-> (), id);
safe_insert2_into_box_scanner (scanner, is.operator-> (), iid);
rec.same (id, iid);
}
scanner.process (rec, dist, db::box_convert<TS> (), db::box_convert<TI> ());
} else {
addressable_shape_delivery<TS> is (subjects.confined (common_box, false));
addressable_shape_delivery<TI> ii ((*il).confined (common_box, false));
unsigned int id = id_first;
for ( ; ! is.at_end (); ++is, ++id) {
scanner.insert1 (is.operator-> (), id);
}
for (; !ii.at_end (); ++ii) {
scanner.insert2 (ii.operator-> (), interactions.next_id ());
}
scanner.process (rec, dist, db::box_convert<TS> (), db::box_convert<TI> ());
}
}
}
}
}
if (interactions.begin () != interactions.end ()) {
if (interactions.begin_intruders () == interactions.end_intruders ()) {
OnEmptyIntruderHint eh = op->on_empty_intruder_hint ();
if (eh == OnEmptyIntruderHint::Drop) {
return;
}
}
std::vector<std::unordered_set<TR> > result;
result.resize (result_shapes.size ());
op->compute_local (mp_subject_layout, 0, interactions, result, this);
for (std::vector<db::Shapes *>::const_iterator r = result_shapes.begin (); r != result_shapes.end (); ++r) {
if (*r) {
const std::unordered_set<TR> rs = result [r - result_shapes.begin ()];
(*r)->insert (rs.begin (), rs.end ());
}
}
}
}
// ---------------------------------------------------------------------------------------------
// NOTE: don't forget to update the explicit instantiations in dbHierProcessor2.cc
// explicit instantiations
template class DB_PUBLIC shape_interactions<db::Polygon, db::Polygon>;
template class DB_PUBLIC shape_interactions<db::Polygon, db::Text>;
template class DB_PUBLIC shape_interactions<db::Polygon, db::TextRef>;
template class DB_PUBLIC shape_interactions<db::Polygon, db::Edge>;
template class DB_PUBLIC shape_interactions<db::PolygonRefWithProperties, db::PolygonRefWithProperties>;
template class DB_PUBLIC shape_interactions<db::PolygonWithProperties, db::PolygonWithProperties>;
template class DB_PUBLIC shape_interactions<db::PolygonWithProperties, db::TextWithProperties>;
template class DB_PUBLIC shape_interactions<db::PolygonWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC shape_interactions<db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC shape_interactions<db::PolygonRef, db::TextRef>;
template class DB_PUBLIC shape_interactions<db::PolygonRefWithProperties, db::TextRefWithProperties>;
template class DB_PUBLIC shape_interactions<db::PolygonRef, db::Text>;
template class DB_PUBLIC shape_interactions<db::PolygonRef, db::Edge>;
template class DB_PUBLIC shape_interactions<db::PolygonRefWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC shape_interactions<db::Edge, db::Edge>;
template class DB_PUBLIC shape_interactions<db::EdgeWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC shape_interactions<db::Edge, db::PolygonRef>;
template class DB_PUBLIC shape_interactions<db::Edge, db::Polygon>;
template class DB_PUBLIC shape_interactions<db::TextRef, db::TextRef>;
template class DB_PUBLIC shape_interactions<db::TextRef, db::PolygonRef>;
template class DB_PUBLIC shape_interactions<db::EdgePair, db::Polygon>;
template class DB_PUBLIC shape_interactions<db::EdgePair, db::PolygonRef>;
template class DB_PUBLIC shape_interactions<db::EdgePair, db::Edge>;
// explicit instantiations
template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, db::Polygon, db::Polygon>;
template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, db::Text, db::Polygon>;
template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, db::Text, db::Text>;
template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, db::Edge, db::Polygon>;
template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, db::Edge, db::Edge>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::PolygonRefWithProperties>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgePairWithProperties>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgePair>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonWithProperties, db::PolygonWithProperties, db::PolygonWithProperties>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePairWithProperties>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePair>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonWithProperties, db::EdgeWithProperties, db::PolygonWithProperties>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonWithProperties, db::EdgeWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRef, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRef, db::TextRef, db::TextRef>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRef, db::TextRef, db::PolygonRef>;
template class DB_PUBLIC local_processor_context_computation_task<db::TextRef, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor_context_computation_task<db::TextRef, db::PolygonRef, db::TextRef>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRef, db::Edge, db::PolygonRef>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRef, db::Edge, db::Edge>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRef, db::PolygonRef, db::Edge>;
template class DB_PUBLIC local_processor_context_computation_task<db::PolygonRef, db::PolygonRef, db::EdgePair>;
template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, db::Polygon, db::EdgePair>;
template class DB_PUBLIC local_processor_context_computation_task<db::Polygon, db::Polygon, db::Edge>;
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::Edge, db::Edge>;
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::PolygonRef, db::Edge>;
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::Polygon, db::Edge>;
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::Edge, db::EdgePair>;
template class DB_PUBLIC local_processor_context_computation_task<db::Edge, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor_context_computation_task<db::EdgePair, db::Polygon, db::EdgePair>;
template class DB_PUBLIC local_processor_context_computation_task<db::EdgePair, db::PolygonRef, db::EdgePair>;
template class DB_PUBLIC local_processor_context_computation_task<db::EdgePair, db::Edge, db::EdgePair>;
template class DB_PUBLIC local_processor_context_computation_task<db::EdgePair, db::Polygon, db::Polygon>;
template class DB_PUBLIC local_processor_context_computation_task<db::EdgePair, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor_context_computation_task<db::EdgePair, db::Edge, db::Edge>;
// explicit instantiations
template class DB_PUBLIC local_processor_result_computation_task<db::Polygon, db::Polygon, db::Polygon>;
template class DB_PUBLIC local_processor_result_computation_task<db::Polygon, db::Text, db::Polygon>;
template class DB_PUBLIC local_processor_result_computation_task<db::Polygon, db::Text, db::Text>;
template class DB_PUBLIC local_processor_result_computation_task<db::Polygon, db::Edge, db::Polygon>;
template class DB_PUBLIC local_processor_result_computation_task<db::Polygon, db::Edge, db::Edge>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::PolygonRefWithProperties>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgePairWithProperties>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgePair>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonWithProperties, db::PolygonWithProperties, db::PolygonWithProperties>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePairWithProperties>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePair>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonWithProperties, db::EdgeWithProperties, db::PolygonWithProperties>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonWithProperties, db::EdgeWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonRef, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonRef, db::Edge, db::PolygonRef>;
template class DB_PUBLIC local_processor_result_computation_task<db::PolygonRef, db::PolygonRef, db::EdgePair>;
template class DB_PUBLIC local_processor_result_computation_task<db::Polygon, db::Polygon, db::EdgePair>;
template class DB_PUBLIC local_processor_result_computation_task<db::Edge, db::Edge, db::Edge>;
template class DB_PUBLIC local_processor_result_computation_task<db::Edge, db::PolygonRef, db::Edge>;
template class DB_PUBLIC local_processor_result_computation_task<db::Edge, db::Polygon, db::Edge>;
template class DB_PUBLIC local_processor_result_computation_task<db::Edge, db::Edge, db::EdgePair>;
template class DB_PUBLIC local_processor_result_computation_task<db::EdgePair, db::Polygon, db::EdgePair>;
template class DB_PUBLIC local_processor_result_computation_task<db::EdgePair, db::PolygonRef, db::EdgePair>;
template class DB_PUBLIC local_processor_result_computation_task<db::EdgePair, db::Edge, db::EdgePair>;
template class DB_PUBLIC local_processor_result_computation_task<db::EdgePair, db::Polygon, db::Polygon>;
template class DB_PUBLIC local_processor_result_computation_task<db::EdgePair, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor_result_computation_task<db::EdgePair, db::Edge, db::Edge>;
// explicit instantiations
template class DB_PUBLIC local_processor<db::Polygon, db::Polygon, db::Polygon>;
template class DB_PUBLIC local_processor<db::Polygon, db::Text, db::Polygon>;
template class DB_PUBLIC local_processor<db::Polygon, db::Text, db::Text>;
template class DB_PUBLIC local_processor<db::Polygon, db::Edge, db::Polygon>;
template class DB_PUBLIC local_processor<db::Polygon, db::Edge, db::Edge>;
template class DB_PUBLIC local_processor<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::PolygonRefWithProperties>;
template class DB_PUBLIC local_processor<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgePair>;
template class DB_PUBLIC local_processor<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::EdgePairWithProperties>;
template class DB_PUBLIC local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::PolygonWithProperties>;
template class DB_PUBLIC local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePair>;
template class DB_PUBLIC local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePairWithProperties>;
template class DB_PUBLIC local_processor<db::PolygonWithProperties, db::EdgeWithProperties, db::PolygonWithProperties>;
template class DB_PUBLIC local_processor<db::PolygonWithProperties, db::EdgeWithProperties, db::EdgeWithProperties>;
template class DB_PUBLIC local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor<db::PolygonRef, db::Edge, db::PolygonRef>;
template class DB_PUBLIC local_processor<db::PolygonRef, db::Edge, db::Edge>;
template class DB_PUBLIC local_processor<db::PolygonRef, db::TextRef, db::PolygonRef>;
template class DB_PUBLIC local_processor<db::PolygonRef, db::TextRef, db::TextRef>;
template class DB_PUBLIC local_processor<db::PolygonRef, db::PolygonRef, db::EdgePair>;
template class DB_PUBLIC local_processor<db::PolygonRef, db::PolygonRef, db::Edge>;
template class DB_PUBLIC local_processor<db::Polygon, db::Polygon, db::EdgePair>;
template class DB_PUBLIC local_processor<db::Polygon, db::Polygon, db::Edge>;
template class DB_PUBLIC local_processor<db::Edge, db::Edge, db::Edge>;
template class DB_PUBLIC local_processor<db::Edge, db::PolygonRef, db::Edge>;
template class DB_PUBLIC local_processor<db::Edge, db::Polygon, db::Edge>;
template class DB_PUBLIC local_processor<db::Edge, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor<db::Edge, db::Edge, db::EdgePair>;
template class DB_PUBLIC local_processor<db::TextRef, db::PolygonRef, db::TextRef>;
template class DB_PUBLIC local_processor<db::TextRef, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor<db::EdgePair, db::Polygon, db::EdgePair>;
template class DB_PUBLIC local_processor<db::EdgePair, db::PolygonRef, db::EdgePair>;
template class DB_PUBLIC local_processor<db::EdgePair, db::Edge, db::EdgePair>;
template class DB_PUBLIC local_processor<db::EdgePair, db::Polygon, db::Polygon>;
template class DB_PUBLIC local_processor<db::EdgePair, db::PolygonRef, db::PolygonRef>;
template class DB_PUBLIC local_processor<db::EdgePair, db::Edge, db::Edge>;
}