Merge pull request #928 from KLayout/performance

Performance enhancements for hierarchical processor
This commit is contained in:
Matthias Köfferlein 2021-11-13 00:45:34 +01:00 committed by GitHub
commit c63ffb928c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 2059 additions and 1474 deletions

View File

@ -452,6 +452,22 @@ run_demo gen, "input1.drc(enclosing(input2) < 2.0.um)", "drc_enc1u.png"
run_demo gen, "input1.drc(enclosing(input2,\n"+
" projection) < 2.0.um)", "drc_enc2u.png"
class Gen
def produce(s1, s2)
s1.insert(RBA::Box::new(3000, 0, 6000, 6000))
s2.insert(RBA::Box::new(0, 1000, 6000, 7000))
end
end
gen = Gen::new
run_demo gen, "input1.enclosed(input2, 2.0.um)", "drc_encd1.png"
run_demo gen, "input1.enclosed(input2, 2.0.um, projection)", "drc_encd2.png"
run_demo gen, "input1.drc(enclosed(input2) < 2.0.um)", "drc_encd1u.png"
run_demo gen, "input1.drc(enclosed(input2,\n"+
" projection) < 2.0.um)", "drc_encd2u.png"
class Gen
def produce(s1, s2)

View File

@ -554,6 +554,7 @@ bool run_deep_xor (const XORData &xor_data)
{
db::DeepShapeStore dss;
dss.set_threads (xor_data.threads);
dss.set_keep_layouts (true); // avoids excessive cell mapping
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());
@ -598,6 +599,8 @@ bool run_deep_xor (const XORData &xor_data)
} else {
tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ());
db::RecursiveShapeIterator ri_a, ri_b;
if (ll->second.first >= 0) {
@ -612,15 +615,14 @@ bool run_deep_xor (const XORData &xor_data)
db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu));
db::Region xor_res;
xor_res = in_a ^ in_b;
{
tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ());
xor_res = in_a ^ in_b;
}
int tol_index = 0;
for (std::vector<double>::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) {
if (tl::verbosity () >= 20) {
tl::log << "Running XOR on layer " << ll->first.to_string () << " with tolerance " << *t;
}
db::LayerProperties lp = ll->first;
if (lp.layer >= 0) {
lp.layer += tol_index * xor_data.tolerance_bump;
@ -633,6 +635,7 @@ bool run_deep_xor (const XORData &xor_data)
result.top_cell = xor_data.output_cell;
if (*t > db::epsilon) {
tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ());
xor_res.size (-db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
xor_res.size (db::coord_traits<db::Coord>::rounded (0.5 * *t / dbu));
}

View File

@ -72,6 +72,7 @@ SOURCES = \
dbRecursiveInstanceIterator.cc \
dbRecursiveShapeIterator.cc \
dbRegion.cc \
dbRegionCheckUtils.cc \
dbRegionLocalOperations.cc \
dbSaveLayoutOptions.cc \
dbShape.cc \
@ -290,6 +291,7 @@ HEADERS = \
dbRecursiveInstanceIterator.h \
dbRecursiveShapeIterator.h \
dbRegion.h \
dbRegionCheckUtils.h \
dbRegionLocalOperations.h \
dbSaveLayoutOptions.h \
dbShape.h \

View File

@ -964,9 +964,7 @@ struct iterated_array
virtual std::pair <basic_array_iterator <Coord> *, bool>
begin_touching (const box_type &b) const
{
if (b.empty ()) {
return std::make_pair (new iterated_array_iterator <Coord> (m_v.begin (), m_v.end ()), false);
} else if (! b.touches (m_box)) {
if (b.empty () || ! b.touches (m_box)) {
return std::make_pair (new iterated_array_iterator <Coord> (m_v.end (), m_v.end ()), false);
} else {
box_convert_type bc;
@ -1699,7 +1697,7 @@ struct array
* an appropriate basic_array object using a complex transformation.
*/
array (const Obj &obj, const complex_trans_type &ct, const vector_type &a, const vector_type &b, unsigned long amax, unsigned long bmax)
: m_obj (obj), m_trans (ct), mp_base (new regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax) : new regular_array <coord_type> (a, b, amax, bmax))
{
// .. nothing yet ..
}
@ -1727,7 +1725,7 @@ struct array
* it's own storage.
*/
array (const Obj &obj, const complex_trans_type &ct, ArrayRepository &rep, const vector_type &a, const vector_type &b, unsigned long amax, unsigned long bmax)
: m_obj (obj), m_trans (ct), mp_base (rep.insert (regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax)))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (regular_complex_array <coord_type> (ct.rcos (), ct.mag (), a, b, amax, bmax)) : rep.insert (regular_array <coord_type> (a, b, amax, bmax)))
{
// .. nothing yet ..
}
@ -1766,7 +1764,7 @@ struct array
*/
template <class Iter>
array (const Obj &obj, const complex_trans_type &ct, Iter from, Iter to)
: m_obj (obj), m_trans (ct), mp_base (new iterated_complex_array <coord_type> (ct.rcos (), ct.mag (), from, to))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new iterated_complex_array <coord_type> (ct.rcos (), ct.mag (), from, to) : new iterated_array <coord_type> (from, to))
{
// .. nothing yet ..
}
@ -1779,7 +1777,7 @@ struct array
*/
explicit
array (const Obj &obj, const complex_trans_type &ct)
: m_obj (obj), m_trans (ct), mp_base (new single_complex_inst <coord_type> (ct.rcos (), ct.mag ()))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new single_complex_inst <coord_type> (ct.rcos (), ct.mag ()) : 0)
{
// .. nothing yet ..
}
@ -1809,7 +1807,7 @@ struct array
*/
explicit
array (const Obj &obj, const complex_trans_type &ct, ArrayRepository &rep)
: m_obj (obj), m_trans (ct), mp_base (rep.insert (single_complex_inst <coord_type> (ct.rcos (), ct.mag ())))
: m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (single_complex_inst <coord_type> (ct.rcos (), ct.mag ())) : 0)
{
// .. nothing yet ..
}
@ -1890,17 +1888,13 @@ struct array
array_iterator <coord_type, Trans> begin_touching (const box_type &b, const BoxConv &bc) const
{
if (b.empty ()) {
if (mp_base) {
return array_iterator <coord_type, Trans> (m_trans, mp_base->begin_touching (box_type ()));
} else {
return array_iterator <coord_type, Trans> (m_trans, true);
}
return array_iterator <coord_type, Trans> (m_trans, true);
} else if (b == box_type::world ()) {
return begin ();
} else if (mp_base) {
box_type ob (bc (m_obj));
if (ob.empty ()) {
return array_iterator <coord_type, Trans> (m_trans, mp_base->begin_touching (box_type ()));
return array_iterator <coord_type, Trans> (m_trans, true);
} else {
if (mp_base->is_complex ()) {
complex_trans_type ct = mp_base->complex_trans (simple_trans_type (m_trans));

View File

@ -1150,7 +1150,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord
size_t n = 0;
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
poly_check.enter (*p, n);
poly_check.single (*p, n);
n += 2;
}

View File

@ -487,6 +487,41 @@ Cell::hierarchy_levels () const
return m_hier_levels;
}
static bool
has_shapes_touching_impl (const db::Cell &cell, unsigned int layer, const db::Box &box)
{
if (! cell.shapes (layer).begin_touching (box, db::ShapeIterator::All).at_end ()) {
return true;
}
for (db::Cell::touching_iterator i = cell.begin_touching (box); ! i.at_end (); ++i) {
for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (box, db::box_convert<db::CellInst> (*cell.layout (), layer)); ! ia.at_end (); ++ia) {
db::Box cbox;
if (i->is_complex ()) {
cbox = i->complex_trans (*ia).inverted () * box;
} else {
cbox = (*ia).inverted () * box;
}
if (has_shapes_touching_impl (cell.layout ()->cell (i->cell_index ()), layer, cbox)) {
return true;
}
}
}
return false;
}
bool
Cell::has_shapes_touching (unsigned int layer, const db::Box &box) const
{
return has_shapes_touching_impl (*this, layer, box);
}
void
Cell::collect_caller_cells (std::set<cell_index_type> &callers) const
{

View File

@ -721,6 +721,11 @@ public:
return shapes (layer).begin_touching (box, flags, prop_sel, inv_prop_sel);
}
/**
* @brief A quick, recursive test whether the cell has shapes touching the given box on the given layer
*/
bool has_shapes_touching (unsigned int layer, const db::Box &box) const;
/**
* @brief Collect all calling cells (either calling this cell directly or indirectly)
*

View File

@ -269,6 +269,11 @@ void CellMapping::clear ()
m_b2a_mapping.clear ();
}
void CellMapping::swap (CellMapping &other)
{
m_b2a_mapping.swap (other.m_b2a_mapping);
}
std::vector<db::cell_index_type> CellMapping::source_cells () const
{
std::vector<db::cell_index_type> s;
@ -409,9 +414,9 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
void
CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
{
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Cell mapping")));
tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Cell mapping")));
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - first step: mapping instance count and instance identity";
}
@ -449,7 +454,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
++a;
} else {
if (tl::verbosity () >= 30) {
if (tl::verbosity () >= 50) {
size_t na = 0, nb = 0;
for (std::multimap<size_t, db::cell_index_type>::const_iterator aa = a; aa != cm_a.end () && aa->first == w; ++aa) {
++na;
@ -497,7 +502,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Checked cell " << layout_a.cell_name (a->second) << ": " << candidates [a->second].size () << " candidates remaining.";
}
@ -519,7 +524,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
++a;
}
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Mapping candidates:";
dump_mapping (candidates, layout_a, layout_b);
}
@ -536,7 +541,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
reduction = false;
++iteration;
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - iteration " << iteration << ": cross-instance cone reduction";
}
@ -553,7 +558,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
refined_cand.clear ();
refined_cand.insert (refined_cand.end (), cand->second.begin (), cand->second.end ());
if (tl::verbosity () >= 50) {
if (tl::verbosity () >= 70) {
tl::info << "--- Cell: " << layout_a.cell_name (cand->first);
tl::info << "Before reduction: " << tl::noendl;
for (size_t i = 0; i < refined_cand.size (); ++i) {
@ -582,7 +587,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
if (tl::verbosity () >= 70 && cout != refined_cand.end ()) {
tl::info << "Reduction because of caller mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
tl::info << " -> " << tl::noendl;
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
@ -619,7 +624,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
if (tl::verbosity () >= 70 && cout != refined_cand.end ()) {
tl::info << "Reduction because of callee mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
tl::info << " -> " << tl::noendl;
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
@ -646,7 +651,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
if (ed < uc->second.second) {
uc->second = std::make_pair (ca, ed);
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as new unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
}
}
@ -654,7 +659,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
} else {
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
unique_candidates.insert (std::make_pair (cb, std::make_pair (ca, ed)));
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
}
}
@ -679,12 +684,12 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "Further refined candidates:";
dump_mapping (candidates, layout_a, layout_b);
}
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - iteration " << iteration << ": removal of uniquely mapped cells on B side";
}
@ -709,15 +714,15 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
}
if (tl::verbosity () >= 40) {
if (tl::verbosity () >= 60) {
tl::info << "After reduction of mapped cells on b side:";
dump_mapping (candidates, layout_a, layout_b);
}
}
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
int total = 0;
int not_mapped = 0;
@ -747,7 +752,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
// Resolve mapping according to string match
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << "Cell mapping - string mapping as last resort";
}
@ -784,7 +789,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
}
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
int total = 0;
int not_mapped = 0;
@ -795,7 +800,7 @@ CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_ty
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
++total;
if (cand->second.size () == 0) {
if (tl::verbosity () >= 30) {
if (tl::verbosity () >= 50) {
tl::info << "Unmapped cell: " << layout_a.cell_name (cand->first);
}
++not_mapped;
@ -823,12 +828,12 @@ CellMapping::extract_unique (std::map <db::cell_index_type, std::vector<db::cell
{
if (cand->second.size () == 1) {
if (tl::verbosity () >= 20) {
if (tl::verbosity () >= 40) {
tl::info << " (U) " << layout_a.cell_name (cand->first) << " -> " << layout_b.cell_name (cand->second.front ()) << " (" << cand->first << " -> " << cand->second.front () << ")";
}
unique_mapping.insert (std::make_pair (cand->second.front (), cand->first));
} else if (tl::verbosity () >= 30) {
} else if (tl::verbosity () >= 50) {
tl::info << " " << layout_a.cell_name (cand->first) << " ->" << tl::noendl;
int n = 5;

View File

@ -64,6 +64,11 @@ public:
*/
void clear ();
/**
* @brief Swaps the cell mapping with another one
*/
void swap (CellMapping &other);
/**
* @brief Create a single cell mapping
*

View File

@ -22,7 +22,7 @@
#include "dbCellVariants.h"
#include "dbRegionUtils.h"
#include "dbPolygonTools.h"
#include "tlUtils.h"
namespace db

View File

@ -1725,7 +1725,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, c
s->polygon (poly);
do {
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (edge_check.prepare_next_pass ());
}

View File

@ -154,17 +154,25 @@ DeepLayer::add_from (const DeepLayer &dl)
db::cell_index_type source_cell = dl.initial_cell ().cell_index ();
const db::Layout *source_layout = &dl.layout ();
db::CellMapping cm;
cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell);
// create or reuse a layout mapping
// Actually copy the shapes
const db::CellMapping *cell_mapping = 0;
db::CellMapping cm;
if (store () == dl.store ()) {
cell_mapping = &const_cast<db::DeepShapeStore *> (mp_store.get ())->internal_cell_mapping (layout_index (), dl.layout_index ());
} else {
cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell);
cell_mapping = &cm;
}
// actually copy the shapes
std::map<unsigned int, unsigned int> lm;
lm.insert (std::make_pair (dl.layer (), layer ()));
std::vector <db::cell_index_type> source_cells;
source_cells.push_back (source_cell);
db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm);
db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cell_mapping->table (), lm);
}
}
@ -418,11 +426,13 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter
}
DeepShapeStore::DeepShapeStore ()
: m_keep_layouts (false)
{
++s_instance_count;
}
DeepShapeStore::DeepShapeStore (const std::string &topcell_name, double dbu)
: m_keep_layouts (false)
{
++s_instance_count;
@ -711,6 +721,16 @@ db::Layout &DeepShapeStore::layout (unsigned int n)
return m_layouts [n]->layout;
}
unsigned int DeepShapeStore::layout_index (const db::Layout *layout) const
{
for (std::vector<LayoutHolder *>::const_iterator i = m_layouts.begin (); i != m_layouts.end (); ++i) {
if (&(*i)->layout == layout) {
return (unsigned int) (i - m_layouts.begin ());
}
}
tl_assert (false);
}
size_t DeepShapeStore::instance_count ()
{
return s_instance_count;
@ -743,7 +763,7 @@ void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer)
}
if ((m_layouts[layout]->refs -= 1) <= 0) {
if ((m_layouts[layout]->refs -= 1) <= 0 && ! m_keep_layouts) {
delete m_layouts[layout];
m_layouts[layout] = 0;
clear_breakout_cells (layout);
@ -768,7 +788,6 @@ DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db:
}
db::Layout &layout = m_layouts[layout_index]->layout;
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
if (si.layout ()) {
layout.dbu (si.layout ()->dbu () / trans.mag ());
}
@ -792,7 +811,6 @@ void DeepShapeStore::make_layout (unsigned int layout_index, const db::Recursive
m_layouts[layout_index] = new LayoutHolder (trans);
db::Layout &layout = m_layouts[layout_index]->layout;
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
if (si.layout ()) {
layout.dbu (si.layout ()->dbu () / trans.mag ());
}
@ -915,17 +933,9 @@ DeepLayer DeepShapeStore::create_text_layer (const db::RecursiveShapeIterator &s
return create_custom_layer (si, &refs, trans);
}
void
DeepShapeStore::invalidate_hier ()
{
m_delivery_mapping_cache.clear ();
}
void
DeepShapeStore::issue_variants (unsigned int layout_index, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_map)
{
invalidate_hier ();
db::HierarchyBuilder &builder = m_layouts [layout_index]->builder;
for (std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> >::const_iterator i = var_map.begin (); i != var_map.end (); ++i) {
for (std::map<db::ICplxTrans, db::cell_index_type>::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
@ -934,6 +944,28 @@ DeepShapeStore::issue_variants (unsigned int layout_index, const std::map<db::ce
}
}
const db::CellMapping &
DeepShapeStore::internal_cell_mapping (unsigned int into_layout_index, unsigned int from_layout_index)
{
db::Layout &into_layout = layout (into_layout_index);
db::cell_index_type into_cell = initial_cell (into_layout_index).cell_index ();
const db::Layout &source_layout = layout (from_layout_index);
db::cell_index_type source_cell = initial_cell (from_layout_index).cell_index ();
std::map<std::pair<unsigned int, unsigned int>, CellMappingWithGenerationIds>::iterator cm = m_internal_mapping_cache.find (std::make_pair (from_layout_index, into_layout_index));
if (cm == m_internal_mapping_cache.end () || ! cm->second.is_valid (into_layout, source_layout)) {
cm = m_internal_mapping_cache.insert (std::make_pair (std::make_pair (from_layout_index, into_layout_index), CellMappingWithGenerationIds ())).first;
cm->second.clear ();
cm->second.create_from_geometry_full (into_layout, into_cell, source_layout, source_cell);
cm->second.set_generation_ids (into_layout, source_layout);
}
return cm->second;
}
const db::CellMapping &
DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells, const std::set<db::cell_index_type> *included_cells)
{
@ -953,10 +985,11 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
DeliveryMappingCacheKey key (layout_index, tl::id_of (into_layout), into_cell);
std::map<DeliveryMappingCacheKey, db::CellMapping>::iterator cm = m_delivery_mapping_cache.find (key);
if (cm == m_delivery_mapping_cache.end ()) {
std::map<DeliveryMappingCacheKey, CellMappingWithGenerationIds>::iterator cm = m_delivery_mapping_cache.find (key);
if (cm == m_delivery_mapping_cache.end () || ! cm->second.is_valid (*into_layout, *source_layout)) {
cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first;
cm = m_delivery_mapping_cache.insert (std::make_pair (key, CellMappingWithGenerationIds ())).first;
cm->second.clear ();
// collects the cell mappings we skip because they are variants (variant building or box variants)
std::map<db::cell_index_type, db::HierarchyBuilder::CellMapKey> cm_skipped_variants;
@ -1045,6 +1078,8 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
into_layout->delete_cells (cells_to_delete);
}
cm->second.set_generation_ids (*into_layout, *source_layout);
}
return cm->second;

View File

@ -32,6 +32,7 @@
#include "dbLayout.h"
#include "dbRecursiveShapeIterator.h"
#include "dbHierarchyBuilder.h"
#include "dbCellMapping.h"
#include "gsiObject.h"
#include <set>
@ -291,6 +292,41 @@ struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy
}
};
/**
* @brief An object holding a cell mapping with the hierarchy generation Ids of the involved layouts
*/
class DB_PUBLIC CellMappingWithGenerationIds
: public db::CellMapping
{
public:
CellMappingWithGenerationIds ()
: db::CellMapping (), m_into_generation_id (0), m_from_generation_id (0)
{
// .. nothing yet ..
}
void swap (CellMappingWithGenerationIds &other)
{
db::CellMapping::swap (other);
std::swap (m_into_generation_id, other.m_into_generation_id);
std::swap (m_from_generation_id, other.m_from_generation_id);
}
bool is_valid (const db::Layout &into_layout, const db::Layout &from_layout) const
{
return into_layout.hier_generation_id () == m_into_generation_id && from_layout.hier_generation_id () == m_from_generation_id;
}
void set_generation_ids (const db::Layout &into_layout, const db::Layout &from_layout)
{
m_into_generation_id = into_layout.hier_generation_id ();
m_from_generation_id = from_layout.hier_generation_id ();
}
private:
size_t m_into_generation_id, m_from_generation_id;
};
/**
* @brief The "deep shape store" is a working model for the hierarchical ("deep") processor
*
@ -339,6 +375,25 @@ public:
*/
bool is_singular () const;
/**
* @brief Sets a value indicating whether to keep layouts
*
* If this value is set to true, layouts are not released when their reference count
* goes down to zero.
*/
void set_keep_layouts (bool f)
{
m_keep_layouts = f;
}
/**
* @brief Gets a value indicating whether to keep layouts
*/
bool keep_layouts () const
{
return m_keep_layouts;
}
/**
* @brief Creates a new layer from a flat region (or the region is made flat)
*
@ -479,6 +534,11 @@ public:
*/
const db::CellMapping &cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells = 0, const std::set<db::cell_index_type> *included_cells = 0);
/**
* @brief Gets the cell mapping from one internal layout to another
*/
const db::CellMapping &internal_cell_mapping (unsigned int from_layout_index, unsigned int into_layout_index);
/**
* @brief Create cell variants from the given variant collector
*
@ -544,6 +604,11 @@ public:
*/
db::Cell &initial_cell (unsigned int n);
/**
* @brief Gets the layout index for a given internal layout
*/
unsigned int layout_index (const db::Layout *layout) const;
/**
* @brief Gets the singular layout (const version)
*
@ -730,7 +795,6 @@ private:
struct LayoutHolder;
void invalidate_hier ();
void add_ref (unsigned int layout, unsigned int layer);
void remove_ref (unsigned int layout, unsigned int layer);
@ -752,6 +816,7 @@ private:
layout_map_type m_layout_map;
DeepShapeStoreState m_state;
std::list<DeepShapeStoreState> m_state_stack;
bool m_keep_layouts;
tl::Mutex m_lock;
struct DeliveryMappingCacheKey
@ -780,7 +845,8 @@ private:
db::cell_index_type into_cell;
};
std::map<DeliveryMappingCacheKey, db::CellMapping> m_delivery_mapping_cache;
std::map<DeliveryMappingCacheKey, CellMappingWithGenerationIds> m_delivery_mapping_cache;
std::map<std::pair<unsigned int, unsigned int>, CellMappingWithGenerationIds> m_internal_mapping_cache;
};
template <class VarCollector>

View File

@ -1015,79 +1015,15 @@ private:
}
};
static bool
instances_interact (const db::Layout *layout1, const db::CellInstArray *inst1, unsigned int layer1, const db::Layout *layout2, const db::CellInstArray *inst2, unsigned int layer2, db::Coord dist)
{
// TODO: this algorithm is not in particular effective for identical arrays
const db::Cell &cell1 = layout1->cell (inst1->object ().cell_index ());
const db::Cell &cell2 = layout2->cell (inst2->object ().cell_index ());
db::box_convert <db::CellInst, true> inst2_bc (*layout2, layer2);
std::unordered_set<db::ICplxTrans> relative_trans_seen;
for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tn1 = inst1->complex_trans (*n);
db::ICplxTrans tni1 = tn1.inverted ();
db::Box ibox1 = tn1 * cell1.bbox (layer1).enlarged (db::Vector (dist, dist));
if (! ibox1.empty ()) {
// TODO: in some cases, it may be possible to optimize this for arrays
for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) {
if (inst1 == inst2 && *n == *k) {
// skip self-interactions - this is handled inside the cell
continue;
}
db::ICplxTrans tn2 = inst2->complex_trans (*k);
// NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
db::Box ibox2 = tn2 * cell2.bbox (layer2).enlarged (db::Vector (dist, dist));
db::ICplxTrans tn21 = tni1 * tn2;
if (! relative_trans_seen.insert (tn21).second) {
// this relative transformation was already seen
continue;
}
db::Box cbox = ibox1 & ibox2;
if (! cbox.empty ()) {
db::ICplxTrans tni2 = tn2.inverted ();
// not very strong, but already useful: the cells interact if there is a layer1 in cell1
// in the common box and a layer2 in the cell2 in the common box
// NOTE: don't use overlap mode for the RecursiveShapeIterator as this would not capture dot-like
// objects like texts. Instead safe-shrink the search box and use touching mode ("false" for the last
// argument)
if (! db::RecursiveShapeIterator (*layout1, cell1, layer1, safe_box_enlarged (tni1 * cbox, -1, -1), false).at_end () &&
! db::RecursiveShapeIterator (*layout2, cell2, layer2, safe_box_enlarged (tni2 * cbox, -1, -1), false).at_end ()) {
return true;
}
}
}
}
}
return false;
}
template <class T>
template <class TS, class TI, class TR>
struct interaction_registration_inst2inst
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, db::CellInstArray, unsigned int>
{
public:
typedef std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > interaction_value_type;
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, db::Coord dist, std::unordered_map<const db::CellInstArray *, interaction_value_type> *result)
interaction_registration_inst2inst (const db::Layout *subject_layout, unsigned int subject_layer, const db::Layout *intruder_layout, unsigned int intruder_layer, bool foreign, db::Coord dist, interactions_type *result)
: mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout), m_subject_layer (subject_layer), m_intruder_layer (intruder_layer), m_dist (dist), mp_result (result), m_foreign (foreign)
{
// nothing yet ..
@ -1109,8 +1045,8 @@ public:
}
}
if (! ignore && instances_interact (mp_subject_layout, inst1, m_subject_layer, mp_intruder_layout, inst2, m_intruder_layer, m_dist)) {
(*mp_result) [inst1].first.insert (inst2);
if (! ignore) {
collect_instance_interactions (inst1, inst2);
}
}
@ -1120,66 +1056,176 @@ private:
const db::Layout *mp_subject_layout, *mp_intruder_layout;
unsigned int m_subject_layer, m_intruder_layer;
db::Coord m_dist;
std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *mp_result;
interactions_type *mp_result;
std::unordered_set<std::pair<unsigned int, unsigned int> > m_interactions;
bool m_foreign;
};
template <class T>
static bool
instance_shape_interacts (const db::Layout *layout, const db::CellInstArray *inst, unsigned int layer, const T &ref, db::Coord dist)
{
const db::Cell &cell = layout->cell (inst->object ().cell_index ());
db::box_convert <db::CellInst, true> inst_bc (*layout, layer);
db::Box rbox = db::box_convert<T> () (ref);
void
collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2)
{
#if 0
printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n",
mp_subject_layout->cell_name (inst1->object ().cell_index ()), int (inst1->size ()), (inst1->bbox(db::box_convert<db::CellInst> (*mp_subject_layout)).to_string()).c_str(),
mp_intruder_layout->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert<db::CellInst> (*mp_intruder_layout)).to_string()).c_str()); // @@@
#endif
// TODO: this algorithm is not in particular effective for identical arrays
for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ());
const db::Cell &cell2 = mp_intruder_layout->cell (inst2->object ().cell_index ());
db::box_convert <db::CellInst, true> inst2_bc (*mp_intruder_layout, m_intruder_layer);
db::ICplxTrans tn = inst->complex_trans (*n);
db::Box cbox = (tn * cell.bbox (layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
std::unordered_map<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > > interactions_cache;
if (! cbox.empty ()) {
#if 0
printf("@@@ -> #%d\n", int(inst1->size()));
#endif
for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tni = tn.inverted ();
db::ICplxTrans tn1 = inst1->complex_trans (*n);
db::ICplxTrans tni1 = tn1.inverted ();
db::Box ibox1 = tn1 * cell1.bbox (m_subject_layer).enlarged (db::Vector (m_dist, m_dist));
std::set<db::CellInstArray> *insts = 0;
if (! ibox1.empty ()) {
// TODO: in some cases, it may be possible to optimize this for arrays
for (db::CellInstArray::iterator k = inst2->begin_touching (safe_box_enlarged (ibox1, -1, -1), inst2_bc); ! k.at_end (); ++k) {
if (inst1 == inst2 && *n == *k) {
// skip self-interactions - this is handled inside the cell
continue;
}
db::ICplxTrans tn2 = inst2->complex_trans (*k);
// NOTE: we need to enlarge both subject *and* intruder boxes - either object comes close to intruder or the other way around
db::Box ibox2 = tn2 * cell2.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
db::Box cbox = ibox1 & ibox2;
if (! cbox.empty () && cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1))) {
db::ICplxTrans tn21 = tni1 * tn2;
std::list<std::pair<db::cell_index_type, db::ICplxTrans> > *interactions = 0;
std::unordered_map<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > >::iterator ic = interactions_cache.find (tn21);
if (ic != interactions_cache.end ()) {
interactions = &ic->second;
} else {
interactions = &interactions_cache [tn21];
collect_intruder_tree_interactions (cell1, cell2, tni1, tn21, cbox, *interactions);
}
for (std::list<std::pair<db::cell_index_type, db::ICplxTrans> >::const_iterator i = interactions->begin (); i != interactions->end (); ++i) {
if (! insts) {
insts = & (*mp_result) [std::make_pair (cell1.cell_index (), tn1)].first;
}
insts->insert (db::CellInstArray (db::CellInst (i->first), i->second));
}
}
}
// not very strong, but already useful: the cells interact if there is a layer in cell
// in the common box
// NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
// dot edges. Instead safe-shrink the search box and use touching mode.
if (! db::RecursiveShapeIterator (*layout, cell, layer, safe_box_enlarged (tni * cbox, -1, -1), false).at_end ()) {
return true;
}
}
}
return false;
}
void collect_intruder_tree_interactions (const db::Cell &subject_cell, const db::Cell &intruder_cell, const db::ICplxTrans &tni1, const db::ICplxTrans &tn21, const db::Box &cbox, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > &interactions)
{
db::ICplxTrans tni2 = tn21.inverted () * tni1;
db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
template <class T>
if (! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
// we're overlapping with shapes from the intruder - do not recursive further
interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
return;
}
for (db::Cell::touching_iterator i = intruder_cell.begin_touching (tbox2); ! i.at_end (); ++i) {
const db::Cell &ic = mp_intruder_layout->cell (i->cell_index ());
for (db::CellInstArray::iterator ia = i->cell_inst ().begin_touching (tbox2, db::box_convert<db::CellInst> (*mp_intruder_layout, m_intruder_layer)); ! ia.at_end (); ++ia) {
db::ICplxTrans it = i->complex_trans (*ia);
db::Box ibox2 = tni2.inverted () * it * ic.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
db::Box ccbox = cbox & ibox2;
if (! ccbox.empty ()) {
collect_intruder_tree_interactions (subject_cell, ic, tni1, tn21 * it, ccbox, interactions);
}
}
}
}
};
template <class TS, class TI, class TR>
struct interaction_registration_inst2shape
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, T, unsigned int>
: db::box_scanner_receiver2<db::CellInstArray, unsigned int, TI, unsigned int>
{
public:
interaction_registration_inst2shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *result)
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result)
{
// nothing yet ..
}
void add (const db::CellInstArray *inst, unsigned int, const T *ref, unsigned int layer)
void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer)
{
if (instance_shape_interacts (mp_subject_layout, inst, m_subject_layer, *ref, m_dist)) {
(*mp_result) [inst].second [layer].insert (*ref);
}
collect_instance_shape_interactions (inst, layer, *ref, m_dist);
}
private:
const db::Layout *mp_subject_layout;
db::Layout *mp_subject_layout;
unsigned int m_subject_layer;
db::Coord m_dist;
std::unordered_map<const db::CellInstArray *, std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<T> > > > *mp_result;
interactions_type *mp_result;
void
collect_instance_shape_interactions (const db::CellInstArray *inst, unsigned int layer, const TI &ref, db::Coord dist)
{
const db::Cell &cell = mp_subject_layout->cell (inst->object ().cell_index ());
db::box_convert <db::CellInst, true> inst_bc (*mp_subject_layout, m_subject_layer);
db::Box rbox = db::box_convert<TI> () (ref);
for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) {
db::ICplxTrans tn = inst->complex_trans (*n);
db::Box cbox = (tn * cell.bbox (m_subject_layer)).enlarged (db::Vector (dist, dist)) & rbox.enlarged (db::Vector (dist, dist));
if (! cbox.empty ()) {
db::ICplxTrans tni = tn.inverted ();
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> rt (mp_subject_layout, tni);
std::set<TI> *shapes = 0;
// not very strong, but already useful: the cells interact if there is a layer in cell
// in the common box
// NOTE: don't use overlapping mode here, because this will not select point-like objects as texts or
// dot edges. Instead safe-shrink the search box and use touching mode.
for (db::RecursiveShapeIterator s (*mp_subject_layout, cell, m_subject_layer, safe_box_enlarged (tni * cbox, -1, -1), false); !s.at_end (); ++s) {
if (! shapes) {
shapes = & (*mp_result) [std::make_pair (cell.cell_index (), tn)].second [layer];
}
shapes->insert (rt (ref));
}
}
}
}
};
}
@ -1436,6 +1482,9 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist) const
{
#if 0 // @@@
printf("@@@ --- compute_contexts (%s @ %s)\n", mp_subject_layout->cell_name (subject_cell->cell_index ()), subject_cell_inst.to_string().c_str()); fflush(stdout);
#endif
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
if (tl::verbosity () >= m_base_verbosity + 20) {
@ -1504,26 +1553,32 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
if (! subject_cell->begin ().at_end ()) {
typedef std::pair<std::unordered_set<const db::CellInstArray *>, std::map<unsigned int, std::unordered_set<TI> > > interaction_value_type;
std::unordered_map<const db::CellInstArray *, interaction_value_type> interactions;
// Key: single instance given by cell index and transformation
// Value the contexts for the child cell for this instance
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interactions_type interactions;
// insert dummy interactions to handle at least the child cell vs. itself
// - this is important so we will always handle the instances unless they are
// entirely empty in the subject layer
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty ()) {
interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ()));
db::cell_index_type ci = i->cell_inst ().object ().cell_index ();
for (db::CellInstArray::iterator n = i->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tn = i->complex_trans (*n);
interactions.insert (std::make_pair (std::make_pair (ci, tn), interactions_value_type ()));
}
}
}
// TODO: can we shortcut this if interactions is empty?
// TODO: can we shortcut this if interactions is empty?
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
db::box_convert <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner;
interaction_registration_inst2inst<TI> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
unsigned int id = 0;
@ -1570,10 +1625,10 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
}
// TODO: can we shortcut this if interactions is empty?
// TODO: can we shortcut this if interactions is empty?
{
db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
interaction_registration_inst2shape<TI> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
@ -1596,76 +1651,13 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
}
// this cache should reduce the effort of checking array vs. array
typedef std::pair<unsigned int, std::pair<db::cell_index_type, db::ICplxTrans> > effective_instance_cache_key_type;
typedef std::map<effective_instance_cache_key_type, std::pair<bool, db::CellInstArray> > effective_instance_cache_type;
effective_instance_cache_type effective_instance_cache;
// produce the tasks for computing the next-level interactions
for (typename interactions_type::iterator i = interactions.begin (); i != interactions.end (); ++i) {
for (typename std::unordered_map<const db::CellInstArray *, interaction_value_type>::const_iterator i = interactions.begin (); i != interactions.end (); ++i) {
db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first);
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ());
for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) {
db::ICplxTrans tn = i->first->complex_trans (*n);
db::ICplxTrans tni = tn.inverted ();
db::Box nbox = tn * subject_child_cell.bbox (contexts.subject_layer ()).enlarged (db::Vector (dist, dist));
if (! nbox.empty ()) {
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type intruders_below;
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> rt (mp_subject_layout, tni);
for (typename std::map<unsigned int, std::unordered_set<TI> >::const_iterator pl = i->second.second.begin (); pl != i->second.second.end (); ++pl) {
std::set<TI> &out = intruders_below.second [pl->first];
for (typename std::unordered_set<TI>::const_iterator p = pl->second.begin (); p != pl->second.end (); ++p) {
if (nbox.overlaps (db::box_convert<TI> () (*p))) {
out.insert (rt (*p));
}
}
}
// TODO: in some cases, it may be possible to optimize this for arrays
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
unsigned int ail = contexts.actual_intruder_layer (*il);
db::box_convert <db::CellInst, true> inst_bcii (*mp_intruder_layout, ail);
for (std::unordered_set<const db::CellInstArray *>::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) {
for (db::CellInstArray::iterator k = (*j)->begin_touching (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
db::ICplxTrans tk = (*j)->complex_trans (*k);
// NOTE: no self-interactions
if (i->first != *j || tn != tk) {
// optimize the intruder instance so it will be as low as possible
effective_instance_cache_key_type key (ail, std::make_pair ((*j)->object ().cell_index (), tni * tk));
effective_instance_cache_type::iterator cached = effective_instance_cache.find (key);
if (cached == effective_instance_cache.end ()) {
std::pair<bool, db::CellInstArray> ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
cached = effective_instance_cache.insert (std::make_pair (key, ei)).first;
}
if (cached->second.first) {
intruders_below.first.insert (cached->second.second);
}
}
}
}
}
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? &subject_child_cell : 0);
issue_compute_contexts (contexts, cell_context, subject_cell, &subject_child_cell, tn, intruder_child_cell, intruders_below, dist);
}
}
issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist);
}
@ -1673,63 +1665,6 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
}
/**
* @brief Returns a cell instance array suitable for adding as intruder
*
* The given intruder cell with the transformation ti2s - which transforms the intruder instance into
* the coordinate system of the subject cell - is analysed and either this instance or a sub-instance
* is chosen.
* Sub-instances are chosen if the intruder cell does not have shapes which interact with the subject
* cell and there is exactly one sub-instance interacting with the subject cell.
*/
template <class TS, class TI, class TR>
std::pair<bool, db::CellInstArray>
local_processor<TS, TI, TR>::effective_instance (unsigned int subject_layer, db::cell_index_type subject_cell_index, unsigned int intruder_layer, db::cell_index_type intruder_cell_index, const db::ICplxTrans &ti2s, db::Coord dist) const
{
db::Box bbox = safe_box_enlarged (mp_subject_layout->cell (subject_cell_index).bbox (subject_layer), dist - 1, dist - 1);
if (bbox.empty ()) {
// should not happen, but skip if it does
return std::make_pair (false, db::CellInstArray ());
}
db::Box ibbox = bbox.transformed (ti2s.inverted ());
const db::Cell &intruder_cell = mp_intruder_layout->cell (intruder_cell_index);
const db::Shapes &intruder_shapes = intruder_cell.shapes (intruder_layer);
if (! intruder_shapes.empty () && ! intruder_shapes.begin_touching (ibbox, db::ShapeIterator::All).at_end ()) {
return std::make_pair (true, db::CellInstArray (db::CellInst (intruder_cell_index), ti2s));
}
db::box_convert <db::CellInst, true> inst_bcii (*mp_intruder_layout, intruder_layer);
size_t ni = 0;
db::cell_index_type eff_cell_index = 0;
db::ICplxTrans eff_trans;
for (db::Cell::touching_iterator i = intruder_cell.begin_touching (ibbox); ! i.at_end() && ni < 2; ++i) {
const db::CellInstArray &ci = i->cell_inst ();
db::Box cbox = mp_intruder_layout->cell (ci.object ().cell_index ()).bbox (intruder_layer);
for (db::CellInstArray::iterator k = ci.begin_touching (ibbox, inst_bcii); ! k.at_end () && ni < 2; ++k) {
db::ICplxTrans tk = ci.complex_trans (*k);
if (ibbox.overlaps (cbox.transformed (tk))) {
eff_trans = tk;
eff_cell_index = ci.object ().cell_index ();
++ni;
}
}
}
if (ni == 0) {
// should not happen, but skip if it does
return std::make_pair (false, db::CellInstArray ());
} else if (ni == 1) {
// one instance - dive down
return effective_instance (subject_layer, subject_cell_index, intruder_layer, eff_cell_index, ti2s * eff_trans, dist);
} else {
return std::make_pair (true, db::CellInstArray (db::CellInst (intruder_cell_index), ti2s));
}
}
template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, const std::vector<unsigned int> &output_layers) const

View File

@ -381,14 +381,16 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
} else {
db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox ();
db::cell_index_type inst_cell = inst.object ().cell_index ();
db::Box cell_bbox = iter->cell_bbox (inst_cell);
std::pair<bool, std::set<db::Box> > clip_variant = compute_clip_variant (cell_bbox, trans, region, complex_region);
if (! clip_variant.first) {
return false;
}
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), clip_variant.second);
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second);
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell));
// for a new cell, create this instance
if (m_cell_stack.back ().first) {

View File

@ -528,12 +528,12 @@ public:
*
* The editable mode will be taken from db::default_editable_mode.
*/
Layout (db::Manager *manager = 0);
explicit Layout (db::Manager *manager = 0);
/**
* @brief Standard constructor which allows one to specify editable mode
*/
Layout (bool editable, db::Manager *manager = 0);
explicit Layout (bool editable, db::Manager *manager = 0);
/**
* @brief The copy ctor

View File

@ -29,13 +29,13 @@ namespace db
{
LayoutStateModel::LayoutStateModel (bool busy)
: m_hier_dirty (false), m_all_bboxes_dirty (false), m_busy (busy)
: m_hier_dirty (false), m_hier_generation_id (0), m_all_bboxes_dirty (false), m_busy (busy)
{
// .. nothing yet ..
}
LayoutStateModel::LayoutStateModel (const LayoutStateModel &d)
: m_hier_dirty (d.m_hier_dirty), m_bboxes_dirty (d.m_bboxes_dirty), m_all_bboxes_dirty (d.m_all_bboxes_dirty), m_busy (d.m_busy)
: m_hier_dirty (d.m_hier_dirty), m_hier_generation_id (d.m_hier_generation_id), m_bboxes_dirty (d.m_bboxes_dirty), m_all_bboxes_dirty (d.m_all_bboxes_dirty), m_busy (d.m_busy)
{
// .. nothing yet ..
}
@ -44,6 +44,7 @@ LayoutStateModel &
LayoutStateModel::operator= (const LayoutStateModel &d)
{
m_hier_dirty = d.m_hier_dirty;
m_hier_generation_id = d.m_hier_generation_id;
m_bboxes_dirty = d.m_bboxes_dirty;
m_all_bboxes_dirty = d.m_all_bboxes_dirty;
m_busy = d.m_busy;

View File

@ -85,6 +85,7 @@ public:
*/
void invalidate_hier ()
{
++m_hier_generation_id;
if (! m_hier_dirty || m_busy) {
do_invalidate_hier (); // must be called before the hierarchy is invalidated (stopping of redraw thread requires this)
m_hier_dirty = true;
@ -127,6 +128,17 @@ public:
return m_hier_dirty;
}
/**
* @brief Gets the hierarchy generation ID
*
* The hierarchy generation ID is a number which is incremented on every hierarchy
* change.
*/
size_t hier_generation_id () const
{
return m_hier_generation_id;
}
/**
* @brief The "dirty bounding box" attribute
*
@ -195,6 +207,7 @@ public:
private:
bool m_hier_dirty;
size_t m_hier_generation_id;
std::vector<bool> m_bboxes_dirty;
bool m_all_bboxes_dirty;
bool m_busy;

View File

@ -23,7 +23,7 @@
#include "dbLayoutUtils.h"
#include "dbCellVariants.h"
#include "dbRegionUtils.h"
#include "dbPolygonTools.h"
#include "tlProgress.h"
namespace db

View File

@ -2827,5 +2827,93 @@ decompose_trapezoids (const db::SimplePolygon &sp, TrapezoidDecompositionMode mo
}
}
// -------------------------------------------------------------------------------------
// Polygon snapping
db::Polygon
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap)
{
db::Polygon pnew;
for (size_t i = 0; i < poly.holes () + 1; ++i) {
heap.clear ();
db::Polygon::polygon_contour_iterator b, e;
if (i == 0) {
b = poly.begin_hull ();
e = poly.end_hull ();
} else {
b = poly.begin_hole ((unsigned int) (i - 1));
e = poly.end_hole ((unsigned int) (i - 1));
}
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy)));
}
if (i == 0) {
pnew.assign_hull (heap.begin (), heap.end ());
} else {
pnew.insert_hole (heap.begin (), heap.end ());
}
}
return pnew;
}
db::Polygon
scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap)
{
db::Polygon pnew;
int64_t dgx = int64_t (gx) * int64_t (dx);
int64_t dgy = int64_t (gy) * int64_t (dy);
for (size_t i = 0; i < poly.holes () + 1; ++i) {
heap.clear ();
db::Polygon::polygon_contour_iterator b, e;
if (i == 0) {
b = poly.begin_hull ();
e = poly.end_hull ();
} else {
b = poly.begin_hole ((unsigned int) (i - 1));
e = poly.end_hole ((unsigned int) (i - 1));
}
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy);
heap.push_back (db::Point (db::Coord (x), db::Coord (y)));
}
if (i == 0) {
pnew.assign_hull (heap.begin (), heap.end ());
} else {
pnew.insert_hole (heap.begin (), heap.end ());
}
}
return pnew;
}
db::Vector
scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy)
{
int64_t dgx = int64_t (gx) * int64_t (dx);
int64_t dgy = int64_t (gy) * int64_t (dy);
int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy);
return db::Vector (db::Coord (x), db::Coord (y));
}
}

View File

@ -760,6 +760,37 @@ void DB_PUBLIC decompose_trapezoids (const db::Polygon &p, TrapezoidDecompositio
*/
void DB_PUBLIC decompose_trapezoids (const db::SimplePolygon &p, TrapezoidDecompositionMode mode, SimplePolygonSink &sink);
template <class C>
static inline C snap_to_grid (C c, C g)
{
// This form of snapping always snaps g/2 to right/top.
if (c < 0) {
c = -g * ((-c + (g - 1) / 2) / g);
} else {
c = g * ((c + g / 2) / g);
}
return c;
}
/**
* @brief Snaps a polygon to the given grid
* Heap is a vector of points reused for the point list
*/
DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
/**
* @brief Scales and snaps a polygon to the given grid
* Heap is a vector of points reused for the point list
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
*/
DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap);
/**
* @brief Scales and snaps a vector to the given grid
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
*/
DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy);
}
#endif

View File

@ -646,7 +646,7 @@ RecursiveShapeIterator::skip_inst_iter_for_complex_region () const
// skip insts outside the complex region
if (! m_inst.at_end ()) {
if (! is_outside_complex_region (m_inst->bbox ())) {
if (! is_outside_complex_region (m_inst->bbox (m_box_convert))) {
break;
} else {
++m_inst;
@ -775,7 +775,7 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const
// compute the region inside the new cell
if (new_region != m_region) {
new_region = m_trans.inverted () * m_region;
new_region &= cell ()->bbox ();
new_region &= cell_bbox (cell_index ());
}
m_local_region_stack.push_back (new_region);
@ -969,7 +969,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const
// 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 ());
db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ());
if (! is_outside_complex_region (ia_box)) {
break;
} else {

View File

@ -665,6 +665,16 @@ public:
return reinterpret_cast<const cell_type *> (c - (c & size_t (3)));
}
/**
* @brief Gets the current cell's bounding box
*
* The returned box is limited to the selected layer if applicable.
*/
box_type cell_bbox (db::cell_index_type cell_index) const
{
return m_box_convert (db::CellInst (cell_index));
}
/**
* @brief Increments the iterator (operator version)
*/

View File

@ -0,0 +1,638 @@
/*
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 "dbRegionCheckUtils.h"
#include "dbPolygonTools.h"
#include "dbEdgeBoolean.h"
#include "tlSelect.h"
namespace db
{
// -------------------------------------------------------------------------------------
// Edge2EdgeCheckBase implementation
Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons),
m_first_pseudo (std::numeric_limits<size_t>::max ()),
m_with_shielding (with_shielding),
m_symmetric_edges (symmetric_edges),
m_has_edge_pair_output (true),
m_has_negative_edge_output (false),
m_pass (0)
{
m_distance = check.distance ();
}
bool
Edge2EdgeCheckBase::prepare_next_pass ()
{
++m_pass;
if (m_pass == 1) {
m_first_pseudo = m_ep.size ();
if (m_with_shielding && ! m_ep.empty ()) {
m_ep_discarded.resize (m_ep.size (), false);
// second pass:
return true;
} else if (m_has_negative_edge_output) {
// second pass:
return true;
}
}
if (! m_ep.empty () && m_has_edge_pair_output) {
std::vector<bool>::const_iterator d = m_ep_discarded.begin ();
std::vector<bool>::const_iterator i = m_ep_intra_polygon.begin ();
std::vector<db::EdgePair>::const_iterator ep = m_ep.begin ();
while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) {
bool use_result = true;
if (d != m_ep_discarded.end ()) {
use_result = ! *d;
++d;
}
if (use_result) {
put (*ep, *i);
}
++ep;
++i;
}
}
return false;
}
static inline bool shields (const db::EdgePair &ep, const db::Edge &q)
{
db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ());
db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ());
std::pair<bool, db::Point> ip1 = pe1.intersect_point (q);
std::pair<bool, db::Point> ip2 = pe2.intersect_point (q);
if (ip1.first && ip2.first) {
return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0);
} else {
return false;
}
}
void
Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
{
if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) {
std::pair<db::Edge, size_t> k (*o, p);
std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i0 = m_e2ep.find (k);
bool fully_removed = false;
bool any = false;
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
any = true;
fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o);
}
}
if (! any) {
put_negative (*o, (int) p);
} else if (! fully_removed) {
std::set<db::Edge> partial_edges;
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
ec.add (o, 0);
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1);
}
}
ec.finish ();
for (std::set<db::Edge>::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) {
put_negative (*e, (int) p);
}
}
}
}
bool
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
{
if (m_pass == 1) {
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
scanner.insert (&e->first, e->second);
}
return ! m_pseudo_edges.empty ();
} else {
return false;
}
}
inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2)
{
if (p1 == p2) {
if (requires_different_polygons) {
return false;
} else if ((p1 & size_t (1)) != 0) {
// edges from the same polygon are only considered on first layer.
// Reasoning: this case happens when "intruder" polygons are put on layer 1
// while "subject" polygons are put on layer 0. We don't want "intruders"
// to generate intra-polygon markers.
return false;
}
}
if (((p1 ^ p2) & size_t (1)) == 0) {
if (requires_different_layers) {
return false;
} else if ((p1 & size_t (1)) != 0) {
// edges on the same layer are only considered on first layer.
// Reasoning: this case happens when "intruder" polygons are put on layer 1
// while "subject" polygons are put on layer 0. We don't want "intruders"
// to generate inter-polygon markers between them.
return false;
}
}
return true;
}
void
Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
if (m_pass == 0) {
// Overlap or inside checks require input from different layers
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
if (l1 > l2) {
std::swap (o1, o2);
std::swap (p1, p2);
}
db::EdgePair ep;
if (mp_check->check (*o1, *o2, &ep)) {
ep.set_symmetric (m_symmetric_edges);
// found a violation: store inside the local buffer for now. In the second
// pass we will eliminate those which are shielded completely (with shielding)
// and/or compute the negative edges.
size_t n = m_ep.size ();
m_ep.push_back (ep);
m_ep_intra_polygon.push_back (p1 == p2);
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
if (m_has_negative_edge_output) {
bool antiparallel = (mp_check->relation () == WidthRelation || mp_check->relation () == SpaceRelation);
// pseudo1 and pseudo2 are the connecting edges of the edge pairs. Together with the
// original edges they form a quadrangle.
db::Edge pseudo1 (ep.first ().p1 (), antiparallel ? ep.second ().p2 () : ep.second ().p1 ());
db::Edge pseudo2 (antiparallel ? ep.second ().p1 () : ep.second ().p2 (), ep.first ().p2 ());
m_pseudo_edges.insert (std::make_pair (pseudo1, p1));
m_pseudo_edges.insert (std::make_pair (pseudo2, p1));
if (p1 != p2) {
m_pseudo_edges.insert (std::make_pair (pseudo1, p2));
m_pseudo_edges.insert (std::make_pair (pseudo2, p2));
}
}
}
}
} else {
// set the discarded flags for shielded output
if (m_with_shielding) {
// a simple (complete) shielding implementation which is based on the
// assumption that shielding is relevant as soon as a foreign edge cuts through
// both of the edge pair's connecting edges.
// TODO: this implementation does not take into account the nature of the
// EdgePair - because of "whole_edge" it may not reflect the part actually
// violating the distance.
std::vector<size_t> n1, n2;
for (unsigned int p = 0; p < 2; ++p) {
std::pair<db::Edge, size_t> k (*o1, p1);
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n < m_first_pseudo && ! m_ep_discarded [n]) {
n1.push_back (n);
}
}
std::sort (n1.begin (), n1.end ());
std::swap (o1, o2);
std::swap (p1, p2);
n1.swap (n2);
}
for (unsigned int p = 0; p < 2; ++p) {
std::vector<size_t> nn;
std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn));
for (std::vector<size_t>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::EdgePair ep = m_ep [*i].normalized ();
if (shields (ep, *o2)) {
m_ep_discarded [*i] = true;
}
}
std::swap (o1, o2);
std::swap (p1, p2);
n1.swap (n2);
}
}
// for negative output edges are cancelled by short interactions perpendicular to them
// For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real
// edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the
// negative case this means we cancel a real edge.
if (m_has_negative_edge_output &&
(m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) {
// Overlap or inside checks require input from different layers
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
if (l1 > l2) {
std::swap (o1, o2);
std::swap (p1, p2);
}
db::EdgePair ep;
if (mp_check->check (*o1, *o2, &ep)) {
size_t n = m_ep.size ();
m_ep.push_back (ep);
m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
}
}
}
}
}
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool
Edge2EdgeCheckBase::requires_different_layers () const
{
return m_requires_different_layers;
}
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void
Edge2EdgeCheckBase::set_requires_different_layers (bool f)
{
m_requires_different_layers = f;
}
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool
Edge2EdgeCheckBase::different_polygons () const
{
return m_different_polygons;
}
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void
Edge2EdgeCheckBase::set_different_polygons (bool f)
{
m_different_polygons = f;
}
/**
* @brief Gets the distance value
*/
EdgeRelationFilter::distance_type
Edge2EdgeCheckBase::distance () const
{
return m_distance;
}
// -------------------------------------------------------------------------------------
// Poly2PolyCheckBase implementation
template <class PolygonType>
poly2poly_check<PolygonType>::poly2poly_check (Edge2EdgeCheckBase &output)
: mp_output (& output)
{
// .. nothing yet ..
}
template <class PolygonType>
poly2poly_check<PolygonType>::poly2poly_check ()
: mp_output (0)
{
// .. nothing yet ..
}
static size_t vertices (const db::Polygon &p)
{
return p.vertices ();
}
static size_t vertices (const db::PolygonRef &p)
{
return p.obj ().vertices ();
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::single (const PolygonType &o, size_t p)
{
tl_assert (! mp_output->requires_different_layers () && ! mp_output->different_polygons ());
// finally we check the polygons vs. itself for checks involving intra-polygon interactions
m_scanner.clear ();
m_scanner.reserve (vertices (o));
m_edge_heap.clear ();
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
m_edge_heap.push_back (*e);
m_scanner.insert (& m_edge_heap.back (), p);
}
mp_output->feed_pseudo_edges (m_scanner);
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::connect (Edge2EdgeCheckBase &output)
{
mp_output = &output;
clear ();
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::clear ()
{
m_scanner.clear ();
m_edge_heap.clear ();
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
{
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
m_edge_heap.push_back (*e);
m_scanner.insert (& m_edge_heap.back (), p);
}
}
// TODO: move to generic header
static bool interact (const db::Box &box, const db::Edge &e)
{
if (! e.bbox ().touches (box)) {
return false;
} else if (e.is_ortho ()) {
return true;
} else {
return e.clipped (box).first;
}
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p, const poly2poly_check<PolygonType>::box_type &box)
{
if (box.empty ()) {
return;
}
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
if (interact (box, *e)) {
m_edge_heap.push_back (*e);
m_scanner.insert (& m_edge_heap.back (), p);
}
}
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::process ()
{
mp_output->feed_pseudo_edges (m_scanner);
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
}
// explicit instantiations
template class poly2poly_check<db::Polygon>;
template class poly2poly_check<db::PolygonRef>;
// -------------------------------------------------------------------------------------
// RegionToEdgeInteractionFilterBase implementation
template <class PolygonType, class EdgeType, class OutputType>
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::region_to_edge_interaction_filter_base (bool inverse, bool get_all)
: m_inverse (inverse), m_get_all (get_all)
{
// .. nothing yet ..
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::preset (const OutputType *s)
{
m_seen.insert (s);
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::add (const PolygonType *p, size_t, const EdgeType *e, size_t)
{
const OutputType *o = 0;
tl::select (o, p, e);
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
// A polygon and an edge interact if the edge is either inside completely
// of at least one edge of the polygon intersects with the edge
bool interacts = false;
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
interacts = true;
} else {
for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
if ((*pe).intersect (*e)) {
interacts = true;
}
}
}
if (interacts) {
if (m_inverse) {
m_seen.erase (o);
} else {
if (! m_get_all) {
m_seen.insert (o);
}
put (*o);
}
}
}
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::fill_output ()
{
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
put (**s);
}
}
// explicit instantiations
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Polygon>;
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::PolygonRef>;
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Edge>;
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::Edge>;
// -------------------------------------------------------------------------------------
// RegionToTextInteractionFilterBase implementation
template <class PolygonType, class TextType, class OutputType>
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::region_to_text_interaction_filter_base (bool inverse, bool get_all)
: m_inverse (inverse), m_get_all (get_all)
{
// .. nothing yet ..
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::preset (const OutputType *s)
{
m_seen.insert (s);
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::add (const PolygonType *p, size_t, const TextType *t, size_t)
{
const OutputType *o = 0;
tl::select (o, p, t);
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
// A polygon and an text interact if the text is either inside completely
// of at least one text of the polygon intersects with the text
db::Point pt = db::box_convert<TextType> () (*t).p1 ();
if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) {
if (m_inverse) {
m_seen.erase (o);
} else {
if (! m_get_all) {
m_seen.insert (o);
}
put (*o);
}
}
}
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::fill_output ()
{
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
put (**s);
}
}
// explicit instantiations
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::PolygonRef>;
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Polygon>;
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Text>;
template class region_to_text_interaction_filter_base<db::Polygon, db::TextRef, db::TextRef>;
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::TextRef>;
}

View File

@ -0,0 +1,427 @@
/*
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_dbRegionCheckUtils
#define HDR_dbRegionCheckUtils
#include "dbCommon.h"
#include "dbCellVariants.h"
#include "dbBoxScanner.h"
#include "dbEdgePairRelations.h"
namespace db {
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*/
class DB_PUBLIC Edge2EdgeCheckBase
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges);
/**
* @brief Call this to initiate a new pass until the return value is false
*/
bool prepare_next_pass ();
/**
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
* (required for negative edge output - cancellation of perpendicular edges)
*/
bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
/**
* @brief Reimplementation of the box_scanner_receiver interface
*/
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2);
/**
* @brief Reimplementation of the box_scanner_receiver interface
*/
void finish (const Edge *o, size_t);
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool requires_different_layers () const;
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void set_requires_different_layers (bool f);
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool different_polygons () const;
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void set_different_polygons (bool f);
/**
* @brief Sets a flag indicating that this class wants negative edge output
*/
void set_has_negative_edge_output (bool f)
{
m_has_negative_edge_output = f;
}
/**
* @brief Gets a flag indicating that this class wants negative edge output
*/
bool has_negative_edge_output () const
{
return m_has_negative_edge_output;
}
/**
* @brief Sets a flag indicating that this class wants normal edge pair output
*/
void set_has_edge_pair_output (bool f)
{
m_has_edge_pair_output = f;
}
/**
* @brief Gets a flag indicating that this class wants normal edge pair output
*/
bool has_edge_pair_output () const
{
return m_has_edge_pair_output;
}
/**
* @brief Gets the distance value
*/
EdgeRelationFilter::distance_type distance () const;
protected:
/**
* @brief Normal edge pair output (violations)
*/
virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { }
/**
* @brief Negative edge output
*/
virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { }
private:
const EdgeRelationFilter *mp_check;
bool m_requires_different_layers;
bool m_different_polygons;
EdgeRelationFilter::distance_type m_distance;
std::vector<db::EdgePair> m_ep;
std::multimap<std::pair<db::Edge, size_t>, size_t> m_e2ep;
std::set<std::pair<db::Edge, size_t> > m_pseudo_edges;
size_t m_first_pseudo;
std::vector<bool> m_ep_discarded, m_ep_intra_polygon;
bool m_with_shielding;
bool m_symmetric_edges;
bool m_has_edge_pair_output;
bool m_has_negative_edge_output;
unsigned int m_pass;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
*/
template <class Output>
class DB_PUBLIC_TEMPLATE edge2edge_check
: public Edge2EdgeCheckBase
{
public:
edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0)
{
// .. nothing yet ..
}
edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra)
{
// .. nothing yet ..
}
protected:
void put (const db::EdgePair &edge, bool inter_polygon) const
{
if (! inter_polygon || ! mp_output_intra) {
mp_output_inter->insert (edge);
} else {
mp_output_intra->insert (edge);
}
}
private:
Output *mp_output_inter;
Output *mp_output_intra;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version allows delivery of the negative edges.
*/
template <class Output, class NegativeEdgeOutput>
class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output
: public edge2edge_check<Output>
{
public:
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
edge2edge_check<Output>::set_has_negative_edge_output (true);
}
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
edge2edge_check<Output>::set_has_negative_edge_output (true);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
mp_l1_negative_output->insert (edge);
}
if (layer == 1) {
mp_l2_negative_output->insert (edge);
}
}
private:
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version has only negative edge output.
*/
template <class NegativeEdgeOutput>
class DB_PUBLIC_TEMPLATE edge2edge_check_negative
: public Edge2EdgeCheckBase
{
public:
edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
set_has_negative_edge_output (true);
set_has_edge_pair_output (false);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
mp_l1_negative_output->insert (edge);
}
if (layer == 1) {
mp_l2_negative_output->insert (edge);
}
}
private:
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version has positive or negative output. Negative output is mapped to edge pairs
* as well.
*/
template <class Output>
class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive
: public edge2edge_check<Output>
{
public:
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric)
{
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
}
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric)
{
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
}
#if 0
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
// the outer edges to not represent the actual contour.
if (layer == 1) {
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
}
#endif
}
};
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*/
template <class PolygonType>
class DB_PUBLIC poly2poly_check
{
public:
typedef typename PolygonType::box_type box_type;
poly2poly_check (Edge2EdgeCheckBase &output);
poly2poly_check ();
void clear ();
void single (const PolygonType&o, size_t p);
void connect (Edge2EdgeCheckBase &output);
void enter (const PolygonType &o, size_t p);
void enter (const PolygonType &o, size_t p, const box_type &search_box);
void process ();
private:
db::Edge2EdgeCheckBase *mp_output;
db::box_scanner<db::Edge, size_t> m_scanner;
std::list<db::Edge> m_edge_heap;
};
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class PolygonType, class EdgeType, class OutputType>
class DB_PUBLIC region_to_edge_interaction_filter_base
: public db::box_scanner_receiver2<PolygonType, size_t, db::Edge, size_t>
{
public:
region_to_edge_interaction_filter_base (bool inverse, bool get_all);
void preset (const OutputType *s);
void add (const PolygonType *p, size_t, const EdgeType *e, size_t);
void fill_output ();
protected:
virtual void put (const OutputType &s) const = 0;
private:
std::set<const OutputType *> m_seen;
bool m_inverse, m_get_all;
};
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class PolygonType, class EdgeType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter
: public region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>
{
public:
region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
: region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType> (inverse, get_all), mp_output (&output)
{
// .. nothing yet ..
}
protected:
virtual void put (const OutputType &res) const
{
mp_output->insert (res);
}
private:
OutputContainer *mp_output;
};
/**
* @brief A helper class for the region to text interaction functionality
*/
template <class PolygonType, class TextType, class OutputType>
class DB_PUBLIC region_to_text_interaction_filter_base
: public db::box_scanner_receiver2<PolygonType, size_t, TextType, size_t>
{
public:
region_to_text_interaction_filter_base (bool inverse, bool get_all);
void preset (const OutputType *s);
void add (const PolygonType *p, size_t, const TextType *e, size_t);
void fill_output ();
protected:
virtual void put (const OutputType &s) const = 0;
private:
std::set<const OutputType *> m_seen;
bool m_inverse, m_get_all;
};
/**
* @brief A helper class for the region to text interaction functionality
*/
template <class PolygonType, class TextType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter
: public region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>
{
public:
region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
: region_to_text_interaction_filter_base<PolygonType, TextType, OutputType> (inverse, get_all), mp_output (&output)
{
// .. nothing yet ..
}
protected:
virtual void put (const OutputType &poly) const
{
mp_output->insert (poly);
}
private:
OutputContainer *mp_output;
};
} // namespace db
#endif

View File

@ -21,8 +21,8 @@
*/
#include "dbRegionUtils.h"
#include "dbRegionLocalOperations.h"
#include "dbRegionUtils.h"
#include "dbLocalOperationUtils.h"
#include "dbHierProcessor.h"
@ -209,7 +209,6 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
poly2poly_check<TS> poly_check (edge_check);
std::list<TS> heap;
db::box_scanner<TS, size_t> scanner;
std::unordered_set<TI> polygons;
std::set<unsigned int> ids;
@ -219,12 +218,40 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
}
}
bool take_all = edge_check.has_negative_edge_output () || interactions.num_intruders () == 0;
db::Box common_box;
if (! take_all) {
db::Vector e (edge_check.distance (), edge_check.distance ());
db::Box subject_box;
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
subject_box += db::box_convert<TS> () (interactions.subject_shape (i->first));
}
if (edge_check.requires_different_layers ()) {
db::Box intruder_box;
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
intruder_box += db::box_convert<TI> () (interactions.intruder_shape (*id).second);
}
common_box = subject_box.enlarged (e) & intruder_box.enlarged (e);
} else {
common_box = subject_box.enlarged (e);
}
}
if (m_has_other) {
size_t n = 0;
for (typename shape_interactions<TS, TI>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
const TS &subject = interactions.subject_shape (i->first);
scanner.insert (push_polygon_to_heap (layout, subject, heap), n);
if (! take_all) {
poly_check.enter (subject, n, common_box);
} else {
poly_check.enter (subject, n);
}
n += 2;
}
@ -262,7 +289,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
n = 1;
for (typename std::unordered_set<TI>::const_iterator o = polygons.begin (); o != polygons.end (); ++o) {
scanner.insert (push_polygon_to_heap (layout, *o, heap), n);
if (! take_all) {
poly_check.enter (*o, n, common_box);
} else {
poly_check.enter (*o, n);
}
n += 2;
}
@ -270,7 +301,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
n = 1;
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
scanner.insert (push_polygon_to_heap (layout, interactions.intruder_shape (*id).second, heap), n);
if (! take_all) {
poly_check.enter (interactions.intruder_shape (*id).second, n, common_box);
} else {
poly_check.enter (interactions.intruder_shape (*id).second, n);
}
n += 2;
}
@ -286,7 +321,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
// we can't directly insert because TS may be != TI
const TS &ts = interactions.subject_shape (i->first);
insert_into_hash (polygons, ts);
scanner.insert (push_polygon_to_heap (layout, ts, heap), n);
if (! take_all) {
poly_check.enter (ts, n, common_box);
} else {
poly_check.enter (ts, n);
}
n += 2;
}
@ -295,7 +334,11 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
for (std::set<unsigned int>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
const TI &ti = interactions.intruder_shape (*id).second;
if (polygons.find (ti) == polygons.end ()) {
scanner.insert (push_polygon_to_heap (layout, ti, heap), n);
if (! take_all) {
poly_check.enter (ti, n, common_box);
} else {
poly_check.enter (ti, n);
}
n += 2;
}
}
@ -303,7 +346,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, const shape
}
do {
scanner.process (poly_check, m_check.distance (), db::box_convert<TS> ());
poly_check.process ();
} while (edge_check.prepare_next_pass ());
// detect and remove parts of the result which have or do not have results "opposite"

View File

@ -28,6 +28,7 @@
#include "dbEdgePairRelations.h"
#include "dbLocalOperation.h"
#include "dbEdgeProcessor.h"
#include "dbRegionCheckUtils.h"
#include <vector>
#include <unordered_set>

View File

@ -22,6 +22,7 @@
#include "dbRegionUtils.h"
#include "dbRegionCheckUtils.h"
#include "dbPolygonTools.h"
#include "dbEdgeBoolean.h"
#include "tlSelect.h"
@ -29,496 +30,6 @@
namespace db
{
// -------------------------------------------------------------------------------------
// Edge2EdgeCheckBase implementation
Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons),
m_first_pseudo (std::numeric_limits<size_t>::max ()),
m_with_shielding (with_shielding),
m_symmetric_edges (symmetric_edges),
m_has_edge_pair_output (true),
m_has_negative_edge_output (false),
m_pass (0)
{
m_distance = check.distance ();
}
bool
Edge2EdgeCheckBase::prepare_next_pass ()
{
++m_pass;
if (m_pass == 1) {
m_first_pseudo = m_ep.size ();
if (m_with_shielding && ! m_ep.empty ()) {
m_ep_discarded.resize (m_ep.size (), false);
// second pass:
return true;
} else if (m_has_negative_edge_output) {
// second pass:
return true;
}
}
if (! m_ep.empty () && m_has_edge_pair_output) {
std::vector<bool>::const_iterator d = m_ep_discarded.begin ();
std::vector<bool>::const_iterator i = m_ep_intra_polygon.begin ();
std::vector<db::EdgePair>::const_iterator ep = m_ep.begin ();
while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) {
bool use_result = true;
if (d != m_ep_discarded.end ()) {
use_result = ! *d;
++d;
}
if (use_result) {
put (*ep, *i);
}
++ep;
++i;
}
}
return false;
}
static inline bool shields (const db::EdgePair &ep, const db::Edge &q)
{
db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ());
db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ());
std::pair<bool, db::Point> ip1 = pe1.intersect_point (q);
std::pair<bool, db::Point> ip2 = pe2.intersect_point (q);
if (ip1.first && ip2.first) {
return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0);
} else {
return false;
}
}
void
Edge2EdgeCheckBase::finish (const Edge *o, size_t p)
{
if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) {
std::pair<db::Edge, size_t> k (*o, p);
std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i0 = m_e2ep.find (k);
bool fully_removed = false;
bool any = false;
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
any = true;
fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o);
}
}
if (! any) {
put_negative (*o, (int) p);
} else if (! fully_removed) {
std::set<db::Edge> partial_edges;
db::EdgeBooleanCluster<std::set<db::Edge> > ec (&partial_edges, db::EdgeNot);
ec.add (o, 0);
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) {
ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1);
}
}
ec.finish ();
for (std::set<db::Edge>::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) {
put_negative (*e, (int) p);
}
}
}
}
bool
Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner)
{
if (m_pass == 1) {
for (std::set<std::pair<db::Edge, size_t> >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) {
scanner.insert (&e->first, e->second);
}
return ! m_pseudo_edges.empty ();
} else {
return false;
}
}
inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2)
{
if (p1 == p2) {
if (requires_different_polygons) {
return false;
} else if ((p1 & size_t (1)) != 0) {
// edges from the same polygon are only considered on first layer.
// Reasoning: this case happens when "intruder" polygons are put on layer 1
// while "subject" polygons are put on layer 0. We don't want "intruders"
// to generate intra-polygon markers.
return false;
}
}
if (((p1 ^ p2) & size_t (1)) == 0) {
if (requires_different_layers) {
return false;
} else if ((p1 & size_t (1)) != 0) {
// edges on the same layer are only considered on first layer.
// Reasoning: this case happens when "intruder" polygons are put on layer 1
// while "subject" polygons are put on layer 0. We don't want "intruders"
// to generate inter-polygon markers between them.
return false;
}
}
return true;
}
void
Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
{
if (m_pass == 0) {
// Overlap or inside checks require input from different layers
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
if (l1 > l2) {
std::swap (o1, o2);
std::swap (p1, p2);
}
db::EdgePair ep;
if (mp_check->check (*o1, *o2, &ep)) {
ep.set_symmetric (m_symmetric_edges);
// found a violation: store inside the local buffer for now. In the second
// pass we will eliminate those which are shielded completely (with shielding)
// and/or compute the negative edges.
size_t n = m_ep.size ();
m_ep.push_back (ep);
m_ep_intra_polygon.push_back (p1 == p2);
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
if (m_has_negative_edge_output) {
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p1));
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p1));
if (p1 != p2) {
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p2));
m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p2));
}
}
}
}
} else {
// set the discarded flags for shielded output
if (m_with_shielding) {
// a simple (complete) shielding implementation which is based on the
// assumption that shielding is relevant as soon as a foreign edge cuts through
// both of the edge pair's connecting edges.
// TODO: this implementation does not take into account the nature of the
// EdgePair - because of "whole_edge" it may not reflect the part actually
// violating the distance.
std::vector<size_t> n1, n2;
for (unsigned int p = 0; p < 2; ++p) {
std::pair<db::Edge, size_t> k (*o1, p1);
for (std::multimap<std::pair<db::Edge, size_t>, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) {
size_t n = i->second / 2;
if (n < m_first_pseudo && ! m_ep_discarded [n]) {
n1.push_back (n);
}
}
std::sort (n1.begin (), n1.end ());
std::swap (o1, o2);
std::swap (p1, p2);
n1.swap (n2);
}
for (unsigned int p = 0; p < 2; ++p) {
std::vector<size_t> nn;
std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn));
for (std::vector<size_t>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::EdgePair ep = m_ep [*i].normalized ();
if (shields (ep, *o2)) {
m_ep_discarded [*i] = true;
}
}
std::swap (o1, o2);
std::swap (p1, p2);
n1.swap (n2);
}
}
// for negative output edges are cancelled by short interactions perpendicular to them
// For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real
// edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the
// negative case this means we cancel a real edge.
if (m_has_negative_edge_output &&
(m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) {
// Overlap or inside checks require input from different layers
if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) {
// ensure that the first check argument is of layer 1 and the second of
// layer 2 (unless both are of the same layer)
int l1 = int (p1 & size_t (1));
int l2 = int (p2 & size_t (1));
if (l1 > l2) {
std::swap (o1, o2);
std::swap (p1, p2);
}
db::EdgePair ep;
if (mp_check->check (*o1, *o2, &ep)) {
size_t n = m_ep.size ();
m_ep.push_back (ep);
m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency
m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2));
m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1));
}
}
}
}
}
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool
Edge2EdgeCheckBase::requires_different_layers () const
{
return m_requires_different_layers;
}
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void
Edge2EdgeCheckBase::set_requires_different_layers (bool f)
{
m_requires_different_layers = f;
}
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool
Edge2EdgeCheckBase::different_polygons () const
{
return m_different_polygons;
}
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void
Edge2EdgeCheckBase::set_different_polygons (bool f)
{
m_different_polygons = f;
}
/**
* @brief Gets the distance value
*/
EdgeRelationFilter::distance_type
Edge2EdgeCheckBase::distance () const
{
return m_distance;
}
// -------------------------------------------------------------------------------------
// Poly2PolyCheckBase implementation
template <class PolygonType>
poly2poly_check<PolygonType>::poly2poly_check (Edge2EdgeCheckBase &output)
: mp_output (& output)
{
// .. nothing yet ..
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::finish (const PolygonType *o, size_t p)
{
enter (*o, p);
}
static size_t vertices (const db::Polygon &p)
{
return p.vertices ();
}
static size_t vertices (const db::PolygonRef &p)
{
return p.obj ().vertices ();
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::enter (const PolygonType &o, size_t p)
{
if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) {
// finally we check the polygons vs. itself for checks involving intra-polygon interactions
m_scanner.clear ();
m_scanner.reserve (vertices (o));
m_edges.clear ();
m_edges.reserve (vertices (o));
for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) {
m_edges.push_back (*e);
m_scanner.insert (& m_edges.back (), p);
}
mp_output->feed_pseudo_edges (m_scanner);
tl_assert (m_edges.size () == vertices (o));
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
}
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2)
{
enter (*o1, p1, *o2, p2);
}
static bool interact (const db::Box &box, const db::Edge &e)
{
if (! e.bbox ().touches (box)) {
return false;
} else if (e.is_ortho ()) {
return true;
} else {
return e.clipped (box).first;
}
}
template <class PolygonType>
void
poly2poly_check<PolygonType>::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2)
{
if (p1 != p2 && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) {
bool take_all = mp_output->has_negative_edge_output ();
db::Box common_box;
if (! take_all) {
db::Vector e (mp_output->distance (), mp_output->distance ());
common_box = o1.box ().enlarged (e) & o2.box ().enlarged (e);
if (common_box.empty ()) {
return;
}
}
m_scanner.clear ();
m_scanner.reserve (vertices (o1) + vertices (o2));
m_edges.clear ();
m_edges.reserve (vertices (o1) + vertices (o2));
bool any_o1 = false, any_o2 = false;
for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) {
if (take_all || interact (common_box, *e)) {
m_edges.push_back (*e);
m_scanner.insert (& m_edges.back (), p1);
any_o1 = true;
}
}
for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) {
if (take_all || interact (common_box, *e)) {
m_edges.push_back (*e);
m_scanner.insert (& m_edges.back (), p2);
any_o2 = true;
}
}
if (! take_all && (! any_o1 || ! any_o2)) {
return;
}
mp_output->feed_pseudo_edges (m_scanner);
// temporarily disable intra-polygon check in that step .. we do that later in finish()
// if required (#650).
bool no_intra = mp_output->different_polygons ();
mp_output->set_different_polygons (true);
m_scanner.process (*mp_output, mp_output->distance (), db::box_convert<db::Edge> ());
mp_output->set_different_polygons (no_intra);
}
}
// explicit instantiations
template class poly2poly_check<db::Polygon>;
template class poly2poly_check<db::PolygonRef>;
// -------------------------------------------------------------------------------------
// RegionPerimeterFilter implementation
@ -859,7 +370,7 @@ SinglePolygonCheck::process (const db::Polygon &polygon, std::vector<db::EdgePai
poly2poly_check<db::Polygon> poly_check (edge_check);
do {
poly_check.enter (polygon, 0);
poly_check.single (polygon, 0);
} while (edge_check.prepare_next_pass ());
res.insert (res.end (), result.begin (), result.end ());
@ -967,219 +478,4 @@ HullExtractionProcessor::process (const db::Polygon &poly, std::vector<db::Polyg
res.back ().assign_hull (poly.begin_hull (), poly.end_hull ());
}
// -------------------------------------------------------------------------------------
// RegionToEdgeInteractionFilterBase implementation
template <class PolygonType, class EdgeType, class OutputType>
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::region_to_edge_interaction_filter_base (bool inverse, bool get_all)
: m_inverse (inverse), m_get_all (get_all)
{
// .. nothing yet ..
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::preset (const OutputType *s)
{
m_seen.insert (s);
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::add (const PolygonType *p, size_t, const EdgeType *e, size_t)
{
const OutputType *o = 0;
tl::select (o, p, e);
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
// A polygon and an edge interact if the edge is either inside completely
// of at least one edge of the polygon intersects with the edge
bool interacts = false;
if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) {
interacts = true;
} else {
for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) {
if ((*pe).intersect (*e)) {
interacts = true;
}
}
}
if (interacts) {
if (m_inverse) {
m_seen.erase (o);
} else {
if (! m_get_all) {
m_seen.insert (o);
}
put (*o);
}
}
}
}
template <class PolygonType, class EdgeType, class OutputType>
void
region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>::fill_output ()
{
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
put (**s);
}
}
// explicit instantiations
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Polygon>;
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::PolygonRef>;
template class region_to_edge_interaction_filter_base<db::Polygon, db::Edge, db::Edge>;
template class region_to_edge_interaction_filter_base<db::PolygonRef, db::Edge, db::Edge>;
// -------------------------------------------------------------------------------------
// RegionToTextInteractionFilterBase implementation
template <class PolygonType, class TextType, class OutputType>
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::region_to_text_interaction_filter_base (bool inverse, bool get_all)
: m_inverse (inverse), m_get_all (get_all)
{
// .. nothing yet ..
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::preset (const OutputType *s)
{
m_seen.insert (s);
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::add (const PolygonType *p, size_t, const TextType *t, size_t)
{
const OutputType *o = 0;
tl::select (o, p, t);
if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) {
// A polygon and an text interact if the text is either inside completely
// of at least one text of the polygon intersects with the text
db::Point pt = db::box_convert<TextType> () (*t).p1 ();
if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) {
if (m_inverse) {
m_seen.erase (o);
} else {
if (! m_get_all) {
m_seen.insert (o);
}
put (*o);
}
}
}
}
template <class PolygonType, class TextType, class OutputType>
void
region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>::fill_output ()
{
for (typename std::set<const OutputType *>::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) {
put (**s);
}
}
// explicit instantiations
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::PolygonRef>;
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Polygon>;
template class region_to_text_interaction_filter_base<db::Polygon, db::Text, db::Text>;
template class region_to_text_interaction_filter_base<db::Polygon, db::TextRef, db::TextRef>;
template class region_to_text_interaction_filter_base<db::PolygonRef, db::TextRef, db::TextRef>;
// -------------------------------------------------------------------------------------
// Polygon snapping
db::Polygon
snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap)
{
db::Polygon pnew;
for (size_t i = 0; i < poly.holes () + 1; ++i) {
heap.clear ();
db::Polygon::polygon_contour_iterator b, e;
if (i == 0) {
b = poly.begin_hull ();
e = poly.end_hull ();
} else {
b = poly.begin_hole ((unsigned int) (i - 1));
e = poly.end_hole ((unsigned int) (i - 1));
}
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy)));
}
if (i == 0) {
pnew.assign_hull (heap.begin (), heap.end ());
} else {
pnew.insert_hole (heap.begin (), heap.end ());
}
}
return pnew;
}
db::Polygon
scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap)
{
db::Polygon pnew;
int64_t dgx = int64_t (gx) * int64_t (dx);
int64_t dgy = int64_t (gy) * int64_t (dy);
for (size_t i = 0; i < poly.holes () + 1; ++i) {
heap.clear ();
db::Polygon::polygon_contour_iterator b, e;
if (i == 0) {
b = poly.begin_hull ();
e = poly.end_hull ();
} else {
b = poly.begin_hole ((unsigned int) (i - 1));
e = poly.end_hole ((unsigned int) (i - 1));
}
for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) {
int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy);
heap.push_back (db::Point (db::Coord (x), db::Coord (y)));
}
if (i == 0) {
pnew.assign_hull (heap.begin (), heap.end ());
} else {
pnew.insert_hole (heap.begin (), heap.end ());
}
}
return pnew;
}
db::Vector
scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy)
{
int64_t dgx = int64_t (gx) * int64_t (dx);
int64_t dgy = int64_t (gy) * int64_t (dy);
int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx);
int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy);
return db::Vector (db::Coord (x), db::Coord (y));
}
}

View File

@ -583,298 +583,6 @@ public:
virtual bool result_must_not_be_merged () const { return false; }
};
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*/
class DB_PUBLIC Edge2EdgeCheckBase
: public db::box_scanner_receiver<db::Edge, size_t>
{
public:
Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges);
/**
* @brief Call this to initiate a new pass until the return value is false
*/
bool prepare_next_pass ();
/**
* @brief Before the scanner is run, this method must be called to feed additional edges into the scanner
* (required for negative edge output - cancellation of perpendicular edges)
*/
bool feed_pseudo_edges (db::box_scanner<db::Edge, size_t> &scanner);
/**
* @brief Reimplementation of the box_scanner_receiver interface
*/
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2);
/**
* @brief Reimplementation of the box_scanner_receiver interface
*/
void finish (const Edge *o, size_t);
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool requires_different_layers () const;
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void set_requires_different_layers (bool f);
/**
* @brief Gets a value indicating whether the check requires different layers
*/
bool different_polygons () const;
/**
* @brief Sets a value indicating whether the check requires different layers
*/
void set_different_polygons (bool f);
/**
* @brief Sets a flag indicating that this class wants negative edge output
*/
void set_has_negative_edge_output (bool f)
{
m_has_negative_edge_output = f;
}
/**
* @brief Gets a flag indicating that this class wants negative edge output
*/
bool has_negative_edge_output () const
{
return m_has_negative_edge_output;
}
/**
* @brief Sets a flag indicating that this class wants normal edge pair output
*/
void set_has_edge_pair_output (bool f)
{
m_has_edge_pair_output = f;
}
/**
* @brief Gets a flag indicating that this class wants normal edge pair output
*/
bool has_edge_pair_output () const
{
return m_has_edge_pair_output;
}
/**
* @brief Gets the distance value
*/
EdgeRelationFilter::distance_type distance () const;
protected:
/**
* @brief Normal edge pair output (violations)
*/
virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { }
/**
* @brief Negative edge output
*/
virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { }
private:
const EdgeRelationFilter *mp_check;
bool m_requires_different_layers;
bool m_different_polygons;
EdgeRelationFilter::distance_type m_distance;
std::vector<db::EdgePair> m_ep;
std::multimap<std::pair<db::Edge, size_t>, size_t> m_e2ep;
std::set<std::pair<db::Edge, size_t> > m_pseudo_edges;
size_t m_first_pseudo;
std::vector<bool> m_ep_discarded, m_ep_intra_polygon;
bool m_with_shielding;
bool m_symmetric_edges;
bool m_has_edge_pair_output;
bool m_has_negative_edge_output;
unsigned int m_pass;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
*/
template <class Output>
class DB_PUBLIC_TEMPLATE edge2edge_check
: public Edge2EdgeCheckBase
{
public:
edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0)
{
// .. nothing yet ..
}
edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra)
{
// .. nothing yet ..
}
protected:
void put (const db::EdgePair &edge, bool inter_polygon) const
{
if (! inter_polygon || ! mp_output_intra) {
mp_output_inter->insert (edge);
} else {
mp_output_intra->insert (edge);
}
}
private:
Output *mp_output_inter;
Output *mp_output_intra;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version allows delivery of the negative edges.
*/
template <class Output, class NegativeEdgeOutput>
class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output
: public edge2edge_check<Output>
{
public:
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
edge2edge_check<Output>::set_has_negative_edge_output (true);
}
edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges)
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
edge2edge_check<Output>::set_has_negative_edge_output (true);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
mp_l1_negative_output->insert (edge);
}
if (layer == 1) {
mp_l2_negative_output->insert (edge);
}
}
private:
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version has only negative edge output.
*/
template <class NegativeEdgeOutput>
class DB_PUBLIC_TEMPLATE edge2edge_check_negative
: public Edge2EdgeCheckBase
{
public:
edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding)
: Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false),
mp_l1_negative_output (&l1_negative_output),
mp_l2_negative_output (&l2_negative_output)
{
set_has_negative_edge_output (true);
set_has_edge_pair_output (false);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
mp_l1_negative_output->insert (edge);
}
if (layer == 1) {
mp_l2_negative_output->insert (edge);
}
}
private:
NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output;
};
/**
* @brief A helper class for the DRC functionality
*
* This class implements the edge-to-edge part of the polygon DRC.
* This version has positive or negative output. Negative output is mapped to edge pairs
* as well.
*/
template <class Output>
class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive
: public edge2edge_check<Output>
{
public:
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
: edge2edge_check<Output> (check, output, different_polygons, requires_different_layers, with_shielding, symmetric)
{
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
}
edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric)
: edge2edge_check<Output> (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric)
{
edge2edge_check<Output>::set_has_negative_edge_output (negative_output);
edge2edge_check<Output>::set_has_edge_pair_output (! negative_output);
}
protected:
void put_negative (const db::Edge &edge, int layer) const
{
if (layer == 0) {
edge2edge_check<Output>::put (db::EdgePair (edge, edge.swapped_points ()), false);
}
#if 0
// NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence
// the outer edges to not represent the actual contour.
if (layer == 1) {
edge2edge_check<Output>::put (db::EdgePair (edge.swapped_points (), edge), false);
}
#endif
}
};
/**
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
*/
template <class PolygonType>
class DB_PUBLIC poly2poly_check
: public db::box_scanner_receiver<PolygonType, size_t>
{
public:
poly2poly_check (Edge2EdgeCheckBase &output);
void finish (const PolygonType *o, size_t p);
void enter (const PolygonType&o, size_t p);
void add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2);
void enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2);
private:
db::Edge2EdgeCheckBase *mp_output;
db::box_scanner<db::Edge, size_t> m_scanner;
std::vector<db::Edge> m_edges;
};
/**
* @brief A class wrapping the single-polygon checks into a polygon-to-edge pair processor
*/
@ -892,129 +600,6 @@ private:
db::RegionCheckOptions m_options;
};
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class PolygonType, class EdgeType, class OutputType>
class DB_PUBLIC region_to_edge_interaction_filter_base
: public db::box_scanner_receiver2<PolygonType, size_t, db::Edge, size_t>
{
public:
region_to_edge_interaction_filter_base (bool inverse, bool get_all);
void preset (const OutputType *s);
void add (const PolygonType *p, size_t, const EdgeType *e, size_t);
void fill_output ();
protected:
virtual void put (const OutputType &s) const = 0;
private:
std::set<const OutputType *> m_seen;
bool m_inverse, m_get_all;
};
/**
* @brief A helper class for the region to edge interaction functionality
*/
template <class PolygonType, class EdgeType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter
: public region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType>
{
public:
region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
: region_to_edge_interaction_filter_base<PolygonType, EdgeType, OutputType> (inverse, get_all), mp_output (&output)
{
// .. nothing yet ..
}
protected:
virtual void put (const OutputType &res) const
{
mp_output->insert (res);
}
private:
OutputContainer *mp_output;
};
/**
* @brief A helper class for the region to text interaction functionality
*/
template <class PolygonType, class TextType, class OutputType>
class DB_PUBLIC region_to_text_interaction_filter_base
: public db::box_scanner_receiver2<PolygonType, size_t, TextType, size_t>
{
public:
region_to_text_interaction_filter_base (bool inverse, bool get_all);
void preset (const OutputType *s);
void add (const PolygonType *p, size_t, const TextType *e, size_t);
void fill_output ();
protected:
virtual void put (const OutputType &s) const = 0;
private:
std::set<const OutputType *> m_seen;
bool m_inverse, m_get_all;
};
/**
* @brief A helper class for the region to text interaction functionality
*/
template <class PolygonType, class TextType, class OutputContainer, class OutputType = typename OutputContainer::value_type>
class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter
: public region_to_text_interaction_filter_base<PolygonType, TextType, OutputType>
{
public:
region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false)
: region_to_text_interaction_filter_base<PolygonType, TextType, OutputType> (inverse, get_all), mp_output (&output)
{
// .. nothing yet ..
}
protected:
virtual void put (const OutputType &poly) const
{
mp_output->insert (poly);
}
private:
OutputContainer *mp_output;
};
template <class C>
static inline C snap_to_grid (C c, C g)
{
// This form of snapping always snaps g/2 to right/top.
if (c < 0) {
c = -g * ((-c + (g - 1) / 2) / g);
} else {
c = g * ((c + g / 2) / g);
}
return c;
}
/**
* @brief Snaps a polygon to the given grid
* Heap is a vector of points reused for the point list
*/
DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
/**
* @brief Scales and snaps a polygon to the given grid
* Heap is a vector of points reused for the point list
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
*/
DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector<db::Point> &heap);
/**
* @brief Scales and snaps a vector to the given grid
* The coordinate transformation is q = ((p * m + o) snap (g * d)) / d.
*/
DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy);
} // namespace db
#endif

View File

@ -2011,6 +2011,15 @@ public:
m_mag = m_mag < 0.0 ? -m : m;
}
/**
* @brief Returns a value indicating whether the transformation is a complex one
* The transformation can safely be converted to a simple transformation if this value is false.
*/
bool is_complex () const
{
return is_mag () || ! is_ortho ();
}
/**
* @brief Test, if the transformation is mirroring
*/

View File

@ -460,6 +460,11 @@ static db::CompoundRegionOperationNode *new_enclosing_check (db::CompoundRegionO
return new_check_node (other, db::OverlapRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative);
}
static db::CompoundRegionOperationNode *new_enclosed_check (db::CompoundRegionOperationNode *other, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative)
{
return new_check_node (other, db::InsideRelation, true, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative);
}
static db::CompoundRegionOperationNode *new_perimeter_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits<db::Coord>::perimeter_type pmin, db::coord_traits<db::Coord>::perimeter_type pmax)
{
check_non_null (input, "input");
@ -673,6 +678,11 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
gsi::constructor ("new_enclosing_check", &new_enclosing_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
"@brief Creates a node providing an inside (enclosure) check.\n"
) +
gsi::constructor ("new_enclosed_check", &new_enclosed_check, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max."), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
"@brief Creates a node providing an enclosed (secondary enclosing primary) check.\n"
"\n"
"This method has been added in version 0.27.5.\n"
) +
gsi::constructor ("new_perimeter_filter", &new_perimeter_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0), gsi::arg ("pmax", std::numeric_limits<db::coord_traits<db::Coord>::perimeter_type>::max (), "max"),
"@brief Creates a node filtering the input by perimeter.\n"
"This node renders the input if the perimeter is between pmin and pmax (exclusively). If 'inverse' is set to true, the "

View File

@ -1160,7 +1160,7 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". "
"If you don't want to specify one threshold, pass nil to the respective value.\n"
) +
method_ext ("inside_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
method_ext ("inside_check|enclosed_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
"@brief Performs an inside check with options\n"
"@param d The minimum distance for which the edges are checked\n"
"@param other The other edge collection against which to check\n"
@ -1186,6 +1186,8 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"It is sufficient if the projection of one edge on the other matches the specified condition. "
"The projected length must be larger or equal to \"min_projection\" and less than \"max_projection\". "
"If you don't want to specify one threshold, pass nil to the respective value.\n"
"\n"
"The 'enclosed_check' alias was introduced in version 0.27.5.\n"
) +
method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"),
"@brief Performs an enclosing check with options\n"

View File

@ -2591,7 +2591,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n"
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27."
) +
method_ext ("inside_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
method_ext ("inside_check|enclosed_check", &inside2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
"@brief Performs an inside check with options\n"
"@param d The minimum distance for which the polygons are checked\n"
"@param other The other region against which to check\n"
@ -2639,6 +2639,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n"
"The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. "
"The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n"
"The 'enclosed_check' alias was introduced in version 0.27.5.\n"
) +
method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoRectFilter, "NoRectFilter"), gsi::arg ("negative", false),
"@brief Performs an overlap check with options\n"

View File

@ -916,7 +916,7 @@ struct cplx_trans_defs
"@brief Gets the magnification\n"
) +
method ("is_mag?", &C::is_mag,
"@brief Test, if the transformation is a magnifying one\n"
"@brief Tests, if the transformation is a magnifying one\n"
"\n"
"This is the recommended test for checking if the transformation represents\n"
"a magnification.\n"
@ -925,6 +925,15 @@ struct cplx_trans_defs
"@brief Sets the magnification\n"
"@param m The new magnification"
) +
method ("is_complex?", &C::is_complex,
"@brief Returns true if the transformation is a complex one\n"
"\n"
"If this predicate is false, the transformation can safely be converted to a simple transformation.\n"
"Otherwise, this conversion will be lossy.\n"
"The predicate value is equivalent to 'is_mag || !is_ortho'.\n"
"\n"
"This method has been introduced in version 0.27.5."
) +
method ("R0", &trans_r0,
"@brief A constant giving \"unrotated\" (unit) transformation\n"
"The previous integer constant has been turned into a transformation in version 0.25."

View File

@ -1014,3 +1014,49 @@ TEST(6)
}
TEST(10_HasShapesTouching)
{
db::Layout ly;
unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
db::Cell &a = ly.cell (ly.add_cell ("A"));
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
a.shapes (l1).insert (db::Box (-100, -100, 0, 0));
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 100)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 1, 100, 100)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, -1, 100, 100)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (-1, -1, -1, -1)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (1, 1, 1, 1)), false);
}
TEST(11_HasShapesTouchingWithHier)
{
db::Layout ly;
unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
unsigned int l2 = ly.insert_layer (db::LayerProperties (2, 0));
db::Cell &a = ly.cell (ly.add_cell ("A"));
db::Cell &b = ly.cell (ly.add_cell ("B"));
a.insert (db::CellInstArray (b.cell_index (), db::Trans (db::Vector (100, 100)), db::Vector (0, 200), db::Vector (200, 0), 2, 2));
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
EXPECT_EQ (a.has_shapes_touching (l2, db::Box ()), false);
b.shapes (l1).insert (db::Box (0, 0, 10, 10));
EXPECT_EQ (a.has_shapes_touching (l1, db::Box ()), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 100)), true);
EXPECT_EQ (a.has_shapes_touching (l2, db::Box (0, 0, 100, 100)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 99, 100)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (0, 0, 100, 99)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (100, 100, 110, 110)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (150, 150, 160, 160)), false);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 300, 310, 310)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 100, 310, 110)), true);
EXPECT_EQ (a.has_shapes_touching (l1, db::Box (300, 400, 310, 410)), false);
}

View File

@ -261,12 +261,15 @@ TEST(2)
db::cell_index_type ci;
db::Cell *top;
EXPECT_EQ (g.hier_generation_id (), size_t (0));
ci = g.add_cell ("TOP");
EXPECT_EQ (el.flags, (unsigned int) 0);
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
EXPECT_EQ (g.hier_generation_id (), size_t (1));
el.reset ();
top = &g.cell (ci);
@ -276,6 +279,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, false); // needs g.update() before being issues again
EXPECT_EQ (g.hier_generation_id (), size_t (2));
el.reset ();
top->insert (db::CellInstArray (ci, db::Trans ()));
@ -284,10 +288,12 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, false); // needs g.update() before being issues again
EXPECT_EQ (g.hier_generation_id (), size_t (3));
g.clear ();
g.update ();
el.reset ();
EXPECT_EQ (g.hier_generation_id (), size_t (4));
ci = g.add_cell ("TOP");
@ -295,6 +301,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
EXPECT_EQ (g.hier_generation_id (), size_t (5));
el.reset ();
g.update ();
@ -305,6 +312,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
EXPECT_EQ (g.hier_generation_id (), size_t (6));
el.reset ();
g.update ();
@ -314,6 +322,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
EXPECT_EQ (g.hier_generation_id (), size_t (7));
// busy mode will make events issued always
g.clear ();
@ -326,6 +335,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true);
EXPECT_EQ (g.hier_generation_id (), size_t (9));
el.reset ();
top = &g.cell (ci);
@ -335,6 +345,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, false);
EXPECT_EQ (el.bboxes_all_dirty, false);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
EXPECT_EQ (g.hier_generation_id (), size_t (10));
el.reset ();
top->insert (db::CellInstArray (ci, db::Trans ()));
@ -343,6 +354,7 @@ TEST(2)
EXPECT_EQ (el.bboxes_dirty, true);
EXPECT_EQ (el.bboxes_all_dirty, true);
EXPECT_EQ (el.hier_dirty, true); // OK - see above
EXPECT_EQ (g.hier_generation_id (), size_t (11));
}

View File

@ -25,7 +25,7 @@
#include "tlUnitTest.h"
#include "tlStringEx.h"
#include "dbRegionUtils.h"
#include "dbRegionCheckUtils.h"
TEST(1_SimpleLShape)
{
@ -52,7 +52,7 @@ TEST(1_SimpleLShape)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(2000,1000;1000,1000)|(1000,2000;2000,2000)");
@ -85,7 +85,7 @@ TEST(1s_SimpleLShape)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)/(1000,1000;1000,0),(1000,2000;2000,2000)/(2000,1000;1000,1000)");
@ -118,7 +118,7 @@ TEST(2_SimpleLWithBigPart)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0)");
@ -153,7 +153,7 @@ TEST(3_SimpleTWithBigPart)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(0,2500;0,3500)|(1000,3500;1000,2500)");
@ -188,7 +188,7 @@ TEST(4_SimpleNotch)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(1000,1000;2000,1000)|(2000,2000;1000,2000)");
@ -225,7 +225,7 @@ TEST(5_LShapeNotch)
do {
// single polygon check
poly_check.enter (poly, 0);
poly_check.single (poly, 0);
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(1500,500;2000,500)|(2000,1500;1500,1500),(1500,1500;1500,2500)|(500,2500;500,1500)");
@ -264,14 +264,12 @@ TEST(6_SeparationLvsBox)
db::Polygon poly2;
poly2.assign_hull (pts2, pts2 + sizeof (pts2) / sizeof (pts2[0]));
db::box_scanner<db::Polygon, size_t> scanner;
scanner.insert (&poly1, 0); // layer 0
scanner.insert (&poly2, 1); // layer 1
db::poly2poly_check<db::Polygon> poly_check (e2e);
poly_check.enter (poly1, 0); // layer 0
poly_check.enter (poly2, 1); // layer 1
do {
scanner.process (poly_check, er.distance (), db::box_convert<db::Polygon> ());
poly_check.process ();
} while (e2e.prepare_next_pass ());
EXPECT_EQ (tl::to_string (ep), "(1000,1000;1000,0)/(2000,0;2000,1000),(3000,2000;2000,2000)/(2000,1000;3000,1000)");

View File

@ -10,7 +10,7 @@ SOURCES = \
dbCompoundOperationTests.cc \
dbFillToolTests.cc \
dbRecursiveInstanceIteratorTests.cc \
dbRegionUtilsTests.cc \
dbRegionCheckUtilsTests.cc \
dbUtilsTests.cc \
dbWriterTools.cc \
dbLoadLayoutOptionsTests.cc \

View File

@ -1888,7 +1888,7 @@ class DRCOpNodeCheck < DRCOpNodeWithCompare
factory = { :width => :new_width_check, :space => :new_space_check,
:notch => :new_notch_check, :separation => :new_separation_check,
:isolated => :new_isolated_check, :overlap => :new_overlap_check,
:enclosing => :new_enclosing_check }[self.check]
:enclosing => :new_enclosing_check, :enclosed => :new_enclosed_check }[self.check]
oargs = []
if self.other

View File

@ -1042,6 +1042,84 @@ CODE
# actual edges from the first input (see \separation for an example).
#
# %DRC%
# @name enclosed
# @brief Performs an enclosing check (other enclosing layer)
# @synopsis enclosed(other [, options ]) (in conditions)
# @synopsis enclosed(layer, other [, options ])
#
# This check verifies if the polygons of the input layer are enclosed by shapes
# of the other input layer by a certain distance.
# It has manifold options. See \Layer#width for the basic options such
# as metrics, projection and angle constraints etc. This check also features
# opposite and rectangle filtering. See \Layer#separation for details about opposite and
# rectangle error filtering.
#
# This function is essentially the reverse of \enclosing. In case of
# "enclosed", the other layer must be bigger than the primary layer.
#
# @h3 Classic mode @/h3
#
# This function can be used in classic mode with a layer argument. In this case it
# is equivalent to "layer.enclosed" (see \Layer#enclosed).
#
# @code
# # classic "enclosed" check for < 0.2 um
# in = layer(1, 0)
# other = layer(2, 0)
# errors = enclosed(in, other, 0.2.um)
# @/code
#
# @h3 Universal DRC @/h3
#
# The version without a first layer is intended for use within \DRC# expressions
# together with the "universal DRC" method \Layer#drc. In this case, this function needs to be
# put into a condition to specify the check constraints. The other options
# of \Layer#enclosed (e.g. metrics, projection constraints, angle limits etc.)
# apply to this version too:
#
# @code
# # universal DRC "enclosed" check for < 0.2 um
# in = layer(1, 0)
# other = layer(2, 0)
# errors = in.drc(enclosed(other) < 0.2.um)
# @/code
#
# The conditions may involve an upper and lower limit. The following examples
# illustrate the use of this function with conditions:
#
# @code
# out = in.drc(enclosed(other) < 0.2.um)
# out = in.drc(enclosed(other) <= 0.2.um)
# out = in.drc(enclosed(other) > 0.2.um)
# out = in.drc(enclosed(other) >= 0.2.um)
# out = in.drc(enclosed(other) == 0.2.um)
# out = in.drc(enclosed(other) != 0.2.um)
# out = in.drc(0.1.um <= enclosed(other) < 0.2.um)
# @/code
#
# The result of the enclosed check are edges or edge pairs forming the markers.
# These markers indicate the presence of the specified condition.
#
# With a lower and upper limit, the results are edges marking the positions on the
# primary shape where the condition is met.
# With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
# the primary shape. Without an upper limit only, the first edge of the marker is attached to the
# primary shape while the second edge is attached to the shape of the "other" layer.
#
# @table
# @tr
# @td @img(/images/drc_encd1u.png) @/td
# @td @img(/images/drc_encd2u.png) @/td
# @/tr
# @/table
#
# When "larger than" constraints are used, this function will produce the edges from the
# first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
# the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
# actual edges from the first input (see \separation for an example).
#
# %DRC%
# @name separation
# @brief Performs a separation check
@ -1367,6 +1445,7 @@ CODE
%w(
enclosing
enclosed
isolated
notch
overlap

View File

@ -1751,6 +1751,7 @@ CODE
%w(
enc
enclosing
enclosed
overlap
sep
separation

View File

@ -3547,7 +3547,7 @@ CODE
# method will only report space violations to other polygons. \separation is a two-layer
# space check where space is checked against polygons of another layer.
#
# Like for the \width method, merged semantics applies.
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
@ -3632,6 +3632,7 @@ CODE
# will also trigger an error while for \space it will not.
#
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
@ -3739,8 +3740,8 @@ CODE
# the orientation of the edges matters: only edges which run back to back with their
# inside side pointing towards each other are checked for distance.
#
# As for the other DRC methods, merged semantics applies. The options available
# are the same than for \width.
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
@ -3755,7 +3756,7 @@ CODE
# %DRC%
# @name enclosing
# @brief An enclosing check
# @brief An enclosing check (layer enclosing other_layer)
# @synopsis layer.enclosing(other_layer, value [, options])
# @synopsis layer.enc(other_layer, value [, options])
#
@ -3763,8 +3764,8 @@ CODE
# the \DRC framework. These variants have more options and are more intuitive to use.
# See \global#enclosing for more details.
#
# This method checks whether layer encloses (is bigger than) other_layer by the
# given dimension. Locations, where this is not the case will be reported in form
# This method checks whether layer encloses (is bigger than) other_layer by not less than the
# given distance value. Locations, where the distance is less will be reported in form
# of edge pair error markers.
# Locations, where both edges coincide will be reported as errors as well. Formally
# such locations form an enclosure with a distance of 0. Locations, where other_layer
@ -3779,8 +3780,8 @@ CODE
# the orientation of the edges matters and only edges looking into the same direction
# are checked.
#
# As for the other DRC methods, merged semantics applies. The options available
# are the same than for \width.
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
@ -3793,13 +3794,48 @@ CODE
# @/tr
# @/table
%w(width space overlap enclosing separation isolated notch).each do |f|
# %DRC%
# @name enclosed
# @brief An enclosing check (other_layer enclosing layer)
# @synopsis layer.enclosed(other_layer, value [, options])
#
# @b Note: @/b "enclosed" is available as operators for the "universal DRC" function \drc within
# the \DRC framework. These variants have more options and are more intuitive to use.
# See \global#enclosed for more details.
#
# This method checks whether layer is enclosed by (is inside of) other_layer by not less than the
# given distance value. Locations, where the distance is less will be reported in form
# of edge pair error markers.
# Locations, where both edges coincide will be reported as errors as well. Formally
# such locations form an enclosure with a distance of 0. Locations, where other_layer
# is inside layer will not be reported as errors. Such regions can be detected
# by \inside or a boolean "not" operation.
#
# The options are the same as for \separation.
#
# This method is available for edge and polygon layers.
#
# As for the other DRC methods, merged semantics applies.
#
# Distance values can be given as floating-point values (in micron) or integer values (in
# database units). To explicitly specify the unit, use the unit denominators.
#
# The following images show the effect of two enclosed checks (red: input1, blue: input2):
#
# @table
# @tr
# @td @img(/images/drc_encd1.png) @/td
# @td @img(/images/drc_encd2.png) @/td
# @/tr
# @/table
%w(width space overlap enclosing enclosed separation isolated notch).each do |f|
eval <<"CODE"
def #{f}(*args)
@engine._context("#{f}") do
if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosing || :#{f} == :separation
if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosed || :#{f} == :enclosing || :#{f} == :separation
requires_edges_or_region
else
requires_region

View File

@ -536,6 +536,85 @@ See <a href="/about/drc_ref_source.xml#edges">Source#edges</a> for a description
<p>
"enc" is the short form for <a href="#enclosing">enclosing</a>.
</p>
<a name="enclosed"/><h2>"enclosed" - Performs an enclosing check (other enclosing layer)</h2>
<keyword name="enclosed"/>
<p>Usage:</p>
<ul>
<li><tt>enclosed(other [, options ]) (in conditions)</tt></li>
<li><tt>enclosed(layer, other [, options ])</tt></li>
</ul>
<p>
This check verifies if the polygons of the input layer are enclosed by shapes
of the other input layer by a certain distance.
It has manifold options. See <a href="/about/drc_ref_layer.xml#width">Layer#width</a> for the basic options such
as metrics, projection and angle constraints etc. This check also features
opposite and rectangle filtering. See <a href="/about/drc_ref_layer.xml#separation">Layer#separation</a> for details about opposite and
rectangle error filtering.
</p><p>
This function is essentially the reverse of <a href="#enclosing">enclosing</a>. In case of
"enclosed", the other layer must be bigger than the primary layer.
</p><p>
<h3>Classic mode </h3>
</p><p>
This function can be used in classic mode with a layer argument. In this case it
is equivalent to "layer.enclosed" (see <a href="/about/drc_ref_layer.xml#enclosed">Layer#enclosed</a>).
</p><p>
<pre>
# classic "enclosed" check for &lt; 0.2 um
in = layer(1, 0)
other = layer(2, 0)
errors = enclosed(in, other, 0.2.um)
</pre>
</p><p>
<h3>Universal DRC </h3>
</p><p>
The version without a first layer is intended for use within <a href="/about/drc_ref_drc.xml">DRC</a> expressions
together with the "universal DRC" method <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a>. In this case, this function needs to be
put into a condition to specify the check constraints. The other options
of <a href="/about/drc_ref_layer.xml#enclosed">Layer#enclosed</a> (e.g. metrics, projection constraints, angle limits etc.)
apply to this version too:
</p><p>
<pre>
# universal DRC "enclosed" check for &lt; 0.2 um
in = layer(1, 0)
other = layer(2, 0)
errors = in.drc(enclosed(other) &lt; 0.2.um)
</pre>
</p><p>
The conditions may involve an upper and lower limit. The following examples
illustrate the use of this function with conditions:
</p><p>
<pre>
out = in.drc(enclosed(other) &lt; 0.2.um)
out = in.drc(enclosed(other) &lt;= 0.2.um)
out = in.drc(enclosed(other) &gt; 0.2.um)
out = in.drc(enclosed(other) &gt;= 0.2.um)
out = in.drc(enclosed(other) == 0.2.um)
out = in.drc(enclosed(other) != 0.2.um)
out = in.drc(0.1.um &lt;= enclosed(other) &lt; 0.2.um)
</pre>
</p><p>
The result of the enclosed check are edges or edge pairs forming the markers.
These markers indicate the presence of the specified condition.
</p><p>
With a lower and upper limit, the results are edges marking the positions on the
primary shape where the condition is met.
With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to
the primary shape. Without an upper limit only, the first edge of the marker is attached to the
primary shape while the second edge is attached to the shape of the "other" layer.
</p><p>
<table>
<tr>
<td><img src="/images/drc_encd1u.png"/></td>
<td><img src="/images/drc_encd2u.png"/></td>
</tr>
</table>
</p><p>
When "larger than" constraints are used, this function will produce the edges from the
first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from
the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the
actual edges from the first input (see <a href="#separation">separation</a> for an example).
</p>
<a name="enclosing"/><h2>"enclosing" - Performs an enclosing check</h2>
<keyword name="enclosing"/>
<p>Usage:</p>

View File

@ -778,7 +778,44 @@ individual ones unless raw mode is chosen.
<p>
See <a href="#enclosing">enclosing</a> for a description of that method
</p>
<a name="enclosing"/><h2>"enclosing" - An enclosing check</h2>
<a name="enclosed"/><h2>"enclosed" - An enclosing check (other_layer enclosing layer)</h2>
<keyword name="enclosed"/>
<p>Usage:</p>
<ul>
<li><tt>layer.enclosed(other_layer, value [, options])</tt></li>
</ul>
<p>
<b>Note: </b>"enclosed" is available as operators for the "universal DRC" function <a href="#drc">drc</a> within
the <a href="#DRC">DRC</a> framework. These variants have more options and are more intuitive to use.
See <a href="/about/drc_ref_global.xml#enclosed">enclosed</a> for more details.
</p><p>
This method checks whether layer is enclosed by (is inside of) other_layer by not less than the
given distance value. Locations, where the distance is less will be reported in form
of edge pair error markers.
Locations, where both edges coincide will be reported as errors as well. Formally
such locations form an enclosure with a distance of 0. Locations, where other_layer
is inside layer will not be reported as errors. Such regions can be detected
by <a href="#inside">inside</a> or a boolean "not" operation.
</p><p>
The options are the same as for <a href="#separation">separation</a>.
</p><p>
This method is available for edge and polygon layers.
</p><p>
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
The following images show the effect of two enclosed checks (red: input1, blue: input2):
</p><p>
<table>
<tr>
<td><img src="/images/drc_encd1.png"/></td>
<td><img src="/images/drc_encd2.png"/></td>
</tr>
</table>
</p>
<a name="enclosing"/><h2>"enclosing" - An enclosing check (layer enclosing other_layer)</h2>
<keyword name="enclosing"/>
<p>Usage:</p>
<ul>
@ -790,8 +827,8 @@ See <a href="#enclosing">enclosing</a> for a description of that method
the <a href="#DRC">DRC</a> framework. These variants have more options and are more intuitive to use.
See <a href="/about/drc_ref_global.xml#enclosing">enclosing</a> for more details.
</p><p>
This method checks whether layer encloses (is bigger than) other_layer by the
given dimension. Locations, where this is not the case will be reported in form
This method checks whether layer encloses (is bigger than) other_layer by not less than the
given distance value. Locations, where the distance is less will be reported in form
of edge pair error markers.
Locations, where both edges coincide will be reported as errors as well. Formally
such locations form an enclosure with a distance of 0. Locations, where other_layer
@ -806,8 +843,8 @@ The enclosing method can be applied to both edge or polygon layers. On edge laye
the orientation of the edges matters and only edges looking into the same direction
are checked.
</p><p>
As for the other DRC methods, merged semantics applies. The options available
are the same than for <a href="#width">width</a>.
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
@ -2046,8 +2083,8 @@ The overlap method can be applied to both edge or polygon layers. On edge layers
the orientation of the edges matters: only edges which run back to back with their
inside side pointing towards each other are checked for distance.
</p><p>
As for the other DRC methods, merged semantics applies. The options available
are the same than for <a href="#width">width</a>.
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
@ -2569,6 +2606,7 @@ layers touch are also reported. More specifically, the case of zero spacing
will also trigger an error while for <a href="#space">space</a> it will not.
</p><p>
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.
</p><p>
@ -2789,7 +2827,7 @@ The <a href="#notch">notch</a> method is similar, but will only report self-spac
method will only report space violations to other polygons. <a href="#separation">separation</a> is a two-layer
space check where space is checked against polygons of another layer.
</p><p>
Like for the <a href="#width">width</a> method, merged semantics applies.
As for the other DRC methods, merged semantics applies.
</p><p>
Distance values can be given as floating-point values (in micron) or integer values (in
database units). To explicitly specify the unit, use the unit denominators.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -36,6 +36,10 @@
<file alias="drc_enc2.png">doc/images/drc_enc2.png</file>
<file alias="drc_enc1u.png">doc/images/drc_enc1u.png</file>
<file alias="drc_enc2u.png">doc/images/drc_enc2u.png</file>
<file alias="drc_encd1.png">doc/images/drc_encd1.png</file>
<file alias="drc_encd2.png">doc/images/drc_encd2.png</file>
<file alias="drc_encd1u.png">doc/images/drc_encd1u.png</file>
<file alias="drc_encd2u.png">doc/images/drc_encd2u.png</file>
<file alias="drc_overlap1.png">doc/images/drc_overlap1.png</file>
<file alias="drc_overlap2.png">doc/images/drc_overlap2.png</file>
<file alias="drc_overlap1u.png">doc/images/drc_overlap1u.png</file>

View File

@ -246,7 +246,8 @@ run_tests (const std::vector<tl::TestBase *> &selected_tests, bool editable, boo
timer.stop();
ut::noctrl << "Time: " << timer.sec_wall () << "s (wall) " << timer.sec_user () << "s (user) " << timer.sec_sys () << "s (sys)";
ut::ctrl << "<x-testcase-times wall=\"" << timer.sec_wall () << "\" user=\"" << timer.sec_user () << "\" sys=\"" << timer.sec_sys () << "\"/>";
ut::noctrl << "Memory: " << timer.memory_size () / 1024 << "k";
ut::ctrl << "<x-testcase-times wall=\"" << timer.sec_wall () << "\" user=\"" << timer.sec_user () << "\" sys=\"" << timer.sec_sys () << "\" memory=\"" << timer.memory_size () << "\"/>";
} catch (tl::CancelException &) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -43,4 +43,5 @@ l1.drc(isolated(projection, not_opposite) < 1.0).output(144, 0)
# enclosing, overlap
l2.drc(enclosing(l1, projection) < 1.0).polygons.output(150, 0)
l1.drc(overlap(l2, projection) < 2.0).polygons.output(151, 0)
l1.drc(enclosed(l2, projection) < 1.0).polygons.output(152, 0)

Binary file not shown.

Binary file not shown.

View File

@ -33,9 +33,17 @@ l2.separation(l1, projection, 1.0, whole_edges).output(211, 0)
l1.drc(enclosing(l2, projection) < 1.0).output(400, 0)
l1.drc(enclosing(l2, whole_edges, projection) < 1.0).output(401, 0)
l1.drc(enclosing(l2, projection) >= 1.0).output(402, 0)
l2.drc(enclosed(l1, projection) < 1.0).output(403, 0)
l2.drc(enclosed(l1, whole_edges, projection) < 1.0).output(404, 0)
l2.drc(enclosed(l1, projection) >= 1.0).output(405, 0)
l1.enclosing(l2, projection, 1.0).output(410, 0)
l1.enclosing(l2, projection, 1.0, whole_edges).output(411, 0)
l2.enclosed(l1, projection, 1.0).output(412, 0)
l2.enclosed(l1, projection, 1.0, whole_edges).output(413, 0)
l1.drc(overlap(l2, projection) < 1.0).output(500, 0)
l1.drc(overlap(l2, whole_edges, projection) < 1.0).output(501, 0)
l1.drc(overlap(l2, projection) >= 1.0).output(502, 0)

Binary file not shown.

Binary file not shown.

View File

@ -134,6 +134,10 @@ def run_testsuite(dm, ic, tiled = false, hier = false)
a.enclosing(b, 0.5, whole_edges).second_edges.extended_out(0.01).output(lb + 1, dm)
a.enclosing(b, 0.5, whole_edges).edges.extended_out(0.01).output(lb + 2, dm)
b.enclosed(a, 0.5, whole_edges).second_edges.extended_out(0.01).output(lb + 3, dm)
b.enclosed(a, 0.5, whole_edges).first_edges.extended_out(0.01).output(lb + 4, dm)
b.enclosed(a, 0.5, whole_edges).edges.extended_out(0.01).output(lb + 5, dm)
lb += 10 #150
message "--- hulls, holes #{lb}"
@ -323,6 +327,8 @@ def run_testsuite(dm, ic, tiled = false, hier = false)
else
a.enclosing(b, 0.5, euclidian, projection_limits(0, 0.4)).polygons.output(lb + 7, dm)
end
b.enclosed(a, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 8, dm)
lb += 10 #220
message "--- enclosing (edges) #{lb}"
@ -343,6 +349,8 @@ def run_testsuite(dm, ic, tiled = false, hier = false)
ae.enclosing(be, 0.5, euclidian, projection_limits(0, 0.4)).polygons.output(lb + 7, dm)
end
be.enclosed(ae, 0.5, euclidian, whole_edges).polygons.output(lb + 8, dm)
lb += 10 #230
message "--- isolated #{lb}"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -398,6 +398,8 @@ class DBEdges_TestClass < TestBase
r3b = RBA::Edges::new(RBA::Box::new(-10, -10, 10, 10))
assert_equal(r2.inside_check(r3, 15).to_s, "(120,20;120,380)/(110,9;110,391)")
# "enclosed" alias
assert_equal(r2.enclosed_check(r3, 15).to_s, "(120,20;120,380)/(110,9;110,391)")
assert_equal(r2.inside_check(r3, 15, false, RBA::Edges::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(110,20;110,380)")
assert_equal(r2.inside_check(r3, 15, true, RBA::Edges::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(110,0;110,400)")
assert_equal(r2.inside_check(r3, 15, true, RBA::Edges::Projection, 0.0, nil, nil).to_s, "")

View File

@ -454,6 +454,8 @@ class DBRegion_TestClass < TestBase
r3b = RBA::Region::new(RBA::Box::new(-10, -10, 10, 10))
assert_equal(r2.inside_check(r3, 15).to_s, "(120,20;120,380)/(110,9;110,391)")
# "enclosed" alias
assert_equal(r2.enclosed_check(r3, 15).to_s, "(120,20;120,380)/(110,9;110,391)")
assert_equal(r2.inside_check(r3, 15, false, RBA::Region::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(110,20;110,380)")
assert_equal(r2.inside_check(r3, 15, true, RBA::Region::Projection, nil, nil, nil).to_s, "(120,20;120,380)/(110,0;110,400)")
assert_equal(r2.inside_check(r3, 15, true, RBA::Region::Projection, 0.0, nil, nil).to_s, "")

View File

@ -155,6 +155,7 @@ class DBTrans_TestClass < TestBase
assert_equal( c.is_unity?, false )
assert_equal( c.is_ortho?, true )
assert_equal( c.is_mag?, false )
assert_equal( c.is_complex?, false )
assert_equal( c.is_mirror?, true )
assert_equal( c.rot, RBA::DCplxTrans::M135.rot )
assert_equal( c.s_trans.to_s, "m135 0,0" )
@ -179,6 +180,7 @@ class DBTrans_TestClass < TestBase
assert_equal( c.is_unity?, false )
assert_equal( c.is_ortho?, true )
assert_equal( c.is_mag?, true )
assert_equal( c.is_complex?, true )
assert_equal( c.is_mirror?, false )
assert_equal( c.rot, RBA::DCplxTrans::R0.rot )
assert_equal( c.s_trans.to_s, "r0 0,0" )
@ -190,6 +192,7 @@ class DBTrans_TestClass < TestBase
assert_equal( c.to_s, "m22.5 *0.75 2.5,-12.5" )
assert_equal( c.is_unity?, false )
assert_equal( c.is_ortho?, false )
assert_equal( c.is_complex?, true )
assert_equal( c.is_mag?, true )
assert_equal( c.rot, RBA::DCplxTrans::M0.rot )
assert_equal( c.s_trans.to_s, "m0 2.5,-12.5" )