diff --git a/scripts/create_drc_samples.rb b/scripts/create_drc_samples.rb index f7963be5d..2f0da729b 100644 --- a/scripts/create_drc_samples.rb +++ b/scripts/create_drc_samples.rb @@ -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) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 6072e1e73..876373056 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -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::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::rounded (0.5 * *t / dbu)); xor_res.size (db::coord_traits::rounded (0.5 * *t / dbu)); } diff --git a/src/db/db/db.pro b/src/db/db/db.pro index b6c9cb53a..df5648b48 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -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 \ diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index a61f1a8cd..ba2efe78a 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -964,9 +964,7 @@ struct iterated_array virtual std::pair *, bool> begin_touching (const box_type &b) const { - if (b.empty ()) { - return std::make_pair (new iterated_array_iterator (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 (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 (ct.rcos (), ct.mag (), a, b, amax, bmax)) + : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new regular_complex_array (ct.rcos (), ct.mag (), a, b, amax, bmax) : new regular_array (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 (ct.rcos (), ct.mag (), a, b, amax, bmax))) + : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (regular_complex_array (ct.rcos (), ct.mag (), a, b, amax, bmax)) : rep.insert (regular_array (a, b, amax, bmax))) { // .. nothing yet .. } @@ -1766,7 +1764,7 @@ struct array */ template 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 (ct.rcos (), ct.mag (), from, to)) + : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new iterated_complex_array (ct.rcos (), ct.mag (), from, to) : new iterated_array (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 (ct.rcos (), ct.mag ())) + : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? new single_complex_inst (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 (ct.rcos (), ct.mag ()))) + : m_obj (obj), m_trans (ct), mp_base (ct.is_complex () ? rep.insert (single_complex_inst (ct.rcos (), ct.mag ())) : 0) { // .. nothing yet .. } @@ -1890,17 +1888,13 @@ struct array array_iterator begin_touching (const box_type &b, const BoxConv &bc) const { if (b.empty ()) { - if (mp_base) { - return array_iterator (m_trans, mp_base->begin_touching (box_type ())); - } else { - return array_iterator (m_trans, true); - } + return array_iterator (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 (m_trans, mp_base->begin_touching (box_type ())); + return array_iterator (m_trans, true); } else { if (mp_base->is_complex ()) { complex_trans_type ct = mp_base->complex_trans (simple_trans_type (m_trans)); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 0362a5088..9efc43576 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -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; } diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 346f3d8ab..906752112 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -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 (*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 &callers) const { diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h index b20803953..db7089b2a 100644 --- a/src/db/db/dbCell.h +++ b/src/db/db/dbCell.h @@ -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) * diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index 4e138fb20..6184e740a 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -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 CellMapping::source_cells () const { std::vector 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::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 >::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 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; diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h index a17ef92aa..31f72be94 100644 --- a/src/db/db/dbCellMapping.h +++ b/src/db/db/dbCellMapping.h @@ -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 * diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 081d77a92..3b8fd6b03 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -22,7 +22,7 @@ #include "dbCellVariants.h" -#include "dbRegionUtils.h" +#include "dbPolygonTools.h" #include "tlUtils.h" namespace db diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index b536941c8..bfe1938c2 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -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 ()); } diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 74e8f6e8b..89b7e84e1 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -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 (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 lm; lm.insert (std::make_pair (dl.layer (), layer ())); std::vector 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::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 > &var_map) { - invalidate_hier (); - db::HierarchyBuilder &builder = m_layouts [layout_index]->builder; for (std::map >::const_iterator i = var_map.begin (); i != var_map.end (); ++i) { for (std::map::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, 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 *excluded_cells, const std::set *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::iterator cm = m_delivery_mapping_cache.find (key); - if (cm == m_delivery_mapping_cache.end ()) { + std::map::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 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; diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 9532ffd2b..877cd2bc9 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -32,6 +32,7 @@ #include "dbLayout.h" #include "dbRecursiveShapeIterator.h" #include "dbHierarchyBuilder.h" +#include "dbCellMapping.h" #include "gsiObject.h" #include @@ -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 *excluded_cells = 0, const std::set *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 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 m_delivery_mapping_cache; + std::map m_delivery_mapping_cache; + std::map, CellMappingWithGenerationIds> m_internal_mapping_cache; }; template diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 33d9fbc7b..1bb829fd8 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -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 inst2_bc (*layout2, layer2); - - std::unordered_set 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 +template struct interaction_registration_inst2inst : db::box_scanner_receiver2 { public: - typedef std::pair, std::map > > interaction_value_type; + typedef typename local_processor_cell_contexts::context_key_type interactions_value_type; + typedef std::unordered_map, 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 *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, std::map > > > *mp_result; + interactions_type *mp_result; std::unordered_set > m_interactions; bool m_foreign; -}; -template -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 inst_bc (*layout, layer); - db::Box rbox = db::box_convert () (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 (*mp_subject_layout)).to_string()).c_str(), + mp_intruder_layout->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert (*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 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 > > 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 *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 > *interactions = 0; + + std::unordered_map > >::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 >::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 > &interactions) + { + db::ICplxTrans tni2 = tn21.inverted () * tni1; + db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1); -template + 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 (*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 struct interaction_registration_inst2shape - : db::box_scanner_receiver2 + : db::box_scanner_receiver2 { public: - interaction_registration_inst2shape (const db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, std::unordered_map, std::map > > > *result) + typedef typename local_processor_cell_contexts::context_key_type interactions_value_type; + typedef std::unordered_map, 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, std::map > > > *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 inst_bc (*mp_subject_layout, m_subject_layer); + db::Box rbox = db::box_convert () (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 rt (mp_subject_layout, tni); + + std::set *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::compute_contexts (local_processor_contexts::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::compute_contexts (local_processor_contextsbegin ().at_end ()) { - typedef std::pair, std::map > > interaction_value_type; - - std::unordered_map 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::context_key_type interactions_value_type; + typedef std::unordered_map, 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::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) { db::box_convert inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il)); db::box_scanner2 scanner; - interaction_registration_inst2inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions); + interaction_registration_inst2inst 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::compute_contexts (local_processor_contexts scanner; - interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); + interaction_registration_inst2shape 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::compute_contexts (local_processor_contexts ()); } - // this cache should reduce the effort of checking array vs. array - typedef std::pair > effective_instance_cache_key_type; - typedef std::map > 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_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::context_key_type intruders_below; - - db::shape_reference_translator_with_trans rt (mp_subject_layout, tni); - - for (typename std::map >::const_iterator pl = i->second.second.begin (); pl != i->second.second.end (); ++pl) { - std::set &out = intruders_below.second [pl->first]; - for (typename std::unordered_set::const_iterator p = pl->second.begin (); p != pl->second.end (); ++p) { - if (nbox.overlaps (db::box_convert () (*p))) { - out.insert (rt (*p)); - } - } - } - - // TODO: in some cases, it may be possible to optimize this for arrays - - for (std::vector::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) { - - unsigned int ail = contexts.actual_intruder_layer (*il); - db::box_convert inst_bcii (*mp_intruder_layout, ail); - - for (std::unordered_set::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 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::compute_contexts (local_processor_contexts -std::pair -local_processor::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 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 void local_processor::compute_results (local_processor_contexts &contexts, const local_operation *op, const std::vector &output_layers) const diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 838e93243..fe48fd086 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -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 > 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) { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index e5ee39006..478620390 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -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 diff --git a/src/db/db/dbLayoutStateModel.cc b/src/db/db/dbLayoutStateModel.cc index 4bcb0755f..63cc42177 100644 --- a/src/db/db/dbLayoutStateModel.cc +++ b/src/db/db/dbLayoutStateModel.cc @@ -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; diff --git a/src/db/db/dbLayoutStateModel.h b/src/db/db/dbLayoutStateModel.h index f99e04c32..20760a70d 100644 --- a/src/db/db/dbLayoutStateModel.h +++ b/src/db/db/dbLayoutStateModel.h @@ -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 m_bboxes_dirty; bool m_all_bboxes_dirty; bool m_busy; diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 0343e4828..98718791e 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -23,7 +23,7 @@ #include "dbLayoutUtils.h" #include "dbCellVariants.h" -#include "dbRegionUtils.h" +#include "dbPolygonTools.h" #include "tlProgress.h" namespace db diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index dc252fc9b..4c7e22c55 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -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 &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 &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)); +} + } diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index c90691ba4..0a4bc11d6 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -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 +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 &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 &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 diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 075752113..df95d9201 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -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 { diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 5537619c6..ee70e8538 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -665,6 +665,16 @@ public: return reinterpret_cast (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) */ diff --git a/src/db/db/dbRegionCheckUtils.cc b/src/db/db/dbRegionCheckUtils.cc new file mode 100644 index 000000000..db3434914 --- /dev/null +++ b/src/db/db/dbRegionCheckUtils.cc @@ -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::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::const_iterator d = m_ep_discarded.begin (); + std::vector::const_iterator i = m_ep_intra_polygon.begin (); + std::vector::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 ip1 = pe1.intersect_point (q); + std::pair 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 k (*o, p); + std::multimap, size_t>::const_iterator i0 = m_e2ep.find (k); + + bool fully_removed = false; + bool any = false; + for (std::multimap, 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 partial_edges; + + db::EdgeBooleanCluster > ec (&partial_edges, db::EdgeNot); + ec.add (o, 0); + + for (std::multimap, 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::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) { + put_negative (*e, (int) p); + } + + } + + } +} + +bool +Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner &scanner) +{ + if (m_pass == 1) { + for (std::set >::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 n1, n2; + + for (unsigned int p = 0; p < 2; ++p) { + + std::pair k (*o1, p1); + for (std::multimap, 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 nn; + std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); + + for (std::vector::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 +poly2poly_check::poly2poly_check (Edge2EdgeCheckBase &output) + : mp_output (& output) +{ + // .. nothing yet .. +} + +template +poly2poly_check::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 +void +poly2poly_check::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 ()); +} + +template +void +poly2poly_check::connect (Edge2EdgeCheckBase &output) +{ + mp_output = &output; + clear (); +} + +template +void +poly2poly_check::clear () +{ + m_scanner.clear (); + m_edge_heap.clear (); +} + +template +void +poly2poly_check::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 +void +poly2poly_check::enter (const PolygonType &o, size_t p, const poly2poly_check::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 +void +poly2poly_check::process () +{ + mp_output->feed_pseudo_edges (m_scanner); + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); +} + +// explicit instantiations +template class poly2poly_check; +template class poly2poly_check; + +// ------------------------------------------------------------------------------------- +// RegionToEdgeInteractionFilterBase implementation + +template +region_to_edge_interaction_filter_base::region_to_edge_interaction_filter_base (bool inverse, bool get_all) + : m_inverse (inverse), m_get_all (get_all) +{ + // .. nothing yet .. +} + +template +void +region_to_edge_interaction_filter_base::preset (const OutputType *s) +{ + m_seen.insert (s); +} + +template +void +region_to_edge_interaction_filter_base::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 +void +region_to_edge_interaction_filter_base::fill_output () +{ + for (typename std::set::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) { + put (**s); + } +} + +// explicit instantiations +template class region_to_edge_interaction_filter_base; +template class region_to_edge_interaction_filter_base; +template class region_to_edge_interaction_filter_base; +template class region_to_edge_interaction_filter_base; + +// ------------------------------------------------------------------------------------- +// RegionToTextInteractionFilterBase implementation + +template +region_to_text_interaction_filter_base::region_to_text_interaction_filter_base (bool inverse, bool get_all) + : m_inverse (inverse), m_get_all (get_all) +{ + // .. nothing yet .. +} + +template +void +region_to_text_interaction_filter_base::preset (const OutputType *s) +{ + m_seen.insert (s); +} + +template +void +region_to_text_interaction_filter_base::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 () (*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 +void +region_to_text_interaction_filter_base::fill_output () +{ + for (typename std::set::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) { + put (**s); + } +} + +// explicit instantiations +template class region_to_text_interaction_filter_base; +template class region_to_text_interaction_filter_base; +template class region_to_text_interaction_filter_base; +template class region_to_text_interaction_filter_base; +template class region_to_text_interaction_filter_base; + +} diff --git a/src/db/db/dbRegionCheckUtils.h b/src/db/db/dbRegionCheckUtils.h new file mode 100644 index 000000000..08297468d --- /dev/null +++ b/src/db/db/dbRegionCheckUtils.h @@ -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 +{ +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 &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 m_ep; + std::multimap, size_t> m_e2ep; + std::set > m_pseudo_edges; + size_t m_first_pseudo; + std::vector 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 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 DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output + : public edge2edge_check +{ +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 (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::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 (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::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 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 DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive + : public edge2edge_check +{ +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 (check, output, different_polygons, requires_different_layers, with_shielding, symmetric) + { + edge2edge_check::set_has_negative_edge_output (negative_output); + edge2edge_check::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 (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric) + { + edge2edge_check::set_has_negative_edge_output (negative_output); + edge2edge_check::set_has_edge_pair_output (! negative_output); + } + +protected: + void put_negative (const db::Edge &edge, int layer) const + { + if (layer == 0) { + edge2edge_check::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::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 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 m_scanner; + std::list m_edge_heap; +}; + +/** + * @brief A helper class for the region to edge interaction functionality + */ +template +class DB_PUBLIC region_to_edge_interaction_filter_base + : public db::box_scanner_receiver2 +{ +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 m_seen; + bool m_inverse, m_get_all; +}; + +/** + * @brief A helper class for the region to edge interaction functionality + */ +template +class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter + : public region_to_edge_interaction_filter_base +{ +public: + region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false) + : region_to_edge_interaction_filter_base (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 DB_PUBLIC region_to_text_interaction_filter_base + : public db::box_scanner_receiver2 +{ +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 m_seen; + bool m_inverse, m_get_all; +}; + +/** + * @brief A helper class for the region to text interaction functionality + */ +template +class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter + : public region_to_text_interaction_filter_base +{ +public: + region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false) + : region_to_text_interaction_filter_base (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 + diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 0a09c411e..6c14bc5d1 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -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::do_compute_local (db::Layout *layout, const shape poly2poly_check poly_check (edge_check); std::list heap; - db::box_scanner scanner; std::unordered_set polygons; std::set ids; @@ -219,12 +218,40 @@ check_local_operation::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::iterator i = interactions.begin (); i != interactions.end (); ++i) { + subject_box += db::box_convert () (interactions.subject_shape (i->first)); + } + + if (edge_check.requires_different_layers ()) { + db::Box intruder_box; + for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { + intruder_box += db::box_convert () (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::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::do_compute_local (db::Layout *layout, const shape n = 1; for (typename std::unordered_set::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::do_compute_local (db::Layout *layout, const shape n = 1; for (std::set::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::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::do_compute_local (db::Layout *layout, const shape for (std::set::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::do_compute_local (db::Layout *layout, const shape } do { - scanner.process (poly_check, m_check.distance (), db::box_convert ()); + poly_check.process (); } while (edge_check.prepare_next_pass ()); // detect and remove parts of the result which have or do not have results "opposite" diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index f4bec55f0..40ca85057 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -28,6 +28,7 @@ #include "dbEdgePairRelations.h" #include "dbLocalOperation.h" #include "dbEdgeProcessor.h" +#include "dbRegionCheckUtils.h" #include #include diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index e4e33e563..4c8acd690 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -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::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::const_iterator d = m_ep_discarded.begin (); - std::vector::const_iterator i = m_ep_intra_polygon.begin (); - std::vector::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 ip1 = pe1.intersect_point (q); - std::pair 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 k (*o, p); - std::multimap, size_t>::const_iterator i0 = m_e2ep.find (k); - - bool fully_removed = false; - bool any = false; - for (std::multimap, 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 partial_edges; - - db::EdgeBooleanCluster > ec (&partial_edges, db::EdgeNot); - ec.add (o, 0); - - for (std::multimap, 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::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) { - put_negative (*e, (int) p); - } - - } - - } -} - -bool -Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner &scanner) -{ - if (m_pass == 1) { - for (std::set >::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 n1, n2; - - for (unsigned int p = 0; p < 2; ++p) { - - std::pair k (*o1, p1); - for (std::multimap, 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 nn; - std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); - - for (std::vector::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 -poly2poly_check::poly2poly_check (Edge2EdgeCheckBase &output) - : mp_output (& output) -{ - // .. nothing yet .. -} - -template -void -poly2poly_check::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 -void -poly2poly_check::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 ()); - - } -} - -template -void -poly2poly_check::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 -void -poly2poly_check::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 ()); - - mp_output->set_different_polygons (no_intra); - - } -} - -// explicit instantiations -template class poly2poly_check; -template class poly2poly_check; - // ------------------------------------------------------------------------------------- // RegionPerimeterFilter implementation @@ -859,7 +370,7 @@ SinglePolygonCheck::process (const db::Polygon &polygon, std::vector 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 -region_to_edge_interaction_filter_base::region_to_edge_interaction_filter_base (bool inverse, bool get_all) - : m_inverse (inverse), m_get_all (get_all) -{ - // .. nothing yet .. -} - -template -void -region_to_edge_interaction_filter_base::preset (const OutputType *s) -{ - m_seen.insert (s); -} - -template -void -region_to_edge_interaction_filter_base::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 -void -region_to_edge_interaction_filter_base::fill_output () -{ - for (typename std::set::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) { - put (**s); - } -} - -// explicit instantiations -template class region_to_edge_interaction_filter_base; -template class region_to_edge_interaction_filter_base; -template class region_to_edge_interaction_filter_base; -template class region_to_edge_interaction_filter_base; - -// ------------------------------------------------------------------------------------- -// RegionToTextInteractionFilterBase implementation - -template -region_to_text_interaction_filter_base::region_to_text_interaction_filter_base (bool inverse, bool get_all) - : m_inverse (inverse), m_get_all (get_all) -{ - // .. nothing yet .. -} - -template -void -region_to_text_interaction_filter_base::preset (const OutputType *s) -{ - m_seen.insert (s); -} - -template -void -region_to_text_interaction_filter_base::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 () (*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 -void -region_to_text_interaction_filter_base::fill_output () -{ - for (typename std::set::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) { - put (**s); - } -} - -// explicit instantiations -template class region_to_text_interaction_filter_base; -template class region_to_text_interaction_filter_base; -template class region_to_text_interaction_filter_base; -template class region_to_text_interaction_filter_base; -template class region_to_text_interaction_filter_base; - -// ------------------------------------------------------------------------------------- -// Polygon snapping - -db::Polygon -snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &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 &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)); -} - } diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h index 62fca5cd3..61eb467bf 100644 --- a/src/db/db/dbRegionUtils.h +++ b/src/db/db/dbRegionUtils.h @@ -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 -{ -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 &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 m_ep; - std::multimap, size_t> m_e2ep; - std::set > m_pseudo_edges; - size_t m_first_pseudo; - std::vector 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 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 DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output - : public edge2edge_check -{ -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 (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::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 (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::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 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 DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive - : public edge2edge_check -{ -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 (check, output, different_polygons, requires_different_layers, with_shielding, symmetric) - { - edge2edge_check::set_has_negative_edge_output (negative_output); - edge2edge_check::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 (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric) - { - edge2edge_check::set_has_negative_edge_output (negative_output); - edge2edge_check::set_has_edge_pair_output (! negative_output); - } - -protected: - void put_negative (const db::Edge &edge, int layer) const - { - if (layer == 0) { - edge2edge_check::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::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 DB_PUBLIC poly2poly_check - : public db::box_scanner_receiver -{ -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 m_scanner; - std::vector 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 DB_PUBLIC region_to_edge_interaction_filter_base - : public db::box_scanner_receiver2 -{ -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 m_seen; - bool m_inverse, m_get_all; -}; - -/** - * @brief A helper class for the region to edge interaction functionality - */ -template -class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter - : public region_to_edge_interaction_filter_base -{ -public: - region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false) - : region_to_edge_interaction_filter_base (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 DB_PUBLIC region_to_text_interaction_filter_base - : public db::box_scanner_receiver2 -{ -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 m_seen; - bool m_inverse, m_get_all; -}; - -/** - * @brief A helper class for the region to text interaction functionality - */ -template -class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter - : public region_to_text_interaction_filter_base -{ -public: - region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false) - : region_to_text_interaction_filter_base (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 -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 &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 &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 diff --git a/src/db/db/dbTrans.h b/src/db/db/dbTrans.h index 93a6b08a7..c08915e74 100644 --- a/src/db/db/dbTrans.h +++ b/src/db/db/dbTrans.h @@ -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 */ diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 9f2759c1a..9fbdf518f 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -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::perimeter_type pmin, db::coord_traits::perimeter_type pmax) { check_non_null (input, "input"); @@ -673,6 +678,11 @@ Class 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::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 " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 84a0c9f6d..e302557cf 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -1160,7 +1160,7 @@ Class 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 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" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index f7e560d39..621235460 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -2591,7 +2591,7 @@ Class 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 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" diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc index 0ebbb4d0b..397378c17 100644 --- a/src/db/db/gsiDeclDbTrans.cc +++ b/src/db/db/gsiDeclDbTrans.cc @@ -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." diff --git a/src/db/unit_tests/dbCellTests.cc b/src/db/unit_tests/dbCellTests.cc index 448583d81..6578dea84 100644 --- a/src/db/unit_tests/dbCellTests.cc +++ b/src/db/unit_tests/dbCellTests.cc @@ -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); +} diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index c4630e5ae..303187ecb 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -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)); } diff --git a/src/db/unit_tests/dbRegionUtilsTests.cc b/src/db/unit_tests/dbRegionCheckUtilsTests.cc similarity index 94% rename from src/db/unit_tests/dbRegionUtilsTests.cc rename to src/db/unit_tests/dbRegionCheckUtilsTests.cc index c27a41e3d..cfad79fdd 100644 --- a/src/db/unit_tests/dbRegionUtilsTests.cc +++ b/src/db/unit_tests/dbRegionCheckUtilsTests.cc @@ -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 scanner; - scanner.insert (&poly1, 0); // layer 0 - scanner.insert (&poly2, 1); // layer 1 - db::poly2poly_check 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 ()); + 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)"); diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 1fc2fbe1d..8601dfb65 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -10,7 +10,7 @@ SOURCES = \ dbCompoundOperationTests.cc \ dbFillToolTests.cc \ dbRecursiveInstanceIteratorTests.cc \ - dbRegionUtilsTests.cc \ + dbRegionCheckUtilsTests.cc \ dbUtilsTests.cc \ dbWriterTools.cc \ dbLoadLayoutOptionsTests.cc \ diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb index e0d2ea017..48b534a37 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -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 diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb index 58b5518a4..e299350f7 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -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 diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index f5fc0e9cf..3600355ca 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -1751,6 +1751,7 @@ CODE %w( enc enclosing + enclosed overlap sep separation diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 31bc4b4d2..2cb31656a 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -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 diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml index 96c622861..34d3d7f07 100644 --- a/src/lay/lay/doc/about/drc_ref_global.xml +++ b/src/lay/lay/doc/about/drc_ref_global.xml @@ -536,6 +536,85 @@ See Source#edges for a description

"enc" is the short form for enclosing.

+

"enclosed" - Performs an enclosing check (other enclosing layer)

+ +

Usage:

+
    +
  • enclosed(other [, options ]) (in conditions)
  • +
  • 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. +

+

Classic mode

+

+This function can be used in classic mode with a layer argument. In this case it +is equivalent to "layer.enclosed" (see Layer#enclosed). +

+

+# classic "enclosed" check for < 0.2 um
+in = layer(1, 0)
+other = layer(2, 0)
+errors = enclosed(in, other, 0.2.um)
+
+

+

Universal DRC

+

+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: +

+

+# universal DRC "enclosed" check for < 0.2 um
+in = layer(1, 0)
+other = layer(2, 0)
+errors = in.drc(enclosed(other) < 0.2.um)
+
+

+The conditions may involve an upper and lower limit. The following examples +illustrate the use of this function with conditions: +

+

+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)
+
+

+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. +

+ + + + + +
+

+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). +

"enclosing" - Performs an enclosing check

Usage:

diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml index 651945346..56dfbe4c8 100644 --- a/src/lay/lay/doc/about/drc_ref_layer.xml +++ b/src/lay/lay/doc/about/drc_ref_layer.xml @@ -778,7 +778,44 @@ individual ones unless raw mode is chosen.

See enclosing for a description of that method

-

"enclosing" - An enclosing check

+

"enclosed" - An enclosing check (other_layer enclosing layer)

+ +

Usage:

+
    +
  • layer.enclosed(other_layer, value [, options])
  • +
+

+Note: "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 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): +

+ + + + + +
+

+

"enclosing" - An enclosing check (layer enclosing other_layer)

Usage:

    @@ -790,8 +827,8 @@ See enclosing for a description of that method the DRC framework. These variants have more options and are more intuitive to use. See 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 @@ -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.

    -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.

    @@ -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.

    -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.

    @@ -2569,6 +2606,7 @@ layers touch are also reported. More specifically, the case of zero spacing 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.

    @@ -2789,7 +2827,7 @@ The notch method is similar, but will only report self-spac 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. diff --git a/src/lay/lay/doc/images/drc_encd1.png b/src/lay/lay/doc/images/drc_encd1.png new file mode 100644 index 000000000..4be32165a Binary files /dev/null and b/src/lay/lay/doc/images/drc_encd1.png differ diff --git a/src/lay/lay/doc/images/drc_encd1u.png b/src/lay/lay/doc/images/drc_encd1u.png new file mode 100644 index 000000000..8c9403be8 Binary files /dev/null and b/src/lay/lay/doc/images/drc_encd1u.png differ diff --git a/src/lay/lay/doc/images/drc_encd2.png b/src/lay/lay/doc/images/drc_encd2.png new file mode 100644 index 000000000..88e7ad63e Binary files /dev/null and b/src/lay/lay/doc/images/drc_encd2.png differ diff --git a/src/lay/lay/doc/images/drc_encd2u.png b/src/lay/lay/doc/images/drc_encd2u.png new file mode 100644 index 000000000..ec3615fca Binary files /dev/null and b/src/lay/lay/doc/images/drc_encd2u.png differ diff --git a/src/lay/lay/layDRCLVSHelpResources.qrc b/src/lay/lay/layDRCLVSHelpResources.qrc index d11b89b5e..2ad75afed 100644 --- a/src/lay/lay/layDRCLVSHelpResources.qrc +++ b/src/lay/lay/layDRCLVSHelpResources.qrc @@ -36,6 +36,10 @@ doc/images/drc_enc2.png doc/images/drc_enc1u.png doc/images/drc_enc2u.png + doc/images/drc_encd1.png + doc/images/drc_encd2.png + doc/images/drc_encd1u.png + doc/images/drc_encd2u.png doc/images/drc_overlap1.png doc/images/drc_overlap2.png doc/images/drc_overlap1u.png diff --git a/src/unit_tests/unit_test_main.cc b/src/unit_tests/unit_test_main.cc index b506ff893..51b675222 100644 --- a/src/unit_tests/unit_test_main.cc +++ b/src/unit_tests/unit_test_main.cc @@ -246,7 +246,8 @@ run_tests (const std::vector &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 << ""; + ut::noctrl << "Memory: " << timer.memory_size () / 1024 << "k"; + ut::ctrl << ""; } catch (tl::CancelException &) { diff --git a/testdata/algo/hierarchy_builder_au2a.gds b/testdata/algo/hierarchy_builder_au2a.gds index 9d556aed6..0f6afd622 100644 Binary files a/testdata/algo/hierarchy_builder_au2a.gds and b/testdata/algo/hierarchy_builder_au2a.gds differ diff --git a/testdata/algo/hierarchy_builder_au2b.gds b/testdata/algo/hierarchy_builder_au2b.gds index 66a8a6d76..e5e3ad445 100644 Binary files a/testdata/algo/hierarchy_builder_au2b.gds and b/testdata/algo/hierarchy_builder_au2b.gds differ diff --git a/testdata/algo/hierarchy_builder_au2c.gds b/testdata/algo/hierarchy_builder_au2c.gds index 85f5d6082..d822f9a7a 100644 Binary files a/testdata/algo/hierarchy_builder_au2c.gds and b/testdata/algo/hierarchy_builder_au2c.gds differ diff --git a/testdata/algo/hierarchy_builder_au2d.gds b/testdata/algo/hierarchy_builder_au2d.gds index 9589536ac..e5e3ad445 100644 Binary files a/testdata/algo/hierarchy_builder_au2d.gds and b/testdata/algo/hierarchy_builder_au2d.gds differ diff --git a/testdata/algo/hierarchy_builder_au2f.gds b/testdata/algo/hierarchy_builder_au2f.gds index 7d244fcf7..369ee7d65 100644 Binary files a/testdata/algo/hierarchy_builder_au2f.gds and b/testdata/algo/hierarchy_builder_au2f.gds differ diff --git a/testdata/algo/hierarchy_builder_au4a.gds b/testdata/algo/hierarchy_builder_au4a.gds index 504df9920..8c9f4d9ce 100644 Binary files a/testdata/algo/hierarchy_builder_au4a.gds and b/testdata/algo/hierarchy_builder_au4a.gds differ diff --git a/testdata/drc/drcGenericTests_2.drc b/testdata/drc/drcGenericTests_2.drc index 5a241e6cf..54a49540c 100644 --- a/testdata/drc/drcGenericTests_2.drc +++ b/testdata/drc/drcGenericTests_2.drc @@ -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) diff --git a/testdata/drc/drcGenericTests_au2.gds b/testdata/drc/drcGenericTests_au2.gds index 0cd389b25..2e9bb50f4 100644 Binary files a/testdata/drc/drcGenericTests_au2.gds and b/testdata/drc/drcGenericTests_au2.gds differ diff --git a/testdata/drc/drcGenericTests_au2d.gds b/testdata/drc/drcGenericTests_au2d.gds index 3fb8e4e50..9035770a4 100644 Binary files a/testdata/drc/drcGenericTests_au2d.gds and b/testdata/drc/drcGenericTests_au2d.gds differ diff --git a/testdata/drc/drcSimpleTests_48.drc b/testdata/drc/drcSimpleTests_48.drc index 672fe9270..51f882378 100644 --- a/testdata/drc/drcSimpleTests_48.drc +++ b/testdata/drc/drcSimpleTests_48.drc @@ -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) diff --git a/testdata/drc/drcSimpleTests_au48.gds b/testdata/drc/drcSimpleTests_au48.gds index 4f81f169a..e70a42f47 100644 Binary files a/testdata/drc/drcSimpleTests_au48.gds and b/testdata/drc/drcSimpleTests_au48.gds differ diff --git a/testdata/drc/drcSimpleTests_au48d.gds b/testdata/drc/drcSimpleTests_au48d.gds index 4f81f169a..dfc7a8ada 100644 Binary files a/testdata/drc/drcSimpleTests_au48d.gds and b/testdata/drc/drcSimpleTests_au48d.gds differ diff --git a/testdata/drc/drcSuiteTests.drc b/testdata/drc/drcSuiteTests.drc index 35615d564..34ea0613c 100644 --- a/testdata/drc/drcSuiteTests.drc +++ b/testdata/drc/drcSuiteTests.drc @@ -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}" diff --git a/testdata/drc/drcSuiteTests_au1.oas b/testdata/drc/drcSuiteTests_au1.oas index 0dd5c6851..dfe13b4c4 100644 Binary files a/testdata/drc/drcSuiteTests_au1.oas and b/testdata/drc/drcSuiteTests_au1.oas differ diff --git a/testdata/drc/drcSuiteTests_au2.oas b/testdata/drc/drcSuiteTests_au2.oas index 6ccbca614..68929488a 100644 Binary files a/testdata/drc/drcSuiteTests_au2.oas and b/testdata/drc/drcSuiteTests_au2.oas differ diff --git a/testdata/drc/drcSuiteTests_au3.oas b/testdata/drc/drcSuiteTests_au3.oas index c42f0b098..df46e93b8 100644 Binary files a/testdata/drc/drcSuiteTests_au3.oas and b/testdata/drc/drcSuiteTests_au3.oas differ diff --git a/testdata/drc/drcSuiteTests_au4.oas b/testdata/drc/drcSuiteTests_au4.oas index 1b2dc47b6..fb0dff32a 100644 Binary files a/testdata/drc/drcSuiteTests_au4.oas and b/testdata/drc/drcSuiteTests_au4.oas differ diff --git a/testdata/drc/drcSuiteTests_au5.oas b/testdata/drc/drcSuiteTests_au5.oas index 95b0fa533..114e23cc7 100644 Binary files a/testdata/drc/drcSuiteTests_au5.oas and b/testdata/drc/drcSuiteTests_au5.oas differ diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas index 1b47dc5fb..f5341e4e3 100644 Binary files a/testdata/drc/drcSuiteTests_au6.oas and b/testdata/drc/drcSuiteTests_au6.oas differ diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index ffc0e0d36..e6a71a71e 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -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, "") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index a340781e3..ecf2a8853 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -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, "") diff --git a/testdata/ruby/dbTransTest.rb b/testdata/ruby/dbTransTest.rb index d69244910..e98cc4d87 100644 --- a/testdata/ruby/dbTransTest.rb +++ b/testdata/ruby/dbTransTest.rb @@ -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" )