diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 16f9df099..bd6371526 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 \ @@ -114,6 +115,7 @@ SOURCES = \ gsiDeclDbPoint.cc \ gsiDeclDbPolygon.cc \ gsiDeclDbReader.cc \ + gsiDeclDbRecursiveInstanceIterator.cc \ gsiDeclDbRecursiveShapeIterator.cc \ gsiDeclDbRegion.cc \ gsiDeclDbShape.cc \ @@ -269,6 +271,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..c4782ad8d --- /dev/null +++ b/src/db/db/dbRecursiveInstanceIterator.cc @@ -0,0 +1,755 @@ + +/* + + 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_all_targets = d.m_all_targets; + m_targets = d.m_targets; + + m_max_depth = d.m_max_depth; + m_min_depth = d.m_min_depth; + 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_needs_reinit = false; + m_inst_quad_id = 0; + m_all_targets = true; +} + +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_inst_quad_id = 0; + mp_cell = 0; + m_all_targets = true; +} + +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; +} + +void +RecursiveInstanceIterator::enable_all_targets () +{ + if (! m_all_targets) { + m_all_targets = true; + m_targets.clear (); + m_needs_reinit = true; + } +} + +void +RecursiveInstanceIterator::set_targets (const std::set &tgt) +{ + if (m_all_targets || m_targets != tgt) { + m_targets = tgt; + m_all_targets = false; + 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) { + + if (! m_all_targets) { + m_target_tree.clear (); + mp_top_cell->collect_called_cells (m_target_tree); + } + + 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; + + } +} + +const RecursiveInstanceIterator::instance_element_type * +RecursiveInstanceIterator::operator-> () const +{ + validate (0); + m_combined_instance = db::InstElement (*m_inst, m_inst_array); + return &m_combined_instance; +} + +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_array; + if (! m_inst_array.at_end ()) { + new_inst_member (receiver); + } else { + ++m_inst; + new_inst (receiver); + next_instance (receiver); + } + } +} + +bool +RecursiveInstanceIterator::needs_visit () const +{ + return int (m_inst_iterators.size ()) >= m_min_depth && ! is_inactive () && (m_all_targets || m_targets.find (m_inst->cell_index ()) != m_targets.end ()); +} + +void +RecursiveInstanceIterator::next_instance (RecursiveInstanceReceiver *receiver) const +{ + while (true) { + + if (! m_inst.at_end ()) { + + if (int (m_inst_iterators.size ()) < m_max_depth && (m_all_targets || m_target_tree.find (m_inst->cell_index ()) != m_target_tree.end ())) { + down (receiver); + } + + } else { + + if (! m_inst_iterators.empty ()) { + // no more instances: up and next instance + up (receiver); + } else { + break; + } + + } + + if (! m_inst.at_end ()) { + if (! needs_visit ()) { + ++m_inst_array; + if (! m_inst_array.at_end ()) { + new_inst_member (receiver); + } else { + ++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); + } + + 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..8b2eabc50 --- /dev/null +++ b/src/db/db/dbRecursiveInstanceIterator.h @@ -0,0 +1,713 @@ + +/* + + 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 depth-first and child instances before parent instances. + */ +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::InstElement instance_element_type; + typedef db::ICplxTrans cplx_trans_type; + typedef instance_element_type value_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 instances from the initial cell. + * A higher depth instructs the iterator to look deeper. + * The depth must be specified before the instances 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 instance from the top level and below. + * 1 instructs to deliver instance from the first child level. + * The minimum depth must be specified before the instances 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 instances are selected when a region is used + */ + bool overlapping () const + { + return m_overlapping; + } + + /** + * @brief Sets a flag indicating whether overlapping instances 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 Gets the selected target cells + * + * Only instances of cells in the targets set are reported. + * By default the iterator is configured to deliver all instances. + * By using "set_targets" with a set of cell indexes, the reporting + * can be confined to certain cells only. To enable all-cell reporting + * use "enable_all_targets". + * + * "all_targets_enabled" can be used to check which mode is used. + */ + const std::set &targets () const + { + return m_targets; + } + + /** + * @brief Gets a flags indicating whether all targets are selected + * See \targets for more details. + */ + bool all_targets_enabled () const + { + return m_all_targets; + } + + /** + * @brief Selects all target cells + * See \targets for more details. + */ + void enable_all_targets (); + + /** + * @brief Selects the given targets + * + * This will reset the "all_targets" flag to false. + * See \targets for more details. + */ + void set_targets (const std::set &set_targets); + + /** + * @brief Select cells + * + * Cell selection allows confining the hierarchy traversal to subtrees of the + * hierarchy tree. This happens by "selecting" and "unselecting" cells in the traversal path. + * "selected" cells will make iterator traverse the tree below this cell while + * "unselected" cells make the iterator ignore this cell. + * Cells which are neither selected nor unselected will be traversed depending + * on their parent's state. They are traversed if their parents are and are not traversed + * if their parents are not. + * + * 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. + * + * See \select_cells for more details. + */ + void select_all_cells (); + + /** + * @brief Unselect cells + * + * This method will remove the given cells (plus their sub-hierarchy) from the selection. + * + * See \select_cells for more details. + */ + 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. + * + * See \select_cells for more details. + */ + 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. + * + * See \select_cells for more details. + */ + 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_element_type instance () const + { + return *operator-> (); + } + + /** + * @brief Access operator + * + * The access operator is identical to the instance method. + */ + instance_element_type operator* () const + { + return *operator-> (); + } + + /** + * @brief Access (arrow) operator + * + * The access operator is identical to the instance method. + */ + const instance_element_type *operator-> () const; + + /** + * @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; + bool m_overlapping; + std::set m_start, m_stop; + std::set m_targets; + bool m_all_targets; + + 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 instance_element_type m_combined_instance; + 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; + mutable std::set m_target_tree; + + 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); + bool needs_visit () const; + 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/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index e11e5c2b8..de891d776 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -38,6 +38,8 @@ #include "dbCellMapping.h" #include "dbPCellDeclaration.h" #include "dbSaveLayoutOptions.h" +#include "dbRecursiveShapeIterator.h" +#include "dbRecursiveInstanceIterator.h" #include "dbWriter.h" #include "dbHash.h" #include "tlStream.h" @@ -1204,6 +1206,56 @@ begin_shapes_rec_overlapping_um (const db::Cell *cell, unsigned int layer, db::D return db::RecursiveShapeIterator (*layout, *cell, layer, db::CplxTrans (layout->dbu ()).inverted () * region, true); } +static db::RecursiveInstanceIterator +begin_instances_rec (const db::Cell *cell) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell is not inside layout"))); + } + return db::RecursiveInstanceIterator (*layout, *cell); +} + +static db::RecursiveInstanceIterator +begin_instances_rec_touching (const db::Cell *cell, db::Box region) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell is not inside layout"))); + } + return db::RecursiveInstanceIterator (*layout, *cell, region, false); +} + +static db::RecursiveInstanceIterator +begin_instances_rec_touching_um (const db::Cell *cell, db::DBox region) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell is not inside layout"))); + } + return db::RecursiveInstanceIterator (*layout, *cell, db::CplxTrans (layout->dbu ()).inverted () * region, false); +} + +static db::RecursiveInstanceIterator +begin_instances_rec_overlapping (const db::Cell *cell, db::Box region) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell is not inside layout"))); + } + return db::RecursiveInstanceIterator (*layout, *cell, region, true); +} + +static db::RecursiveInstanceIterator +begin_instances_rec_overlapping_um (const db::Cell *cell, db::DBox region) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell is not inside layout"))); + } + return db::RecursiveInstanceIterator (*layout, *cell, db::CplxTrans (layout->dbu ()).inverted () * region, true); +} + static void copy_shapes2 (db::Cell *cell, const db::Cell &source_cell, const db::LayerMapping &layer_mapping) { cell->copy_shapes (source_cell, layer_mapping); @@ -1833,6 +1885,54 @@ Class decl_Cell ("db", "Cell", "\n" "This variant has been added in version 0.25.\n" ) + + gsi::method_ext ("begin_instances_rec", &begin_instances_rec, + "@brief Delivers a recursive instance iterator for the instances below the cell\n" + "@return A suitable iterator\n" + "\n" + "For details see the description of the \\RecursiveInstanceIterator class.\n" + "\n" + "This method has been added in version 0.27.\n" + ) + + gsi::method_ext ("begin_instances_rec_touching", &begin_instances_rec_touching, gsi::arg ("region"), + "@brief Delivers a recursive instance iterator for the instances below the cell\n" + "@param region The search region\n" + "@return A suitable iterator\n" + "\n" + "For details see the description of the \\RecursiveInstanceIterator class.\n" + "This version gives an iterator delivering instances whose bounding box touches the given region.\n" + "\n" + "This method has been added in version 0.27.\n" + ) + + gsi::method_ext ("begin_instances_rec_touching", &begin_instances_rec_touching_um, gsi::arg ("region"), + "@brief Delivers a recursive instance iterator for the instances below the cell using a region search, with the region given in micrometer units\n" + "@param region The search region as \\DBox object in micrometer units\n" + "@return A suitable iterator\n" + "\n" + "For details see the description of the \\RecursiveInstanceIterator class.\n" + "This version gives an iterator delivering instances whose bounding box touches the given region.\n" + "\n" + "This variant has been added in version 0.27.\n" + ) + + gsi::method_ext ("begin_instances_rec_overlapping", &begin_instances_rec_overlapping, gsi::arg ("region"), + "@brief Delivers a recursive instance iterator for the instances below the cell using a region search\n" + "@param region The search region\n" + "@return A suitable iterator\n" + "\n" + "For details see the description of the \\RecursiveInstanceIterator class.\n" + "This version gives an iterator delivering instances whose bounding box overlaps the given region.\n" + "\n" + "This method has been added in version 0.27.\n" + ) + + gsi::method_ext ("begin_instances_rec_overlapping", &begin_instances_rec_overlapping_um, gsi::arg ("region"), + "@brief Delivers a recursive instance iterator for the instances below the cell using a region search, with the region given in micrometer units\n" + "@param region The search region as \\DBox object in micrometer units\n" + "@return A suitable iterator\n" + "\n" + "For details see the description of the \\RecursiveInstanceIterator class.\n" + "This version gives an iterator delivering instances whose bounding box overlaps the given region.\n" + "\n" + "This variant has been added in version 0.27.\n" + ) + gsi::method_ext ("copy_shapes", ©_shapes1, gsi::arg ("source_cell"), "@brief Copies the shapes from the given cell into this cell\n" "@param source_cell The cell from where to copy shapes\n" diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index d08a06a2d..b950970b5 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -1716,9 +1716,11 @@ Class decl_Layout ("db", "Layout", "For details see the description of the \\RecursiveShapeIterator class.\n" "This version is convenience overload which takes a cell object instead of a cell index.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec instead.\n" + "\n" "This method has been added in version 0.24.\n" ) + - gsi::method_ext ("begin_shapes", &begin_shapes, gsi::arg ("cell_index"), gsi::arg ("layer"), + gsi::method_ext ("#begin_shapes", &begin_shapes, gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer\n" "@param cell_index The index of the initial (top) cell\n" "@param layer The layer from which to get the shapes\n" @@ -1726,9 +1728,11 @@ Class decl_Layout ("db", "Layout", "\n" "For details see the description of the \\RecursiveShapeIterator class.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec instead.\n" + "\n" "This method has been added in version 0.18.\n" ) + - gsi::method_ext ("begin_shapes_touching", &begin_shapes_touching, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), + gsi::method_ext ("#begin_shapes_touching", &begin_shapes_touching, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer using a region search\n" "@param cell_index The index of the starting cell\n" "@param layer The layer from which to get the shapes\n" @@ -1738,9 +1742,11 @@ Class decl_Layout ("db", "Layout", "For details see the description of the \\RecursiveShapeIterator class.\n" "This version gives an iterator delivering shapes whose bounding box touches the given region.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec_touching instead.\n" + "\n" "This method has been added in version 0.18.\n" ) + - gsi::method_ext ("begin_shapes_touching", &begin_shapes_touching2, gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("region"), + gsi::method_ext ("#begin_shapes_touching", &begin_shapes_touching2, gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer using a region search\n" "@param cell The cell object for the starting cell\n" "@param layer The layer from which to get the shapes\n" @@ -1751,9 +1757,11 @@ Class decl_Layout ("db", "Layout", "This version gives an iterator delivering shapes whose bounding box touches the given region.\n" "It is convenience overload which takes a cell object instead of a cell index.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec_touching instead.\n" + "\n" "This method has been added in version 0.24.\n" ) + - gsi::method_ext ("begin_shapes_overlapping", &begin_shapes_overlapping, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), + gsi::method_ext ("#begin_shapes_overlapping", &begin_shapes_overlapping, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer using a region search\n" "@param cell_index The index of the starting cell\n" "@param layer The layer from which to get the shapes\n" @@ -1763,9 +1771,11 @@ Class decl_Layout ("db", "Layout", "For details see the description of the \\RecursiveShapeIterator class.\n" "This version gives an iterator delivering shapes whose bounding box overlaps the given region.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec_overlapping instead.\n" + "\n" "This method has been added in version 0.18.\n" ) + - gsi::method_ext ("begin_shapes_overlapping", &begin_shapes_overlapping2, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), + gsi::method_ext ("#begin_shapes_overlapping", &begin_shapes_overlapping2, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer using a region search\n" "@param cell The cell object for the starting cell\n" "@param layer The layer from which to get the shapes\n" @@ -1776,9 +1786,11 @@ Class decl_Layout ("db", "Layout", "This version gives an iterator delivering shapes whose bounding box overlaps the given region.\n" "It is convenience overload which takes a cell object instead of a cell index.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec_overlapping instead.\n" + "\n" "This method has been added in version 0.24.\n" ) + - gsi::method_ext ("begin_shapes_touching", &begin_shapes_touching_um, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), + gsi::method_ext ("#begin_shapes_touching", &begin_shapes_touching_um, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer using a region search, the region given in micrometer units\n" "@param cell_index The index of the starting cell\n" "@param layer The layer from which to get the shapes\n" @@ -1788,9 +1800,11 @@ Class decl_Layout ("db", "Layout", "For details see the description of the \\RecursiveShapeIterator class.\n" "This version gives an iterator delivering shapes whose bounding box touches the given region.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec_touching instead.\n" + "\n" "This variant has been added in version 0.25.\n" ) + - gsi::method_ext ("begin_shapes_touching", &begin_shapes_touching2_um, gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("region"), + gsi::method_ext ("#begin_shapes_touching", &begin_shapes_touching2_um, gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer using a region search, the region given in micrometer units\n" "@param cell The cell object for the starting cell\n" "@param layer The layer from which to get the shapes\n" @@ -1801,9 +1815,11 @@ Class decl_Layout ("db", "Layout", "This version gives an iterator delivering shapes whose bounding box touches the given region.\n" "It is convenience overload which takes a cell object instead of a cell index.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec_touching instead.\n" + "\n" "This variant has been added in version 0.25.\n" ) + - gsi::method_ext ("begin_shapes_overlapping", &begin_shapes_overlapping_um, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), + gsi::method_ext ("#begin_shapes_overlapping", &begin_shapes_overlapping_um, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer using a region search, the region given in micrometer units\n" "@param cell_index The index of the starting cell\n" "@param layer The layer from which to get the shapes\n" @@ -1813,9 +1829,11 @@ Class decl_Layout ("db", "Layout", "For details see the description of the \\RecursiveShapeIterator class.\n" "This version gives an iterator delivering shapes whose bounding box overlaps the given region.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec_overlapping instead.\n" + "\n" "This variant has been added in version 0.25.\n" ) + - gsi::method_ext ("begin_shapes_overlapping", &begin_shapes_overlapping2_um, gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("region"), + gsi::method_ext ("#begin_shapes_overlapping", &begin_shapes_overlapping2_um, gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Delivers a recursive shape iterator for the shapes below the given cell on the given layer using a region search, the region given in micrometer units\n" "@param cell The cell object for the starting cell\n" "@param layer The layer from which to get the shapes\n" @@ -1826,6 +1844,8 @@ Class decl_Layout ("db", "Layout", "This version gives an iterator delivering shapes whose bounding box overlaps the given region.\n" "It is convenience overload which takes a cell object instead of a cell index.\n" "\n" + "This method is deprecated. Use \\Cell#begin_shapes_rec_overlapping instead.\n" + "\n" "This variant has been added in version 0.25.\n" ) + gsi::method_ext ("#write", &write_options2, gsi::arg ("filename"), gsi::arg ("gzip"), gsi::arg ("options"), diff --git a/src/db/db/gsiDeclDbRecursiveInstanceIterator.cc b/src/db/db/gsiDeclDbRecursiveInstanceIterator.cc new file mode 100644 index 000000000..3a2fdf2df --- /dev/null +++ b/src/db/db/gsiDeclDbRecursiveInstanceIterator.cc @@ -0,0 +1,558 @@ + +/* + + 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 "gsiDecl.h" +#include "dbRecursiveInstanceIterator.h" +#include "dbRegion.h" + +#include "tlGlobPattern.h" + +namespace gsi +{ + +// --------------------------------------------------------------- +// db::RecursiveInstanceIterator binding + +static db::RecursiveInstanceIterator *new_si1 (const db::Layout &layout, const db::Cell &cell) +{ + return new db::RecursiveInstanceIterator (layout, cell); +} + +static db::RecursiveInstanceIterator *new_si2 (const db::Layout &layout, const db::Cell &cell, const db::Box &box, bool overlapping) +{ + return new db::RecursiveInstanceIterator (layout, cell, box, overlapping); +} + +static db::RecursiveInstanceIterator *new_si2a (const db::Layout &layout, const db::Cell &cell, const db::Region ®ion, bool overlapping) +{ + return new db::RecursiveInstanceIterator (layout, cell, region, overlapping); +} + +static db::DCplxTrans si_dtrans (const db::RecursiveInstanceIterator *r) +{ + const db::Layout *ly = r->layout (); + tl_assert (ly != 0); + return db::CplxTrans (ly->dbu ()) * r->trans () * db::VCplxTrans (1.0 / ly->dbu ()); +} + +static void set_targets1 (db::RecursiveInstanceIterator *r, const std::vector &cells) +{ + std::set cc; + cc.insert (cells.begin (), cells.end ()); + r->set_targets (cc); +} + +static db::DCplxTrans inst_dtrans (const db::RecursiveInstanceIterator *r) +{ + const db::Layout *ly = r->layout (); + tl_assert (ly != 0); + return db::CplxTrans (ly->dbu ()) * (*r)->complex_trans () * db::VCplxTrans (1.0 / ly->dbu ()); +} + +static db::ICplxTrans inst_trans (const db::RecursiveInstanceIterator *r) +{ + return (*r)->complex_trans (); +} + +static db::Cell *inst_cell (const db::RecursiveInstanceIterator *r) +{ + const db::Layout *ly = r->layout (); + tl_assert (ly != 0); + return const_cast (&ly->cell ((*r)->inst_ptr.cell_index ())); +} + +static void set_targets2 (db::RecursiveInstanceIterator *r, const std::string &pattern) +{ + tl::GlobPattern p (pattern); + std::set cc; + for (db::Layout::const_iterator ci = r->layout ()->begin (); ci != r->layout ()->end (); ++ci) { + if (p.match (r->layout ()->cell_name (ci->cell_index ()))) { + cc.insert (ci->cell_index ()); + } + } + + r->set_targets (cc); +} + +static void select_cells1 (db::RecursiveInstanceIterator *r, const std::vector &cells) +{ + std::set cc; + cc.insert (cells.begin (), cells.end ()); + r->select_cells (cc); +} + +static void select_cells2 (db::RecursiveInstanceIterator *r, const std::string &pattern) +{ + tl::GlobPattern p (pattern); + std::set cc; + for (db::Layout::const_iterator ci = r->layout ()->begin (); ci != r->layout ()->end (); ++ci) { + if (p.match (r->layout ()->cell_name (ci->cell_index ()))) { + cc.insert (ci->cell_index ()); + } + } + + r->select_cells (cc); +} + +static void unselect_cells1 (db::RecursiveInstanceIterator *r, const std::vector &cells) +{ + std::set cc; + cc.insert (cells.begin (), cells.end ()); + r->unselect_cells (cc); +} + +static void unselect_cells2 (db::RecursiveInstanceIterator *r, const std::string &pattern) +{ + tl::GlobPattern p (pattern); + std::set cc; + for (db::Layout::const_iterator ci = r->layout ()->begin (); ci != r->layout ()->end (); ++ci) { + if (p.match (r->layout ()->cell_name (ci->cell_index ()))) { + cc.insert (ci->cell_index ()); + } + } + + r->unselect_cells (cc); +} + +static db::Region complex_region (const db::RecursiveInstanceIterator *iter) +{ + if (iter->has_complex_region ()) { + return iter->complex_region (); + } else { + return db::Region (iter->region ()); + } +} + +Class decl_RecursiveInstanceIterator ("db", "RecursiveInstanceIterator", + gsi::constructor ("new", &new_si1, gsi::arg ("layout"), gsi::arg ("cell"), + "@brief Creates a recursive instance iterator.\n" + "@param layout The layout which shall be iterated\n" + "@param cell The initial cell which shall be iterated (including its children)\n" + "@param layer The layer (index) from which the shapes are taken\n" + "\n" + "This constructor creates a new recursive instance iterator which delivers the instances of " + "the given cell plus its children.\n" + ) + + gsi::constructor ("new", &new_si2, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("box"), gsi::arg ("overlapping", false), + "@brief Creates a recursive instance iterator with a search region.\n" + "@param layout The layout which shall be iterated\n" + "@param cell The initial cell which shall be iterated (including its children)\n" + "@param box The search region\n" + "@param overlapping If set to true, instances overlapping the search region are reported, otherwise touching is sufficient\n" + "\n" + "This constructor creates a new recursive instance iterator which delivers the instances of " + "the given cell plus its children.\n" + "\n" + "The search is confined to the region given by the \"box\" parameter. If \"overlapping\" is true, instances whose " + "bounding box is overlapping the search region are reported. If \"overlapping\" is false, instances whose " + "bounding box touches the search region are reported. The bounding box of instances is measured taking all layers " + "of the target cell into account.\n" + ) + + gsi::constructor ("new", &new_si2a, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("region"), gsi::arg ("overlapping"), + "@brief Creates a recursive instance iterator with a search region.\n" + "@param layout The layout which shall be iterated\n" + "@param cell The initial cell which shall be iterated (including its children)\n" + "@param region The search region\n" + "@param overlapping If set to true, instances overlapping the search region are reported, otherwise touching is sufficient\n" + "\n" + "This constructor creates a new recursive instance iterator which delivers the instances of " + "the given cell plus its children.\n" + "\n" + "The search is confined to the region given by the \"region\" parameter. The region needs to be a rectilinear region.\n" + "If \"overlapping\" is true, instances whose " + "bounding box is overlapping the search region are reported. If \"overlapping\" is false, instances whose " + "bounding box touches the search region are reported. The bounding box of instances is measured taking all layers " + "of the target cell into account.\n" + ) + + gsi::method ("max_depth=", (void (db::RecursiveInstanceIterator::*) (int)) &db::RecursiveInstanceIterator::max_depth, gsi::arg ("depth"), + "@brief Specifies the maximum hierarchy depth to look into\n" + "\n" + "A depth of 0 instructs the iterator to deliver only instances from the initial cell.\n" + "A higher depth instructs the iterator to look deeper.\n" + "The depth must be specified before the instances are being retrieved.\n" + ) + + gsi::method ("max_depth", (int (db::RecursiveInstanceIterator::*) () const) &db::RecursiveInstanceIterator::max_depth, + "@brief Gets the maximum hierarchy depth\n" + "\n" + "See \\max_depth= for a description of that attribute.\n" + ) + + gsi::method ("min_depth=", (void (db::RecursiveInstanceIterator::*) (int)) &db::RecursiveInstanceIterator::min_depth, gsi::arg ("depth"), + "@brief Specifies the minimum hierarchy depth to look into\n" + "\n" + "A depth of 0 instructs the iterator to deliver instances from the top level.\n" + "1 instructs to deliver instances from the first child level.\n" + "The minimum depth must be specified before the instances are being retrieved.\n" + ) + + gsi::method ("min_depth", (int (db::RecursiveInstanceIterator::*) () const) &db::RecursiveInstanceIterator::min_depth, + "@brief Gets the minimum hierarchy depth\n" + "\n" + "See \\min_depth= for a description of that attribute.\n" + ) + + gsi::method ("reset", &db::RecursiveInstanceIterator::reset, + "@brief Resets the iterator to the initial state\n" + ) + + gsi::method ("reset_selection", &db::RecursiveInstanceIterator::reset_selection, + "@brief Resets the selection to the default state\n" + "\n" + "In the initial state, the top cell and its children are selected. Child cells can be switched on and off " + "together with their sub-hierarchy using \\select_cells and \\unselect_cells.\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method ("layout", &db::RecursiveInstanceIterator::layout, + "@brief Gets the layout this iterator is connected to\n" + ) + + gsi::method ("top_cell", &db::RecursiveInstanceIterator::top_cell, + "@brief Gets the top cell this iterator is connected to\n" + ) + + gsi::method ("region", &db::RecursiveInstanceIterator::region, + "@brief Gets the basic region that is iterator is using\n" + "The basic region is the overall box the region iterator iterates over. " + "There may be an additional complex region that confines the region iterator. " + "See \\complex_region for this attribute.\n" + ) + + gsi::method_ext ("complex_region", &complex_region, + "@brief Gets the complex region that is iterator is using\n" + "The complex region is the effective region (a \\Region object) that the " + "iterator is selecting from the layout. This region can be a single box " + "or a complex region.\n" + ) + + gsi::method ("region=", (void (db::RecursiveInstanceIterator::*)(const db::RecursiveInstanceIterator::box_type &)) &db::RecursiveInstanceIterator::set_region, gsi::arg ("box_region"), + "@brief Sets the rectangular region that is iterator is iterating over\n" + "See \\region for a description of this attribute.\n" + "Setting a simple region will reset the complex region to a rectangle and reset the iterator to " + "the beginning of the sequence." + ) + + gsi::method ("region=", (void (db::RecursiveInstanceIterator::*)(const db::RecursiveInstanceIterator::region_type &)) &db::RecursiveInstanceIterator::set_region, gsi::arg ("complex_region"), + "@brief Sets the complex region that is iterator is using\n" + "See \\complex_region for a description of this attribute. Setting the complex region will " + "reset the basic region (see \\region) to the bounding box of the complex region and " + "reset the iterator to the beginning of the sequence.\n" + ) + + gsi::method ("confine_region", (void (db::RecursiveInstanceIterator::*)(const db::RecursiveInstanceIterator::box_type &)) &db::RecursiveInstanceIterator::confine_region, gsi::arg ("box_region"), + "@brief Confines the region that is iterator is iterating over\n" + "This method is similar to setting the region (see \\region=), but will confine any region (complex or simple) already set. " + "Essentially it does a logical AND operation between the existing and given region. " + "Hence this method can only reduce a region, not extend it.\n" + ) + + gsi::method ("confine_region", (void (db::RecursiveInstanceIterator::*)(const db::RecursiveInstanceIterator::region_type &)) &db::RecursiveInstanceIterator::confine_region, gsi::arg ("complex_region"), + "@brief Confines the region that is iterator is iterating over\n" + "This method is similar to setting the region (see \\region=), but will confine any region (complex or simple) already set. " + "Essentially it does a logical AND operation between the existing and given region. " + "Hence this method can only reduce a region, not extend it.\n" + ) + + gsi::method ("overlapping?", &db::RecursiveInstanceIterator::overlapping, + "@brief Gets a flag indicating whether overlapping instances are selected when a region is used\n" + ) + + gsi::method ("overlapping=", &db::RecursiveInstanceIterator::set_overlapping, gsi::arg ("region"), + "@brief Sets a flag indicating whether overlapping instances are selected when a region is used\n" + "\n" + "If this flag is false, instances touching the search region are returned.\n" + ) + + gsi::method ("unselect_all_cells", &db::RecursiveInstanceIterator::unselect_all_cells, + "@brief Unselects all cells.\n" + "\n" + "This method will set the \"unselected\" mark on all cells. The effect is " + "that subsequent calls of \\select_cells will select only the specified cells, not " + "their children, because they are still unselected.\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method ("select_all_cells", &db::RecursiveInstanceIterator::select_all_cells, + "@brief Selects all cells.\n" + "\n" + "This method will set the \"selected\" mark on all cells. The effect is " + "that subsequent calls of \\unselect_cells will unselect only the specified cells, not " + "their children, because they are still unselected.\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method_ext ("unselect_cells", &unselect_cells1, gsi::arg ("cells"), + "@brief Unselects the given cells.\n" + "\n" + "This method will sets the \"unselected\" mark on the given cells. " + "That means that these cells or their child cells will not be visited, unless " + "they are marked as \"selected\" again with the \\select_cells method.\n" + "\n" + "The cells are given as a list of cell indexes.\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method_ext ("unselect_cells", &unselect_cells2, gsi::arg ("cells"), + "@brief Unselects the given cells.\n" + "\n" + "This method will sets the \"unselected\" mark on the given cells. " + "That means that these cells or their child cells will not be visited, unless " + "they are marked as \"selected\" again with the \\select_cells method.\n" + "\n" + "The cells are given as a glob pattern.\n" + "A glob pattern follows the syntax of " + "file names on the shell (i.e. \"A*\" are all cells starting with a letter \"A\").\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method_ext ("select_cells", &select_cells1, gsi::arg ("cells"), + "@brief Unselects the given cells.\n" + "\n" + "This method will sets the \"selected\" mark on the given cells. " + "That means that these cells or their child cells are visited, unless " + "they are marked as \"unselected\" again with the \\unselect_cells method.\n" + "\n" + "The cells are given as a list of cell indexes.\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method_ext ("select_cells", &select_cells2, gsi::arg ("cells"), + "@brief Unselects the given cells.\n" + "\n" + "This method will sets the \"selected\" mark on the given cells. " + "That means that these cells or their child cells are visited, unless " + "they are marked as \"unselected\" again with the \\unselect_cells method.\n" + "\n" + "The cells are given as a glob pattern.\n" + "A glob pattern follows the syntax of " + "file names on the shell (i.e. \"A*\" are all cells starting with a letter \"A\").\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method_ext ("targets=", &set_targets1, gsi::arg ("cells"), + "@brief Specifies the target cells.\n" + "\n" + "If target cells are specified, only instances of these cells are delivered. " + "This version takes a list of cell indexes for the targets. " + "By default, no target cell list is present and the instances of all cells " + "are delivered by the iterator. See \\all_targets_enabled? and \\enable_all_targets for " + "a description of this mode. Once a target list is specified, the iteration is " + "confined to the cells from this list." + "\n" + "The cells are given as a list of cell indexes.\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method_ext ("targets=", &set_targets2, gsi::arg ("cells"), + "@brief Specifies the target cells.\n" + "\n" + "If target cells are specified, only instances of these cells are delivered. " + "This version takes a cell list as a glob pattern. " + "A glob pattern follows the syntax of " + "file names on the shell (i.e. \"A*\" are all cells starting with a letter \"A\").\n" + "Use the curly-bracket notation to list different cells, e.g \"{A,B,C}\" for cells A, B and C.\n" + "\n" + "By default, no target cell list is present and the instances of all cells " + "are delivered by the iterator. See \\all_targets_enabled? and \\enable_all_targets for " + "a description of this mode. Once a target list is specified, the iteration is " + "confined to the cells from this list." + "\n" + "The cells are given as a list of cell indexes.\n" + "\n" + "This method will also reset the iterator.\n" + ) + + gsi::method ("targets", &db::RecursiveInstanceIterator::targets, + "@brief Gets the list of target cells\n" + "See \\targets= for a description of the target cell concept. " + "This method returns a list of cell indexes of the selected target cells." + ) + + gsi::method ("all_targets_enabled?", &db::RecursiveInstanceIterator::all_targets_enabled, + "@brief Gets a value indicating whether instances of all cells are reported\n" + "See \\targets= for a description of the target cell concept. " + ) + + gsi::method ("enable_all_targets", &db::RecursiveInstanceIterator::enable_all_targets, + "@brief Enables 'all targets' mode in which instances of all cells are reported\n" + "See \\targets= for a description of the target cell concept. " + ) + + gsi::method ("trans", &db::RecursiveInstanceIterator::trans, + "@brief Gets the accumulated transformation of the current instance parent cell to the top cell\n" + "\n" + "This transformation represents how the current instance is seen in the top cell.\n" + ) + + gsi::method_ext ("dtrans", &gsi::si_dtrans, + "@brief Gets the accumulated transformation of the current instance parent cell to the top cell\n" + "\n" + "This transformation represents how the current instance is seen in the top cell.\n" + "This version returns the micon-unit transformation.\n" + ) + + gsi::method ("at_end?", &db::RecursiveInstanceIterator::at_end, + "@brief End of iterator predicate\n" + "\n" + "Returns true, if the iterator is at the end of the sequence\n" + ) + + gsi::method ("cell", &db::RecursiveInstanceIterator::cell, + "@brief Gets the cell the current instance sits in\n" + ) + + gsi::method ("cell_index", &db::RecursiveInstanceIterator::cell_index, + "@brief Gets the index of the cell the current instance sits in\n" + "This is equivalent to 'cell.cell_index'." + ) + + gsi::method_ext ("inst_trans", &inst_trans, + "@brief Gets the integer-unit transformation of the current instance\n" + "This is the transformation of the current instance inside its parent.\n" + "'trans * inst_trans' gives the full transformation how the current cell is seen in the top cell.\n" + "See also \\inst_dtrans and \\inst_cell.\n" + ) + + gsi::method_ext ("inst_dtrans", &inst_dtrans, + "@brief Gets the micron-unit transformation of the current instance\n" + "This is the transformation of the current instance inside its parent.\n" + "'dtrans * inst_dtrans' gives the full micron-unit transformation how the current cell is seen in the top cell.\n" + "See also \\inst_trans and \\inst_cell.\n" + ) + + gsi::method_ext ("inst_cell", &inst_cell, + "@brief Gets the target cell of the current instance\n" + "This is the cell the current instance refers to. It is one of the \\targets if a target list is given.\n" + ) + + gsi::method ("current_inst_element", &db::RecursiveInstanceIterator::instance, + "@brief Gets the current instance\n" + "\n" + "This is the instance/array element the iterator currently refers to.\n" + "This is a \\InstElement object representing the current instance and the array element the iterator currently points at.\n" + "\n" + "See \\inst_trans, \\inst_dtrans and \\inst_cell for convenience methods to access the details of the current element.\n" + ) + + gsi::method ("next", (void (db::RecursiveInstanceIterator::*) ()) &db::RecursiveInstanceIterator::next, + "@brief Increments the iterator\n" + "This moves the iterator to the next instance inside the search scope." + ) + + gsi::method ("path", &db::RecursiveInstanceIterator::path, + "@brief Gets the instantatiation path of the instance addressed currently\n" + "\n" + "This attribute is a sequence of \\InstElement objects describing the cell instance path from the initial " + "cell to the current instance. The path is empty if the current instance is in the top cell.\n" + ) + + gsi::method ("==", &db::RecursiveInstanceIterator::operator==, gsi::arg ("other"), + "@brief Comparison of iterators - equality\n" + "\n" + "Two iterators are equal if they point to the same instance.\n" + ) + + gsi::method ("!=", &db::RecursiveInstanceIterator::operator!=, gsi::arg ("other"), + "@brief Comparison of iterators - inequality\n" + "\n" + "Two iterators are not equal if they do not point to the same instance.\n" + ), + "@brief An iterator delivering instances recursively\n" + "\n" + "The iterator can be obtained from a cell and optionally a region.\n" + "It simplifies retrieval of instances while considering\n" + "subcells as well.\n" + "Some options can be specified in addition, i.e. the hierarchy level to which to look into.\n" + "The search can be confined to instances of certain cells (see \\targets=) or to certain regions. " + "Subtrees can be selected for traversal or excluded from it (see \\select_cells).\n" + "\n" + "This is some sample code:\n" + "\n" + "@code\n" + "# prints the effective instances of cell \"A\" as seen from the initial cell \"cell\"\n" + "iter = cell.begin_instances_rec\n" + "iter.targets = \"A\"\n" + "while !iter.at_end?\n" + " puts \"Instance of #{iter.inst_cell.name} in #{cell.name}: \" + (iter.dtrans * iter.inst_dtrans).to_s\n" + " iter.next\n" + "end\n" + "@/code\n" + "\n" + "Here, a target cell is specified which confines the search to instances of this particular cell.\n" + "'iter.dtrans' gives us the accumulated transformation of all parents up to the top cell. " + "'iter.inst_dtrans' gives us the transformation from the current instance. " + "'iter.inst_cell' finally gives us the target cell of the current instance (which is always 'A' in our case).\n" + "\n" + "\\Cell offers three methods to get these iterators: begin_instances_rec, begin_instances_rec_touching and begin_instances_rec_overlapping.\n" + "\\Cell#begin_instances_rec will deliver a standard recursive instance iterator which starts from the given cell and iterates " + "over all child cells. \\Cell#begin_instances_rec_touching creates a RecursiveInstanceIterator which delivers the instances " + "whose bounding boxed touch the given search box. \\Layout#begin_instances_rec_overlapping gives an iterator which delivers all instances whose bounding box " + "overlaps the search box.\n" + "\n" + "A RecursiveInstanceIterator object can also be created directly, like this:\n" + "\n" + "@code\n" + "iter = RBA::RecursiveInstanceIterator::new(layout, cell [, options ])\n" + "@/code\n" + "\n" + "\"layout\" is the layout object, \"cell\" the \\Cell object of the initial cell.\n" + "\n" + "The recursive instance iterator can be confined to a maximum hierarchy depth. By using \\max_depth=, the " + "iterator will restrict the search depth to the given depth in the cell tree.\n" + "In the same way, the iterator can be configured to start from a certain hierarchy depth using \\min_depth=. " + "The hierarchy depth always applies to the parent of the instances iterated.\n" + "\n" + "In addition, the recursive instance iterator supports selection and exclusion of subtrees. For that purpose " + "it keeps flags per cell telling it for which cells to turn instance delivery on and off. The \\select_cells method " + "sets the \"start delivery\" flag while \\unselect_cells sets the \"stop delivery\" flag. In effect, using " + "\\unselect_cells will exclude that cell plus the subtree from delivery. Parts of that subtree can be " + "turned on again using \\select_cells. For the cells selected that way, the instances of these cells and their " + "child cells are delivered, even if their parent was unselected.\n" + "\n" + "To get instances from a specific cell, i.e. \"MACRO\" plus its child cells, unselect the top cell first " + "and the select the desired cell again:\n" + "\n" + "@code\n" + "# deliver all instances inside \"MACRO\" and the sub-hierarchy:\n" + "iter = RBA::RecursiveInstanceIterator::new(layout, cell)\n" + "iter.unselect_cells(cell.cell_index)\n" + "iter.select_cells(\"MACRO\")\n" + "...\n" + "@/code\n" + "\n" + "The \\unselect_all_cells and \\select_all_cells methods turn on the \"stop\" and \"start\" flag " + "for all cells respectively. If you use \\unselect_all_cells and use \\select_cells for a specific cell, " + "the iterator will deliver only the instances of the selected cell, not its children. Those are still " + "unselected by \\unselect_all_cells:\n" + "\n" + "@code\n" + "# deliver all instance inside \"MACRO\" but not of child cells:\n" + "iter = RBA::RecursiveInstanceIterator::new(layout, cell)\n" + "iter.unselect_all_cells\n" + "iter.select_cells(\"MACRO\")\n" + "...\n" + "@/code\n" + "\n" + "Cell selection is done using cell indexes or glob pattern. Glob pattern are equivalent to the usual " + "file name wildcards used on various command line shells. For example \"A*\" matches all cells starting with " + "an \"A\". The curly brace notation and character classes are supported as well. For example \"C{125,512}\" matches " + "\"C125\" and \"C512\" and \"[ABC]*\" matches all cells starting with an \"A\", a \"B\" or \"C\". \"[^ABC]*\" matches " + "all cells not starting with one of that letters.\n" + "\n" + "To confine instance iteration to instances of certain cells, use the \\targets feature:\n" + "\n" + "@code\n" + "# deliver all instance of \"INV1\":\n" + "iter = RBA::RecursiveInstanceIterator::new(layout, cell)\n" + "iter.targets = \"INV1\"\n" + "...\n" + "@/code\n" + "\n" + "Targets can be specified either as lists of cell indexes or through a glob pattern.\n" + "\n" + "Instances are always delivered depth-first with child instances before their parents. A default recursive instance " + "iterator will first deliver leaf cells, followed by the parent of these cells.\n" + "\n" + "When a search region is used, instances whose bounding box touch or overlap (depending on 'overlapping' flag) will " + "be reported. The instance bounding box taken as reference is computed using all layers of the layout.\n" + "\n" + "The iterator will deliver the individual elements of instance arrays, confined to the search region if one is given. " + "Consequently the return value (\\current_inst_element) is an \\InstElement " + "object which is basically a combination of an \\Instance object and information about the current array element.\n" + "\\inst_cell, \\inst_trans and \\inst_dtrans are methods provided for convenience to access the current array member's transformation " + "and the target cell of the current instance.\n" + "\n" + "The RecursiveInstanceIterator class has been introduced in version 0.27.\n" +); + +} diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index be01b271f..72070d1e7 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -123,89 +123,89 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS gsi::constructor ("new", &new_si1, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"), "@brief Creates a recursive, single-layer shape iterator.\n" "@param layout The layout which shall be iterated\n" - "@param cell The initial cell which shall be iterated (including it's children)\n" + "@param cell The initial cell which shall be iterated (including its children)\n" "@param layer The layer (index) from which the shapes are taken\n" "\n" "This constructor creates a new recursive shape iterator which delivers the shapes of " - "the given cell plus it's children from the layer given by the layer index in the \"layer\" parameter.\n" + "the given cell plus its children from the layer given by the layer index in the \"layer\" parameter.\n" "\n" "This constructor has been introduced in version 0.23.\n" ) + gsi::constructor ("new", &new_si2, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layers"), "@brief Creates a recursive, multi-layer shape iterator.\n" "@param layout The layout which shall be iterated\n" - "@param cell The initial cell which shall be iterated (including it's children)\n" + "@param cell The initial cell which shall be iterated (including its children)\n" "@param layers The layer indexes from which the shapes are taken\n" "\n" "This constructor creates a new recursive shape iterator which delivers the shapes of " - "the given cell plus it's children from the layers given by the layer indexes in the \"layers\" parameter.\n" + "the given cell plus its children from the layers given by the layer indexes in the \"layers\" parameter.\n" "While iterating use the \\layer method to retrieve the layer of the current shape.\n" "\n" "This constructor has been introduced in version 0.23.\n" ) + - gsi::constructor ("new", &new_si3, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("box"), gsi::arg ("overlapping"), + gsi::constructor ("new", &new_si3, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("box"), gsi::arg ("overlapping", false), "@brief Creates a recursive, single-layer shape iterator with a region.\n" "@param layout The layout which shall be iterated\n" - "@param cell The initial cell which shall be iterated (including it's children)\n" + "@param cell The initial cell which shall be iterated (including its children)\n" "@param layer The layer (index) from which the shapes are taken\n" "@param box The search region\n" "@param overlapping If set to true, shapes overlapping the search region are reported, otherwise touching is sufficient\n" "\n" "This constructor creates a new recursive shape iterator which delivers the shapes of " - "the given cell plus it's children from the layer given by the layer index in the \"layer\" parameter.\n" + "the given cell plus its children from the layer given by the layer index in the \"layer\" parameter.\n" "\n" "The search is confined to the region given by the \"box\" parameter. If \"overlapping\" is true, shapes whose " "bounding box is overlapping the search region are reported. If \"overlapping\" is false, shapes whose " "bounding box touches the search region are reported.\n" "\n" - "This constructor has been introduced in version 0.23.\n" + "This constructor has been introduced in version 0.23. The 'overlapping' parameter has been made optional in version 0.27.\n" ) + - gsi::constructor ("new", &new_si3a, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("region"), gsi::arg ("overlapping"), + gsi::constructor ("new", &new_si3a, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"), gsi::arg ("region"), gsi::arg ("overlapping", false), "@brief Creates a recursive, single-layer shape iterator with a region.\n" "@param layout The layout which shall be iterated\n" - "@param cell The initial cell which shall be iterated (including it's children)\n" + "@param cell The initial cell which shall be iterated (including its children)\n" "@param layer The layer (index) from which the shapes are taken\n" "@param region The search region\n" "@param overlapping If set to true, shapes overlapping the search region are reported, otherwise touching is sufficient\n" "\n" "This constructor creates a new recursive shape iterator which delivers the shapes of " - "the given cell plus it's children from the layer given by the layer index in the \"layer\" parameter.\n" + "the given cell plus its children from the layer given by the layer index in the \"layer\" parameter.\n" "\n" "The search is confined to the region given by the \"region\" parameter. The region needs to be a rectilinear region.\n" "If \"overlapping\" is true, shapes whose " "bounding box is overlapping the search region are reported. If \"overlapping\" is false, shapes whose " "bounding box touches the search region are reported.\n" "\n" - "This constructor has been introduced in version 0.25.\n" + "This constructor has been introduced in version 0.25. The 'overlapping' parameter has been made optional in version 0.27.\n" ) + - gsi::constructor ("new", &new_si4, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layers"), gsi::arg ("box"), gsi::arg ("overlapping"), + gsi::constructor ("new", &new_si4, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layers"), gsi::arg ("box"), gsi::arg ("overlapping", false), "@brief Creates a recursive, multi-layer shape iterator with a region.\n" "@param layout The layout which shall be iterated\n" - "@param cell The initial cell which shall be iterated (including it's children)\n" + "@param cell The initial cell which shall be iterated (including its children)\n" "@param layers The layer indexes from which the shapes are taken\n" "@param box The search region\n" "@param overlapping If set to true, shapes overlapping the search region are reported, otherwise touching is sufficient\n" "\n" "This constructor creates a new recursive shape iterator which delivers the shapes of " - "the given cell plus it's children from the layers given by the layer indexes in the \"layers\" parameter.\n" + "the given cell plus its children from the layers given by the layer indexes in the \"layers\" parameter.\n" "While iterating use the \\layer method to retrieve the layer of the current shape.\n" "\n" "The search is confined to the region given by the \"box\" parameter. If \"overlapping\" is true, shapes whose " "bounding box is overlapping the search region are reported. If \"overlapping\" is false, shapes whose " "bounding box touches the search region are reported.\n" "\n" - "This constructor has been introduced in version 0.23.\n" + "This constructor has been introduced in version 0.23. The 'overlapping' parameter has been made optional in version 0.27.\n" ) + - gsi::constructor ("new", &new_si4a, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layers"), gsi::arg ("region"), gsi::arg ("overlapping"), + gsi::constructor ("new", &new_si4a, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layers"), gsi::arg ("region"), gsi::arg ("overlapping", false), "@brief Creates a recursive, multi-layer shape iterator with a region.\n" "@param layout The layout which shall be iterated\n" - "@param cell The initial cell which shall be iterated (including it's children)\n" + "@param cell The initial cell which shall be iterated (including its children)\n" "@param layers The layer indexes from which the shapes are taken\n" "@param region The search region\n" "@param overlapping If set to true, shapes overlapping the search region are reported, otherwise touching is sufficient\n" "\n" "This constructor creates a new recursive shape iterator which delivers the shapes of " - "the given cell plus it's children from the layers given by the layer indexes in the \"layers\" parameter.\n" + "the given cell plus its children from the layers given by the layer indexes in the \"layers\" parameter.\n" "While iterating use the \\layer method to retrieve the layer of the current shape.\n" "\n" "The search is confined to the region given by the \"region\" parameter. The region needs to be a rectilinear region.\n" @@ -213,10 +213,10 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "bounding box is overlapping the search region are reported. If \"overlapping\" is false, shapes whose " "bounding box touches the search region are reported.\n" "\n" - "This constructor has been introduced in version 0.23.\n" + "This constructor has been introduced in version 0.23. The 'overlapping' parameter has been made optional in version 0.27.\n" ) + gsi::method ("max_depth=", (void (db::RecursiveShapeIterator::*) (int)) &db::RecursiveShapeIterator::max_depth, gsi::arg ("depth"), - "@brief Specify the maximum hierarchy depth to look into\n" + "@brief Specifies the maximum hierarchy depth to look into\n" "\n" "A depth of 0 instructs the iterator to deliver only shapes from the initial cell.\n" "The depth must be specified before the shapes are being retrieved.\n" @@ -229,7 +229,23 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This method has been introduced in version 0.23.\n" ) + - gsi::method ("reset", &db::RecursiveShapeIterator::reset, + gsi::method ("min_depth=", (void (db::RecursiveShapeIterator::*) (int)) &db::RecursiveShapeIterator::min_depth, gsi::arg ("depth"), + "@brief Specifies the minimum hierarchy depth to look into\n" + "\n" + "A depth of 0 instructs the iterator to deliver shapes from the top level.\n" + "1 instructs to deliver shapes from the first child level.\n" + "The minimum depth must be specified before the shapes are being retrieved.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method ("min_depth", (int (db::RecursiveShapeIterator::*) () const) &db::RecursiveShapeIterator::min_depth, + "@brief Gets the minimum hierarchy depth\n" + "\n" + "See \\min_depth= for a description of that attribute.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method ("reset", &db::RecursiveShapeIterator::reset, "@brief Resets the iterator to the initial state\n" "\n" "This method has been introduced in version 0.23.\n" @@ -237,7 +253,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS gsi::method ("reset_selection", &db::RecursiveShapeIterator::reset_selection, "@brief Resets the selection to the default state\n" "\n" - "In the initial state, the top cell and it's children are selected. Child cells can be switched on and off " + "In the initial state, the top cell and its children are selected. Child cells can be switched on and off " "together with their sub-hierarchy using \\select_cells and \\unselect_cells.\n" "\n" "This method will also reset the iterator.\n" @@ -436,7 +452,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "@brief Gets the current cell's index \n" ) + gsi::method ("next", (void (db::RecursiveShapeIterator::*) ()) &db::RecursiveShapeIterator::next, - "@brief Increment the iterator\n" + "@brief Increments the iterator\n" "This moves the iterator to the next shape inside the search scope." ) + gsi::method ("layer", &db::RecursiveShapeIterator::layer, @@ -462,12 +478,12 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "Two iterators are not equal if they do not point to the same shape.\n" ), - "@brief An iterator delivering shapes that touch or overlap the given region recursively\n" + "@brief An iterator delivering shapes recursively\n" "\n" - "The iterator can be obtained from a layout, specifying a starting cell, a layer and optionally a region.\n" + "The iterator can be obtained from a cell, a layer and optionally a region.\n" "It simplifies retrieval of shapes from a geometrical region while considering\n" "subcells as well.\n" - "Some options can be specified, i.e. the level to which to look into or\n" + "Some options can be specified in addition, i.e. the level to which to look into or\n" "shape classes and shape properties. The shapes are retrieved by using the \\shape method,\n" "\\next moves to the next shape and \\at_end tells, if the iterator has move shapes to deliver.\n" "\n" @@ -475,7 +491,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "@code\n" "# print the polygon-like objects as seen from the initial cell \"cell\"\n" - "iter = layout.begin_shapes(cell_index, layer)\n" + "iter = cell.begin_shapes_rec(layer)\n" "while !iter.at_end?\n" " if iter.shape.renders_polygon?\n" " polygon = iter.shape.polygon.transformed(iter.itrans)\n" @@ -485,10 +501,10 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "end\n" "@/code\n" "\n" - "\\Layout offers three methods to get these iterators: begin_shapes, begin_shapes_touching and begin_shapes_overlapping.\n" - "\\Layout#begin_shapes will deliver a standard recursive shape iterator which starts from the given cell and iterates " - "over all child cells. \\Layout#begin_shapes_touching delivers a RecursiveShapeIterator which delivers the shapes " - "whose bounding boxed touch the given search box. \\Layout#begin_shapes_overlapping delivers all shapes whose bounding box " + "\\Cell offers three methods to get these iterators: begin_shapes_rec, begin_shapes_rec_touching and begin_shapes_rec_overlapping.\n" + "\\Cell#begin_shapes_rec will deliver a standard recursive shape iterator which starts from the given cell and iterates " + "over all child cells. \\Cell#begin_shapes_rec_touching delivers a RecursiveShapeIterator which delivers the shapes " + "whose bounding boxed touch the given search box. \\Cell#begin_shapes_rec_overlapping delivers all shapes whose bounding box " "overlaps the search box.\n" "\n" "A RecursiveShapeIterator object can also be created explicitly. This allows some more options, i.e. using " @@ -510,7 +526,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "sets the \"start delivery\" flag while \\unselect_cells sets the \"stop delivery\" flag. In effect, using " "\\unselect_cells will exclude that cell plus the subtree from delivery. Parts of that subtree can be " "turned on again using \\select_cells. For the cells selected that way, the shapes of these cells and their " - "child cells are delivered, even if their parents was unselected.\n" + "child cells are delivered, even if their parent was unselected.\n" "\n" "To get shapes from a specific cell, i.e. \"MACRO\" plus its child cells, unselect the top cell first " "and the select the desired cell again:\n" diff --git a/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc b/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc new file mode 100644 index 000000000..a13dad263 --- /dev/null +++ b/src/db/unit_tests/dbRecursiveInstanceIteratorTests.cc @@ -0,0 +1,621 @@ + +/* + + 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->inst_ptr.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(0); + 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, 2000, 100)); + i1_11.min_depth(0); + i1_11.max_depth(0); + 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, 2000, 100)); + i1_12.min_depth(0); + i1_12.max_depth(1); + x = collect(i1_12, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100"); + x = collect_with_copy(i1_12, g); + EXPECT_EQ (x, "[$1]$2 r0 0,0/[$3]$4 r0 1100,0/[$1]$3 r0 100,-100"); + + db::RecursiveInstanceIterator i1_22 (g, c0, db::Box (0, 0, 2000, 100)); + i1_22.min_depth(1); + i1_22.max_depth(1); + x = collect(i1_22, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0"); + x = collect_with_copy(i1_22, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0"); + + 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::Region r; + r.insert (db::Box (-600, -100, -500, 0)); + r.insert (db::Box (1600, 0, 1700, 100)); + db::RecursiveInstanceIterator i2r (g, c0, r); + db::RecursiveInstanceIterator i2rc = i2r; + x = collect(i2r, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i2r, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect(i2rc, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + x = collect_with_copy(i2rc, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0/[$1]$3 r0 100,-100/[$1]$4 r90 0,0"); + db::RecursiveInstanceIterator i2ro (g, c0, r, true); + x = collect(i2ro, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0/[$1]$3 r0 100,-100"); + x = collect_with_copy(i2ro, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0/[$1]$3 r0 100,-100"); + + 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"); + + db::RecursiveInstanceIterator i1z (g, c0); + EXPECT_EQ (i1z.all_targets_enabled (), true); + std::set ct; + ct.insert (c3.cell_index ()); + i1z.set_targets (ct); + EXPECT_EQ (i1z.all_targets_enabled (), false); + EXPECT_EQ (i1z.targets () == ct, true); + i1z.enable_all_targets (); + EXPECT_EQ (i1z.all_targets_enabled (), true); + + i1z.set_targets (ct); + EXPECT_EQ (i1z.all_targets_enabled (), false); + + x = collect(i1z, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0/[$1]$4 r90 0,0"); + x = collect_with_copy(i1z, g); + EXPECT_EQ (x, "[$3]$4 r0 1100,0/[$1]$4 r90 0,0"); +} + +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; +} + +namespace { + + class FlatPusher + : public db::RecursiveInstanceReceiver + { + public: + FlatPusher (std::set *boxes) : mp_boxes (boxes) { } + + void enter_cell (const db::RecursiveInstanceIterator *iter, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + mp_boxes->insert (iter->trans () * cell->bbox ()); + } + + private: + std::set *mp_boxes; + }; + +} + +TEST(2) +{ + // 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, search_box, true); !iter.at_end (); ++iter) { + selected_boxes.insert (iter.trans () * iter->inst_ptr.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, 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, reg, true); !iter.at_end (); ++iter) { + selected_boxes.insert (iter.trans () * iter->bbox (db::box_convert (g))); + } + + 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, 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(3) +{ + // Big fun with cells - 2 hierarchy levels + + 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 basic_box (0, 0, 10, 10); + c2.shapes (0).insert (basic_box); + c1.insert (db::CellInstArray (c2.cell_index (), db::Trans (db::Vector (1, -1)))); + + std::set boxes; + + int nboxes = 100000; + for (int i = 0; i < nboxes; ++i) { + + int x, y; + + do { + x = rand () % 10000; + y = rand () % 10000; + } while (boxes.find (basic_box.moved (db::Vector (x + 1, y - 1))) != boxes.end ()); + + boxes.insert (basic_box.moved (db::Vector (x + 1, y - 1))); + + 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; + + db::RecursiveInstanceIterator iter = db::RecursiveInstanceIterator (g, c0, search_box, true); + std::set tc; + tc.insert (c2.cell_index ()); + iter.set_targets (tc); + int n = 0; + for ( ; !iter.at_end (); ++iter) { + ++n; + selected_boxes.insert (iter.trans () * iter->bbox (db::box_convert (g))); + } + + int nn = 0; + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.overlaps (*b)) { + ++nn; + selected_boxes2.insert (*b); + } + } + + EXPECT_EQ (n, nn); + + 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, 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, reg, true); !iter.at_end (); ++iter) { + selected_boxes.insert (iter.trans () * iter->bbox (db::box_convert (g))); + } + + 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, 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(4) +{ + // Big fun with cells - 2 hierarchy levels + touching mode + + 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 basic_box (0, 0, 10, 10); + c2.shapes (0).insert (basic_box); + c1.insert (db::CellInstArray (c2.cell_index (), db::Trans (db::Vector (1, -1)))); + + std::set boxes; + + int nboxes = 100000; + for (int i = 0; i < nboxes; ++i) { + + int x, y; + + do { + x = rand () % 10000; + y = rand () % 10000; + } while (boxes.find (basic_box.moved (db::Vector (x + 1, y - 1))) != boxes.end ()); + + boxes.insert (basic_box.moved (db::Vector (x + 1, y - 1))); + + 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; + + db::RecursiveInstanceIterator iter = db::RecursiveInstanceIterator (g, c0, search_box); + std::set tc; + tc.insert (c2.cell_index ()); + iter.set_targets (tc); + int n = 0; + for ( ; !iter.at_end (); ++iter) { + ++n; + selected_boxes.insert (iter.trans () * iter->bbox (db::box_convert (g))); + } + + int nn = 0; + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.touches (*b)) { + ++nn; + selected_boxes2.insert (*b); + } + } + + EXPECT_EQ (n, nn); + + 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, search_box).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, reg); !iter.at_end (); ++iter) { + selected_boxes.insert (iter.trans () * iter->bbox (db::box_convert (g))); + } + + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.touches (*b) || search_box2.touches (*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, reg).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); +} + diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index bfef1084f..cf559bb1f 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -723,20 +723,24 @@ static db::Layout boxes2layout (const std::set &boxes) return l; } -class FlatPusher - : public db::RecursiveShapeReceiver -{ -public: - FlatPusher (std::set *boxes) : mp_boxes (boxes) { } +namespace { - void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + class FlatPusher + : public db::RecursiveShapeReceiver { - mp_boxes->insert (trans * shape.bbox ()); - } + public: + FlatPusher (std::set *boxes) : mp_boxes (boxes) { } -private: - std::set *mp_boxes; -}; + void shape (const db::RecursiveShapeIterator * /*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) { @@ -1023,6 +1027,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 +1042,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 +1135,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 +1163,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 +1207,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 +1286,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 +1299,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 \ diff --git a/src/rba/unit_tests/rbaTests.cc b/src/rba/unit_tests/rbaTests.cc index 9ac96cbf9..7cdac9988 100644 --- a/src/rba/unit_tests/rbaTests.cc +++ b/src/rba/unit_tests/rbaTests.cc @@ -106,6 +106,8 @@ RUBYTEST (dbInstElementTest, "dbInstElementTest.rb") RUBYTEST (dbLayerMapping, "dbLayerMapping.rb") RUBYTEST (dbLibrary, "dbLibrary.rb") RUBYTEST (dbLayout, "dbLayout.rb") +RUBYTEST (dbRecursiveShapeIterator, "dbRecursiveShapeIterator.rb") +RUBYTEST (dbRecursiveInstanceIterator, "dbRecursiveInstanceIterator.rb") RUBYTEST (dbLayoutTest, "dbLayoutTest.rb") RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb") diff --git a/testdata/ruby/dbLayout.rb b/testdata/ruby/dbLayout.rb index 2d805dd7d..b9723def0 100644 --- a/testdata/ruby/dbLayout.rb +++ b/testdata/ruby/dbLayout.rb @@ -335,212 +335,6 @@ class DBLayout_TestClass < TestBase end - def test_5 - - # Recursive shape iterator tests - - l = RBA::Layout.new - l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) - l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) - c0 = l.cell(l.add_cell("c0")) - c1 = l.cell(l.add_cell("c1")) - c2 = l.cell(l.add_cell("c2")) - c3 = l.cell(l.add_cell("c3")) - - b = RBA::Box.new(0, 100, 1000, 1200) - c0.shapes(0).insert(b) - c1.shapes(0).insert(b) - c2.shapes(0).insert(b) - c3.shapes(0).insert(b) - - bb = RBA::Box.new(1, 101, 1001, 1201) - c3.shapes(1).insert(bb) - - tt = RBA::Trans.new - c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) - c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) - c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) - c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) - - i1 = l.begin_shapes_touching(c0.cell_index, 0, RBA::Box.new(0, 0, 100, 100)) - i1copy = i1.dup - assert_equal(i1copy.overlapping?, false) - assert_equal(collect(i1, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") - i1.reset - assert_equal(dcollect(i1, l), "[c0](0,0.1;1,1.2)/[c1](0,0.1;1,1.2)/[c2](0.1,0;1.1,1.1)") - assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") - - i1 = l.begin_shapes_touching(c0.cell_index, 0, RBA::DBox.new(0, 0, 0.100, 0.100)) - assert_equal(collect(i1, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") - - i1 = c0.begin_shapes_rec_touching(0, RBA::Box.new(0, 0, 100, 100)) - assert_equal(collect(i1, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") - - i1 = c0.begin_shapes_rec_touching(0, RBA::DBox.new(0, 0, 0.100, 0.100)) - assert_equal(collect(i1, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") - - i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(0, 0, 100, 100)); - assert_equal(collect(i1o, l), ""); - i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::DBox.new(0, 0, 0.100, 0.100)); - assert_equal(collect(i1o, l), ""); - i1copy.overlapping = true - assert_equal(i1copy.overlapping?, true) - assert_equal(collect(i1copy, l), ""); - i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(0, 0, 100, 101)); - assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); - i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::DBox.new(0, 0, 0.100, 0.101)); - assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); - i1copy.region = RBA::Box.new(0, 0, 100, 101) - assert_equal(i1copy.region.to_s, "(0,0;100,101)") - assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); - i1copy.region = RBA::Region::new(RBA::Box.new(0, 0, 100, 101)) - assert_equal(i1copy.region.to_s, "(0,0;100,101)") - assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); - i1copy.region = RBA::Box.new(-1000, -1000, 1100, 1101) - i1copy.confine_region(RBA::Box.new(0, 0, 100, 101)) - assert_equal(i1copy.region.to_s, "(0,0;100,101)") - assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); - i1copy.region = RBA::Box.new(-1000, -1000, 1100, 1101) - i1copy.confine_region(RBA::Region::new(RBA::Box.new(0, 0, 100, 101))) - assert_equal(i1copy.region.to_s, "(0,0;100,101)") - assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); - i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(0, 0, 101, 101)); - assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)"); - i1o = c0.begin_shapes_rec_overlapping(0, RBA::Box.new(0, 0, 101, 101)); - assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)"); - - i2 = l.begin_shapes_touching(c0.cell_index, 0, RBA::Box.new(-100, 0, 100, 100)); - assert_equal(collect(i2, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](-1200,0;-100,1000)"); - i2o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(-100, 0, 100, 100)); - assert_equal(collect(i2o, l), ""); - i2o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(-101, 0, 101, 101)); - assert_equal(collect(i2o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](-1200,0;-100,1000)"); - - i4 = l.begin_shapes_touching(c0.cell_index, 0, RBA::Box.new(-100, 0, 2000, 100)); - i4_copy = l.begin_shapes_touching(c0.cell_index, 0, RBA::Box.new(-100, 0, 2000, 100)); - i4.max_depth = 0; - assert_equal(collect(i4, l), "[c0](0,100;1000,1200)"); - - assert_equal(i4 == i4, true); - assert_equal(i4 != i4, false); - assert_equal(i4 == i4_copy, false); - assert_equal(i4 != i4_copy, true); - i4 = i4_copy.dup; - assert_equal(i4 == i4_copy, true); - assert_equal(i4 != i4_copy, false); - i4.max_depth = 1; - assert_equal(collect(i4, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](-1200,0;-100,1000)"); - - i4.assign(i4_copy); - assert_equal(collect(i4, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)"); - - i5 = l.begin_shapes(c0.cell_index, 0); - assert_equal(collect(i5, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)"); - - ii = RBA::RecursiveShapeIterator::new - assert_equal(collect(ii, l), "") - - ii = RBA::RecursiveShapeIterator::new(l, c1, 0) - assert_equal(collect(ii, l), "[c1](0,100;1000,1200)") - assert_equal(ii.top_cell.name, "c1") - assert_equal(ii.layout == l, true) - - ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1]) - ic = ii.dup - assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)/[c3](1101,101;2101,1201)") - assert_equal(collect(ic, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)/[c3](1101,101;2101,1201)") - - ii = RBA::RecursiveShapeIterator::new(l, c2, 0, RBA::Box.new(-100, 0, 2000, 100), false) - assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)") - - ii = RBA::RecursiveShapeIterator::new(l, c2, 0, RBA::Box.new(-100, 0, 2000, 100), true) - assert_equal(collect(ii, l), "") - - ii = RBA::RecursiveShapeIterator::new(l, c2, 0, RBA::Box.new(-100, 0, 2000, 101), true) - assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)") - - ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 0, 2000, 100), false) - assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)") - - ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 0, 2000, 101), false) - assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)/[c3](1101,101;2101,1201)") - - ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 0, 2000, 101), true) - assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)") - - ii = RBA::RecursiveShapeIterator::new(l, c0, 0) - assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") - - ii.reset - ii.unselect_cells("c0") - ii.select_cells("c2") - assert_equal(collect(ii, l), "[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)") - - ii.reset_selection - ii.unselect_cells("c*") - ii.select_cells([ c2.cell_index ]) - assert_equal(collect(ii, l), "[c2](100,0;1100,1100)") - - ii.reset_selection - ii.unselect_all_cells - ii.select_cells("c2") - assert_equal(collect(ii, l), "[c2](100,0;1100,1100)") - - ii.reset_selection - ii.select_all_cells - ii.unselect_cells("c2") - assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") - - ii.reset_selection - ii.select_cells("c*") - ii.unselect_cells([ c1.cell_index, c2.cell_index ]) - assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") - - end - - def test_5x - - # Recursive shape iterator tests - - l = RBA::Layout.new - l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) - l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) - c0 = l.cell(l.add_cell("c0")) - c1 = l.cell(l.add_cell("c1")) - c2 = l.cell(l.add_cell("c2")) - c3 = l.cell(l.add_cell("c3")) - - b = RBA::Box.new(0, 100, 1000, 1200) - c3.shapes(0).insert(b) - - bb = RBA::Box.new(1, 101, 1001, 1201) - c3.shapes(1).insert(bb) - - tt = RBA::Trans.new - c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) - c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) - c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1), RBA::Vector::new(10, 20), RBA::Vector::new(11, 21), 2, 3)) - c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) - - res = [] - i = l.begin_shapes(c0.cell_index, 0) - while !i.at_end? - res << i.shape.box.transformed(i.trans).to_s + " " + i.path.collect { |e| "[" + l.cell(e.cell_inst.cell_index).name + " #" + e.ia.to_s + "," + e.ib.to_s + " -> " + e.specific_trans.to_s + "]" }.join("") - i.next - end - - assert_equal(res.join("\n") + "\n", <<"END") -(1200,0;2200,1100) [c2 #-1,-1 -> r0 100,-100][c3 #-1,-1 -> r0 1100,0] -(-1200,0;-100,1000) [c3 #0,0 -> r90 0,0] -(-1190,20;-90,1020) [c3 #1,0 -> r90 10,20] -(-1189,21;-89,1021) [c3 #0,1 -> r90 11,21] -(-1179,41;-79,1041) [c3 #1,1 -> r90 21,41] -(-1178,42;-78,1042) [c3 #0,2 -> r90 22,42] -(-1168,62;-68,1062) [c3 #1,2 -> r90 32,62] -END - - end - def collect_hier(l) s = "" diff --git a/testdata/ruby/dbRecursiveInstanceIterator.rb b/testdata/ruby/dbRecursiveInstanceIterator.rb new file mode 100644 index 000000000..d3cf5b206 --- /dev/null +++ b/testdata/ruby/dbRecursiveInstanceIterator.rb @@ -0,0 +1,265 @@ +# encoding: UTF-8 + +# 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 + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBLayout_TestClass < TestBase + + def collect(s, l) + + res = [] + while !s.at_end? + r = "[#{s.inst_cell.name}]" + r += (s.trans * s.inst_trans).to_s + res.push(r) + s.next + end + + return res.join("/") + + end + + def dcollect(s, l) + + res = [] + while !s.at_end? + r = "[#{s.inst_cell.name}]" + r += (s.dtrans * s.inst_dtrans).to_s + res.push(r) + s.next + end + + return res.join("/") + + end + + def test_1 + + # Recursive instance iterator tests + + l = RBA::Layout.new + l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + c2 = l.cell(l.add_cell("c2")) + c3 = l.cell(l.add_cell("c3")) + + b = RBA::Box.new(0, 100, 1000, 1200) + c0.shapes(0).insert(b) + c1.shapes(0).insert(b) + c2.shapes(0).insert(b) + c3.shapes(0).insert(b) + + bb = RBA::Box.new(1, 101, 1001, 1201) + c3.shapes(1).insert(bb) + + tt = RBA::Trans.new + c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) + c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) + c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) + c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) + + i1 = c0.begin_instances_rec_touching(RBA::Box.new(0, 0, 100, 100)) + i1copy = i1.dup + assert_equal(i1copy.overlapping?, false) + assert_equal(collect(i1, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100") + i1.reset + assert_equal(dcollect(i1, l), "[c1]r0 *1 0,0/[c2]r0 *1 0.1,-0.1") + assert_equal(collect(i1copy, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100") + + i1 = c0.begin_instances_rec_touching(RBA::DBox.new(0, 0, 0.100, 0.100)) + assert_equal(collect(i1, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100") + + i1o = c0.begin_instances_rec_overlapping(RBA::Box.new(0, 0, 100, 100)); + assert_equal(collect(i1o, l), ""); + i1o = c0.begin_instances_rec_overlapping(RBA::DBox.new(0, 0, 0.100, 0.100)); + assert_equal(collect(i1o, l), ""); + i1copy.overlapping = true + assert_equal(i1copy.overlapping?, true) + assert_equal(collect(i1copy, l), ""); + i1o = c0.begin_instances_rec_overlapping(RBA::Box.new(0, 0, 100, 101)); + assert_equal(collect(i1o, l), "[c1]r0 *1 0,0"); + i1o = c0.begin_instances_rec_overlapping(RBA::DBox.new(0, 0, 0.100, 0.101)); + assert_equal(collect(i1o, l), "[c1]r0 *1 0,0"); + i1copy.region = RBA::Box.new(0, 0, 100, 101) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c1]r0 *1 0,0"); + i1copy.region = RBA::Region::new(RBA::Box.new(0, 0, 100, 101)) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c1]r0 *1 0,0"); + i1copy.region = RBA::Box.new(-1000, -1000, 1100, 1101) + i1copy.confine_region(RBA::Box.new(0, 0, 100, 101)) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c1]r0 *1 0,0"); + i1copy.region = RBA::Box.new(-1000, -1000, 1100, 1101) + i1copy.confine_region(RBA::Region::new(RBA::Box.new(0, 0, 100, 101))) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c1]r0 *1 0,0"); + i1o = c0.begin_instances_rec_overlapping(RBA::Box.new(0, 0, 101, 101)); + assert_equal(collect(i1o, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100"); + i1o = c0.begin_instances_rec_overlapping(RBA::Box.new(0, 0, 101, 101)); + assert_equal(collect(i1o, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100"); + + i2 = c0.begin_instances_rec_touching(RBA::Box.new(-100, 0, 100, 100)); + assert_equal(collect(i2, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100/[c3]r90 *1 0,0"); + i2o = c0.begin_instances_rec_overlapping(RBA::Box.new(-100, 0, 100, 100)); + assert_equal(collect(i2o, l), ""); + i2o = c0.begin_instances_rec_overlapping(RBA::Box.new(-101, 0, 101, 101)); + assert_equal(collect(i2o, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100/[c3]r90 *1 0,0"); + + i4 = c0.begin_instances_rec_touching(RBA::Box.new(-100, 0, 2000, 100)); + i4_copy = c0.begin_instances_rec_touching(RBA::Box.new(-100, 0, 2000, 100)); + i4.max_depth = 0; + assert_equal(collect(i4, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100/[c3]r90 *1 0,0"); + + assert_equal(i4 == i4, true); + assert_equal(i4 != i4, false); + assert_equal(i4 == i4_copy, false); + assert_equal(i4 != i4_copy, true); + i4 = i4_copy.dup + assert_equal(i4 == i4_copy, true); + assert_equal(i4 != i4_copy, false); + i4.max_depth = 1; + assert_equal(i4.max_depth, 1) + assert_equal(collect(i4, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100/[c3]r90 *1 0,0"); + i4.min_depth = 1; + assert_equal(i4.min_depth, 1) + assert_equal(collect(i4, l), "[c3]r0 *1 1200,-100"); + + i4.assign(i4_copy); + assert_equal(collect(i4, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100/[c3]r90 *1 0,0"); + + i5 = c0.begin_instances_rec + assert_equal(collect(i5, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100/[c3]r90 *1 0,0"); + + ii = RBA::RecursiveInstanceIterator::new + assert_equal(collect(ii, l), "") + + ii = RBA::RecursiveInstanceIterator::new(l, c2) + assert_equal(collect(ii, l), "[c3]r0 *1 1100,0") + assert_equal(ii.top_cell.name, "c2") + assert_equal(ii.layout == l, true) + + ii = RBA::RecursiveInstanceIterator::new(l, c0, RBA::Box.new(-100, 0, 2000, 100), false) + assert_equal(collect(ii, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100/[c3]r90 *1 0,0") + + ii = RBA::RecursiveInstanceIterator::new(l, c0, RBA::Box.new(-100, 0, 2000, 100), true) + assert_equal(collect(ii, l), "[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100") + + ii = RBA::RecursiveInstanceIterator::new(l, c0, RBA::Box.new(-100, 0, 2000, 101), true) + assert_equal(collect(ii, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100") + + ii = RBA::RecursiveInstanceIterator::new(l, c0, RBA::Region::new(RBA::Box.new(-100, 0, 2000, 101)), true) + assert_equal(collect(ii, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100") + + ii = RBA::RecursiveInstanceIterator::new(l, c0) + assert_equal(collect(ii, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100/[c3]r90 *1 0,0") + + ii.reset + ii.unselect_cells("c0") + ii.select_cells("c2") + assert_equal(collect(ii, l), "[c3]r0 *1 1200,-100") + + ii.reset_selection + ii.unselect_cells("c*") + ii.select_cells([ c2.cell_index ]) + assert_equal(collect(ii, l), "[c3]r0 *1 1200,-100") + + ii.reset_selection + ii.unselect_all_cells + ii.select_cells("c2") + assert_equal(collect(ii, l), "[c3]r0 *1 1200,-100") + + ii.reset_selection + ii.select_all_cells + ii.unselect_cells("c2") + assert_equal(collect(ii, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100/[c3]r90 *1 0,0") + + ii.reset_selection + ii.select_cells("c*") + ii.unselect_cells([ c1.cell_index, c2.cell_index ]) + assert_equal(collect(ii, l), "[c1]r0 *1 0,0/[c2]r0 *1 100,-100/[c3]r90 *1 0,0") + + ii = RBA::RecursiveInstanceIterator::new(l, c0) + assert_equal(ii.all_targets_enabled?, true) + ii.targets = "c3" + assert_equal(ii.all_targets_enabled?, false) + assert_equal(collect(ii, l), "[c3]r0 *1 1200,-100/[c3]r90 *1 0,0") + ii.enable_all_targets + assert_equal(ii.all_targets_enabled?, true) + assert_equal(collect(ii, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c2]r0 *1 100,-100/[c3]r90 *1 0,0") + ii.targets = [ c3.cell_index, c1.cell_index ] + assert_equal(ii.all_targets_enabled?, false) + assert_equal(collect(ii, l), "[c1]r0 *1 0,0/[c3]r0 *1 1200,-100/[c3]r90 *1 0,0") + + end + + def test_2 + + # Recursive instance iterator tests + + l = RBA::Layout.new + l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + c2 = l.cell(l.add_cell("c2")) + c3 = l.cell(l.add_cell("c3")) + + b = RBA::Box.new(0, 100, 1000, 1200) + c3.shapes(0).insert(b) + + bb = RBA::Box.new(1, 101, 1001, 1201) + c3.shapes(1).insert(bb) + + tt = RBA::Trans.new + c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) + c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) + c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1), RBA::Vector::new(10, 20), RBA::Vector::new(11, 21), 2, 3)) + c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) + + res = [] + i = c0.begin_instances_rec + while !i.at_end? + res << i.inst_cell.bbox.transformed(i.trans * i.inst_trans).to_s + " " + (i.path + [ i.current_inst_element ]).collect { |e| "[" + l.cell(e.cell_inst.cell_index).name + " #" + e.ia.to_s + "," + e.ib.to_s + " -> " + e.specific_trans.to_s + "]" }.join("") + i.next + end + + assert_equal(res.join("\n") + "\n", <<"END") +() [c1 #-1,-1 -> r0 0,0] +(1200,0;2201,1101) [c2 #-1,-1 -> r0 100,-100][c3 #-1,-1 -> r0 1100,0] +(1200,0;2201,1101) [c2 #-1,-1 -> r0 100,-100] +(-1201,0;-100,1001) [c3 #0,0 -> r90 0,0] +(-1191,20;-90,1021) [c3 #1,0 -> r90 10,20] +(-1190,21;-89,1022) [c3 #0,1 -> r90 11,21] +(-1180,41;-79,1042) [c3 #1,1 -> r90 21,41] +(-1179,42;-78,1043) [c3 #0,2 -> r90 22,42] +(-1169,62;-68,1063) [c3 #1,2 -> r90 32,62] +END + + end + +end + +load("test_epilogue.rb") diff --git a/testdata/ruby/dbRecursiveShapeIterator.rb b/testdata/ruby/dbRecursiveShapeIterator.rb new file mode 100644 index 000000000..cbec7ee84 --- /dev/null +++ b/testdata/ruby/dbRecursiveShapeIterator.rb @@ -0,0 +1,279 @@ +# encoding: UTF-8 + +# 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 + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBRecursiveShapeIterator_TestClass < TestBase + + def collect(s, l) + + res = [] + while !s.at_end? + r = "[#{l.cell_name(s.cell_index)}]" + if s.shape.is_box? + box = s.shape.box + r += box.transformed(s.trans).to_s + else + r += "X"; + end + s.next + res.push(r) + end + + return res.join("/") + + end + + def dcollect(s, l) + + res = [] + while !s.at_end? + r = "[#{l.cell_name(s.cell_index)}]" + if s.shape.is_box? + box = s.shape.dbox + r += box.transformed(s.dtrans).to_s + else + r += "X"; + end + s.next + res.push(r) + end + + return res.join("/") + + end + + def test_1 + + # Recursive shape iterator tests + + l = RBA::Layout.new + l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + c2 = l.cell(l.add_cell("c2")) + c3 = l.cell(l.add_cell("c3")) + + b = RBA::Box.new(0, 100, 1000, 1200) + c0.shapes(0).insert(b) + c1.shapes(0).insert(b) + c2.shapes(0).insert(b) + c3.shapes(0).insert(b) + + bb = RBA::Box.new(1, 101, 1001, 1201) + c3.shapes(1).insert(bb) + + tt = RBA::Trans.new + c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) + c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) + c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) + c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) + + i1 = l.begin_shapes_touching(c0.cell_index, 0, RBA::Box.new(0, 0, 100, 100)) + i1copy = i1.dup + assert_equal(i1copy.overlapping?, false) + assert_equal(collect(i1, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") + i1.reset + assert_equal(dcollect(i1, l), "[c0](0,0.1;1,1.2)/[c1](0,0.1;1,1.2)/[c2](0.1,0;1.1,1.1)") + assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") + + i1 = l.begin_shapes_touching(c0.cell_index, 0, RBA::DBox.new(0, 0, 0.100, 0.100)) + assert_equal(collect(i1, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") + + i1 = c0.begin_shapes_rec_touching(0, RBA::Box.new(0, 0, 100, 100)) + assert_equal(collect(i1, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") + + i1 = c0.begin_shapes_rec_touching(0, RBA::DBox.new(0, 0, 0.100, 0.100)) + assert_equal(collect(i1, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)") + + i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(0, 0, 100, 100)); + assert_equal(collect(i1o, l), ""); + i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::DBox.new(0, 0, 0.100, 0.100)); + assert_equal(collect(i1o, l), ""); + i1copy.overlapping = true + assert_equal(i1copy.overlapping?, true) + assert_equal(collect(i1copy, l), ""); + i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(0, 0, 100, 101)); + assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::DBox.new(0, 0, 0.100, 0.101)); + assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1copy.region = RBA::Box.new(0, 0, 100, 101) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1copy.region = RBA::Region::new(RBA::Box.new(0, 0, 100, 101)) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1copy.region = RBA::Box.new(-1000, -1000, 1100, 1101) + i1copy.confine_region(RBA::Box.new(0, 0, 100, 101)) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1copy.region = RBA::Box.new(-1000, -1000, 1100, 1101) + i1copy.confine_region(RBA::Region::new(RBA::Box.new(0, 0, 100, 101))) + assert_equal(i1copy.region.to_s, "(0,0;100,101)") + assert_equal(collect(i1copy, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)"); + i1o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(0, 0, 101, 101)); + assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)"); + i1o = c0.begin_shapes_rec_overlapping(0, RBA::Box.new(0, 0, 101, 101)); + assert_equal(collect(i1o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)"); + + i2 = l.begin_shapes_touching(c0.cell_index, 0, RBA::Box.new(-100, 0, 100, 100)); + assert_equal(collect(i2, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](-1200,0;-100,1000)"); + i2o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(-100, 0, 100, 100)); + assert_equal(collect(i2o, l), ""); + i2o = l.begin_shapes_overlapping(c0.cell_index, 0, RBA::Box.new(-101, 0, 101, 101)); + assert_equal(collect(i2o, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](-1200,0;-100,1000)"); + + i4 = l.begin_shapes_touching(c0.cell_index, 0, RBA::Box.new(-100, 0, 2000, 100)); + i4_copy = l.begin_shapes_touching(c0.cell_index, 0, RBA::Box.new(-100, 0, 2000, 100)); + i4.max_depth = 0; + assert_equal(collect(i4, l), "[c0](0,100;1000,1200)"); + + assert_equal(i4 == i4, true); + assert_equal(i4 != i4, false); + assert_equal(i4 == i4_copy, false); + assert_equal(i4 != i4_copy, true); + i4 = i4_copy.dup; + assert_equal(i4 == i4_copy, true); + assert_equal(i4 != i4_copy, false); + i4.max_depth = 1; + assert_equal(collect(i4, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](-1200,0;-100,1000)"); + + i4.assign(i4_copy); + assert_equal(collect(i4, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)"); + + i5 = l.begin_shapes(c0.cell_index, 0); + assert_equal(collect(i5, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)"); + + ii = RBA::RecursiveShapeIterator::new + assert_equal(collect(ii, l), "") + + ii = RBA::RecursiveShapeIterator::new(l, c1, 0) + assert_equal(collect(ii, l), "[c1](0,100;1000,1200)") + assert_equal(ii.top_cell.name, "c1") + assert_equal(ii.layout == l, true) + + ii.max_depth = 2 + assert_equal(ii.max_depth, 2) + ii.min_depth = 1 + assert_equal(ii.min_depth, 1) + + ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1]) + ic = ii.dup + assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)/[c3](1101,101;2101,1201)") + assert_equal(collect(ic, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)/[c3](1101,101;2101,1201)") + + ii = RBA::RecursiveShapeIterator::new(l, c2, 0, RBA::Box.new(-100, 0, 2000, 100), false) + assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)") + + ii = RBA::RecursiveShapeIterator::new(l, c2, 0, RBA::Box.new(-100, 0, 2000, 100), true) + assert_equal(collect(ii, l), "") + + ii = RBA::RecursiveShapeIterator::new(l, c2, 0, RBA::Box.new(-100, 0, 2000, 101), true) + assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)") + + ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 0, 2000, 100), false) + assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)") + + ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 0, 2000, 101), false) + assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)/[c3](1101,101;2101,1201)") + + ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 0, 2000, 101), true) + assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)") + + ii = RBA::RecursiveShapeIterator::new(l, c0, 0) + assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") + + ii.reset + ii.unselect_cells("c0") + ii.select_cells("c2") + assert_equal(collect(ii, l), "[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)") + + ii.reset_selection + ii.unselect_cells("c*") + ii.select_cells([ c2.cell_index ]) + assert_equal(collect(ii, l), "[c2](100,0;1100,1100)") + + ii.reset_selection + ii.unselect_all_cells + ii.select_cells("c2") + assert_equal(collect(ii, l), "[c2](100,0;1100,1100)") + + ii.reset_selection + ii.select_all_cells + ii.unselect_cells("c2") + assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c1](0,100;1000,1200)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") + + ii.reset_selection + ii.select_cells("c*") + ii.unselect_cells([ c1.cell_index, c2.cell_index ]) + assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") + + end + + def test_2 + + # Recursive shape iterator tests + + l = RBA::Layout.new + l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + c2 = l.cell(l.add_cell("c2")) + c3 = l.cell(l.add_cell("c3")) + + b = RBA::Box.new(0, 100, 1000, 1200) + c3.shapes(0).insert(b) + + bb = RBA::Box.new(1, 101, 1001, 1201) + c3.shapes(1).insert(bb) + + tt = RBA::Trans.new + c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) + c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) + c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1), RBA::Vector::new(10, 20), RBA::Vector::new(11, 21), 2, 3)) + c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) + + res = [] + i = l.begin_shapes(c0.cell_index, 0) + while !i.at_end? + res << i.shape.box.transformed(i.trans).to_s + " " + i.path.collect { |e| "[" + l.cell(e.cell_inst.cell_index).name + " #" + e.ia.to_s + "," + e.ib.to_s + " -> " + e.specific_trans.to_s + "]" }.join("") + i.next + end + + assert_equal(res.join("\n") + "\n", <<"END") +(1200,0;2200,1100) [c2 #-1,-1 -> r0 100,-100][c3 #-1,-1 -> r0 1100,0] +(-1200,0;-100,1000) [c3 #0,0 -> r90 0,0] +(-1190,20;-90,1020) [c3 #1,0 -> r90 10,20] +(-1189,21;-89,1021) [c3 #0,1 -> r90 11,21] +(-1179,41;-79,1041) [c3 #1,1 -> r90 21,41] +(-1178,42;-78,1042) [c3 #0,2 -> r90 22,42] +(-1168,62;-68,1062) [c3 #1,2 -> r90 32,62] +END + + end + +end + +load("test_epilogue.rb")