From 412056afed763b5f23f6716ace6350384a6d2c00 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 20 Feb 2021 00:40:05 +0100 Subject: [PATCH] Recursive instance iterator, first draft. --- src/db/db/db.pro | 2 + src/db/db/dbRecursiveInstanceIterator.cc | 714 ++++++++++ src/db/db/dbRecursiveInstanceIterator.h | 660 +++++++++ .../dbRecursiveInstanceIteratorTests.cc | 1252 +++++++++++++++++ .../dbRecursiveShapeIteratorTests.cc | 15 + src/db/unit_tests/unit_tests.pro | 1 + 6 files changed, 2644 insertions(+) create mode 100644 src/db/db/dbRecursiveInstanceIterator.cc create mode 100644 src/db/db/dbRecursiveInstanceIterator.h create mode 100644 src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 16f9df099..1903f4e3d 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -61,6 +61,7 @@ SOURCES = \ dbPolygonGenerators.cc \ dbPropertiesRepository.cc \ dbReader.cc \ + dbRecursiveInstanceIterator.cc \ dbRecursiveShapeIterator.cc \ dbRegion.cc \ dbRegionLocalOperations.cc \ @@ -269,6 +270,7 @@ HEADERS = \ dbPolygonGenerators.h \ dbPropertiesRepository.h \ dbReader.h \ + dbRecursiveInstanceIterator.h \ dbRecursiveShapeIterator.h \ dbRegion.h \ dbRegionLocalOperations.h \ diff --git a/src/db/db/dbRecursiveInstanceIterator.cc b/src/db/db/dbRecursiveInstanceIterator.cc new file mode 100644 index 000000000..b6b56745d --- /dev/null +++ b/src/db/db/dbRecursiveInstanceIterator.cc @@ -0,0 +1,714 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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 "dbRecursiveInstanceIterator.h" +#include "dbRegion.h" +#include "dbEdgeProcessor.h" +#include "tlProgress.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------ +// Recursive shape iterator implementation + +RecursiveInstanceIterator::RecursiveInstanceIterator (const RecursiveInstanceIterator &d) +{ + operator= (d); +} + +RecursiveInstanceIterator &RecursiveInstanceIterator::operator= (const RecursiveInstanceIterator &d) +{ + if (&d != this) { + + m_max_depth = d.m_max_depth; + m_min_depth = d.m_min_depth; + m_shape_flags = d.m_shape_flags; + m_shape_inv_prop_sel = d.m_shape_inv_prop_sel; + m_overlapping = d.m_overlapping; + m_start = d.m_start; + m_stop = d.m_stop; + + mp_layout = d.mp_layout; + mp_top_cell = d.mp_top_cell; + + m_region = d.m_region; + if (d.mp_complex_region.get () != 0) { + mp_complex_region.reset (new region_type (*d.mp_complex_region.get ())); + } else { + mp_complex_region.reset (0); + } + + m_box_convert = d.m_box_convert; + + m_inst = d.m_inst; + m_inst_array = d.m_inst_array; + m_empty_cells_cache = d.m_empty_cells_cache; + mp_cell = d.mp_cell; + m_trans = d.m_trans; + m_trans_stack = d.m_trans_stack; + m_inst_iterators = d.m_inst_iterators; + m_inst_array_iterators = d.m_inst_array_iterators; + m_cells = d.m_cells; + m_local_complex_region_stack = d.m_local_complex_region_stack; + m_local_region_stack = d.m_local_region_stack; + m_needs_reinit = d.m_needs_reinit; + m_inst_quad_id = d.m_inst_quad_id; + m_inst_quad_id_stack = d.m_inst_quad_id_stack; + + } + return *this; +} + +RecursiveInstanceIterator::RecursiveInstanceIterator () +{ + // anything. Not necessary reasonable. + mp_layout = 0; + mp_top_cell = 0; + mp_cell = 0; + m_overlapping = false; + m_max_depth = std::numeric_limits::max (); // all + m_min_depth = 0; + m_shape_inv_prop_sel = false; + m_needs_reinit = false; + m_inst_quad_id = 0; +} + +RecursiveInstanceIterator::RecursiveInstanceIterator (const layout_type &layout, const cell_type &cell, const box_type ®ion, bool overlapping) + : m_box_convert (layout) +{ + mp_layout = &layout; + mp_top_cell = &cell; + m_overlapping = overlapping; + init (); + init_region (region); +} + +RecursiveInstanceIterator::RecursiveInstanceIterator (const layout_type &layout, const cell_type &cell, const region_type ®ion, bool overlapping) + : m_box_convert (layout) +{ + mp_layout = &layout; + mp_top_cell = &cell; + m_overlapping = overlapping; + init (); + init_region (region); +} + +RecursiveInstanceIterator::RecursiveInstanceIterator (const layout_type &layout, const cell_type &cell) + : m_box_convert (layout) +{ + mp_layout = &layout; + mp_top_cell = &cell; + m_overlapping = false; + init (); + init_region (box_type::world ()); +} + +RecursiveInstanceIterator::~RecursiveInstanceIterator () +{ + // .. nothing yet .. +} + + +void +RecursiveInstanceIterator::init () +{ + m_needs_reinit = true; + m_max_depth = std::numeric_limits::max (); // all + m_min_depth = 0; // from the beginning + m_shape_inv_prop_sel = false; + m_inst_quad_id = 0; + mp_cell = 0; +} + +void +RecursiveInstanceIterator::init_region (const RecursiveInstanceIterator::box_type ®ion) +{ + m_region = region; + mp_complex_region.reset (0); +} + +void +RecursiveInstanceIterator::init_region (const RecursiveInstanceIterator::region_type ®ion) +{ + if (region.empty ()) { + + m_region = box_type (); + mp_complex_region.reset (0); + + } else if (region.is_box ()) { + + m_region = region.bbox (); + mp_complex_region.reset (0); + + } else { + + mp_complex_region.reset (new region_type (region)); + m_region = region.bbox (); + // A small optimization. We can do this since we merge and translate to trapezoids anyway. + mp_complex_region->set_strict_handling (false); + + } +} + +void +RecursiveInstanceIterator::set_region (const box_type ®ion) +{ + if (m_region != region || mp_complex_region.get () != 0) { + init_region (region); + m_needs_reinit = true; + } +} + +void +RecursiveInstanceIterator::set_region (const region_type ®ion) +{ + init_region (region); + m_needs_reinit = true; +} + +void +RecursiveInstanceIterator::confine_region (const box_type ®ion) +{ + if (m_region.empty ()) { + // no more confinement + } else if (mp_complex_region.get ()) { + init_region (*mp_complex_region & region_type (region)); + } else { + init_region (m_region & region); + } + m_needs_reinit = true; +} + +void +RecursiveInstanceIterator::confine_region (const region_type ®ion) +{ + if (m_region.empty ()) { + // no more confinement + } else if (mp_complex_region.get ()) { + init_region (*mp_complex_region & region); + } else { + init_region (region & region_type (m_region)); + } + m_needs_reinit = true; +} + +namespace { + +struct BoxTreePusher + : public db::SimplePolygonSink +{ + BoxTreePusher (RecursiveInstanceIterator::box_tree_type *bt) + : mp_bt (bt) + { + // .. nothing yet .. + } + + void put (const db::SimplePolygon &sp) + { + mp_bt->insert (sp.box ()); + } + +private: + RecursiveInstanceIterator::box_tree_type *mp_bt; +}; + +} + +void +RecursiveInstanceIterator::validate (RecursiveInstanceReceiver *receiver) const +{ + if (! m_needs_reinit) { + return; + } + + m_needs_reinit = false; + + // re-initialize + mp_cell = mp_top_cell; + m_trans_stack.clear (); + m_inst_iterators.clear (); + m_inst_quad_id_stack.clear (); + m_inst_array_iterators.clear (); + m_cells.clear (); + m_trans = cplx_trans_type (); + + m_local_region_stack.clear (); + m_local_region_stack.push_back (m_region); + + m_local_complex_region_stack.clear (); + if (mp_complex_region.get ()) { + + // prepare a local complex region + m_local_complex_region_stack.push_back (box_tree_type ()); + + // Use a merge and the trapezoid generator to produce a decomposition that goes into the complex region + + db::EdgeProcessor ep; + size_t n = 0; + for (region_type::const_iterator p = mp_complex_region->begin (); !p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + BoxTreePusher btp (&m_local_complex_region_stack.back ()); + db::TrapezoidGenerator tg (btp); + + db::MergeOp op (0); + ep.process (tg, op); + + m_local_complex_region_stack.back ().sort (db::box_convert ()); + + } + + if (mp_top_cell) { + new_cell (receiver); + next_instance (receiver); + } +} + +void +RecursiveInstanceIterator::reset_selection () +{ + if (mp_layout) { + + m_start.clear (); + m_stop.clear (); + + m_needs_reinit = true; + + } +} + +void +RecursiveInstanceIterator::unselect_cells (const std::set &cells) +{ + if (mp_layout) { + + for (std::set::const_iterator c = cells.begin (); c != cells.end (); ++c) { + m_stop.insert (*c); + m_start.erase (*c); + } + + m_needs_reinit = true; + + } +} + +void +RecursiveInstanceIterator::unselect_all_cells () +{ + if (mp_layout) { + + m_start.clear (); + for (db::Layout::const_iterator c = mp_layout->begin (); c != mp_layout->end (); ++c) { + m_stop.insert (c->cell_index ()); + } + + m_needs_reinit = true; + + } +} + +void +RecursiveInstanceIterator::select_cells (const std::set &cells) +{ + if (mp_layout) { + + for (std::set::const_iterator c = cells.begin (); c != cells.end (); ++c) { + m_start.insert (*c); + m_stop.erase (*c); + } + + m_needs_reinit = true; + + } +} + +void +RecursiveInstanceIterator::select_all_cells () +{ + if (mp_layout) { + + m_stop.clear (); + for (db::Layout::const_iterator c = mp_layout->begin (); c != mp_layout->end (); ++c) { + m_start.insert (c->cell_index ()); + } + + m_needs_reinit = true; + + } +} + +bool +RecursiveInstanceIterator::at_end () const +{ + validate (0); + return m_inst.at_end (); +} + +std::vector +RecursiveInstanceIterator::path () const +{ + std::vector elements; + for (size_t i = 0; i < m_inst_array_iterators.size () && i < m_inst_iterators.size (); ++i) { + elements.push_back (db::InstElement (*m_inst_iterators [i], m_inst_array_iterators [i])); + } + return elements; +} + +void +RecursiveInstanceIterator::skip_inst_iter_for_complex_region () const +{ + while (! m_inst.at_end ()) { + + // skip inst quad if possible + while (! m_inst.at_end ()) { + if (is_outside_complex_region (m_inst.quad_box ())) { + m_inst.skip_quad (); + } else { + m_inst_quad_id = m_inst.quad_id (); + break; + } + } + + // skip insts outside the complex region + if (! m_inst.at_end ()) { + if (! is_outside_complex_region (m_inst->bbox ())) { + break; + } else { + ++m_inst; + } + } + + } +} + +void +RecursiveInstanceIterator::next (RecursiveInstanceReceiver *receiver) +{ + if (! at_end ()) { + ++m_inst; + new_inst (receiver); + next_instance (receiver); + } +} + +void +RecursiveInstanceIterator::next_instance (RecursiveInstanceReceiver *receiver) const +{ + while (true) { + + if (! m_inst.at_end ()) { + + if (int (m_inst_iterators.size ()) < m_max_depth) { + down (receiver); + } + + } else { + + if (! m_inst_iterators.empty ()) { + // no more instances: up and next instance + up (receiver); + } else { + break; + } + + if (! m_inst.at_end () && int (m_inst_iterators.size () + 1) >= m_min_depth && ! is_inactive ()) { + break; + } + + } + + if (! m_inst.at_end ()) { + if (int (m_inst_iterators.size () + 1) < m_min_depth || is_inactive ()) { + ++m_inst; + new_inst (receiver); + } else { + break; + } + } + + } +} + +void +RecursiveInstanceIterator::down (RecursiveInstanceReceiver *receiver) const +{ + m_trans_stack.push_back (m_trans); + m_cells.push_back (mp_cell); + + m_inst_iterators.push_back (m_inst); + m_inst_array_iterators.push_back (m_inst_array); + m_inst_quad_id_stack.push_back (m_inst_quad_id); + + bool ia = is_inactive (); + bool aoi = is_all_of_instance (); + mp_cell = &mp_layout->cell (m_inst->cell_index ()); + set_inactive (ia); + set_all_of_instance (aoi); + + m_trans = m_trans * m_inst->complex_trans (*m_inst_array); + + // don't transform the world region, since transformation of that region might not work properly + box_type new_region = box_type::world (); + + // compute the region inside the new cell + if (new_region != m_local_region_stack.front ()) { + new_region = m_trans.inverted () * m_local_region_stack.front (); + new_region &= cell ()->bbox (); + } + m_local_region_stack.push_back (new_region); + + if (! m_local_complex_region_stack.empty ()) { + + m_local_complex_region_stack.push_back (box_tree_type ()); + const box_tree_type &pcl = m_local_complex_region_stack.end ()[-2]; + + if (! new_region.empty ()) { + + // compute a new, reduced complex region for use inside the new cell + + db::CellInstArray::complex_trans_type tinst = m_inst->complex_trans (*m_inst_array); + db::CellInstArray::complex_trans_type tinst_inv = tinst.inverted (); + + db::Box bb; + + for (box_tree_type::touching_iterator b = pcl.begin_touching (correct_box_overlapping (new_region.transformed (tinst)), db::box_convert ()); ! b.at_end (); ++b) { + db::Box lb = (b->transformed (tinst_inv) & new_region); + if (! lb.empty ()) { + m_local_complex_region_stack.back ().insert (lb); + bb += lb; + } + } + + m_local_complex_region_stack.back ().sort (db::box_convert ()); + + // re-adjust the new local region, so we take into account additional clipping by the complex region. + // in the extreme case, this box is empty: + m_local_region_stack.back () = bb; + + } + + } + + if (receiver) { + receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + } + + new_cell (receiver); +} + +void +RecursiveInstanceIterator::up (RecursiveInstanceReceiver *receiver) const +{ + if (receiver) { + receiver->leave_cell (this, cell ()); + } + + m_inst = m_inst_iterators.back (); + m_inst_array = m_inst_array_iterators.back (); + m_inst_quad_id = m_inst_quad_id_stack.back (); + m_inst_iterators.pop_back (); + m_inst_array_iterators.pop_back (); + m_inst_quad_id_stack.pop_back (); + + m_trans = m_trans_stack.back (); + m_trans_stack.pop_back (); + mp_cell = m_cells.back (); + m_cells.pop_back (); + m_local_region_stack.pop_back (); + if (! m_local_complex_region_stack.empty ()) { + m_local_complex_region_stack.pop_back (); + } +} + +void +RecursiveInstanceIterator::new_cell (RecursiveInstanceReceiver *receiver) const +{ + bool new_cell_inactive = is_child_inactive (cell_index ()); + if (is_inactive () != new_cell_inactive) { + set_inactive (new_cell_inactive); + } + +// @@@ drop? + // skip instance quad if possible + if (! m_local_complex_region_stack.empty ()) { + skip_inst_iter_for_complex_region (); + } +// @@@ + + m_inst = cell ()->begin_touching (correct_box_overlapping (m_local_region_stack.back ())); + + m_inst_quad_id = 0; + + // skip instance quad if possible + if (! m_local_complex_region_stack.empty ()) { + skip_inst_iter_for_complex_region (); + } + + new_inst (receiver); +} + +void +RecursiveInstanceIterator::new_inst (RecursiveInstanceReceiver *receiver) const +{ + // look for the next instance with a non-empty array iterator. The array iterator can be empty because we + // use a lookup region. + while (! m_inst.at_end ()) { + + // skip instance quad if possible + if (! m_local_complex_region_stack.empty ()) { + skip_inst_iter_for_complex_region (); + if (m_inst.at_end ()) { + break; + } + } + + bool all_of_instance = false; + bool with_region = false; + + if (m_local_region_stack.back () != box_type::world () && ! m_inst->cell_inst ().bbox (m_box_convert).inside (m_local_region_stack.back ())) { + with_region = true; + } else { + // TODO: optimization potential: only report all_of_instance == false, if not entirely within the complex region + all_of_instance = m_local_complex_region_stack.empty (); + } + + RecursiveInstanceReceiver::new_inst_mode ni = RecursiveInstanceReceiver::NI_all; + if (receiver) { + ni = receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + } + + if (ni == RecursiveInstanceReceiver::NI_skip) { + m_inst_array = inst_array_iterator (); + } else if (ni == RecursiveInstanceReceiver::NI_single) { + // a singular iterator + m_inst_array = db::CellInstArray::iterator (m_inst->cell_inst ().front (), false); + } else if (with_region) { + m_inst_array = m_inst->cell_inst ().begin_touching (correct_box_overlapping (m_local_region_stack.back ()), m_box_convert); + } else { + m_inst_array = m_inst->cell_inst ().begin (); + } + + set_all_of_instance (all_of_instance); + + new_inst_member (receiver); + + if (! m_inst_array.at_end ()) { + break; + } else { + ++m_inst; + } + + } +} + +void +RecursiveInstanceIterator::new_inst_member (RecursiveInstanceReceiver *receiver) const +{ + if (! m_local_complex_region_stack.empty ()) { + + // skip instance array members not part of the complex region + while (! m_inst_array.at_end ()) { + db::Box ia_box = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); + if (! is_outside_complex_region (ia_box)) { + break; + } else { + ++m_inst_array; + } + } + + } + + while (! m_inst_array.at_end () && receiver) { + if (receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) { + break; + } else { + ++m_inst_array; + } + } +} + +RecursiveInstanceIterator::box_type +RecursiveInstanceIterator::correct_box_overlapping (const box_type &box) const +{ + if (! m_overlapping) { + return box; + } else if (box.empty () || box == box_type::world ()) { + return box; + } else if (box.width () < 2 || box.height () < 2) { + return box; + } else { + return box.enlarged (box_type::vector_type (-1, -1)); + } +} + +bool +RecursiveInstanceIterator::is_outside_complex_region (const box_type &box) const +{ + if (m_overlapping) { + return m_local_complex_region_stack.back ().begin_overlapping (box, db::box_convert ()).at_end (); + } else { + return m_local_complex_region_stack.back ().begin_touching (box, db::box_convert ()).at_end (); + } +} + +bool +RecursiveInstanceIterator::is_child_inactive (db::cell_index_type new_child) const +{ + bool inactive = is_inactive (); + if (! m_start.empty () && m_start.find (new_child) != m_start.end ()) { + inactive = false; + } else if (! m_stop.empty () && m_stop.find (new_child) != m_stop.end ()) { + inactive = true; + } + return inactive; +} + +void +RecursiveInstanceIterator::push (RecursiveInstanceReceiver *receiver) +{ + // force reset so we can validate with a receiver + reset (); + + receiver->begin (this); + + try { + + validate (receiver); + + while (! at_end ()) { + next (receiver); + } + + receiver->end (this); + + } catch (...) { + + receiver->end (this); + throw; + + } +} + +} + diff --git a/src/db/db/dbRecursiveInstanceIterator.h b/src/db/db/dbRecursiveInstanceIterator.h new file mode 100644 index 000000000..9d94d84d1 --- /dev/null +++ b/src/db/db/dbRecursiveInstanceIterator.h @@ -0,0 +1,660 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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_dbRecursiveInstanceIterator +#define HDR_dbRecursiveInstanceIterator + +#include "dbCommon.h" + +#include "dbLayout.h" +#include "dbInstElement.h" +#include "tlAssert.h" + +#include +#include +#include + +namespace db +{ + +class Region; +class RecursiveInstanceReceiver; + +/** + * @brief An iterator delivering shapes that touch or overlap the given region recursively + * + * The iterator can be constructed from a layout, a cell and a region. + * It simplifies retrieval of instances from a geometrical region while considering + * subcells as well. + * Some options can be specified, i.e. the level to which to look into or which cells + * to select. + * + * The general iteration scheme is iterating is top-down and breadth-first. + * + * While the iterator delivers instances, it will first deliver the instances of cells + * and then the instances of cells inside cells whose instances have been delivered already. + * No differentiation is made for leaf or non-leaf cells. + */ +class DB_PUBLIC RecursiveInstanceIterator +{ +public: + typedef db::Layout layout_type; + typedef db::Box box_type; + typedef db::Region region_type; + typedef db::Cell cell_type; + typedef db::Cell::touching_iterator inst_iterator; + typedef db::CellInstArray::iterator inst_array_iterator; + typedef db::Instances::overlapping_iterator overlapping_instance_iterator; + typedef db::Instances::touching_iterator touching_instance_iterator; + typedef db::Instance instance_type; + typedef db::ICplxTrans cplx_trans_type; + typedef db::box_tree, 20, 20> box_tree_type; + + /** + * @brief Default constructor + */ + RecursiveInstanceIterator (); + + /** + * @brief Copy constructor + */ + RecursiveInstanceIterator (const RecursiveInstanceIterator &d); + + /** + * @brief Assignment + */ + RecursiveInstanceIterator &operator= (const RecursiveInstanceIterator &d); + + /** + * @brief Standard constructor + * + * @param layout The layout from which to get the cell hierarchy + * @param cell The starting cell + * @param region The region from which to select the instances + * @param overlapping Specify overlapping mode + * + * By default the iterator operates in touching mode - i.e. instances that touch the given region + * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers instances that + * overlap the given region by at least one database unit. + * The cell instances are selected according to their overall bounding box. + */ + RecursiveInstanceIterator (const layout_type &layout, const cell_type &cell, const box_type ®ion, bool overlapping = false); + + /** + * @brief Standard constructor + * + * @param layout The layout from which to get the cell hierarchy + * @param cell The starting cell + * @param region The complex region from which to select the shapes + * @param overlapping Specify overlapping mode + * + * By default the iterator operates in touching mode - i.e. instances that touch the given region + * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers instances that + * overlap the given region by at least one database unit. + * The cell instances are selected according to their overall bounding box. + * This version offers a complex search region instead of a simple box. + */ + RecursiveInstanceIterator (const layout_type &layout, const cell_type &cell, const region_type ®ion, bool overlapping = false); + + /** + * @brief Standard constructor for "overall" iteration + * + * This iterator delivers all instances recursively. + * + * @param layout The layout from which to get the cell hierarchy + * @param cell The starting cell + */ + RecursiveInstanceIterator (const layout_type &layout, const cell_type &cell); + + /** + * @brief Destructor + */ + ~RecursiveInstanceIterator (); + + /** + * @brief Specify the maximum hierarchy depth to look into + * + * A depth of 0 instructs the iterator to deliver only shapes from the initial cell. + * The depth must be specified before the shapes are being retrieved. + */ + void max_depth (int depth) + { + if (m_max_depth != depth) { + m_max_depth = depth; + m_needs_reinit = true; + } + } + + /** + * @brief Gets the maximum hierarchy depth to search for + */ + int max_depth () const + { + return m_max_depth; + } + + /** + * @brief Specify the minimum hierarchy depth to look into + * + * A depth of 0 instructs the iterator to deliver shapes from the top level. + * 1 instructs to deliver shapes from the first child level. + * The minimum depth must be specified before the shapes are being retrieved. + */ + void min_depth (int depth) + { + if (m_min_depth != depth) { + m_min_depth = depth; + m_needs_reinit = true; + } + } + + /** + * @brief Gets the minimum hierarchy depth to search for + */ + int min_depth () const + { + return m_min_depth; + } + + /** + * @brief Gets the layout + */ + const layout_type *layout () const + { + return mp_layout; + } + + /** + * @brief Gets the top cell + * + * The top cell is the cell with which the iterator was started + */ + const cell_type *top_cell () const + { + return mp_top_cell; + } + + /** + * @brief Gets the basic region the iterator is using (will be world if none is set) + * In addition to the basic region, a complex region may be defined that is further confining the + * search to a subregion of the basic region. + */ + const box_type ®ion () const + { + return m_region; + } + + /** + * @brief Returns true if a complex region is given + */ + bool has_complex_region () const + { + return mp_complex_region.get () != 0; + } + + /** + * @brief Gets the complex region the iterator is using + */ + const region_type &complex_region () const + { + tl_assert (mp_complex_region.get ()); + return *mp_complex_region; + } + + /** + * @brief Sets the region to a basic rectangle + * This will reset the iterator. + */ + void set_region (const box_type ®ion); + + /** + * @brief Sets a complex search region + * This will reset the iterator to the beginning. + */ + void set_region (const region_type ®ion); + + /** + * @brief Confines the search further to the given rectangle. + * This will reset the iterator and confine the search to the given rectangle + * in addition to any region or complex region already defined. + */ + void confine_region (const box_type ®ion); + + /** + * @brief Confines the search further to the given complex region. + * This will reset the iterator and confine the search to the given region + * in addition to any simple region or complex region already defined. + */ + void confine_region (const region_type ®ion); + + /** + * @brief Gets a flag indicating whether overlapping shapes are selected when a region is used + */ + bool overlapping () const + { + return m_overlapping; + } + + /** + * @brief Sets a flag indicating whether overlapping shapes are selected when a region is used + */ + void set_overlapping (bool f) + { + if (m_overlapping != f) { + m_overlapping = f; + m_needs_reinit = true; + } + } + + /** + * @brief Reset the iterator + */ + void reset () + { + m_needs_reinit = true; + } + + /** + * @brief Select cells + * + * If no specific cells have been selected before, this method will confine the selection + * to the given cells (plus their sub-hierarchy). + * If cells have been selected before, this will add the given cells to the selection. + */ + void select_cells (const std::set &cells); + + /** + * @brief Select all cells + * + * Makes all cells selected. After doing so, all unselect_cells calls + * will unselect only that specific cell without children. + */ + void select_all_cells (); + + /** + * @brief Unselect cells + * + * This method will remove the given cells (plus their sub-hierarchy) from the selection. + */ + void unselect_cells (const std::set &cells); + + /** + * @brief Unselect all cells + * + * Makes all cells unselected. After doing so, select_cells calls + * will select only that specific cell without children. + */ + void unselect_all_cells (); + + /** + * @brief Resets the selection + * + * This will reset all selections and unselections. + * After calling this methods, all select_cells will again select the cells + * including their children. + */ + void reset_selection (); + + /** + * @brief Returns the cells in the "enable" selection + * + * Cells in this set make the iterator become active, while cells in the + * disable selection make the iterator inactive. Only when active, the + * iterator will deliver shapes. + */ + const std::set &enables () const + { + return m_start; + } + + /** + * @brief Returns the cells in the "disable" selection + */ + const std::set &disables () const + { + return m_stop; + } + + /** + * @brief Get the current transformation by which the instances must be transformed into the initial cell + * + * The instances delivered are not transformed. Instead, this transformation must be applied to + * get the instance in the coordinate system of the top cell. + */ + const cplx_trans_type &trans () const + { + validate (0); + return m_trans; + } + + /** + * @brief Gets the current depth + * + * Returns the number of hierarchy levels we are below top level currently. + */ + unsigned int depth () const + { + validate (0); + return (unsigned int) m_trans_stack.size (); + } + + /** + * @brief Gets the current instance + * + * Returns the instance currently referred to by the recursive iterator. + * This instance is not transformed yet and is located in the current cell. + */ + instance_type instance () const + { + return *operator-> (); + } + + /** + * @brief Access operator + * + * The access operator is identical to the instance method. + */ + instance_type operator* () const + { + return *operator-> (); + } + + /** + * @brief Access (arrow) operator + * + * The access operator is identical to the instance method. + */ + const instance_type *operator-> () const + { + validate (0); + return m_inst.operator-> (); + } + + /** + * @brief End of iterator predicate + * + * Returns true, if the iterator is at the end of the sequence + */ + bool at_end () const; + + /** + * @brief Gets the current cell's index + */ + db::cell_index_type cell_index () const + { + return cell ()->cell_index (); + } + + /** + * @brief Gets the current cell's reference + */ + const cell_type *cell () const + { + validate (0); + size_t c = reinterpret_cast (mp_cell); + return reinterpret_cast (c - (c & size_t (3))); + } + + /** + * @brief Increments the iterator (operator version) + */ + RecursiveInstanceIterator &operator++() + { + next (0); + return *this; + } + + /** + * @brief Increments the iterator + */ + void next () + { + next (0); + } + + /** + * @brief Comparison of iterators - equality + */ + bool operator==(const RecursiveInstanceIterator &d) const + { + if (at_end () != d.at_end ()) { + return false; + } else if (at_end ()) { + return true; + } else { + return (*m_inst == *d.m_inst); + } + } + + /** + * @brief Comparison of iterators - inequality + */ + bool operator!=(const RecursiveInstanceIterator &d) const + { + return !operator==(d); + } + + /** + * @brief The instance path + */ + std::vector path () const; + + /** + * @brief Push-mode delivery + * + * This method will deliver all instances to the given receiver. + * In contrast to pull mode, this method allows tailoring the + * traversal of the hierarchy tree during iteration. + * For this purpose, the receiver has methods that receive + * events and to some extend may modify the traversal (e.g. + * return value of enter_cell). + * + * See RecursiveInstanceReceiver class for more details. + */ + void push (RecursiveInstanceReceiver *receiver); + + /** + * @brief Returns a value indicating whether the current cell is inactive (disabled) + */ + bool is_inactive () const + { + return (reinterpret_cast (mp_cell) & size_t (1)) != 0; + } + + /** + * @brief Returns a value indicating whether a new child cell of the current cell will be inactive + */ + bool is_child_inactive (db::cell_index_type new_child) const; + +private: + int m_max_depth; + int m_min_depth; + unsigned int m_shape_flags; + bool m_shape_inv_prop_sel; + bool m_overlapping; + std::set m_start, m_stop; + + const layout_type *mp_layout; + const cell_type *mp_top_cell; + + box_type m_region; + std::unique_ptr mp_complex_region; + db::box_convert m_box_convert; + + mutable inst_iterator m_inst; + mutable inst_array_iterator m_inst_array; + mutable std::map m_empty_cells_cache; + mutable const cell_type *mp_cell; + mutable cplx_trans_type m_trans; + mutable std::vector m_trans_stack; + mutable std::vector m_inst_iterators; + mutable std::vector m_inst_array_iterators; + mutable std::vector m_cells; + mutable std::vector m_local_complex_region_stack; + mutable std::vector m_local_region_stack; + mutable bool m_needs_reinit; + mutable size_t m_inst_quad_id; + mutable std::vector m_inst_quad_id_stack; + + void init (); + void init_region (const region_type ®ion); + void init_region (const box_type ®ion); + void skip_inst_iter_for_complex_region () const; + void validate (RecursiveInstanceReceiver *receiver) const; + void next (RecursiveInstanceReceiver *receiver); + void next_instance (RecursiveInstanceReceiver *receiver) const; + void new_inst (RecursiveInstanceReceiver *receiver) const; + void new_inst_member (RecursiveInstanceReceiver *receiver) const; + void new_cell (RecursiveInstanceReceiver *receiver) const; + void up (RecursiveInstanceReceiver *receiver) const; + void down (RecursiveInstanceReceiver *receiver) const; + + bool is_outside_complex_region (const box_type &box) const; + box_type correct_box_overlapping (const box_type &box) const; + + void set_inactive (bool a) const + { + size_t c = reinterpret_cast (mp_cell); + c -= (c & size_t (1)); + mp_cell = reinterpret_cast (c + (a ? 1 : 0)); + } + + bool is_all_of_instance () const + { + return (reinterpret_cast (mp_cell) & size_t (2)) != 0; + } + + void set_all_of_instance (bool a) const + { + size_t c = reinterpret_cast (mp_cell); + c -= (c & size_t (2)); + mp_cell = reinterpret_cast (c + (a ? 2 : 0)); + } +}; + +/** + * @brief A receiver interface for "push" mode + * + * In push mode, the iterator will deliver the instances and hierarchy transitions + * to this interface. See "RecursiveInstanceIterator::push" for details about this + * mode. + * + * The receiver receives events for the start of the delivery, on each cell + * entry and on each instance (followed by a cell entry). + */ +class DB_PUBLIC RecursiveInstanceReceiver +{ +public: + typedef RecursiveInstanceIterator::box_tree_type box_tree_type; + + /** + * @brief See new_inst for details. + */ + enum new_inst_mode { NI_all = 0, NI_single = 1, NI_skip = 2 }; + + /** + * @brief Constructor + */ + RecursiveInstanceReceiver () { } + + /** + * @brief Destructor + */ + virtual ~RecursiveInstanceReceiver () { } + + /** + * @brief Called once when the iterator begins pushing + */ + virtual void begin (const RecursiveInstanceIterator * /*iter*/) { } + + /** + * @brief Called once after the iterator pushed everything + */ + virtual void end (const RecursiveInstanceIterator * /*iter*/) { } + + /** + * @brief Enters a cell + * + * This method is called when the recursive shape iterator + * enters a new cell. It is not called for the top cell. When it is called, "iter->trans()" + * will already be updated. + * + * @param iter The iterator + * @param cell The cell which is entered + * @param region The clip box as seen from "cell" or db::Box::world if there is no clip box + * @param complex_region A complex clip region if one is supplied together with "region" + */ + virtual void enter_cell (const RecursiveInstanceIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } + + /** + * @brief Leaves the current cell + * + * This method is the counterpart for "enter_cell". It is called when traversal of "cell" ended. + */ + virtual void leave_cell (const RecursiveInstanceIterator * /*iter*/, const db::Cell * /*cell*/) { } + + /** + * @brief Enters a new instance + * + * This method is called before "enter_cell" and "new_inst_member" is called and will indicate the instance to follow. + * The sequence of events is + * + * new_inst(A) + * new_inst_member(A[0,0]) + * enter_cell(A) + * ... + * leave_cell(A) + * new_inst_member(A[1,0]) + * enter_cell(A) + * ... + * leave_cell(A) + * ... + * new_inst(B) + * ... + * + * The "all" parameter is true, if all instances of the array will be addressed. + * + * This method can return the following values: + * - NI_all: iterate all members through "new_inst_member" + * - NI_single: iterate a single member (the first one) + * - NI_skip: skips the whole array (not a single instance is iterated) + */ + virtual new_inst_mode new_inst (const RecursiveInstanceIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } + + /** + * @brief Enters a new array member of the instance + * + * See "new_inst" for a description. This method adds the "trans" parameter + * which holds the complex transformation for this particular instance of + * the array. + * + * "all" is true, if an instance array is iterated in "all" mode (see new_inst). + * + * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. + */ + virtual bool new_inst_member (const RecursiveInstanceIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } +}; + +} // namespace db + +#endif diff --git a/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc b/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc new file mode 100644 index 000000000..95bdb8028 --- /dev/null +++ b/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc @@ -0,0 +1,1252 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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 "dbRecursiveInstanceIterator.h" +#include "dbRegion.h" +#include "dbLayoutDiff.h" +#include "tlString.h" +#include "tlUnitTest.h" + +#include + +std::string collect(db::RecursiveInstanceIterator &s, const db::Layout &layout) +{ + std::string res; + while (! s.at_end ()) { + if (! res.empty ()) { + res += "/"; + } + if (s.cell ()) { + res += std::string ("[") + layout.cell_name (s.cell ()->cell_index ()) + "]"; + } else { + res += "[]"; + } + res += s->to_string (true); + ++s; + } + return res; +} + +std::string collect_with_copy(db::RecursiveInstanceIterator s, const db::Layout &layout) +{ + s.reset (); + return collect (s, layout); +} + +TEST(1) +{ + db::Manager m (true); + db::Layout g (&m); + g.insert_layer (0); + g.insert_layer (1); + g.insert_layer (2); + + db::Cell &c0 (g.cell (g.add_cell ())); + + db::RecursiveInstanceIterator idef; + EXPECT_EQ (idef.at_end (), true); + EXPECT_EQ (collect (idef, g), ""); + EXPECT_EQ (collect_with_copy (idef, g), ""); + + db::RecursiveInstanceIterator i00 (g, c0, db::Box (0, 0, 100, 100)); + EXPECT_EQ (collect (i00, g), ""); + EXPECT_EQ (collect_with_copy (i00, g), ""); + + db::Cell &c1 (g.cell (g.add_cell ())); + db::Cell &c2 (g.cell (g.add_cell ())); + db::Cell &c3 (g.cell (g.add_cell ())); + + db::RecursiveInstanceIterator i0 (g, c0, db::Box (0, 0, 100, 100)); + EXPECT_EQ (collect (i0, g), ""); + EXPECT_EQ (collect_with_copy (i0, g), ""); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (b); + c1.shapes (0).insert (b); + c2.shapes (0).insert (b); + c3.shapes (0).insert (b); + + c0.shapes (2).insert (b); + c0.shapes (2).insert (b.moved (db::Vector (50, 50))); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (100, -100)))); + c0.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (1))); + c2.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (db::Vector (1100, 0)))); + + std::string x; + + db::RecursiveInstanceIterator i1 (g, c0, db::Box (0, 0, 100, 100)); + x = collect(i1, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + x = collect_with_copy(i1, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + + db::RecursiveInstanceIterator i1_1inf (g, c0, db::Box (0, 0, 100, 100)); + i1_1inf.min_depth(1); + x = collect(i1_1inf, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + x = collect_with_copy(i1_1inf, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + + db::RecursiveInstanceIterator i1_11 (g, c0, db::Box (0, 0, 100, 100)); + i1_11.min_depth(1); + i1_11.max_depth(1); + x = collect(i1_11, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + x = collect_with_copy(i1_11, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + + db::RecursiveInstanceIterator i1_12 (g, c0, db::Box (0, 0, 100, 100)); + i1_12.min_depth(1); + i1_12.max_depth(2); + x = collect(i1_12, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + x = collect_with_copy(i1_12, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + + db::RecursiveInstanceIterator i1_22 (g, c0, db::Box (0, 0, 100, 100)); + i1_22.min_depth(2); + i1_22.max_depth(2); + x = collect(i1_22, g); + EXPECT_EQ (x, ""); + x = collect_with_copy(i1_22, g); + EXPECT_EQ (x, ""); + + db::RecursiveInstanceIterator i1o (g, c0, db::Box (0, 0, 100, 100), true); + x = collect(i1o, g); + EXPECT_EQ (x, ""); + x = collect_with_copy(i1o, g); + EXPECT_EQ (x, ""); + i1o = db::RecursiveInstanceIterator (g, c0, db::Box (0, 0, 100, 101), true); + x = collect(i1o, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0"); + x = collect_with_copy(i1o, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0"); + i1o = db::RecursiveInstanceIterator (g, c0, db::Box (0, 0, 101, 101), true); + x = collect(i1o, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + x = collect_with_copy(i1o, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100"); + + db::RecursiveInstanceIterator i2 (g, c0, db::Box (-100, 0, 100, 100)); + db::RecursiveInstanceIterator i2c = i2; + x = collect(i2, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i2, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect(i2c, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i2c, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + db::RecursiveInstanceIterator i2o (g, c0, db::Box (-100, 0, 100, 100), true); + x = collect(i2o, g); + EXPECT_EQ (x, ""); + x = collect_with_copy(i2o, g); + EXPECT_EQ (x, ""); + i2o = db::RecursiveInstanceIterator (g, c0, db::Box (-101, 0, 101, 101), true); + x = collect(i2o, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i2o, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + + db::RecursiveInstanceIterator i4 (g, c0, db::Box (-100, 0, 2000, 100)); + db::RecursiveInstanceIterator i4_copy (g, c0, db::Box (-100, 0, 2000, 100)); + i4.max_depth (0); + x = collect(i4, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i4, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + + EXPECT_EQ (i4 == i4, true); + EXPECT_EQ (i4 != i4, false); + EXPECT_EQ (i4 == i4_copy, false); + EXPECT_EQ (i4 != i4_copy, true); + i4 = i4_copy; + EXPECT_EQ (i4 == i4_copy, true); + EXPECT_EQ (i4 != i4_copy, false); + i4.max_depth (1); + x = collect(i4, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i4, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + + i4 = i4_copy; + x = collect(i4, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i4, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + + db::RecursiveInstanceIterator i5 (g, c0, db::Box::world ()); + x = collect(i5, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i5, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + + std::set cc; + db::RecursiveInstanceIterator ii; + + ii = db::RecursiveInstanceIterator (g, c0, db::Box::world ()); + cc.clear (); + cc.insert (c2.cell_index ()); + ii.unselect_all_cells (); + ii.select_cells (cc); + x = collect(ii, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0"); + x = collect_with_copy(ii, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0"); + ii.reset (); + x = collect(ii, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0"); + x = collect_with_copy(ii, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0"); + + ii.reset_selection (); + x = collect(ii, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(ii, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + + ii.reset_selection (); + cc.clear (); + cc.insert (c0.cell_index ()); + cc.insert (c2.cell_index ()); + ii.unselect_cells (cc); + cc.clear (); + cc.insert (c2.cell_index ()); + ii.select_cells (cc); + x = collect(ii, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0"); + x = collect_with_copy(ii, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0"); + + ii = db::RecursiveInstanceIterator (g, c0, db::Box::world ()); + ii.unselect_all_cells (); + cc.clear (); + cc.insert (c0.cell_index ()); + ii.select_cells (cc); + x = collect(ii, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(ii, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + +} + +#if 0 +TEST(1a) +{ + db::Manager m (true); + db::Layout g (&m); + g.insert_layer (0); + g.insert_layer (1); + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + db::Cell &c2 (g.cell (g.add_cell ())); + db::Cell &c3 (g.cell (g.add_cell ())); + + db::Box b (0, 100, 1000, 1200); + c1.shapes (0).insert (b); + c2.shapes (0).insert (b); + c3.shapes (0).insert (b); + + db::Box bb (1, 101, 1001, 1201); + c2.shapes (1).insert (bb); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (100, -100)))); + c0.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (1))); + c2.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (db::Vector (1100, 0)))); + + std::string x; + + db::RecursiveInstanceIterator i0 (g, c0, 0, db::Box ()); + x = collect_with_copy(i0, g); + EXPECT_EQ (x, ""); + x = collect(i0, g); + EXPECT_EQ (x, ""); + + db::RecursiveInstanceIterator i1 (g, c0, 0, db::Box (0, 0, 100, 100)); + x = collect_with_copy(i1, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)"); + x = collect(i1, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)"); + + db::RecursiveInstanceIterator i1o (g, c0, 0, db::Box (0, 0, 100, 100), true); + x = collect_with_copy(i1o, g); + EXPECT_EQ (x, ""); + x = collect(i1o, g); + EXPECT_EQ (x, ""); + i1o = db::RecursiveInstanceIterator (g, c0, 0, db::Box (0, 0, 100, 101), true); + x = collect_with_copy(i1o, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)"); + x = collect(i1o, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)"); + i1o = db::RecursiveInstanceIterator (g, c0, 0, db::Box (0, 0, 101, 101), true); + x = collect_with_copy(i1o, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)"); + x = collect(i1o, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)"); + + db::RecursiveInstanceIterator i2 (g, c0, 0, db::Box (-100, 0, 100, 100)); + x = collect_with_copy(i2, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + x = collect(i2, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + db::RecursiveInstanceIterator i2o (g, c0, 0, db::Box (-100, 0, 100, 100), true); + x = collect_with_copy(i2o, g); + EXPECT_EQ (x, ""); + x = collect(i2o, g); + EXPECT_EQ (x, ""); + i2o = db::RecursiveInstanceIterator (g, c0, 0, db::Box (-101, 0, 101, 101), true); + x = collect_with_copy(i2o, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + x = collect(i2o, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + + db::RecursiveInstanceIterator i4 (g, c0, 0, db::Box (-100, 0, 2000, 100)); + db::RecursiveInstanceIterator i4_copy (g, c0, 0, db::Box (-100, 0, 2000, 100)); + i4.max_depth (0); + x = collect_with_copy(i4, g); + EXPECT_EQ (x, ""); + x = collect(i4, g); + EXPECT_EQ (x, ""); + + EXPECT_EQ (i4 == i4, true); + EXPECT_EQ (i4 != i4, false); + EXPECT_EQ (i4 == i4_copy, false); + EXPECT_EQ (i4 != i4_copy, true); + i4 = i4_copy; + EXPECT_EQ (i4 == i4_copy, true); + EXPECT_EQ (i4 != i4_copy, false); + i4.max_depth (1); + x = collect_with_copy(i4, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + x = collect(i4, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + + i4 = i4_copy; + x = collect_with_copy(i4, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + x = collect(i4, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + db::RecursiveInstanceIterator i5 (g, c0, 0, db::Box::world ()); + x = collect_with_copy(i5, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + x = collect(i5, g); + EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i5.set_layer (1); + x = collect_with_copy(i5, g); + EXPECT_EQ (x, "[$3](101,1;1101,1101)"); + x = collect(i5, g); + EXPECT_EQ (x, "[$3](101,1;1101,1101)"); + + std::set ll; + + db::RecursiveInstanceIterator i5a (g, c0, ll, db::Box::world ()); + x = collect_with_copy(i5a, g, true); + EXPECT_EQ (x, ""); + x = collect(i5a, g, true); + EXPECT_EQ (x, ""); + + ll.insert (0); + db::RecursiveInstanceIterator i5b (g, c0, ll, db::Box::world ()); + x = collect_with_copy(i5b, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + x = collect(i5b, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + + ll.insert (1); + db::RecursiveInstanceIterator i5c (g, c0, ll, db::Box::world ()); + db::RecursiveInstanceIterator i5cc = i5c; + x = collect_with_copy(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + x = collect(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + x = collect_with_copy(i5cc, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + x = collect(i5cc, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + + std::vector ll_new; + ll_new.push_back (0); + i5c.set_layers (ll_new); + x = collect_with_copy(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + x = collect(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); +} + +TEST(1b) +{ + db::Manager m (true); + db::Layout g (&m); + g.insert_layer (0); + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + + db::Box b; + b = db::Box (0, 0, 2000, 2000000); + c1.shapes (0).insert (b); + b = db::Box (1998000, 0, 2000000, 2000000); + c1.shapes (0).insert (b); + b = db::Box (0, 0, 2000000, 2000); + c1.shapes (0).insert (b); + b = db::Box (0, 1998000, 2000000, 2000000); + c1.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + + db::RecursiveInstanceIterator i (g, c0, 0, db::Box (1000000, 1000000, 10001000, 10001000)); + std::string x; + x = collect_with_copy(i, g); + EXPECT_EQ (x, "[$2](1998000,0;2000000,2000000)/[$2](0,1998000;2000000,2000000)/[$2](1998000,0;2000000,2000000)/[$2](0,1998000;2000000,2000000)"); + x = collect(i, g); + EXPECT_EQ (x, "[$2](1998000,0;2000000,2000000)/[$2](0,1998000;2000000,2000000)/[$2](1998000,0;2000000,2000000)/[$2](0,1998000;2000000,2000000)"); + + db::RecursiveInstanceIterator i2 (g, c0, 0, db::Box (1000000, 1000000, 1001000, 1001000)); + x = collect_with_copy(i2, g); + EXPECT_EQ (x, ""); + x = collect(i2, g); + EXPECT_EQ (x, ""); +} + +TEST(2) +{ + db::Manager m (true); + db::Layout g (&m); + g.insert_layer(0); + + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + db::Cell &c2 (g.cell (g.add_cell ())); + + db::Box b (1000, -500, 2000, 500); + c2.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt, db::Vector (0, 6000), db::Vector (6000, 0), 2, 2)); + c1.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), tt, db::Vector (0, 2000), db::Vector (3000, 1000), 2, 2)); + + std::string x; + + db::RecursiveInstanceIterator i0 (g, c0, 0, db::Box ()); + x = collect(i0, g); + EXPECT_EQ (x, ""); + + db::RecursiveInstanceIterator i (g, c0, 0, db::Box::world ()); + x = collect_with_copy(i, g); + EXPECT_EQ (x, "[$3](1000,-500;2000,500)/[$3](1000,1500;2000,2500)/[$3](4000,500;5000,1500)/[$3](4000,2500;5000,3500)/[$3](1000,5500;2000,6500)/[$3](1000,7500;2000,8500)/[$3](4000,6500;5000,7500)/[$3](4000,8500;5000,9500)/[$3](7000,-500;8000,500)/[$3](7000,1500;8000,2500)/[$3](10000,500;11000,1500)/[$3](10000,2500;11000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)/[$3](10000,8500;11000,9500)"); + x = collect(i, g); + EXPECT_EQ (x, "[$3](1000,-500;2000,500)/[$3](1000,1500;2000,2500)/[$3](4000,500;5000,1500)/[$3](4000,2500;5000,3500)/[$3](1000,5500;2000,6500)/[$3](1000,7500;2000,8500)/[$3](4000,6500;5000,7500)/[$3](4000,8500;5000,9500)/[$3](7000,-500;8000,500)/[$3](7000,1500;8000,2500)/[$3](10000,500;11000,1500)/[$3](10000,2500;11000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)/[$3](10000,8500;11000,9500)"); + + db::RecursiveInstanceIterator i2 (g, c0, 0, db::Box (3400, 3450, 5600, 6500)); + x = collect_with_copy(i2, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](4000,6500;5000,7500)"); + x = collect(i2, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](4000,6500;5000,7500)"); + + db::RecursiveInstanceIterator i3 (g, c0, 0, db::Box (6650, 5300, 10000, 7850)); + x = collect_with_copy(i3, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)"); + x = collect(i3, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)"); + + db::RecursiveInstanceIterator i2o (g, c0, 0, db::Box (3400, 3450, 5600, 6500), true); + x = collect_with_copy(i2o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + x = collect(i2o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + + db::RecursiveInstanceIterator i3o (g, c0, 0, db::Box (6650, 5300, 10000, 7850), true); + x = collect_with_copy(i3o, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + x = collect(i3o, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); +} + +TEST(3) +{ + db::Manager m (true); + db::Layout g (&m); + g.insert_layer(0); + + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + db::Cell &c2 (g.cell (g.add_cell ())); + + db::Box b (1000, -500, 2000, 500); + c2.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt, db::Vector (0, 6000), db::Vector (6000, 0), 2, 2)); + c1.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), tt, db::Vector (0, 2000), db::Vector (3000, 1000), 2, 2)); + + std::string x; + + db::RecursiveInstanceIterator i (g, c0, 0, db::Box::world ()); + x = collect_with_copy(i, g); + EXPECT_EQ (x, "[$3](1000,-500;2000,500)/[$3](1000,1500;2000,2500)/[$3](4000,500;5000,1500)/[$3](4000,2500;5000,3500)/[$3](1000,5500;2000,6500)/[$3](1000,7500;2000,8500)/[$3](4000,6500;5000,7500)/[$3](4000,8500;5000,9500)/[$3](7000,-500;8000,500)/[$3](7000,1500;8000,2500)/[$3](10000,500;11000,1500)/[$3](10000,2500;11000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)/[$3](10000,8500;11000,9500)"); + x = collect(i, g); + EXPECT_EQ (x, "[$3](1000,-500;2000,500)/[$3](1000,1500;2000,2500)/[$3](4000,500;5000,1500)/[$3](4000,2500;5000,3500)/[$3](1000,5500;2000,6500)/[$3](1000,7500;2000,8500)/[$3](4000,6500;5000,7500)/[$3](4000,8500;5000,9500)/[$3](7000,-500;8000,500)/[$3](7000,1500;8000,2500)/[$3](10000,500;11000,1500)/[$3](10000,2500;11000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)/[$3](10000,8500;11000,9500)"); + + db::RecursiveInstanceIterator i2 (g, c0, 0, db::Region (db::Box (3400, 3450, 5600, 6500))); + EXPECT_EQ (i2.has_complex_region (), false); + EXPECT_EQ (i2.region ().to_string (), "(3400,3450;5600,6500)"); + x = collect_with_copy(i2, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](4000,6500;5000,7500)"); + x = collect(i2, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](4000,6500;5000,7500)"); + + db::RecursiveInstanceIterator i3 (g, c0, 0, db::Region (db::Box (6650, 5300, 10000, 7850))); + x = collect_with_copy(i3, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)"); + x = collect(i3, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)"); + + db::Region rr; + rr.insert (db::Box (3400, 3450, 5600, 6500)); + rr.insert (db::Box (6650, 5300, 10000, 7850)); + + db::RecursiveInstanceIterator i23 (g, c0, 0, rr); + x = collect_with_copy(i23, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](4000,6500;5000,7500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)"); + x = collect(i23, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](4000,6500;5000,7500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)/[$3](10000,6500;11000,7500)"); + + db::RecursiveInstanceIterator i2o (g, c0, 0, db::Region (db::Box (3400, 3450, 5600, 6500)), true); + x = collect_with_copy(i2o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + x = collect(i2o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + + db::RecursiveInstanceIterator i3o (g, c0, 0, db::Region (db::Box (6650, 5300, 10000, 7850)), true); + x = collect_with_copy(i3o, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + x = collect(i3o, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + + db::Region rro; + rro.insert (db::Box (3400, 3450, 5600, 6500)); + rro.insert (db::Box (6650, 5300, 10000, 7850)); + + db::RecursiveInstanceIterator i23o (g, c0, 0, rro, true); + EXPECT_EQ (i23o.has_complex_region (), true); + EXPECT_EQ (i23o.complex_region ().to_string (), "(3400,3450;3400,6500;5600,6500;5600,3450);(6650,5300;6650,7850;10000,7850;10000,5300)"); + + db::RecursiveInstanceIterator i23ocopy = i23o; + + x = collect_with_copy(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + x = collect(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + + x = collect_with_copy (i23ocopy, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + x = collect (i23ocopy, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + + // reset + i23o.reset (); + x = collect_with_copy (i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + x = collect (i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + + // copy constructor + i23ocopy = i23o; + i23ocopy.reset (); + x = collect_with_copy (i23ocopy, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + x = collect (i23ocopy, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + + // setting of region + + db::Region rg; + i23o.set_region (rg); + x = collect_with_copy(i23o, g); + EXPECT_EQ (x, ""); + x = collect(i23o, g); + EXPECT_EQ (x, ""); + + rg.insert (db::Box (3400, 3450, 5600, 6500)); + rg.insert (db::Box (16650, 5300, 20000, 7850)); + + i23o.set_region (rg); + x = collect_with_copy(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + x = collect(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + + i23o.set_region (db::Box (6650, 5300, 10000, 7850)); + x = collect_with_copy(i23o, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + x = collect(i23o, g); + EXPECT_EQ (x, "[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + + // region confinement + + i23o.confine_region (db::Box (3400, 3450, 5600, 6500)); + x = collect_with_copy(i23o, g); + EXPECT_EQ (x, ""); + x = collect(i23o, g); + EXPECT_EQ (x, ""); + + i23o.set_region (rro); + i23o.confine_region (db::Box (3400, 3450, 5600, 6500)); + x = collect_with_copy(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + x = collect(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + + i23o.set_region (db::Box (3400, 3450, 5600, 6500)); + i23o.confine_region (rro); + x = collect_with_copy(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + x = collect(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)"); + + i23o.set_region (rro); + i23o.confine_region (rro); + x = collect_with_copy(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); + x = collect(i23o, g); + EXPECT_EQ (x, "[$3](4000,2500;5000,3500)/[$3](7000,5500;8000,6500)/[$3](7000,7500;8000,8500)"); +} + +static db::Layout boxes2layout (const std::set &boxes) +{ + db::Layout l; + l.insert_layer(0, db::LayerProperties (1, 0)); + db::Cell &top (l.cell (l.add_cell ())); + + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + top.shapes (0).insert (*b); + } + + return l; +} + +class FlatPusher + : public db::RecursiveShapeReceiver +{ +public: + FlatPusher (std::set *boxes) : mp_boxes (boxes) { } + + void shape (const db::RecursiveInstanceIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + mp_boxes->insert (trans * shape.bbox ()); + } + +private: + std::set *mp_boxes; +}; + +TEST(4) +{ + // Big fun + + db::Manager m (true); + db::Layout g (&m); + g.insert_layer(0); + + db::Cell &c0 (g.cell (g.add_cell ())); + + std::set boxes; + + for (int i = 0; i < 100000; ++i) { + + int x = rand () % 10000; + int y = rand () % 10000; + db::Box box (x, y, x + 10, y + 10); + + boxes.insert (box); + + c0.shapes (0).insert (box); + + } + + db::Box search_box (2500, 2500, 7500, 7500); + + std::set selected_boxes; + std::set selected_boxes2; + + for (db::RecursiveInstanceIterator iter = db::RecursiveInstanceIterator (g, c0, 0, search_box, true); !iter.at_end (); ++iter) { + selected_boxes.insert (iter->bbox ()); + } + + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.overlaps (*b)) { + selected_boxes2.insert (*b); + } + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveInstanceIterator (g, c0, 0, search_box, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + db::Box search_box2 (500, 500, 1000, 1000); + + selected_boxes.clear (); + selected_boxes2.clear (); + + db::Region reg; + reg.insert (search_box); + reg.insert (search_box2); + + for (db::RecursiveInstanceIterator iter = db::RecursiveInstanceIterator (g, c0, 0, reg, true); !iter.at_end (); ++iter) { + selected_boxes.insert (iter->bbox ()); + } + + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.overlaps (*b) || search_box2.overlaps (*b)) { + selected_boxes2.insert (*b); + } + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveInstanceIterator (g, c0, 0, reg, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); +} + +TEST(5) +{ + // Big fun with cells + + db::Manager m (true); + db::Layout g (&m); + g.insert_layer(0); + + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + + db::Box basic_box (0, 0, 10, 10); + c1.shapes (0).insert (basic_box); + + std::set boxes; + + for (int i = 0; i < 100000; ++i) { + + int x = rand () % 10000; + int y = rand () % 10000; + + boxes.insert (basic_box.moved (db::Vector (x, y))); + + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (x, y)))); + + } + + db::Box search_box (2500, 2500, 7500, 7500); + + std::set selected_boxes; + std::set selected_boxes2; + + for (db::RecursiveInstanceIterator iter = db::RecursiveInstanceIterator (g, c0, 0, search_box, true); !iter.at_end (); ++iter) { + selected_boxes.insert (iter.trans () * iter->bbox ()); + } + + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.overlaps (*b)) { + selected_boxes2.insert (*b); + } + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveInstanceIterator (g, c0, 0, search_box, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + db::Box search_box2 (500, 500, 1000, 1000); + + selected_boxes.clear (); + selected_boxes2.clear (); + + db::Region reg; + reg.insert (search_box); + reg.insert (search_box2); + + for (db::RecursiveInstanceIterator iter = db::RecursiveInstanceIterator (g, c0, 0, reg, true); !iter.at_end (); ++iter) { + selected_boxes.insert (iter.trans () * iter->bbox ()); + } + + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.overlaps (*b) || search_box2.overlaps (*b)) { + selected_boxes2.insert (*b); + } + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveInstanceIterator (g, c0, 0, reg, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); +} + +class LoggingReceiver + : public db::RecursiveShapeReceiver +{ +public: + LoggingReceiver () { } + + const std::string &text () const { return m_text; } + + virtual void begin (const db::RecursiveInstanceIterator * /*iter*/) { m_text += "begin\n"; } + virtual void end (const db::RecursiveInstanceIterator * /*iter*/) { m_text += "end\n"; } + + virtual void enter_cell (const db::RecursiveInstanceIterator *iter, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + m_text += std::string ("enter_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; + } + + virtual void leave_cell (const db::RecursiveInstanceIterator *iter, const db::Cell *cell) + { + m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; + } + + virtual new_inst_mode new_inst (const db::RecursiveInstanceIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + { + m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); + if (all) { + m_text += ",all"; + } + m_text += ")\n"; + return NI_all; + } + + virtual bool new_inst_member (const db::RecursiveInstanceIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + { + m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans); + if (all) { + m_text += ",all"; + } + m_text += ")\n"; + return true; + } + + virtual void shape (const db::RecursiveInstanceIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + m_text += "shape(" + shape.to_string () + "," + tl::to_string (trans) + ")\n"; + } + +private: + std::string m_text; +}; + +class ReceiverRejectingACellInstanceArray + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } + + virtual new_inst_mode new_inst (const db::RecursiveInstanceIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + { + LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; + } + +private: + db::cell_index_type m_rejected; +}; + +class ReceiverRejectingACellInstanceArrayExceptOne + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { } + + virtual new_inst_mode new_inst (const db::RecursiveInstanceIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + { + LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; + } + +private: + db::cell_index_type m_rejected; +}; + +class ReceiverRejectingACellInstance + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } + + virtual bool new_inst_member (const db::RecursiveInstanceIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) + { + LoggingReceiver::new_inst_member (iter, inst, trans, region, complex_region, all); + return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; + } + +private: + db::cell_index_type m_rejected; + db::ICplxTrans m_trans_rejected; +}; + +// Push mode with cells +TEST(10) +{ + db::Manager m (true); + db::Layout g (&m); + g.insert_layer(0); + + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + db::Cell &c2 (g.cell (g.add_cell ())); + + db::Box b (1000, -500, 2000, 500); + c2.shapes (0).insert (b); + c0.shapes (0).insert (b.moved (db::Vector (-1000, 500))); + c0.shapes (0).insert (b.moved (db::Vector (-2000, 500))); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt, db::Vector (0, 6000), db::Vector (6000, 0), 2, 2)); + c1.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), tt, db::Vector (0, 2000), db::Vector (3000, 1000), 2, 2)); + + LoggingReceiver lr1; + db::RecursiveInstanceIterator i1 (g, c0, 0); + i1.push (&lr1); + + EXPECT_EQ (lr1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + // It's a bit weird to have shape events after new_inst_member, but remember, new_inst_member is a query callback, not an event. + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstanceArray rr1 (c2.cell_index ()); + db::RecursiveInstanceIterator ir1 (g, c0, 0); + ir1.push (&rr1); + + EXPECT_EQ (rr1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstanceArrayExceptOne rs1 (c2.cell_index ()); + db::RecursiveInstanceIterator is1 (g, c0, 0); + is1.push (&rs1); + + EXPECT_EQ (rs1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstance rri1 (c2.cell_index (), db::ICplxTrans ()); + db::RecursiveInstanceIterator iri1 (g, c0, 0); + iri1.push (&rri1); + + EXPECT_EQ (rri1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" // -> skipped + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACellInstanceArray rr2 (c1.cell_index ()); + db::RecursiveInstanceIterator ir2 (g, c0, 0); + ir2.push (&rr2); + + EXPECT_EQ (rr2.text (), + "begin\n" + "new_inst($2,all)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" + "end\n" + ); + + LoggingReceiver lr2; + db::RecursiveInstanceIterator i2 (g, c0, 0, db::Box (0, 0, 5000, 5000)); + i2.push (&lr2); + + EXPECT_EQ (lr2.text (), + "begin\n" + "new_inst($2)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); +} +#endif diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index bfef1084f..cc61b571b 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1023,6 +1023,8 @@ TEST(10) db::Box b (1000, -500, 2000, 500); c2.shapes (0).insert (b); + c0.shapes (0).insert (b.moved (db::Vector (-1000, 500))); + c0.shapes (0).insert (b.moved (db::Vector (-2000, 500))); db::Trans tt; c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt, db::Vector (0, 6000), db::Vector (6000, 0), 2, 2)); @@ -1036,6 +1038,9 @@ TEST(10) "begin\n" "new_inst($2,all)\n" "new_inst_member($2,r0 *1 0,0,all)\n" + // It's a bit weird to have shape events after new_inst_member, but remember, new_inst_member is a query callback, not an event. + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "new_inst_member($3,r0 *1 0,0,all)\n" @@ -1126,6 +1131,8 @@ TEST(10) "begin\n" "new_inst($2,all)\n" "new_inst_member($2,r0 *1 0,0,all)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" @@ -1152,6 +1159,8 @@ TEST(10) "begin\n" "new_inst($2,all)\n" "new_inst_member($2,r0 *1 0,0,all)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "new_inst_member($3,r0 *1 0,0,all)\n" @@ -1194,6 +1203,8 @@ TEST(10) "begin\n" "new_inst($2,all)\n" "new_inst_member($2,r0 *1 0,0,all)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "new_inst_member($3,r0 *1 0,0,all)\n" // -> skipped @@ -1271,6 +1282,8 @@ TEST(10) EXPECT_EQ (rr2.text (), "begin\n" "new_inst($2,all)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" "end\n" ); @@ -1282,6 +1295,8 @@ TEST(10) "begin\n" "new_inst($2)\n" "new_inst_member($2,r0 *1 0,0)\n" + "shape(box (0,0;1000,1000),r0 *1 0,0)\n" + "shape(box (-1000,0;0,1000),r0 *1 0,0)\n" "enter_cell($2)\n" "new_inst($3)\n" "new_inst_member($3,r0 *1 0,0)\n" diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index ee48d7549..3af34f474 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ dbCompoundOperationTests.cc \ + dbRecursiveInstanceIteratorTests.cc \ dbRegionUtilsTests.cc \ dbUtilsTests.cc \ dbWriterTools.cc \