From 3f8825cfd1faef21c9588551e281d1b73f906fcd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 15 Nov 2018 22:50:02 +0100 Subject: [PATCH] WIP: Improved design of HierarchyBuilder, added tests. --- src/db/db/db.pro | 6 +- src/db/db/dbDeepShapeStore.cc | 37 +- src/db/db/dbDeepShapeStore.h | 26 +- src/db/db/dbHierarchyBuilder.cc | 442 +++++++++++++----- src/db/db/dbHierarchyBuilder.h | 150 +++++- src/db/db/dbLayout.cc | 18 + src/db/db/dbLayout.h | 8 + src/db/db/dbRecursiveShapeIterator.cc | 40 +- src/db/db/dbRecursiveShapeIterator.h | 55 ++- src/db/db/gsiDeclDbLayout.cc | 27 +- src/db/unit_tests/dbHierarchyBuilderTests.cc | 337 +++++++++++++ src/db/unit_tests/dbRecursiveShapeIterator.cc | 166 ++++--- src/db/unit_tests/unit_tests.pro | 3 +- src/tl/tl/tlObject.cc | 4 +- src/tl/tl/tlObject.h | 4 +- testdata/algo/hierarchy_builder_au1.gds | Bin 0 -> 2762 bytes testdata/algo/hierarchy_builder_au2a.gds | Bin 0 -> 11526 bytes testdata/algo/hierarchy_builder_au2b.gds | Bin 0 -> 11614 bytes testdata/algo/hierarchy_builder_au2c.gds | Bin 0 -> 11902 bytes testdata/algo/hierarchy_builder_au2d.gds | Bin 0 -> 11536 bytes testdata/algo/hierarchy_builder_au2e.gds | Bin 0 -> 942 bytes testdata/algo/hierarchy_builder_au3a.gds | Bin 0 -> 1172 bytes testdata/algo/hierarchy_builder_au4a.gds | Bin 0 -> 82618 bytes testdata/algo/hierarchy_builder_l1.gds | Bin 0 -> 2762 bytes testdata/algo/hierarchy_builder_l2.gds | Bin 0 -> 268 bytes testdata/algo/hierarchy_builder_l3.gds | Bin 0 -> 2858 bytes 26 files changed, 1091 insertions(+), 232 deletions(-) create mode 100644 src/db/unit_tests/dbHierarchyBuilderTests.cc create mode 100644 testdata/algo/hierarchy_builder_au1.gds create mode 100644 testdata/algo/hierarchy_builder_au2a.gds create mode 100644 testdata/algo/hierarchy_builder_au2b.gds create mode 100644 testdata/algo/hierarchy_builder_au2c.gds create mode 100644 testdata/algo/hierarchy_builder_au2d.gds create mode 100644 testdata/algo/hierarchy_builder_au2e.gds create mode 100644 testdata/algo/hierarchy_builder_au3a.gds create mode 100644 testdata/algo/hierarchy_builder_au4a.gds create mode 100644 testdata/algo/hierarchy_builder_l1.gds create mode 100644 testdata/algo/hierarchy_builder_l2.gds create mode 100644 testdata/algo/hierarchy_builder_l3.gds diff --git a/src/db/db/db.pro b/src/db/db/db.pro index ff156a235..f32306b99 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -136,7 +136,8 @@ SOURCES = \ dbFlatEdgePairs.cc \ dbOriginalLayerEdgePairs.cc \ dbEdgePairsDelegate.cc \ - dbDeepShapeStore.cc + dbDeepShapeStore.cc \ + dbHierarchyBuilder.cc HEADERS = \ dbArray.h \ @@ -242,7 +243,8 @@ HEADERS = \ dbFlatEdgePairs.h \ dbOriginalLayerEdgePairs.h \ dbEdgePairsDelegate.h \ - dbDeepShapeStore.h + dbDeepShapeStore.h \ + dbHierarchyBuilder.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index e51bd5a3a..abb1469e8 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -21,4 +21,39 @@ */ -// ... +#include "dbDeepShapeStore.h" + +namespace db +{ + +DeepLayer::DeepLayer (const DeepLayer &x) + : mp_store (x.mp_store), m_layout (x.m_layout), m_layer (x.m_layer) +{ + // .. nothing yet .. +} + +DeepLayer::DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer) + : mp_store (store), m_layout (layout), m_layer (layer) +{ + // .. nothing yet .. +} + + + +DeepShapeStore::DeepShapeStore () +{ + // @@@ +} + +DeepShapeStore::~DeepShapeStore () +{ + // @@@ +} + +DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count) +{ + return DeepLayer (0, 0, 0); // @@@ +} + +} + diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 2a32c2439..f76805fd1 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -30,6 +30,9 @@ #include "dbLayout.h" #include "dbRecursiveShapeIterator.h" +#include +#include + namespace db { class DeepShapeStore; @@ -81,9 +84,6 @@ private: */ DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer); - unsigned int layout () const { return m_layout; } - unsigned int layer () const { return m_layer; } - tl::weak_ptr mp_store; unsigned int m_layout; unsigned int m_layer; @@ -92,7 +92,7 @@ private: /** * @brief The "deep shape store" is a working model for the hierarchical ("deep") processor * - * The deep shape store keep temporary data for the deep shape processor. + * The deep shape store keeps temporary data for the deep shape processor. * It mainly consists of layout objects holding the hierarchy trees and layers * for the actual shapes. * @@ -110,10 +110,28 @@ public: */ DeepShapeStore (); + /** + * @brief The destructor + */ + ~DeepShapeStore (); + + /** + * @brief Inserts a polygon layer into the deep shape store + * + * This method will create a new layer inside the deep shape store as a + * working copy of the original layer. Preparation involves re-shaping + * the polygons so their bounding box is a better approximation and the + * polygon complexity is reduced. For this, the polygons are split + * into parts satisfying the area ratio (bounding box vs. polygon area) + * and maximum vertex count constraints. + */ + DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 3.0, size_t max_vertex_count = 16); + private: // no copying DeepShapeStore (const DeepShapeStore &); DeepShapeStore &operator= (const DeepShapeStore &); + }; } diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 388da34e3..be5945412 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -24,10 +24,13 @@ #include "dbHierarchyBuilder.h" #include "dbClip.h" #include "dbRegion.h" +#include "dbPolygonTools.h" namespace db { +static HierarchyBuilderShapeInserter def_inserter; + // ------------------------------------------------------------------------------------------- int @@ -78,104 +81,82 @@ compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIter // ------------------------------------------------------------------------------------------- -static bool is_outside (const db::Box &obj, const std::set ®ion) -{ - if (region.empty ()) { - return false; - } - - for (std::set::const_iterator b = region.begin (); b != region.end (); ++b) { - if (obj.touches (*b)) { - return false; - } - } - - return true; -} - -static bool is_inside (const db::Box &obj, const std::set ®ion) -{ - if (region.empty ()) { - return true; - } - - for (std::set::const_iterator b = region.begin (); b != region.end (); ++b) { - if (obj.inside (*b)) { - return true; - } - } - - if (is_outside (obj, region)) { - return false; - } else { - - // TODO: basically a detailed analysis is required here - return false; - - } -} - /** * @brief Computes the clip variant (a box set) from a cell bbox, a region and a complex region (optional) */ -static std::set compute_clip_variant (const db::Box &cell_bbox, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +static std::pair > compute_clip_variant (const db::Box &cell_bbox, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) { + if (region == db::Box::world ()) { + return std::make_pair (true, std::set ()); + } + + db::ICplxTrans trans_inv (trans.inverted ()); + db::Box region_in_cell = region.transformed (trans_inv); + std::set clip_variant; - if (region != db::Box::world () && ! cell_bbox.inside (region)) { + if (! cell_bbox.overlaps (region_in_cell)) { + // an empty clip variant should not happen, but who knows + return std::make_pair (false, std::set ()); + } - db::Box rect_box = region & cell_bbox; + db::Box rect_box = region_in_cell & cell_bbox; - if (complex_region) { + if (complex_region) { - for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { - if (rect_box.overlaps (*cr)) { - clip_variant.insert (rect_box * *cr); - } + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (region, db::box_convert ()); ! cr.at_end (); ++cr) { + db::Box cr_in_cell = (*cr).transformed (trans_inv); + if (rect_box.overlaps (cr_in_cell)) { + clip_variant.insert (rect_box * cr_in_cell); } - - if (clip_variant.empty ()) { - // an empty clip variant should not happen, but who knows - clip_variant.insert (db::Box ()); - } - - } else { - clip_variant.insert (rect_box); } + if (clip_variant.empty ()) { + // an empty clip variant should not happen, but who knows + return std::make_pair (false, std::set ()); + } + + } else { + clip_variant.insert (rect_box); } - return clip_variant; + return std::make_pair (true, clip_variant); } -static void insert_clipped (const db::Box &box, const std::set &clip, db::Shapes &shapes) +HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, HierarchyBuilderShapeReceiver *pipe) + : mp_target (target), m_initial_pass (true), m_target_layer (target_layer) { - for (std::set::const_iterator b = clip.begin (); b != clip.end (); ++b) { - db::Box bb = *b & box; - if (! bb.empty ()) { - shapes.insert (bb); - } - } + mp_pipe = pipe ? pipe : &def_inserter; } -static void insert_clipped (const db::Polygon &poly, const std::set &clip, db::Shapes &shapes) +HierarchyBuilder::HierarchyBuilder (db::Layout *target, HierarchyBuilderShapeReceiver *pipe) + : mp_target (target), m_initial_pass (true), m_target_layer (0) { - // TODO: is this good way to clip a polygon at a complex boundary? - for (std::set::const_iterator b = clip.begin (); b != clip.end (); ++b) { - std::vector clipped_poly; - db::clip_poly (poly, *b, clipped_poly); - for (std::vector::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) { - shapes.insert (*p); - } - } + mp_pipe = pipe ? pipe : &def_inserter; } - -HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, bool clip_shapes) - : mp_target (target), m_clip_shapes (clip_shapes), m_initial_pass (true), m_target_layer (target_layer) +HierarchyBuilder::~HierarchyBuilder () { // .. nothing yet .. } +void +HierarchyBuilder::set_shape_receiver (HierarchyBuilderShapeReceiver *pipe) +{ + mp_pipe = pipe; +} + +void +HierarchyBuilder::reset () +{ + m_initial_pass = true; + mp_initial_cell = 0; + + m_cell_map.clear (); + m_cells_seen.clear (); + m_cell_stack.clear (); + m_cm_entry = cell_map_type::const_iterator (); +} + void HierarchyBuilder::begin (const RecursiveShapeIterator *iter) { @@ -185,20 +166,32 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) tl_assert (compare_iterators_with_respect_to_target_hierarchy (m_ref_iter, *iter) == 0); } - m_cells_seen.clear (); - m_cm_entry = cell_map_type::const_iterator (); - m_cell_stack.clear (); + m_cells_seen.clear (); + + std::pair > key (iter->top_cell ()->cell_index (), std::set ()); + m_cm_entry = m_cell_map.find (key); + if (m_cm_entry == m_cell_map.end ()) { + + db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.first)); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first; + + } + + db::Cell &new_top = mp_target->cell (m_cm_entry->second); + m_cells_seen.insert (key); - db::Cell &new_top = mp_target->cell (mp_target->add_cell (iter->layout ()->cell_name (iter->top_cell ()->cell_index ()))); m_cell_stack.push_back (&new_top); } void HierarchyBuilder::end (const RecursiveShapeIterator * /*iter*/) { + tl_assert (m_cell_stack.size () == 1); + m_initial_pass = false; m_cells_seen.clear (); + mp_initial_cell = m_cell_stack.back (); m_cell_stack.pop_back (); } @@ -217,7 +210,7 @@ HierarchyBuilder::leave_cell (const RecursiveShapeIterator * /*iter*/, const db: m_cell_stack.pop_back (); } -bool +HierarchyBuilder::new_inst_mode HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { if (all) { @@ -238,67 +231,296 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } - return (m_cells_seen.find (key) == m_cells_seen.end ()); + // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. + return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; } else { - // iterate by instance array members - return true; + + // Iterate by instance array members + return NI_all; + } } bool -HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region) +HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) { - db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox (); - std::set clip_variant = compute_clip_variant (cell_bbox, region, complex_region); + if (all) { - std::pair > key (inst.object ().cell_index (), clip_variant); - m_cm_entry = m_cell_map.find (key); + return true; - if (m_initial_pass) { + } else { - if (m_cm_entry == m_cell_map.end ()) { - std::string suffix; - if (! key.second.empty ()) { - suffix = "$CLIP_VAR"; - } - db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); - m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox (); + std::pair > clip_variant = compute_clip_variant (cell_bbox, trans, region, complex_region); + if (! clip_variant.first) { + return false; } - db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans); - m_cell_stack.back ()->insert (new_inst); + std::pair > key (inst.object ().cell_index (), clip_variant.second); + m_cm_entry = m_cell_map.find (key); + + if (m_initial_pass) { + + if (m_cm_entry == m_cell_map.end ()) { + std::string suffix; + if (! key.second.empty ()) { + suffix = "$CLIP_VAR"; + } + db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ()); + m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first; + } + + db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans); + m_cell_stack.back ()->insert (new_inst); + + } + + return (m_cells_seen.find (key) == m_cells_seen.end ()); } - - return (m_cells_seen.find (key) == m_cells_seen.end ()); } void -HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/) +HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region) { - tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ()); - - // TODO: property mapping? - db::Shapes &shapes = m_cell_stack.back ()->shapes (m_target_layer); + mp_pipe->push (shape, region, complex_region, &shapes); +} - if (m_cm_entry->first.second.empty () || is_inside (shape.bbox (), m_cm_entry->first.second)) { +// --------------------------------------------------------------------------------------------- - shapes.insert (shape); +ClippingHierarchyBuilderShapeReceiver::ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe) + : mp_pipe (pipe ? pipe : &def_inserter) +{ + // .. nothing yet .. +} - } else if (! is_outside (shape.bbox (), m_cm_entry->first.second)) { +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (region == world || is_inside (shape.bbox (), region, complex_region)) { + + mp_pipe->push (shape, world, 0, target); + + } else if (! is_outside (shape.bbox (), region, complex_region)) { // clip the shape if required - if (! m_clip_shapes || shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { - shapes.insert (shape); + if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { + mp_pipe->push (shape, world, 0, target); } else if (shape.is_box ()) { - insert_clipped (shape.box (), m_cm_entry->first.second, shapes); + insert_clipped (shape.box (), region, complex_region, target); } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { - insert_clipped (shape.polygon (), m_cm_entry->first.second, shapes); + db::Polygon poly; + shape.polygon (poly); + insert_clipped (poly, region, complex_region, target); } } } +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (! complex_region) { + db::Box r = shape & region; + if (! r.empty()) { + mp_pipe->push (r, world, 0, target); + } + } else { + insert_clipped (shape, region, complex_region, target); + } +} + +void +ClippingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + static db::Box world = db::Box::world (); + + if (region == world || (shape.box ().inside (region) && ! complex_region)) { + mp_pipe->push (shape, world, 0, target); + } else { + insert_clipped (shape, region, complex_region, target); + } +} + +bool +ClippingHierarchyBuilderShapeReceiver::is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +{ + if (region == db::Box::world ()) { + return true; + } + + if (box.inside (region)) { + + db::Box rect_box = region & box; + + if (complex_region) { + + // TODO: this is not a real test for being inside a complex region + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { + if (rect_box.inside (*cr)) { + return true; + } + } + + } + + } + + return false; +} + +bool +ClippingHierarchyBuilderShapeReceiver::is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region) +{ + if (region == db::Box::world ()) { + return false; + } + + if (box.overlaps (region)) { + + db::Box rect_box = region & box; + + if (complex_region) { + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert ()); ! cr.at_end (); ++cr) { + if (rect_box.overlaps (*cr)) { + return false; + } + } + } else { + return false; + } + + } + + return true; +} + +void +ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + db::Box bb = box & region; + static db::Box world = db::Box::world (); + + if (complex_region) { + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (bb, db::box_convert ()); ! cr.at_end (); ++cr) { + mp_pipe->push (*cr & bb, world, 0, target); + } + } else { + mp_pipe->push (bb, world, 0, target); + } +} + +void +ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + std::vector clipped_poly; + static db::Box world = db::Box::world (); + + if (complex_region) { + // TODO: is this good way to clip a polygon at a complex boundary? + for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (region, db::box_convert ()); ! cr.at_end (); ++cr) { + db::clip_poly (poly, *cr & region, clipped_poly); + } + } else { + db::clip_poly (poly, region, clipped_poly); + } + + for (std::vector::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) { + mp_pipe->push (*p, world, 0, target); + } +} + +// --------------------------------------------------------------------------------------------- + +ReducingHierarchyBuilderShapeReceiver::ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe, double area_ratio, size_t max_vertex_count) + : mp_pipe (pipe ? pipe : &def_inserter), m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count) +{ + // .. nothing yet .. +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) { + mp_pipe->push (shape, region, complex_region, target); + } else if (shape.is_box ()) { + mp_pipe->push (shape.box (), region, complex_region, target); + } else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + db::Polygon poly; + shape.polygon (poly); + reduce (poly, region, complex_region, target); + } +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + mp_pipe->push (shape, region, complex_region, target); +} + +void +ReducingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + reduce (shape, region, complex_region, target); +} + +static double area_ratio (const db::Polygon &poly) +{ + return double (poly.box ().area ()) / double (poly.area ()); +} + +void +ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) +{ + size_t npoints = 0; + for (unsigned int c = 0; c < poly.holes () + 1; ++c) { + npoints += poly.contour (c).size (); + } + + if (npoints > m_max_vertex_count || area_ratio (poly) > m_area_ratio) { + + std::vector split_polygons; + db::split_polygon (poly, split_polygons); + for (std::vector ::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) { + reduce (*sp, region, complex_region, target); + } + + } else { + mp_pipe->push (poly, region, complex_region, target); + } +} + +// --------------------------------------------------------------------------------------------- + +PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout) + : mp_layout (layout) +{ + // nothing yet .. +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + if (shape.is_box () || shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + db::Polygon poly; + shape.polygon (poly); + target->insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + } +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + target->insert (db::PolygonRef (db::Polygon (shape), mp_layout->shape_repository ())); +} + +void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) +{ + target->insert (db::PolygonRef (shape, mp_layout->shape_repository ())); +} + } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index c452beb25..c982ec384 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -43,6 +43,108 @@ namespace db */ int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2); +/** + * @brief A class to receive shapes from the hierarchy builder + * + * This class can be reimplemented to implement clipping and/or + * simplification. + */ +class DB_PUBLIC HierarchyBuilderShapeReceiver +{ +public: + HierarchyBuilderShapeReceiver () { } + virtual ~HierarchyBuilderShapeReceiver () { } + + virtual void push (const db::Shape &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Box &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; + virtual void push (const db::Polygon &shape, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0; +}; + +/** + * @brief A shape receiver that simply pushes into the target + */ +class DB_PUBLIC HierarchyBuilderShapeInserter + : public HierarchyBuilderShapeReceiver +{ +public: + HierarchyBuilderShapeInserter () { } + + virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + target->insert (shape); + } + + virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + target->insert (shape); + } + + virtual void push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) + { + target->insert (shape); + } +}; + +/** + * @brief A clipping shape receiver that forwards to another one + */ +class DB_PUBLIC ClippingHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0); + + virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + void insert_clipped (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + void insert_clipped (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + static bool is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region); + static bool is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region); + + HierarchyBuilderShapeReceiver *mp_pipe; +}; + +/** + * @brief A polygon reducing shape receiver that forwards to another one + */ +class DB_PUBLIC ReducingHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0, double area_ratio = 3.0, size_t max_vertex_count = 16); + + virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + void reduce (const db::Polygon &poly, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target); + + HierarchyBuilderShapeReceiver *mp_pipe; + double m_area_ratio; + size_t m_max_vertex_count; +}; + +/** + * @brief A polygon reference generating shape receiver that feeds a shapes array after turning the shapes into PolygonRefs + */ +class DB_PUBLIC PolygonReferenceHierarchyBuilderShapeReceiver + : public HierarchyBuilderShapeReceiver +{ +public: + PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout); + + virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + virtual void push (const db::Polygon &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); + +private: + db::Layout *mp_layout; +}; + /** * @brief A class building a hierarchy from a recursive shape iterator in push mode * @@ -58,26 +160,55 @@ int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShape * rely on precisely the same hierarchy arrangement. This is not given with * region selections. */ -class HierarchyBuilder +class DB_PUBLIC HierarchyBuilder : public db::RecursiveShapeReceiver { public: typedef std::map >, db::cell_index_type> cell_map_type; - HierarchyBuilder (db::Layout *target, unsigned int target_layer, bool clip_shapes); + HierarchyBuilder (db::Layout *target, unsigned int target_layer, HierarchyBuilderShapeReceiver *pipe = 0); + HierarchyBuilder (db::Layout *target, HierarchyBuilderShapeReceiver *pipe = 0); + virtual ~HierarchyBuilder (); + + /** + * @brief Installs a custom shape receiver + * The hierarchy builder will *NOT* take ownership of this object. + */ + void set_shape_receiver (HierarchyBuilderShapeReceiver *pipe); virtual void begin (const RecursiveShapeIterator *iter); - virtual void end (const RecursiveShapeIterator * /*iter*/); - virtual void enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/); - virtual void leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/); - virtual bool new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all); - virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); - virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans); + virtual void end (const RecursiveShapeIterator *iter); + virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region); + virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell); + virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); + + /** + * @brief Sets the target layer - shapes will be put there + */ + void set_target_layer (unsigned int target_layer) + { + m_target_layer = target_layer; + } + + /** + * @brief Reset the builder - performs a new initial pass + */ + void reset (); + + /** + * @brief Gets the initial cell the builder produced + */ + db::Cell *initial_cell () + { + return mp_initial_cell; + } private: tl::weak_ptr mp_target; - bool m_clip_shapes; + HierarchyBuilderShapeReceiver *mp_pipe; bool m_initial_pass; db::RecursiveShapeIterator m_ref_iter; cell_map_type m_cell_map; @@ -85,6 +216,7 @@ private: cell_map_type::const_iterator m_cm_entry; unsigned int m_target_layer; std::vector m_cell_stack; + db::Cell *mp_initial_cell; }; } diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 90e2a9200..0380deb12 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1511,6 +1511,24 @@ Layout::insert_layer (unsigned int index, const LayerProperties &props) layer_properties_changed (); } +unsigned int +Layout::get_layer (const db::LayerProperties &lp) +{ + if (lp.is_null ()) { + // for a null layer info always create a layer + return insert_layer (); + } else { + // if we have a layer with the requested properties already, return this. + for (db::Layout::layer_iterator li = begin_layers (); li != end_layers (); ++li) { + if ((*li).second->log_equal (lp)) { + return (*li).first; + } + } + // otherwise create a new layer + return insert_layer (lp); + } +} + unsigned int Layout::waste_layer () const { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index e1724657d..8ebaf1fbc 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1447,6 +1447,14 @@ public: */ void insert_layer (unsigned int index, const LayerProperties &props = LayerProperties ()); + /** + * @brief Gets or creates a layer with the given properties + * + * If there already is a layer matching the given properties, it's index will be + * returned. Otherwise a new layer with these properties is created. + */ + unsigned int get_layer (const db::LayerProperties &props); + /** * @brief Insert a new special layer with the given properties * diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 2c9216776..d55bce34c 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -716,8 +716,10 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const m_inst_quad_id_stack.push_back (m_inst_quad_id); bool ia = is_inactive (); + bool aoi = is_all_of_instance (); mp_cell = &mp_layout->cell (m_inst->cell_index ()); set_inactive (ia); + set_all_of_instance (aoi); m_trans = m_trans * m_inst->complex_trans (*m_inst_array); @@ -876,22 +878,34 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const } } + bool all_of_instance = false; + bool with_region = false; + if (m_local_region_stack.back () != box_type::world () && ! m_inst->cell_inst ().bbox (m_box_convert).inside (m_local_region_stack.back ())) { - if (receiver && ! receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), false)) { - m_inst_array = inst_array_iterator (); - } else { - m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); - } + with_region = true; } else { // TODO: optimization potential: only report all_of_instance == false, if not entirely within the complex region - bool all_of_instance = m_local_complex_region_stack.empty (); - if (receiver && ! receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance)) { - m_inst_array = inst_array_iterator (); - } else { - m_inst_array = m_inst->cell_inst ().begin (); - } + all_of_instance = m_local_complex_region_stack.empty (); } + RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all; + if (receiver) { + ni = receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + } + + if (ni == RecursiveShapeReceiver::NI_skip) { + m_inst_array = inst_array_iterator (); + } else if (ni == RecursiveShapeReceiver::NI_single) { + // a singular iterator + m_inst_array = db::CellInstArray::iterator (m_inst->cell_inst ().front (), false); + } else if (with_region) { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + } else { + m_inst_array = m_inst->cell_inst ().begin (); + } + + set_all_of_instance (all_of_instance); + new_inst_member (receiver); if (! m_inst_array.at_end ()) { @@ -921,7 +935,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const } while (! m_inst_array.at_end () && receiver) { - if (receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ())) { + if (receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) { break; } else { ++m_inst_array; @@ -950,7 +964,7 @@ RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver) validate (receiver); while (! at_end ()) { - receiver->shape (this, *m_shape, m_trans); + receiver->shape (this, *m_shape, m_trans, m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); next (receiver); } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index ce54b7c2f..59cc5679f 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -548,7 +548,7 @@ public: } /** - * @brief Get the current depth + * @brief Gets the current depth * * Returns the number of hierarchy levels we are below top level currently. */ @@ -559,7 +559,7 @@ public: } /** - * @brief Get the current shape + * @brief Gets the current shape * * Returns the shape currently referred to by the recursive iterator. * This shape is not transformed yet and is located in the current cell. @@ -600,27 +600,25 @@ public: bool at_end () const; /** - * @brief Get the current cell's index + * @brief Gets the current cell's index */ db::cell_index_type cell_index () const { - validate (0); - size_t c = reinterpret_cast (mp_cell); - return reinterpret_cast (c - (c & size_t (1)))->cell_index (); + return cell ()->cell_index (); } /** - * @brief Get the current cell's reference + * @brief Gets the current cell's reference */ const cell_type *cell () const { validate (0); size_t c = reinterpret_cast (mp_cell); - return reinterpret_cast (c - (c & size_t (1))); + return reinterpret_cast (c - (c & size_t (3))); } /** - * @brief Increment the iterator (operator version) + * @brief Increments the iterator (operator version) */ RecursiveShapeIterator &operator++() { @@ -749,7 +747,19 @@ private: { size_t c = reinterpret_cast (mp_cell); c -= (c & size_t (1)); - mp_cell = reinterpret_cast (c + a); + mp_cell = reinterpret_cast (c + (a ? 1 : 0)); + } + + bool is_all_of_instance () const + { + return (reinterpret_cast (mp_cell) & size_t (2)) != 0; + } + + void set_all_of_instance (bool a) const + { + size_t c = reinterpret_cast (mp_cell); + c -= (c & size_t (2)); + mp_cell = reinterpret_cast (c + (a ? 2 : 0)); } }; @@ -769,7 +779,19 @@ class DB_PUBLIC RecursiveShapeReceiver public: typedef RecursiveShapeIterator::box_tree_type box_tree_type; + /** + * @brief See new_inst for details. + */ + enum new_inst_mode { NI_all = 0, NI_single = 1, NI_skip = 2 }; + + /** + * @brief Constructor + */ RecursiveShapeReceiver () { } + + /** + * @brief Destructor + */ virtual ~RecursiveShapeReceiver () { } /** @@ -824,9 +846,12 @@ public: * * The "all" parameter is true, if all instances of the array will be addressed. * - * If this method returns false, the instance (the whole array) is skipped and the cell is not entered. + * This method can return the following values: + * - NI_all: iterate all members through "new_inst_member" + * - NI_single: iterate a single member (the first one) + * - NI_skip: skips the whole array (not a single instance is iterated) */ - virtual bool new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } + virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } /** * @brief Enters a new array member of the instance @@ -835,16 +860,18 @@ public: * which holds the complex transformation for this particular instance of * the array. * + * "all" is true, if an instance array is iterated in "all" mode (see new_inst). + * * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. */ - virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { return true; } + virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } /** * @brief Delivers a shape * * @param trans The transformation which maps the shape to the top cell. */ - virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*trans*/) { } + virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } }; } // namespace db diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 298655b8f..48c60db98 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -367,41 +367,24 @@ static std::vector multi_clip_into (db::Layout *l, db::cell return db::clip_layout(*l, *t, c, boxes, true); } -static unsigned int get_layer (db::Layout *l, const db::LayerProperties &lp) -{ - if (lp.is_null ()) { - // for a null layer info always create a layer - return l->insert_layer (); - } else { - // if we have a layer with the requested properties already, return this. - for (db::Layout::layer_iterator li = l->begin_layers (); li != l->end_layers (); ++li) { - if ((*li).second->log_equal (lp)) { - return (*li).first; - } - } - // otherwise create a new layer - return l->insert_layer (lp); - } -} - static unsigned int get_layer0 (db::Layout *l) { - return get_layer (l, db::LayerProperties ()); + return l->get_layer (db::LayerProperties ()); } static unsigned int get_layer1 (db::Layout *l, const std::string &name) { - return get_layer (l, db::LayerProperties (name)); + return l->get_layer (db::LayerProperties (name)); } static unsigned int get_layer2 (db::Layout *l, int ln, int dn) { - return get_layer (l, db::LayerProperties (ln, dn)); + return l->get_layer (db::LayerProperties (ln, dn)); } static unsigned int get_layer3 (db::Layout *l, int ln, int dn, const std::string &name) { - return get_layer (l, db::LayerProperties (ln, dn, name)); + return l->get_layer (db::LayerProperties (ln, dn, name)); } static tl::Variant find_layer (db::Layout *l, const db::LayerProperties &lp) @@ -1297,7 +1280,7 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.25.\n" ) + - gsi::method_ext ("layer", &get_layer, + gsi::method ("layer", &db::Layout::get_layer, "@brief Finds or creates a layer with the given properties\n" "@args info\n" "\n" diff --git a/src/db/unit_tests/dbHierarchyBuilderTests.cc b/src/db/unit_tests/dbHierarchyBuilderTests.cc new file mode 100644 index 000000000..1dffa2d9d --- /dev/null +++ b/src/db/unit_tests/dbHierarchyBuilderTests.cc @@ -0,0 +1,337 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbHierarchyBuilder.h" +#include "dbReader.h" +#include "dbTestSupport.h" +#include "dbRegion.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target, false); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1); + + iter.push (&builder); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au1.gds"); +} + +TEST(2_WithoutClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::HierarchyBuilder builder (&target, false); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2a.gds"); +} + +TEST(2_WithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2b.gds"); +} + +TEST(2_WithClipAndSimplification) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ReducingHierarchyBuilderShapeReceiver red(0, 1.2, 4); + db::ClippingHierarchyBuilderShapeReceiver clip(&red); + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2c.gds"); +} + +TEST(2_WithClipAndRefGeneration) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target); + db::ClippingHierarchyBuilderShapeReceiver clip(&ref); + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, -2000, 18500, 6000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2d.gds"); +} + +TEST(2_WithEmptyResult) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::PolygonReferenceHierarchyBuilderShapeReceiver ref(&target); + db::ClippingHierarchyBuilderShapeReceiver clip(&ref); + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, db::Box (5000, 10000, 18500, 15000)); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au2e.gds"); +} + +TEST(3_ComplexRegionWithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Region reg; + reg.insert (db::Box (5000, 13000, 18500, 20000)); + reg.insert (db::Box (11000, 20000, 18500, 36000)); + reg.merge (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, reg); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au3a.gds"); +} + +TEST(4_ComplexRegionAndLayoutWithClip) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/hierarchy_builder_l3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::Layout target; + db::ClippingHierarchyBuilderShapeReceiver clip; + db::HierarchyBuilder builder (&target, &clip); + + db::cell_index_type target_top = target.add_cell ("CLIP_TOP"); + + for (db::Layout::layer_iterator li = ly.begin_layers (); li != ly.end_layers (); ++li) { + + builder.reset (); + + unsigned int li1 = (*li).first; + unsigned int target_layer = target.insert_layer (*(*li).second); + builder.set_target_layer (target_layer); + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Region reg; + reg.insert (db::Box (5000, 13000, 18500, 20000)); + reg.insert (db::Box (11000, 20000, 18500, 36000)); + reg.merge (); + db::RecursiveShapeIterator iter (ly, ly.cell (top_cell_index), li1, reg); + + iter.push (&builder); + + target.cell (target_top).insert (db::CellInstArray (db::CellInst (builder.initial_cell ()->cell_index ()), db::Trans ())); + + } + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/hierarchy_builder_au4a.gds"); +} + diff --git a/src/db/unit_tests/dbRecursiveShapeIterator.cc b/src/db/unit_tests/dbRecursiveShapeIterator.cc index 03dc7f918..dfd115c27 100644 --- a/src/db/unit_tests/dbRecursiveShapeIterator.cc +++ b/src/db/unit_tests/dbRecursiveShapeIterator.cc @@ -918,19 +918,23 @@ public: m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; } - virtual bool new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); if (all) { m_text += ",all"; } m_text += ")\n"; - return true; + return NI_all; } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { - m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans) + ")\n"; + m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans); + if (all) { + m_text += ",all"; + } + m_text += ")\n"; return true; } @@ -949,10 +953,26 @@ class ReceiverRejectingACellInstanceArray public: ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual bool new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) { LoggingReceiver::new_inst (iter, inst, region, complex_region, all); - return inst.object ().cell_index () != m_rejected; + return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; + } + +private: + db::cell_index_type m_rejected; +}; + +class ReceiverRejectingACellInstanceArrayExceptOne + : public LoggingReceiver +{ +public: + ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { } + + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + { + LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; } private: @@ -965,9 +985,9 @@ class ReceiverRejectingACellInstance public: ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) { - LoggingReceiver::new_inst_member (iter, inst, trans, region, complex_region); + LoggingReceiver::new_inst_member (iter, inst, trans, region, complex_region, all); return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; } @@ -1001,82 +1021,82 @@ TEST(10) EXPECT_EQ (lr1.text (), "begin\n" "new_inst($2,all)\n" - "new_inst_member($2,r0 *1 0,0)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 0,6000)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,0)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,6000)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" "leave_cell($3)\n" @@ -1091,25 +1111,67 @@ TEST(10) EXPECT_EQ (rr1.text (), "begin\n" "new_inst($2,all)\n" - "new_inst_member($2,r0 *1 0,0)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 0,6000)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,0)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,6000)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" "leave_cell($2)\n" "end\n" ); + ReceiverRejectingACellInstanceArrayExceptOne rs1 (c2.cell_index ()); + db::RecursiveShapeIterator is1 (g, c0, 0); + is1.push (&rs1); + + EXPECT_EQ (rs1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + ReceiverRejectingACellInstance rri1 (c2.cell_index (), db::ICplxTrans ()); db::RecursiveShapeIterator iri1 (g, c0, 0); iri1.push (&rri1); @@ -1117,70 +1179,70 @@ TEST(10) EXPECT_EQ (rri1.text (), "begin\n" "new_inst($2,all)\n" - "new_inst_member($2,r0 *1 0,0)\n" + "new_inst_member($2,r0 *1 0,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" // -> skipped - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" // -> skipped + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 0,6000)\n" + "new_inst_member($2,r0 *1 0,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,0)\n" + "new_inst_member($2,r0 *1 6000,0,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" "leave_cell($3)\n" "leave_cell($2)\n" - "new_inst_member($2,r0 *1 6000,6000)\n" + "new_inst_member($2,r0 *1 6000,6000,all)\n" "enter_cell($2)\n" "new_inst($3,all)\n" - "new_inst_member($3,r0 *1 0,0)\n" - "new_inst_member($3,r0 *1 0,2000)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,1000)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" "leave_cell($3)\n" - "new_inst_member($3,r0 *1 3000,3000)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" "enter_cell($3)\n" "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" "leave_cell($3)\n" diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index c1a034f6a..2e3f8a0e1 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -53,7 +53,8 @@ SOURCES = \ dbWriterTools.cc \ dbVariableWidthPath.cc \ dbLoadLayoutOptionsTests.cc \ - dbSaveLayoutOptionsTests.cc + dbSaveLayoutOptionsTests.cc \ + dbHierarchyBuilderTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/tl/tl/tlObject.cc b/src/tl/tl/tlObject.cc index 0d1b4cfca..305e0b3bb 100644 --- a/src/tl/tl/tlObject.cc +++ b/src/tl/tl/tlObject.cc @@ -130,12 +130,12 @@ bool Object::has_strong_references () const return false; } -void Object::keep () +void Object::keep_object () { mp_ptrs = (WeakOrSharedPtr *)(size_t (mp_ptrs) | size_t (1)); } -void Object::release () +void Object::release_object () { mp_ptrs = (WeakOrSharedPtr *)(size_t (mp_ptrs) & ~size_t (1)); diff --git a/src/tl/tl/tlObject.h b/src/tl/tl/tlObject.h index df78b0435..a66426675 100644 --- a/src/tl/tl/tlObject.h +++ b/src/tl/tl/tlObject.h @@ -106,14 +106,14 @@ public: * no strong pointer is having a reference to this object and longer, the * object is not deleted. */ - void keep (); + void keep_object (); /** * @brief Releases this object from being kept * This method may delete the object if no strong pointer holds a * reference to it. */ - void release (); + void release_object (); protected: /** diff --git a/testdata/algo/hierarchy_builder_au1.gds b/testdata/algo/hierarchy_builder_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..fa837f4e2a317771de909276f480b397e7cf8199 GIT binary patch literal 2762 zcma);(MuFj6vofa&fb|_S2L5skSHmrP>VDpBvIQ+F*MvwY7z9Ymmo+YqCzOak|3xj z>p_C-!M7j~^eH03ryheM_8$lqd~(U1zB6}@Gb7HJpugpu^WAgrJ?Ea46dA@1YEBqd zi89no2k0cV1wWH*sH=nhM2*Q*Pv5h1Ltj6X&*zp;Exaifs5up_%P3VC?HkS$nKqHC zHOL@UXAn_qebR9@?>SEO7?UXRlgM~36g!j5y}sTI>Z=C)Yxo77Mew&zu~*mP^w>#} z^~l@BdY@vaVw4`SEzqoKI*VYH^J|JdIzQ_;zhzDNMt0P=RdruO7E7VxeB**z%ViF|}>#P>V z_J^(1;PBWPGBZS(2I-Y7_eS-OyO3M^oxPc|&JpsegNbP(tbmiej96h?jI86GP;Mx2 z!o)4^SNBvz-_VScSlOe6#>Qz$n4XHdZ`@8R&Na>?N>8C~uZ3b~#^_7P)v0gUe5>mv z6nnBWq?dZ>_qL86QOLWF(($|bBNRI`N{<`e=lVg`A%Cq~Z_w z7)KKp9EWLf@1sYAVpoOG{_Wsb_9y%s4&j)voo~k6JGjd%8WO zPlWSxpJ;vE53Lu99eos>cPU?wy|&=~9%!0lAm)Wo>|lf*_x29=H?L`7_EsKaKOV)d z=0keR&yU(Z!TqdgI-gL-NBHbf?A4VxJv2kC$KEfo-ly1UC|;jl()!GMtrvDKz}S*;g}y{231vs?3Szd;F}DEi%@KB@^*D-?y9AyHe?z;03_*9#~Re|zmR2k|{Q zIyih~1d*6#c7vXZ?i9l=`A6np$d|bm?Q2|!_8lrm`%d&m`#QoCHMExtrD)y2>1f~R zbhK~$yYIu8<^LaKI=l01`#h+8(er@$Kl5N?0CBx|51@ryMa%=C*j2B-doO=ro)AqO zXT2utCYQCIDfVsp(0kf=g}6E72dx>r#}_`uuI57d1A3yH9jw>1X0ZP@)@zDAPCs#1 z>zS;ZyzP_qp5N^~?>*rBey2~A5pNTBdj6uI} literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_au2a.gds b/testdata/algo/hierarchy_builder_au2a.gds new file mode 100644 index 0000000000000000000000000000000000000000..9d556aed68022a0a7ef97c024d1f7caf7f78fbac GIT binary patch literal 11526 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_au2c.gds b/testdata/algo/hierarchy_builder_au2c.gds new file mode 100644 index 0000000000000000000000000000000000000000..85f5d60828ff1e721f426cbfab24f4ec7f4d4abb GIT binary patch literal 11902 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_au3a.gds b/testdata/algo/hierarchy_builder_au3a.gds new file mode 100644 index 0000000000000000000000000000000000000000..671fad2a127b57e495b462d239b216fad506a449 GIT binary patch literal 1172 zcmZQzV_;&6V31*CVt>TI&A`tf#vsoihs^+>@@d2w)}&o%MSeo zv!g;7WLR@*qxI}NehDck|^k8lSA z1N#MtKOr1Dg~hFs1QGvpNbuQXB)1466f_$-o0jn;@X~0Ad@^3=woQR1AqV z#Vr7jDMrMa5_lm5g`l= LWLSK%urL4sAoi>t literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_au4a.gds b/testdata/algo/hierarchy_builder_au4a.gds new file mode 100644 index 0000000000000000000000000000000000000000..504df9920758aaa48972b806e6876bb7b4ac31b5 GIT binary patch literal 82618 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 literal 0 HcmV?d00001 diff --git a/testdata/algo/hierarchy_builder_l2.gds b/testdata/algo/hierarchy_builder_l2.gds new file mode 100644 index 0000000000000000000000000000000000000000..7447367b20bad86fd70c5bed764beb464751061a GIT binary patch literal 268 zcmZQzV_;&6V31*CVt>TI&A`tf#=yfMhRkN*U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLRhA^7M1}XJBCAU|?Y5Wny4tVB=$AU|`S@VE+IAm&*VDeh3tMA;O~z{1VIz$FB;mqQq2Cl`Z^05i~5EZ|iF+D6vofa&fb~b^0dsNTOz?7#i*-C4&3dOAsUxQ6ZFINf6YN z^&mm^;9C#~`V+Y@DKd;})R-`= zUzDY4+D&KZaPT|XhPvF}LsXwkb#^~FJ@ENm@oewn@wr!{d1_2W>oQ8^hr0)JM5axo zDh)D7RT)IoT$6O1^*fGJKExzSd?zxV3dPPObC<6-gZi=o{|Y{!GY|gsDfaS8oF0{o zvL1QcS?^QqG#aHxY!fsqn$A2}=KPvskIv6J&aathXB|Es#l9)O3hsY>8TlXM{Fb4+ zkK6}^VrPcx59pEmhwBGfhx6|hirv?z_5CHi{OSPTqFtTD?-JG5Fv)TLtul#{`*l`} zV*A5Zs(*0gB$*kaOs(`v1@}hz7EDFGO317BCMJmxgIb@v$=v>ki`jCWjVxHhUBTO- zoKRqgi5uLn?yiWwAiMyLM+=RO-I6dp5p~`;oo4K7j7gN9z`eZ`ik%swUq!Aqeahx* zeP2ScC)+}Lxi8d@e1Fj+iixY^ck@Rmc4m|w=UwOeLDnIErBLkgdhRpS=k}Y_EfjlP z{cB@r!o1@!E$)5vh*0b*AFAIDzL(N8G@Xu9n#Nu}5~c16#m}ocoxBUFL+ebK`B~9l8?(qS>dlY+lDNYZ~0P9iv1=jl%I}OC^(+gUkd8_q8v8NZ} z_1O-suQ;XkLa}E%w7z0v-t93c!ShDHJKT?Iz|;ywVP;6w#x$@S)X>$uH(R!ugZMi+ z+&_3}2+^2hcCDU@$cY;DkIcA`FLT9pZt1I^i}vj+M*EI-Mf+OA^EOn=xz%W0--&47 z@MN@a?5pp?nC1T;WIDU^Z1X%QebV!Qxs!RY)`z$*ya&)iu2IYbq1aVdsGkD!glPN- z>or+dSk!u^*f;4z?`iD?;%1N^w5ISLpZOHKnhE6(=!vekvR>1gLjA{BuPOF8{rGLI zXR@wv(+SX8cKE(>WM2bQ>IEI literal 0 HcmV?d00001