klayout/src/laybasic/layFinder.cc

937 lines
28 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2017 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 "tlProgress.h"
#include "layFinder.h"
namespace lay
{
/**
* @brief A pretty heuristic method to determine the "enclosing distance" of a polygon around a point
*/
template<class Iter, class Point>
double poly_dist (Iter edge, const Point &pt)
{
double distance = std::numeric_limits<double>::max ();
while (! edge.at_end ()) {
std::pair <bool, Point> ret = (*edge).projected (pt);
if (ret.first) {
double d (ret.second.distance (pt));
if (d < distance) {
distance = d;
}
}
++edge;
}
return distance;
}
// -------------------------------------------------------------
// max. number of tries in single-click selection before giving up
static int point_sel_tests = 10000;
// max. number of tries in single-click selection before giving up
static int inst_point_sel_tests = 10000;
// ----------------------------------------------------------------------
// Finder implementation
Finder::Finder (bool point_mode, bool top_level_sel)
: m_min_level (0), m_max_level (0),
mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_top_level_sel (top_level_sel)
{
m_distance = std::numeric_limits<double>::max ();
}
Finder::~Finder ()
{
// .. nothing yet ..
}
bool
Finder::closer (double d)
{
// the proximity is checked and delivered in micron units.
d *= mp_view->cellview (m_cv_index)->layout ().dbu ();
if (d <= m_distance) {
m_distance = d;
return true;
} else {
return false;
}
}
void
Finder::start (lay::LayoutView *view, const lay::CellView &cv, unsigned int cv_index, const std::vector<db::ICplxTrans> &trans, const db::Box &region, int min_level, int max_level, const std::vector<int> &layers)
{
m_layers = layers;
m_region = region;
mp_layout = &cv->layout ();
mp_view = view;
m_cv_index = cv_index;
m_min_level = std::max (0, min_level);
m_max_level = std::max (m_min_level, std::min (max_level, m_top_level_sel ? ((int) cv.specific_path ().size () + 1) : max_level));
if (layers.size () == 1) {
m_box_convert = db::box_convert <db::CellInst> (*mp_layout, (unsigned int) layers [0]);
m_cell_box_convert = db::box_convert <db::Cell> ((unsigned int) layers [0]);
} else {
m_box_convert = db::box_convert <db::CellInst> (*mp_layout);
m_cell_box_convert = db::box_convert <db::Cell> ();
}
m_path.erase (m_path.begin (), m_path.end ());
for (std::vector<db::ICplxTrans>::const_iterator t = trans.begin (); t != trans.end (); ++t) {
do_find (*cv.cell (), cv.specific_path ().size (), *t * cv.context_trans ());
}
}
unsigned int
Finder::test_edge (const db::Edge &edg, double &distance, bool &match)
{
unsigned int ret = 0;
// we hit the region with the edge end points - take the closest vertex
if (m_region.contains (edg.p1 ()) || m_region.contains (edg.p2 ())) {
double d1 = edg.p1 ().double_distance (m_region.center ());
double d2 = edg.p2 ().double_distance (m_region.center ());
// snap to the point - nothing can get closer
distance = 0.0;
if (d1 < d2) {
ret = 1;
} else {
ret = 2;
}
match = true;
}
// if the edge cuts through the active region: test the
// edge as a whole
if (ret == 0 && edg.clipped (m_region).first) {
double d = edg.distance_abs (m_region.center ());
if (! match || d < distance) {
distance = d;
ret = 3;
}
match = true;
}
return ret;
}
void
Finder::do_find (const db::Cell &cell, int level, const db::ICplxTrans &t)
{
if (level <= m_max_level /*take level of cell itself*/
&& cell.is_proxy ()
&& m_layers.size () == 1
&& (unsigned int) m_layers [0] == mp_layout->guiding_shape_layer ()) {
// when looking at the guiding shape layer, we can visit this cell as well allowing to find the guiding shapes
db::Box touch_box (t.inverted () * m_region);
if (level >= m_min_level) {
visit_cell (cell, touch_box, t, level);
}
} else if (level < m_max_level
&& (t * m_cell_box_convert (cell)).touches (m_region)
&& (mp_view->select_inside_pcells_mode () || !cell.is_proxy ())
&& !mp_view->is_cell_hidden (cell.cell_index (), m_cv_index)) {
db::Box touch_box (t.inverted () * m_region);
if (level >= m_min_level) {
visit_cell (cell, touch_box, t, level);
}
db::Cell::touching_iterator inst = cell.begin_touching (touch_box);
while (! inst.at_end ()) {
const db::CellInstArray &cell_inst = inst->cell_inst ();
for (db::CellInstArray::iterator p = cell_inst.begin_touching (touch_box, m_box_convert); ! p.at_end (); ++p) {
m_path.push_back (db::InstElement (*inst, p));
do_find (mp_layout->cell (cell_inst.object ().cell_index ()),
level + 1,
t * cell_inst.complex_trans (*p));
m_path.pop_back ();
}
++inst;
}
}
}
// -------------------------------------------------------------
// ShapeFinder implementation
ShapeFinder::ShapeFinder (bool point_mode, bool top_level_sel, db::ShapeIterator::flags_type flags, const std::set<lay::ObjectInstPath> *excludes)
: Finder (point_mode, top_level_sel),
mp_excludes ((excludes && !excludes->empty ()) ? excludes : 0),
m_flags (flags), m_cv_index (0), m_topcell (0),
mp_prop_sel (0), m_inv_prop_sel (false), mp_progress (0)
{
m_tries = point_sel_tests;
}
struct LPContextEqualOp
{
bool operator() (const lay::LayerPropertiesConstIterator &a, const lay::LayerPropertiesConstIterator &b)
{
if (a->cellview_index () != b->cellview_index ()) {
return false;
}
if (a->inverse_prop_sel () != b->inverse_prop_sel ()) {
return false;
}
if (a->prop_sel () != b->prop_sel ()) {
return false;
}
if (a->trans () != b->trans ()) {
return false;
}
if (a->hier_levels () != b->hier_levels ()) {
return false;
}
return true;
}
};
struct LPContextCompareOp
{
bool operator() (const lay::LayerPropertiesConstIterator &a, const lay::LayerPropertiesConstIterator &b)
{
if (a->cellview_index () != b->cellview_index ()) {
return a->cellview_index () < b->cellview_index ();
}
if (a->inverse_prop_sel () != b->inverse_prop_sel ()) {
return a->inverse_prop_sel () < b->inverse_prop_sel ();
}
if (a->prop_sel () != b->prop_sel ()) {
return a->prop_sel () < b->prop_sel ();
}
if (a->trans () != b->trans ()) {
return a->trans () < b->trans ();
}
if (a->hier_levels () != b->hier_levels ()) {
return a->hier_levels () < b->hier_levels ();
}
return false;
}
};
bool
ShapeFinder::find (lay::LayoutView *view, const db::DBox &region_mu)
{
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Selecting ...")));
progress.set_unit (1000);
progress.set_format ("");
mp_progress = &progress;
m_context_layers.clear ();
m_cells_with_context.clear ();
std::vector<lay::LayerPropertiesConstIterator> lprops;
for (lay::LayerPropertiesConstIterator lp = view->begin_layers (); ! lp.at_end (); ++lp) {
if (lp->is_visual ()) {
lprops.push_back (lp);
}
}
std::sort (lprops.begin (), lprops.end (), LPContextCompareOp ());
std::vector<int> layers;
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator llp = lprops.begin (); llp != lprops.end (); ) {
layers.clear ();
lay::LayerPropertiesConstIterator lp0 = *llp;
LPContextEqualOp eq;
do {
layers.push_back ((*llp)->layer_index ());
++llp;
} while (llp != lprops.end () && eq(lp0, *llp));
find_internal (view, lp0->cellview_index (), &lp0->prop_sel (), lp0->inverse_prop_sel (), lp0->hier_levels (), lp0->trans (), layers, region_mu);
}
// search on the guiding shapes layer as well
// Use the visible layers for the context: the guiding shape is only looked up for cells
// having one of these layers
m_context_layers.clear ();
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator llp = lprops.begin (); llp != lprops.end (); ++llp) {
m_context_layers.push_back ((*llp)->layer_index ());
}
std::set< std::pair<db::DCplxTrans, int> > variants = view->cv_transform_variants ();
for (std::set< std::pair<db::DCplxTrans, int> >::const_iterator v = variants.begin (); v != variants.end (); ++v) {
layers.clear ();
layers.push_back (view->cellview (v->second)->layout ().guiding_shape_layer ());
std::vector<db::DCplxTrans> trans;
trans.push_back (v->first);
find_internal (view, (unsigned int) v->second, 0, false, lay::HierarchyLevelSelection (), trans, layers, region_mu);
}
mp_progress = 0;
m_cells_with_context.clear ();
m_context_layers.clear ();
return ! m_founds.empty ();
}
bool
ShapeFinder::find (lay::LayoutView *view, const lay::LayerProperties &lprops, const db::DBox &region_mu)
{
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Selecting ...")));
progress.set_unit (1000);
progress.set_format ("");
mp_progress = &progress;
m_cells_with_context.clear ();
m_context_layers.clear ();
std::vector<int> layers;
layers.push_back (lprops.layer_index ());
bool result = find_internal (view, lprops.cellview_index (), &lprops.prop_sel (), lprops.inverse_prop_sel (), lprops.hier_levels (), lprops.trans (), layers, region_mu);
mp_progress = 0;
return result;
}
bool
ShapeFinder::find_internal (lay::LayoutView *view, unsigned int cv_index, const std::set<db::properties_id_type> *prop_sel, bool inv_prop_sel, const lay::HierarchyLevelSelection &hier_sel, const std::vector<db::DCplxTrans> &trans_mu, const std::vector<int> &layers, const db::DBox &region_mu)
{
m_cv_index = cv_index;
const lay::CellView &cv = view->cellview (m_cv_index);
if (! cv.is_valid ()) {
return false;
}
m_topcell = cv.cell_index ();
double dbu = cv->layout ().dbu ();
db::Box region = db::VCplxTrans (1.0 / dbu) * region_mu;
std::vector<db::ICplxTrans> trans;
trans.reserve(trans_mu.size());
for (std::vector<db::DCplxTrans>::const_iterator t = trans_mu.begin(); t != trans_mu.end(); ++t) {
trans.push_back(db::VCplxTrans(1.0 / dbu) * *t * db::CplxTrans(dbu));
}
mp_prop_sel = prop_sel;
m_inv_prop_sel = inv_prop_sel;
int ctx_path_length = int (cv.specific_path ().size ());
int min_level = view->get_min_hier_levels ();
int max_level = view->get_max_hier_levels ();
if (hier_sel.has_from_level ()) {
min_level = hier_sel.from_level (ctx_path_length, min_level);
}
if (hier_sel.has_to_level ()) {
max_level = hier_sel.to_level (ctx_path_length, max_level);
}
// actually find
try {
start (view, cv, m_cv_index, trans, region, min_level, max_level, layers);
} catch (StopException) {
// ..
}
// return true if anything was found
return ! m_founds.empty ();
}
void
ShapeFinder::checkpoint ()
{
if (! point_mode ()) {
++*mp_progress;
} else {
if (--m_tries < 0) {
throw StopException ();
}
}
}
void
ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int /*level*/)
{
if (! m_context_layers.empty ()) {
std::map<db::cell_index_type, bool>::const_iterator ctx = m_cells_with_context.find (cell.cell_index ());
if (ctx == m_cells_with_context.end ()) {
bool has_context = false;
for (std::vector<int>::const_iterator l = m_context_layers.begin (); l != m_context_layers.end () && ! has_context; ++l) {
if (! cell.bbox ((unsigned int) *l).empty ()) {
has_context = true;
}
}
ctx = m_cells_with_context.insert (std::make_pair (cell.cell_index (), has_context)).first;
}
if (! ctx->second) {
return;
}
}
if (! point_mode ()) {
checkpoint ();
for (std::vector<int>::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) {
const db::Shapes &shapes = cell.shapes ((unsigned int) *l);
db::ShapeIterator shape = shapes.begin_touching (search_box, m_flags, mp_prop_sel, m_inv_prop_sel);
while (! shape.at_end ()) {
checkpoint ();
// in box mode, just test the boxes
if (shape->bbox ().inside (search_box)) {
m_founds.push_back (lay::ObjectInstPath ());
m_founds.back ().set_cv_index (m_cv_index);
m_founds.back ().set_topcell (m_topcell);
m_founds.back ().assign_path (path ().begin (), path ().end ());
m_founds.back ().set_layer (*l);
m_founds.back ().set_shape (*shape);
// Remove the selection if it's part of the excluded set
if (mp_excludes != 0 && mp_excludes->find (m_founds.back ()) != mp_excludes->end ()) {
m_founds.pop_back ();
}
}
++shape;
}
}
}
} else {
for (std::vector<int>::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (search_box))) {
checkpoint ();
const db::Shapes &shapes = cell.shapes (*l);
db::ShapeIterator shape = shapes.begin_touching (search_box, m_flags, mp_prop_sel, m_inv_prop_sel);
while (! shape.at_end ()) {
bool match = false;
double d = std::numeric_limits<double>::max ();
checkpoint ();
db::Point point (search_box.center ());
// in point mode, test the edges and use a "closest" criterion
if (shape->is_polygon ()) {
for (db::Shape::polygon_edge_iterator e = shape->begin_edge (); ! e.at_end (); ++e) {
test_edge (t * *e, d, match);
}
// test if inside the polygon
if (! match && inside_poly (shape->begin_edge (), point) >= 0) {
d = t.ctrans (poly_dist (shape->begin_edge (), point));
match = true;
}
} else if (shape->is_path ()) {
// test the "spine"
db::Shape::point_iterator pt = shape->begin_point ();
if (pt != shape->end_point ()) {
db::Point p (*pt);
++pt;
for (; pt != shape->end_point (); ++pt) {
test_edge (t * db::Edge (p, *pt), d, match);
p = *pt;
}
}
// convert to polygon and test those edges
db::Polygon poly;
shape->polygon (poly);
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
test_edge (t * *e, d, match);
}
// test if inside the polygon
if (! match && inside_poly (poly.begin_edge (), point) >= 0) {
d = t.ctrans (poly_dist (poly.begin_edge (), point));
match = true;
}
} else if (shape->is_box ()) {
const db::Box &box = shape->box ();
// point-like boxes are handles which attract the finder
if (box.width () == 0 && box.height () == 0) {
d = 0.0;
match = true;
} else {
// convert to polygon and test those edges
db::Polygon poly (box);
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
test_edge (t * *e, d, match);
}
if (! match && box.contains (search_box.center ())) {
d = t.ctrans (poly_dist (poly.begin_edge (), point));
match = true;
}
}
} else if (shape->is_text ()) {
db::Point tp (shape->text_trans () * db::Point ());
if (search_box.contains (tp)) {
d = t.ctrans (tp.distance (search_box.center ()));
match = true;
}
}
if (match) {
if (mp_excludes) {
// with an exclude list first create the selection item so we can check
// if it's part of the exclude set.
lay::ObjectInstPath found;
found.set_cv_index (m_cv_index);
found.set_topcell (m_topcell);
found.assign_path (path ().begin (), path ().end ());
found.set_layer (*l);
found.set_shape (*shape);
// in point mode just store the found object that has the least "distance" and is
// not in the exclude set
if (mp_excludes->find (found) == mp_excludes->end () && closer (d)) {
if (m_founds.empty ()) {
m_founds.push_back (found);
} else {
m_founds.front () = found;
}
}
} else if (closer (d)) {
// in point mode just store that found that has the least "distance"
if (m_founds.empty ()) {
m_founds.push_back (lay::ObjectInstPath ());
}
m_founds.back ().set_cv_index (m_cv_index);
m_founds.back ().set_topcell (m_topcell);
m_founds.back ().assign_path (path ().begin (), path ().end ());
m_founds.back ().set_layer (*l);
m_founds.back ().set_shape (*shape);
}
}
++shape;
}
}
}
}
}
// -------------------------------------------------------------
// InstFinder implementation
InstFinder::InstFinder (bool point_mode, bool top_level_sel, bool full_arrays, bool enclose_inst, const std::set<lay::ObjectInstPath> *excludes, bool visible_layers)
: Finder (point_mode, top_level_sel),
m_cv_index (0), m_topcell (0),
mp_excludes ((excludes && !excludes->empty ()) ? excludes : 0),
m_full_arrays (full_arrays),
m_enclose_insts (enclose_inst),
m_visible_layers (visible_layers),
mp_view (0),
mp_progress (0)
{
m_tries = inst_point_sel_tests;
}
bool
InstFinder::find (lay::LayoutView *view, const db::DBox &region_mu)
{
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Selecting ...")));
progress.set_unit (1000);
progress.set_format ("");
mp_progress = &progress;
std::set< std::pair<db::DCplxTrans, int> > variants = view->cv_transform_variants ();
for (std::set< std::pair<db::DCplxTrans, int> >::const_iterator v = variants.begin (); v != variants.end (); ++v) {
find (view, v->second, v->first, region_mu);
}
mp_progress = 0;
return ! m_founds.empty ();
}
bool
InstFinder::find (lay::LayoutView *view, unsigned int cv_index, const db::DCplxTrans &trans_mu, const db::DBox &region_mu)
{
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Selecting ...")));
progress.set_unit (1000);
progress.set_format ("");
mp_progress = &progress;
bool result = find_internal (view, cv_index, trans_mu, region_mu);
mp_progress = 0;
return result;
}
bool
InstFinder::find_internal (lay::LayoutView *view, unsigned int cv_index, const db::DCplxTrans &trans_mu, const db::DBox &region_mu)
{
const lay::CellView &cv = view->cellview (cv_index);
if (! cv.is_valid ()) {
return false;
}
m_visible_layer_indexes.clear ();
if (m_visible_layers) {
for (lay::LayerPropertiesConstIterator l = view->begin_layers (); !l.at_end (); ++l) {
if (! l->has_children () && l->visible (true /*real*/) && l->valid (true /*real*/) && l->cellview_index () == int (cv_index)) {
m_visible_layer_indexes.push_back (l->layer_index ());
}
}
}
if (!m_visible_layers || view->guiding_shapes_visible ()) {
m_visible_layer_indexes.push_back (cv->layout ().guiding_shape_layer ());
}
m_cv_index = cv_index;
m_topcell = cv.cell ()->cell_index ();
mp_view = view;
double dbu = cv->layout ().dbu ();
db::Box region = db::VCplxTrans (1.0 / dbu) * region_mu;
// actually find
try {
std::vector<db::ICplxTrans> tv;
tv.push_back (db::VCplxTrans (1.0 / dbu) * trans_mu * db::CplxTrans (dbu));
start (view, cv, cv_index, tv, region, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector<int> ());
} catch (StopException) {
// ..
}
// return true if anything was found
return ! m_founds.empty ();
}
void
InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::ICplxTrans &t, int level)
{
if (! point_mode ()) {
++*mp_progress;
// look for instances to check here ..
db::Cell::touching_iterator inst = cell.begin_touching (search_box);
while (! inst.at_end ()) {
const db::CellInstArray &cell_inst = inst->cell_inst ();
const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ());
++*mp_progress;
// just consider the instances exactly at the last level of
// hierarchy (this is where the boxes are drawn) or of cells that
// are hidden.
if (level == max_level () - 1 || inst_cell.is_proxy () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
db::box_convert <db::CellInst, false> bc (layout ());
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
++*mp_progress;
db::Box ibox;
if (inst_cell.bbox ().empty ()) {
ibox = db::Box (db::Point (0, 0), db::Point (0, 0));
} else if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
ibox = inst_cell.bbox ();
} else {
for (std::vector<int>::const_iterator l = m_visible_layer_indexes.begin (); l != m_visible_layer_indexes.end (); ++l) {
ibox += inst_cell.bbox (*l);
}
}
if (! ibox.empty ()) {
db::Box box = cell_inst.complex_trans (*p) * ibox;
// in box mode, just test the boxes
if (! m_enclose_insts || box.inside (search_box)) {
// in point mode just store that found that has the least "distance"
m_founds.push_back (lay::ObjectInstPath ());
m_founds.back ().set_cv_index (m_cv_index);
m_founds.back ().set_topcell (m_topcell);
m_founds.back ().assign_path (path ().begin (), path ().end ());
// add the selected instance as the last element of the path
db::InstElement el;
el.inst_ptr = *inst;
if (! m_full_arrays) {
el.array_inst = p;
}
m_founds.back ().add_path (el);
// Remove the selection if it's part of the excluded set
if (mp_excludes != 0 && mp_excludes->find (m_founds.back ()) != mp_excludes->end ()) {
m_founds.pop_back ();
}
// in "full arrays" mode, a single reference to that array is sufficient
if (m_full_arrays) {
break;
}
}
}
}
}
++inst;
}
} else {
if (--m_tries < 0) {
throw StopException ();
}
// look for instances to check here ..
db::Cell::touching_iterator inst = cell.begin_touching (search_box);
while (! inst.at_end ()) {
if (--m_tries < 0) {
throw StopException ();
}
const db::CellInstArray &cell_inst = inst->cell_inst ();
const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ());
// just consider the instances exactly at the last level of
// hierarchy (this is where the boxes are drawn) or if of cells that
// are hidden.
if (level == max_level () - 1 || inst_cell.is_proxy () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
db::box_convert <db::CellInst, false> bc (layout ());
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
if (--m_tries < 0) {
throw StopException ();
}
bool match = false;
double d = std::numeric_limits<double>::max ();
db::Box ibox;
if (inst_cell.bbox ().empty ()) {
ibox = db::Box (db::Point (0, 0), db::Point (0, 0));
} else if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
ibox = inst_cell.bbox ();
} else {
for (std::vector<int>::const_iterator l = m_visible_layer_indexes.begin (); l != m_visible_layer_indexes.end (); ++l) {
ibox += inst_cell.bbox (*l);
}
}
if (! ibox.empty ()) {
if (ibox.width () == 0 && ibox.height () == 0) {
match = true;
d = 0.0;
} else {
// convert to polygon and test those edges
db::Polygon poly (cell_inst.complex_trans (*p) * db::Polygon (ibox));
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
test_edge (t * *e, d, match);
}
if (! match && db::inside_poly (poly.begin_edge (), search_box.center ())) {
d = t.ctrans (poly_dist (poly.begin_edge (), search_box.center ()));
match = true;
}
}
d += 1; // the instance has a small penalty so that shapes win over instances
if (match && closer (d)) {
// in point mode just store that found that has the least "distance"
if (m_founds.empty ()) {
m_founds.push_back (lay::ObjectInstPath ());
}
m_founds.back ().set_cv_index (m_cv_index);
m_founds.back ().set_topcell (m_topcell);
m_founds.back ().assign_path (path ().begin (), path ().end ());
// add the selected instance as the last element of the path
db::InstElement el;
el.inst_ptr = *inst;
if (! m_full_arrays) {
el.array_inst = p;
}
m_founds.back ().add_path (el);
}
}
if (match) {
if (mp_excludes) {
// with an exclude list first create the selection item so we can check
// if it's part of the exclude set.
lay::ObjectInstPath found;
found.set_cv_index (m_cv_index);
found.set_topcell (m_topcell);
found.assign_path (path ().begin (), path ().end ());
// add the selected instance as the last element of the path
db::InstElement el;
el.inst_ptr = *inst;
if (! m_full_arrays) {
el.array_inst = p;
}
found.add_path (el);
// in point mode just store the found object that has the least "distance" and is
// not in the exclude set
if (mp_excludes->find (found) == mp_excludes->end () && closer (d)) {
if (m_founds.empty ()) {
m_founds.push_back (found);
} else {
m_founds.front () = found;
}
}
} else if (closer (d)) {
// in point mode just store that found that has the least "distance"
if (m_founds.empty ()) {
m_founds.push_back (lay::ObjectInstPath ());
}
m_founds.back ().set_cv_index (m_cv_index);
m_founds.back ().set_topcell (m_topcell);
m_founds.back ().assign_path (path ().begin (), path ().end ());
// add the selected instance as the last element of the path
db::InstElement el;
el.inst_ptr = *inst;
if (! m_full_arrays) {
el.array_inst = p;
}
m_founds.back ().add_path (el);
}
}
}
}
++inst;
}
}
}
} // namespace edt