diff --git a/src/db/db/db.pro b/src/db/db/db.pro index d0fa394b3..299f029e3 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -37,6 +37,7 @@ SOURCES = \ dbGenericShapeIterator.cc \ dbGlyphs.cc \ dbHershey.cc \ + dbHierProcessor2.cc \ dbInstances.cc \ dbInstElement.cc \ dbLayerMapping.cc \ @@ -264,6 +265,7 @@ HEADERS = \ dbHash.h \ dbHersheyFont.h \ dbHershey.h \ + dbHierProcessorUtils.h \ dbInstances.h \ dbInstElement.h \ dbLayer.h \ diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index a6d42449b..328fffbdd 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -31,6 +31,7 @@ #include "dbLocalOperationUtils.h" #include "dbShapeFlags.h" #include "dbCellVariants.h" +#include "dbHierProcessorUtils.h" #include "tlLog.h" #include "tlTimer.h" #include "tlInternational.h" @@ -70,261 +71,6 @@ namespace db // than the overlap box times this factor. const double area_ratio_for_recursion = 3.0; -// --------------------------------------------------------------------------------------------- -// Shape reference translator - -template class shape_reference_translator; -template class shape_reference_translator_with_trans; - -template -class shape_reference_translator -{ -public: - typedef typename Ref::shape_type shape_type; - typedef typename Ref::trans_type ref_trans_type; - - shape_reference_translator (db::Layout *target_layout) - : mp_layout (target_layout) - { - // .. nothing yet .. - } - - Ref operator() (const Ref &ref) const - { - typename std::unordered_map::const_iterator m = m_cache.find (ref.ptr ()); - if (m != m_cache.end ()) { - - return Ref (m->second, ref.trans ()); - - } else { - - const shape_type *ptr; - { - tl::MutexLocker locker (&mp_layout->lock ()); - ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (ref.obj ()); - } - - m_cache[ref.ptr ()] = ptr; - return Ref (ptr, ref.trans ()); - - } - } - - template - Ref operator() (const Ref &ref, const Trans &tr) const - { - shape_type sh = ref.obj ().transformed (tr * Trans (ref.trans ())); - ref_trans_type red_trans; - sh.reduce (red_trans); - - typename std::unordered_map::const_iterator m = m_cache_by_shape.find (sh); - if (m != m_cache_by_shape.end ()) { - - return Ref (m->second, red_trans); - - } else { - - const shape_type *ptr; - { - tl::MutexLocker locker (&mp_layout->lock ()); - ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); - } - - m_cache_by_shape[sh] = ptr; - return Ref (ptr, red_trans); - - } - } - -private: - db::Layout *mp_layout; - mutable std::unordered_map m_cache; - mutable std::unordered_map m_cache_by_shape; -}; - -template -class simple_shape_reference_translator -{ -public: - typedef Shape shape_type; - - simple_shape_reference_translator () - { - // .. nothing yet .. - } - - const shape_type &operator() (const shape_type &s) const - { - return s; - } - - template - shape_type operator() (const shape_type &s, const Trans &tr) const - { - return s.transformed (tr); - } -}; - -template <> -class shape_reference_translator - : public simple_shape_reference_translator -{ -public: - shape_reference_translator (db::Layout * /*target_layout*/) { } -}; - -template <> -class shape_reference_translator - : public simple_shape_reference_translator -{ -public: - shape_reference_translator (db::Layout * /*target_layout*/) { } -}; - -template <> -class shape_reference_translator - : public simple_shape_reference_translator -{ -public: - shape_reference_translator (db::Layout * /*target_layout*/) { } -}; - -template -class shape_reference_translator > - : public shape_reference_translator -{ -public: - typedef db::object_with_properties shape_type; - - shape_reference_translator (db::Layout *target_layout) - : shape_reference_translator (target_layout) - { - // .. nothing yet .. - } - - shape_type operator() (const shape_type &s) const - { - return shape_type (shape_reference_translator::operator () (s), s.properties_id ()); - } - - template - shape_type operator() (const shape_type &s, const Trans &tr) const - { - return shape_type (shape_reference_translator::operator () (s, tr), s.properties_id ()); - } -}; - -template -class shape_reference_translator_with_trans_from_shape_ref -{ -public: - typedef typename Ref::shape_type shape_type; - typedef typename Ref::trans_type ref_trans_type; - - shape_reference_translator_with_trans_from_shape_ref (db::Layout *target_layout) - : mp_layout (target_layout) - { - // .. nothing yet .. - } - - void set_trans (const Trans &trans) - { - m_trans = trans; - m_ref_trans = ref_trans_type (trans); - m_bare_trans = Trans (m_ref_trans.inverted ()) * trans; - } - - Ref operator() (const Ref &ref) const - { - auto m = m_cache.find (std::make_pair (ref.ptr (), m_bare_trans)); - if (m != m_cache.end ()) { - - return Ref (m->second.first, ref_trans_type (m_trans * Trans (ref.trans ())) * m->second.second); - - } else { - - shape_type sh = ref.obj ().transformed (m_bare_trans); - ref_trans_type red_trans; - sh.reduce (red_trans); - - const shape_type *ptr; - { - tl::MutexLocker locker (&mp_layout->lock ()); - ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); - } - - m_cache[std::make_pair (ref.ptr (), m_bare_trans)] = std::make_pair (ptr, red_trans); - - return Ref (ptr, ref_trans_type (m_trans * Trans (ref.trans ())) * red_trans); - - } - } - -private: - db::Layout *mp_layout; - Trans m_trans; - ref_trans_type m_ref_trans; - Trans m_bare_trans; - mutable std::unordered_map, std::pair > m_cache; -}; - -template -class shape_reference_translator_with_trans - : public shape_reference_translator_with_trans_from_shape_ref -{ -public: - shape_reference_translator_with_trans (db::Layout *target_layout) - : shape_reference_translator_with_trans_from_shape_ref (target_layout) - { - // .. nothing yet .. - } -}; - -template -class shape_reference_translator_with_trans -{ -public: - typedef Sh shape_type; - - shape_reference_translator_with_trans (db::Layout * /*target_layout*/) - { - // .. nothing yet .. - } - - void set_trans (const Trans &trans) - { - m_trans = trans; - } - - shape_type operator() (const shape_type &s) const - { - return s.transformed (m_trans); - } - -private: - Trans m_trans; -}; - -template -class shape_reference_translator_with_trans, Trans> - : public shape_reference_translator_with_trans -{ -public: - typedef db::object_with_properties shape_type; - - shape_reference_translator_with_trans (db::Layout *target_layout) - : shape_reference_translator_with_trans (target_layout) - { - // .. nothing yet .. - } - - shape_type operator() (const shape_type &s) const - { - // CAUTION: no property ID translation happens here (reasoning: the main use case is fake ID for net tagging) - return shape_type (shape_reference_translator_with_trans::operator () (s), s.properties_id ()); - } -}; - // --------------------------------------------------------------------------------------------- /** @@ -402,484 +148,6 @@ static void dump_cell_contexts (local_processor_contexts &contexts, } } -// --------------------------------------------------------------------------------------------- -// LocalProcessorCellContext implementation - -template -local_processor_cell_context::local_processor_cell_context () -{ - // .. nothing yet .. -} - -template -local_processor_cell_context::local_processor_cell_context (const local_processor_cell_context &other) - : m_propagated (other.m_propagated), m_drops (other.m_drops) -{ - // .. nothing yet .. -} - -template -local_processor_cell_context & -local_processor_cell_context::operator= (const local_processor_cell_context &other) -{ - if (this != &other) { - m_propagated = other.m_propagated; - m_drops = other.m_drops; - } - return *this; -} - -template -void -local_processor_cell_context::add (db::local_processor_cell_context *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) -{ - m_drops.push_back (local_processor_cell_drop (parent_context, parent, cell_inst)); -} - -template -void -local_processor_cell_context::propagate (unsigned int output_layer, const std::unordered_set &res) -{ - if (res.empty ()) { - return; - } - - db::Layout *subject_layout = 0; - shape_reference_translator_with_trans rt (subject_layout); - - for (typename std::vector >::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { - - tl_assert (d->parent_context != 0); - tl_assert (d->parent != 0); - - if (subject_layout != d->parent->layout ()) { - subject_layout = d->parent->layout (); - rt = shape_reference_translator_with_trans (subject_layout); - } - - rt.set_trans (d->cell_inst); - std::vector new_refs; - new_refs.reserve (res.size ()); - for (typename std::unordered_set::const_iterator r = res.begin (); r != res.end (); ++r) { - new_refs.push_back (rt (*r)); - } - - { - tl::MutexLocker locker (&d->parent_context->lock ()); - d->parent_context->propagated (output_layer).insert (new_refs.begin (), new_refs.end ()); - } - - } -} - -// --------------------------------------------------------------------------------------------- -// LocalProcessorCellContexts implementation - -template -local_processor_cell_contexts::local_processor_cell_contexts () - : mp_intruder_cell (0) -{ - // .. nothing yet .. -} - -template -local_processor_cell_contexts::local_processor_cell_contexts (const db::Cell *intruder_cell) - : mp_intruder_cell (intruder_cell) -{ - // .. nothing yet .. -} - -template -db::local_processor_cell_context * -local_processor_cell_contexts::find_context (const context_key_type &intruders) -{ - typename std::unordered_map >::iterator c = m_contexts.find (intruders); - return c != m_contexts.end () ? &c->second : 0; -} - -template -db::local_processor_cell_context * -local_processor_cell_contexts::create (const context_key_type &intruders) -{ - return &m_contexts[intruders]; -} - -template -static void -subtract_set (std::unordered_set &res, const std::unordered_set &other) -{ - // for everything else, we don't use a boolean core but just set intersection - for (typename std::unordered_set::const_iterator o = other.begin (); o != other.end (); ++o) { - res.erase (*o); - } -} - -template -static void -subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, const db::local_processor *proc) -{ - if (other.empty ()) { - return; - } - - if (! proc->boolean_core ()) { - subtract_set (res, other); - return; - } - - size_t max_vertex_count = proc->max_vertex_count (); - double area_ratio = proc->area_ratio (); - - db::EdgeProcessor ep; - ep.set_base_verbosity (proc->base_verbosity () + 30); - - size_t p1 = 0, p2 = 1; - - for (std::unordered_set::const_iterator i = res.begin (); i != res.end (); ++i) { - const db::PolygonRef &subject = *i; - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - for (std::unordered_set::const_iterator i = other.begin (); i != other.end (); ++i) { - const db::PolygonRef &subject = *i; - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - - res.clear (); - db::BooleanOp op (db::BooleanOp::ANotB); - db::PolygonRefGenerator pr (layout, res); - db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); - db::PolygonGenerator pg (splitter, true, true); - ep.process (pg, op); -} - -template -static void -subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, const db::local_processor *proc) -{ - if (other.empty ()) { - return; - } - - if (! proc->boolean_core ()) { - subtract_set (res, other); - return; - } - - size_t max_vertex_count = proc->max_vertex_count (); - double area_ratio = proc->area_ratio (); - - std::unordered_set first; - first.swap (res); - - std::map, std::vector > > by_prop_id; - for (auto i = first.begin (); i != first.end (); ++i) { - by_prop_id [i->properties_id ()].first.push_back (i.operator-> ()); - } - for (auto i = other.begin (); i != other.end (); ++i) { - by_prop_id [i->properties_id ()].second.push_back (i.operator-> ()); - } - - db::EdgeProcessor ep; - ep.set_base_verbosity (proc->base_verbosity () + 30); - - for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { - - db::properties_id_type prop_id = s2p->first; - size_t p1 = 0, p2 = 1; - - ep.clear (); - - for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { - ep.insert (**i, p1); - p1 += 2; - } - - for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) { - ep.insert (**i, p2); - p2 += 2; - } - - db::BooleanOp op (db::BooleanOp::ANotB); - db::polygon_ref_generator_with_properties pr (layout, res, prop_id); - db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); - db::PolygonGenerator pg (splitter, true, true); - ep.process (pg, op); - - } -} - -template -static void -subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor *proc) -{ - if (other.empty ()) { - return; - } - - if (! proc->boolean_core ()) { - subtract_set (res, other); - return; - } - - db::box_scanner scanner; - scanner.reserve (res.size () + other.size ()); - - for (std::unordered_set::const_iterator i = res.begin (); i != res.end (); ++i) { - scanner.insert (i.operator-> (), 0); - } - for (std::unordered_set::const_iterator i = other.begin (); i != other.end (); ++i) { - scanner.insert (i.operator-> (), 1); - } - - std::unordered_set result; - EdgeBooleanClusterCollector > cluster_collector (&result, EdgeNot); - scanner.process (cluster_collector, 1, db::box_convert ()); - - res.swap (result); -} - -template -static void -subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor *proc) -{ - if (other.empty ()) { - return; - } - - if (! proc->boolean_core ()) { - subtract_set (res, other); - return; - } - - std::unordered_set first; - first.swap (res); - - std::map, std::vector > > by_prop_id; - for (auto i = first.begin (); i != first.end (); ++i) { - by_prop_id [i->properties_id ()].first.push_back (i.operator-> ()); - } - for (auto i = other.begin (); i != other.end (); ++i) { - by_prop_id [i->properties_id ()].second.push_back (i.operator-> ()); - } - - for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { - - if (s2p->second.second.empty ()) { - - for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { - res.insert (**i); - } - - } else { - - db::box_scanner scanner; - scanner.reserve (s2p->second.first.size () + s2p->second.second.size ()); - - for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { - scanner.insert (*i, 0); - } - for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) { - scanner.insert (*i, 1); - } - - db::property_injector > prop_inject (&res, s2p->first); - EdgeBooleanClusterCollector > > cluster_collector (&prop_inject, EdgeNot); - scanner.process (cluster_collector, 1, db::box_convert ()); - - } - - } -} - -template -static void -subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor * /*proc*/) -{ - subtract_set (res, other); -} - -// determines the default boolean core flag per result type - -namespace -{ - -template -struct default_boolean_core -{ - bool operator() () const { return false; } -}; - -template <> -struct default_boolean_core -{ - bool operator() () const { return true; } -}; - -} - -namespace { - -template -struct context_sorter -{ - bool operator () (const std::pair::context_key_type *, db::local_processor_cell_context *> &a, - const std::pair::context_key_type *, db::local_processor_cell_context *> &b) - { - return *a.first < *b.first; - } -}; - -} - -template -void -local_processor_cell_contexts::compute_results (const local_processor_contexts &contexts, db::Cell *cell, const local_operation *op, const std::vector &output_layers, const local_processor *proc) -{ - CRONOLOGY_COMPUTE_BRACKET(event_compute_results) - - bool first = true; - std::vector > common; - common.resize (output_layers.size ()); - - int index = 0; - int total = int (m_contexts.size ()); - - // NOTE: we use the ordering provided by key_type::operator< rather than the unordered map to achieve - // reproducibility across different platforms. unordered_map is faster, but for processing them, - // strict ordering is a more robust choice. - std::vector *> > sorted_contexts; - sorted_contexts.reserve (m_contexts.size ()); - for (typename std::unordered_map >::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { - sorted_contexts.push_back (std::make_pair (&c->first, &c->second)); - } - - std::sort (sorted_contexts.begin (), sorted_contexts.end (), context_sorter ()); - - for (typename std::vector *> >::const_iterator c = sorted_contexts.begin (); c != sorted_contexts.end (); ++c) { - - proc->next (); - ++index; - - if (tl::verbosity () >= proc->base_verbosity () + 20) { - tl::log << tr ("Computing local results for ") << cell->layout ()->cell_name (cell->cell_index ()) << " (context " << index << "/" << total << ")"; - } - - if (first) { - - { - tl::MutexLocker locker (&c->second->lock ()); - for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { - common [o - output_layers.begin ()] = c->second->propagated (*o); - } - } - - CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) - proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, common); - first = false; - - } else { - - std::vector > res; - res.resize (output_layers.size ()); - - { - tl::MutexLocker locker (&c->second->lock ()); - for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { - res [o - output_layers.begin ()] = c->second->propagated (*o); - } - } - - { - CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) - proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, res); - } - - if (common.empty ()) { - - CRONOLOGY_COMPUTE_BRACKET(event_propagate) - for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { - c->second->propagate (*o, res [o - output_layers.begin ()]); - } - -// gcc 4.4.7 (at least) doesn't have an operator== in std::unordered_set, so we skip this -// optimization -#if defined(__GNUC__) && __GNUC__ == 4 - } else { -#else - } else if (res != common) { -#endif - - CRONOLOGY_COMPUTE_BRACKET(event_propagate) - - for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { - - size_t oi = o - output_layers.begin (); - - std::unordered_set lost; - - for (typename std::unordered_set::const_iterator i = common[oi].begin (); i != common[oi].end (); ++i) { - if (res[oi].find (*i) == res[oi].end ()) { - lost.insert (*i); - } - } - - if (! lost.empty ()) { - - subtract (lost, res[oi], cell->layout (), proc); - - if (! lost.empty ()) { - subtract (common[oi], lost, cell->layout (), proc); - for (typename std::vector *> >::const_iterator cc = sorted_contexts.begin (); cc != c; ++cc) { - cc->second->propagate (*o, lost); - } - } - - } - - } - - for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { - - std::unordered_set gained; - - size_t oi = o - output_layers.begin (); - for (typename std::unordered_set::const_iterator i = res[oi].begin (); i != res[oi].end (); ++i) { - if (common[oi].find (*i) == common[oi].end ()) { - gained.insert (*i); - } - } - - if (! gained.empty ()) { - - subtract (gained, common[oi], cell->layout (), proc); - - if (! gained.empty ()) { - c->second->propagate (*o, gained); - } - - } - - } - - } - - } - - } - - for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { - size_t oi = o - output_layers.begin (); - proc->push_results (cell, *o, common[oi]); - } -} - // --------------------------------------------------------------------------------------------- template @@ -2535,64 +1803,7 @@ local_processor::run_flat (const generic_shape_iterator &subject // --------------------------------------------------------------------------------------------- -// NOTE: don't forget to update the explicit instantiations in dbLocalOperation.cc - -// explicit instantiations -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; -template class DB_PUBLIC local_processor_cell_context; - -// explicit instantiations -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; -template class DB_PUBLIC local_processor_cell_contexts; +// NOTE: don't forget to update the explicit instantiations in dbHierProcessor2.cc // explicit instantiations template class DB_PUBLIC shape_interactions; diff --git a/src/db/db/dbHierProcessor2.cc b/src/db/db/dbHierProcessor2.cc new file mode 100644 index 000000000..badfc3b0c --- /dev/null +++ b/src/db/db/dbHierProcessor2.cc @@ -0,0 +1,590 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 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_MAKE_EVENT(event_compute_contexts, "Compute contexts") +CRONOLOGY_MAKE_EVENT(event_compute_contexts_unlocked, "Compute contexts (unlocked)") + +cronology::events::event_collection collect_events; + +#define CRONOLOGY_COLLECTION_BRACKET(event_name) cronology::EventBracket __bracket##event_name (collect_events.threadwise ()->event ().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 compute_events; + +#define CRONOLOGY_COMPUTE_BRACKET(event_name) cronology::EventBracket __bracket##event_name (compute_events.threadwise ()->event ().event ()); + +#else +#define CRONOLOGY_COLLECTION_BRACKET(event_name) +#define CRONOLOGY_COMPUTE_BRACKET(event_name) +#endif + +namespace db +{ + +// --------------------------------------------------------------------------------------------- +// LocalProcessorCellContext implementation + +template +local_processor_cell_context::local_processor_cell_context () +{ + // .. nothing yet .. +} + +template +local_processor_cell_context::local_processor_cell_context (const local_processor_cell_context &other) + : m_propagated (other.m_propagated), m_drops (other.m_drops) +{ + // .. nothing yet .. +} + +template +local_processor_cell_context & +local_processor_cell_context::operator= (const local_processor_cell_context &other) +{ + if (this != &other) { + m_propagated = other.m_propagated; + m_drops = other.m_drops; + } + return *this; +} + +template +void +local_processor_cell_context::add (db::local_processor_cell_context *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) +{ + m_drops.push_back (local_processor_cell_drop (parent_context, parent, cell_inst)); +} + +template +void +local_processor_cell_context::propagate (unsigned int output_layer, const std::unordered_set &res) +{ + if (res.empty ()) { + return; + } + + db::Layout *subject_layout = 0; + shape_reference_translator_with_trans rt (subject_layout); + + for (typename std::vector >::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { + + tl_assert (d->parent_context != 0); + tl_assert (d->parent != 0); + + if (subject_layout != d->parent->layout ()) { + subject_layout = d->parent->layout (); + rt = shape_reference_translator_with_trans (subject_layout); + } + + rt.set_trans (d->cell_inst); + std::vector new_refs; + new_refs.reserve (res.size ()); + for (typename std::unordered_set::const_iterator r = res.begin (); r != res.end (); ++r) { + new_refs.push_back (rt (*r)); + } + + { + tl::MutexLocker locker (&d->parent_context->lock ()); + d->parent_context->propagated (output_layer).insert (new_refs.begin (), new_refs.end ()); + } + + } +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessorCellContexts implementation + +template +local_processor_cell_contexts::local_processor_cell_contexts () + : mp_intruder_cell (0) +{ + // .. nothing yet .. +} + +template +local_processor_cell_contexts::local_processor_cell_contexts (const db::Cell *intruder_cell) + : mp_intruder_cell (intruder_cell) +{ + // .. nothing yet .. +} + +template +db::local_processor_cell_context * +local_processor_cell_contexts::find_context (const context_key_type &intruders) +{ + typename std::unordered_map >::iterator c = m_contexts.find (intruders); + return c != m_contexts.end () ? &c->second : 0; +} + +template +db::local_processor_cell_context * +local_processor_cell_contexts::create (const context_key_type &intruders) +{ + return &m_contexts[intruders]; +} + +template +static void +subtract_set (std::unordered_set &res, const std::unordered_set &other) +{ + // for everything else, we don't use a boolean core but just set intersection + for (typename std::unordered_set::const_iterator o = other.begin (); o != other.end (); ++o) { + res.erase (*o); + } +} + +template +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, const db::local_processor *proc) +{ + if (other.empty ()) { + return; + } + + if (! proc->boolean_core ()) { + subtract_set (res, other); + return; + } + + size_t max_vertex_count = proc->max_vertex_count (); + double area_ratio = proc->area_ratio (); + + db::EdgeProcessor ep; + ep.set_base_verbosity (proc->base_verbosity () + 30); + + size_t p1 = 0, p2 = 1; + + for (std::unordered_set::const_iterator i = res.begin (); i != res.end (); ++i) { + const db::PolygonRef &subject = *i; + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + for (std::unordered_set::const_iterator i = other.begin (); i != other.end (); ++i) { + const db::PolygonRef &subject = *i; + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + res.clear (); + db::BooleanOp op (db::BooleanOp::ANotB); + db::PolygonRefGenerator pr (layout, res); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.process (pg, op); +} + +template +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout *layout, const db::local_processor *proc) +{ + if (other.empty ()) { + return; + } + + if (! proc->boolean_core ()) { + subtract_set (res, other); + return; + } + + size_t max_vertex_count = proc->max_vertex_count (); + double area_ratio = proc->area_ratio (); + + std::unordered_set first; + first.swap (res); + + std::map, std::vector > > by_prop_id; + for (auto i = first.begin (); i != first.end (); ++i) { + by_prop_id [i->properties_id ()].first.push_back (i.operator-> ()); + } + for (auto i = other.begin (); i != other.end (); ++i) { + by_prop_id [i->properties_id ()].second.push_back (i.operator-> ()); + } + + db::EdgeProcessor ep; + ep.set_base_verbosity (proc->base_verbosity () + 30); + + for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { + + db::properties_id_type prop_id = s2p->first; + size_t p1 = 0, p2 = 1; + + ep.clear (); + + for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { + ep.insert (**i, p1); + p1 += 2; + } + + for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) { + ep.insert (**i, p2); + p2 += 2; + } + + db::BooleanOp op (db::BooleanOp::ANotB); + db::polygon_ref_generator_with_properties pr (layout, res, prop_id); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.process (pg, op); + + } +} + +template +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor *proc) +{ + if (other.empty ()) { + return; + } + + if (! proc->boolean_core ()) { + subtract_set (res, other); + return; + } + + db::box_scanner scanner; + scanner.reserve (res.size () + other.size ()); + + for (std::unordered_set::const_iterator i = res.begin (); i != res.end (); ++i) { + scanner.insert (i.operator-> (), 0); + } + for (std::unordered_set::const_iterator i = other.begin (); i != other.end (); ++i) { + scanner.insert (i.operator-> (), 1); + } + + std::unordered_set result; + EdgeBooleanClusterCollector > cluster_collector (&result, EdgeNot); + scanner.process (cluster_collector, 1, db::box_convert ()); + + res.swap (result); +} + +template +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor *proc) +{ + if (other.empty ()) { + return; + } + + if (! proc->boolean_core ()) { + subtract_set (res, other); + return; + } + + std::unordered_set first; + first.swap (res); + + std::map, std::vector > > by_prop_id; + for (auto i = first.begin (); i != first.end (); ++i) { + by_prop_id [i->properties_id ()].first.push_back (i.operator-> ()); + } + for (auto i = other.begin (); i != other.end (); ++i) { + by_prop_id [i->properties_id ()].second.push_back (i.operator-> ()); + } + + for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { + + if (s2p->second.second.empty ()) { + + for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { + res.insert (**i); + } + + } else { + + db::box_scanner scanner; + scanner.reserve (s2p->second.first.size () + s2p->second.second.size ()); + + for (auto i = s2p->second.first.begin (); i != s2p->second.first.end (); ++i) { + scanner.insert (*i, 0); + } + for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) { + scanner.insert (*i, 1); + } + + db::property_injector > prop_inject (&res, s2p->first); + EdgeBooleanClusterCollector > > cluster_collector (&prop_inject, EdgeNot); + scanner.process (cluster_collector, 1, db::box_convert ()); + + } + + } +} + +template +static void +subtract (std::unordered_set &res, const std::unordered_set &other, db::Layout * /*layout*/, const db::local_processor * /*proc*/) +{ + subtract_set (res, other); +} + +namespace { + +template +struct context_sorter +{ + bool operator () (const std::pair::context_key_type *, db::local_processor_cell_context *> &a, + const std::pair::context_key_type *, db::local_processor_cell_context *> &b) + { + return *a.first < *b.first; + } +}; + +} + +template +void +local_processor_cell_contexts::compute_results (const local_processor_contexts &contexts, db::Cell *cell, const local_operation *op, const std::vector &output_layers, const local_processor *proc) +{ + CRONOLOGY_COMPUTE_BRACKET(event_compute_results) + + bool first = true; + std::vector > common; + common.resize (output_layers.size ()); + + int index = 0; + int total = int (m_contexts.size ()); + + // NOTE: we use the ordering provided by key_type::operator< rather than the unordered map to achieve + // reproducibility across different platforms. unordered_map is faster, but for processing them, + // strict ordering is a more robust choice. + std::vector *> > sorted_contexts; + sorted_contexts.reserve (m_contexts.size ()); + for (typename std::unordered_map >::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + sorted_contexts.push_back (std::make_pair (&c->first, &c->second)); + } + + std::sort (sorted_contexts.begin (), sorted_contexts.end (), context_sorter ()); + + for (typename std::vector *> >::const_iterator c = sorted_contexts.begin (); c != sorted_contexts.end (); ++c) { + + proc->next (); + ++index; + + if (tl::verbosity () >= proc->base_verbosity () + 20) { + tl::log << tr ("Computing local results for ") << cell->layout ()->cell_name (cell->cell_index ()) << " (context " << index << "/" << total << ")"; + } + + if (first) { + + { + tl::MutexLocker locker (&c->second->lock ()); + for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { + common [o - output_layers.begin ()] = c->second->propagated (*o); + } + } + + CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, common); + first = false; + + } else { + + std::vector > res; + res.resize (output_layers.size ()); + + { + tl::MutexLocker locker (&c->second->lock ()); + for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { + res [o - output_layers.begin ()] = c->second->propagated (*o); + } + } + + { + CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell) + proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, res); + } + + if (common.empty ()) { + + CRONOLOGY_COMPUTE_BRACKET(event_propagate) + for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { + c->second->propagate (*o, res [o - output_layers.begin ()]); + } + +// gcc 4.4.7 (at least) doesn't have an operator== in std::unordered_set, so we skip this +// optimization +#if defined(__GNUC__) && __GNUC__ == 4 + } else { +#else + } else if (res != common) { +#endif + + CRONOLOGY_COMPUTE_BRACKET(event_propagate) + + for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { + + size_t oi = o - output_layers.begin (); + + std::unordered_set lost; + + for (typename std::unordered_set::const_iterator i = common[oi].begin (); i != common[oi].end (); ++i) { + if (res[oi].find (*i) == res[oi].end ()) { + lost.insert (*i); + } + } + + if (! lost.empty ()) { + + subtract (lost, res[oi], cell->layout (), proc); + + if (! lost.empty ()) { + subtract (common[oi], lost, cell->layout (), proc); + for (typename std::vector *> >::const_iterator cc = sorted_contexts.begin (); cc != c; ++cc) { + cc->second->propagate (*o, lost); + } + } + + } + + } + + for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { + + std::unordered_set gained; + + size_t oi = o - output_layers.begin (); + for (typename std::unordered_set::const_iterator i = res[oi].begin (); i != res[oi].end (); ++i) { + if (common[oi].find (*i) == common[oi].end ()) { + gained.insert (*i); + } + } + + if (! gained.empty ()) { + + subtract (gained, common[oi], cell->layout (), proc); + + if (! gained.empty ()) { + c->second->propagate (*o, gained); + } + + } + + } + + } + + } + + } + + for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { + size_t oi = o - output_layers.begin (); + proc->push_results (cell, *o, common[oi]); + } +} + +// --------------------------------------------------------------------------------------------- + +// NOTE: don't forget to update the explicit instantiations in dbHierProcessor.cc + +// explicit instantiations +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; +template class DB_PUBLIC local_processor_cell_context; + +// explicit instantiations +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; +template class DB_PUBLIC local_processor_cell_contexts; + +} + diff --git a/src/db/db/dbHierProcessorUtils.h b/src/db/db/dbHierProcessorUtils.h new file mode 100644 index 000000000..1bd209315 --- /dev/null +++ b/src/db/db/dbHierProcessorUtils.h @@ -0,0 +1,308 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbHierProcessorUtils +#define HDR_dbHierProcessorUtils + +#include "dbCommon.h" +#include "dbLayout.h" +#include "tlThreads.h" + +#include + +namespace db +{ + +// --------------------------------------------------------------------------------------------- +// determines the default boolean core flag per result type + +template +struct default_boolean_core +{ + bool operator() () const { return false; } +}; + +template <> +struct default_boolean_core +{ + bool operator() () const { return true; } +}; + +// --------------------------------------------------------------------------------------------- +// Shape reference translator + +template class shape_reference_translator; +template class shape_reference_translator_with_trans; + +template +class shape_reference_translator +{ +public: + typedef typename Ref::shape_type shape_type; + typedef typename Ref::trans_type ref_trans_type; + + shape_reference_translator (db::Layout *target_layout) + : mp_layout (target_layout) + { + // .. nothing yet .. + } + + Ref operator() (const Ref &ref) const + { + typename std::unordered_map::const_iterator m = m_cache.find (ref.ptr ()); + if (m != m_cache.end ()) { + + return Ref (m->second, ref.trans ()); + + } else { + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (ref.obj ()); + } + + m_cache[ref.ptr ()] = ptr; + return Ref (ptr, ref.trans ()); + + } + } + + template + Ref operator() (const Ref &ref, const Trans &tr) const + { + shape_type sh = ref.obj ().transformed (tr * Trans (ref.trans ())); + ref_trans_type red_trans; + sh.reduce (red_trans); + + typename std::unordered_map::const_iterator m = m_cache_by_shape.find (sh); + if (m != m_cache_by_shape.end ()) { + + return Ref (m->second, red_trans); + + } else { + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); + } + + m_cache_by_shape[sh] = ptr; + return Ref (ptr, red_trans); + + } + } + +private: + db::Layout *mp_layout; + mutable std::unordered_map m_cache; + mutable std::unordered_map m_cache_by_shape; +}; + +template +class simple_shape_reference_translator +{ +public: + typedef Shape shape_type; + + simple_shape_reference_translator () + { + // .. nothing yet .. + } + + const shape_type &operator() (const shape_type &s) const + { + return s; + } + + template + shape_type operator() (const shape_type &s, const Trans &tr) const + { + return s.transformed (tr); + } +}; + +template <> +class shape_reference_translator + : public simple_shape_reference_translator +{ +public: + shape_reference_translator (db::Layout * /*target_layout*/) { } +}; + +template <> +class shape_reference_translator + : public simple_shape_reference_translator +{ +public: + shape_reference_translator (db::Layout * /*target_layout*/) { } +}; + +template <> +class shape_reference_translator + : public simple_shape_reference_translator +{ +public: + shape_reference_translator (db::Layout * /*target_layout*/) { } +}; + +template +class shape_reference_translator > + : public shape_reference_translator +{ +public: + typedef db::object_with_properties shape_type; + + shape_reference_translator (db::Layout *target_layout) + : shape_reference_translator (target_layout) + { + // .. nothing yet .. + } + + shape_type operator() (const shape_type &s) const + { + return shape_type (shape_reference_translator::operator () (s), s.properties_id ()); + } + + template + shape_type operator() (const shape_type &s, const Trans &tr) const + { + return shape_type (shape_reference_translator::operator () (s, tr), s.properties_id ()); + } +}; + +template +class shape_reference_translator_with_trans_from_shape_ref +{ +public: + typedef typename Ref::shape_type shape_type; + typedef typename Ref::trans_type ref_trans_type; + + shape_reference_translator_with_trans_from_shape_ref (db::Layout *target_layout) + : mp_layout (target_layout) + { + // .. nothing yet .. + } + + void set_trans (const Trans &trans) + { + m_trans = trans; + m_ref_trans = ref_trans_type (trans); + m_bare_trans = Trans (m_ref_trans.inverted ()) * trans; + } + + Ref operator() (const Ref &ref) const + { + auto m = m_cache.find (std::make_pair (ref.ptr (), m_bare_trans)); + if (m != m_cache.end ()) { + + return Ref (m->second.first, ref_trans_type (m_trans * Trans (ref.trans ())) * m->second.second); + + } else { + + shape_type sh = ref.obj ().transformed (m_bare_trans); + ref_trans_type red_trans; + sh.reduce (red_trans); + + const shape_type *ptr; + { + tl::MutexLocker locker (&mp_layout->lock ()); + ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); + } + + m_cache[std::make_pair (ref.ptr (), m_bare_trans)] = std::make_pair (ptr, red_trans); + + return Ref (ptr, ref_trans_type (m_trans * Trans (ref.trans ())) * red_trans); + + } + } + +private: + db::Layout *mp_layout; + Trans m_trans; + ref_trans_type m_ref_trans; + Trans m_bare_trans; + mutable std::unordered_map, std::pair > m_cache; +}; + +template +class shape_reference_translator_with_trans + : public shape_reference_translator_with_trans_from_shape_ref +{ +public: + shape_reference_translator_with_trans (db::Layout *target_layout) + : shape_reference_translator_with_trans_from_shape_ref (target_layout) + { + // .. nothing yet .. + } +}; + +template +class shape_reference_translator_with_trans +{ +public: + typedef Sh shape_type; + + shape_reference_translator_with_trans (db::Layout * /*target_layout*/) + { + // .. nothing yet .. + } + + void set_trans (const Trans &trans) + { + m_trans = trans; + } + + shape_type operator() (const shape_type &s) const + { + return s.transformed (m_trans); + } + +private: + Trans m_trans; +}; + +template +class shape_reference_translator_with_trans, Trans> + : public shape_reference_translator_with_trans +{ +public: + typedef db::object_with_properties shape_type; + + shape_reference_translator_with_trans (db::Layout *target_layout) + : shape_reference_translator_with_trans (target_layout) + { + // .. nothing yet .. + } + + shape_type operator() (const shape_type &s) const + { + // CAUTION: no property ID translation happens here (reasoning: the main use case is fake ID for net tagging) + return shape_type (shape_reference_translator_with_trans::operator () (s), s.properties_id ()); + } +}; + +} + +#endif +