mirror of https://github.com/KLayout/klayout.git
Merge pull request #731 from KLayout/recursive-inst-iterator
Recursive inst iterator
This commit is contained in:
commit
82587e70c3
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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<int>::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<int>::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<db::cell_index_type> &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 <db::Box> ());
|
||||
|
||||
}
|
||||
|
||||
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<db::cell_index_type> &cells)
|
||||
{
|
||||
if (mp_layout) {
|
||||
|
||||
for (std::set<db::cell_index_type>::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<db::cell_index_type> &cells)
|
||||
{
|
||||
if (mp_layout) {
|
||||
|
||||
for (std::set<db::cell_index_type>::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<db::InstElement>
|
||||
RecursiveInstanceIterator::path () const
|
||||
{
|
||||
std::vector<db::InstElement> 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<db::Box> ()); ! 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<db::Box> ());
|
||||
|
||||
// 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<box_type> ()).at_end ();
|
||||
} else {
|
||||
return m_local_complex_region_stack.back ().begin_touching (box, db::box_convert<box_type> ()).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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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 <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
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<db::Box, db::Box, db::box_convert<db::Box>, 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<db::cell_index_type> &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<db::cell_index_type> &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<db::cell_index_type> &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<db::cell_index_type> &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<db::cell_index_type> &enables () const
|
||||
{
|
||||
return m_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the cells in the "disable" selection
|
||||
*/
|
||||
const std::set<db::cell_index_type> &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<size_t> (mp_cell);
|
||||
return reinterpret_cast<const cell_type *> (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<instance_element_type> 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<size_t> (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<db::cell_index_type> m_start, m_stop;
|
||||
std::set<db::cell_index_type> m_targets;
|
||||
bool m_all_targets;
|
||||
|
||||
const layout_type *mp_layout;
|
||||
const cell_type *mp_top_cell;
|
||||
|
||||
box_type m_region;
|
||||
std::unique_ptr<region_type> mp_complex_region;
|
||||
db::box_convert<db::CellInst> 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<db::cell_index_type, bool> m_empty_cells_cache;
|
||||
mutable const cell_type *mp_cell;
|
||||
mutable cplx_trans_type m_trans;
|
||||
mutable std::vector<cplx_trans_type> m_trans_stack;
|
||||
mutable std::vector<inst_iterator> m_inst_iterators;
|
||||
mutable std::vector<inst_array_iterator> m_inst_array_iterators;
|
||||
mutable std::vector<const cell_type *> m_cells;
|
||||
mutable std::vector<box_tree_type> m_local_complex_region_stack;
|
||||
mutable std::vector<box_type> m_local_region_stack;
|
||||
mutable bool m_needs_reinit;
|
||||
mutable size_t m_inst_quad_id;
|
||||
mutable std::vector<size_t> m_inst_quad_id_stack;
|
||||
mutable std::set<db::cell_index_type> 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<size_t> (mp_cell);
|
||||
c -= (c & size_t (1));
|
||||
mp_cell = reinterpret_cast<const db::Cell *> (c + (a ? 1 : 0));
|
||||
}
|
||||
|
||||
bool is_all_of_instance () const
|
||||
{
|
||||
return (reinterpret_cast<size_t> (mp_cell) & size_t (2)) != 0;
|
||||
}
|
||||
|
||||
void set_all_of_instance (bool a) const
|
||||
{
|
||||
size_t c = reinterpret_cast<size_t> (mp_cell);
|
||||
c -= (c & size_t (2));
|
||||
mp_cell = reinterpret_cast<const db::Cell *> (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
|
||||
|
|
@ -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<db::Cell> 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"
|
||||
|
|
|
|||
|
|
@ -1716,9 +1716,11 @@ Class<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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<db::Layout> 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"),
|
||||
|
|
|
|||
|
|
@ -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<db::cell_index_type> &cells)
|
||||
{
|
||||
std::set<db::cell_index_type> 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<db::Cell *> (&ly->cell ((*r)->inst_ptr.cell_index ()));
|
||||
}
|
||||
|
||||
static void set_targets2 (db::RecursiveInstanceIterator *r, const std::string &pattern)
|
||||
{
|
||||
tl::GlobPattern p (pattern);
|
||||
std::set<db::cell_index_type> 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<db::cell_index_type> &cells)
|
||||
{
|
||||
std::set<db::cell_index_type> 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<db::cell_index_type> 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<db::cell_index_type> &cells)
|
||||
{
|
||||
std::set<db::cell_index_type> 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<db::cell_index_type> 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<db::RecursiveInstanceIterator> 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"
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -123,89 +123,89 @@ Class<db::RecursiveShapeIterator> 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<db::RecursiveShapeIterator> 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<db::RecursiveShapeIterator> 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<db::RecursiveShapeIterator> 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<db::RecursiveShapeIterator> 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<db::RecursiveShapeIterator> 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<db::RecursiveShapeIterator> 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<db::RecursiveShapeIterator> 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<db::RecursiveShapeIterator> 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"
|
||||
|
|
|
|||
|
|
@ -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 <vector>
|
||||
|
||||
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<db::cell_index_type> 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<db::cell_index_type> 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<db::Box> &boxes)
|
||||
{
|
||||
db::Layout l;
|
||||
l.insert_layer(0, db::LayerProperties (1, 0));
|
||||
db::Cell &top (l.cell (l.add_cell ()));
|
||||
|
||||
for (std::set<db::Box>::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<db::Box> *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<db::Box> *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<db::Box> 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<db::Box> selected_boxes;
|
||||
std::set<db::Box> 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<db::Box>::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<db::CellInst> (g)));
|
||||
}
|
||||
|
||||
for (std::set<db::Box>::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<db::Box> 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<db::Box> selected_boxes;
|
||||
std::set<db::Box> selected_boxes2;
|
||||
|
||||
db::RecursiveInstanceIterator iter = db::RecursiveInstanceIterator (g, c0, search_box, true);
|
||||
std::set<db::cell_index_type> 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<db::CellInst> (g)));
|
||||
}
|
||||
|
||||
int nn = 0;
|
||||
for (std::set<db::Box>::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<db::CellInst> (g)));
|
||||
}
|
||||
|
||||
for (std::set<db::Box>::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<db::Box> 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<db::Box> selected_boxes;
|
||||
std::set<db::Box> selected_boxes2;
|
||||
|
||||
db::RecursiveInstanceIterator iter = db::RecursiveInstanceIterator (g, c0, search_box);
|
||||
std::set<db::cell_index_type> 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<db::CellInst> (g)));
|
||||
}
|
||||
|
||||
int nn = 0;
|
||||
for (std::set<db::Box>::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<db::CellInst> (g)));
|
||||
}
|
||||
|
||||
for (std::set<db::Box>::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);
|
||||
}
|
||||
|
||||
|
|
@ -723,20 +723,24 @@ static db::Layout boxes2layout (const std::set<db::Box> &boxes)
|
|||
return l;
|
||||
}
|
||||
|
||||
class FlatPusher
|
||||
: public db::RecursiveShapeReceiver
|
||||
{
|
||||
public:
|
||||
FlatPusher (std::set<db::Box> *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<db::Box> *boxes) : mp_boxes (boxes) { }
|
||||
|
||||
private:
|
||||
std::set<db::Box> *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<db::Box> *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"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri)
|
|||
|
||||
SOURCES = \
|
||||
dbCompoundOperationTests.cc \
|
||||
dbRecursiveInstanceIteratorTests.cc \
|
||||
dbRegionUtilsTests.cc \
|
||||
dbUtilsTests.cc \
|
||||
dbWriterTools.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")
|
||||
|
|
|
|||
|
|
@ -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 = ""
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -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")
|
||||
Loading…
Reference in New Issue