From 5187ddbfc0f7a6f0caff7caf3690cb19fab851f7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 Feb 2024 21:20:24 +0100 Subject: [PATCH 01/32] Do not insert the same point twice into edge set in EdgeProcessor - this improves performance in the case of manifold intersecions in one point. Also: added edge count API --- src/db/db/dbEdgeProcessor.cc | 13 +++++++++++++ src/db/db/dbEdgeProcessor.h | 5 +++++ src/db/db/dbShapeProcessor.cc | 6 ++++++ src/db/db/dbShapeProcessor.h | 5 +++++ 4 files changed, 29 insertions(+) diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 9f1c3f11f..4f0534ae8 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -251,6 +251,13 @@ struct CutPoints } + // do not insert points twice + for (auto c = cut_points.begin (); c != cut_points.end (); ++c) { + if (*c == p) { + return; + } + } + cut_points.push_back (p); } @@ -1057,6 +1064,12 @@ EdgeProcessor::reserve (size_t n) mp_work_edges->reserve (n); } +size_t +EdgeProcessor::count () const +{ + return mp_work_edges->size (); +} + void EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p) { diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index a6fb2a680..30de5b35d 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -695,6 +695,11 @@ public: */ void reserve (size_t n); + /** + * @brief Reports the number of edges stored in the processor + */ + size_t count () const; + /** * @brief Insert an edge */ diff --git a/src/db/db/dbShapeProcessor.cc b/src/db/db/dbShapeProcessor.cc index 518aaa9e7..974fd469b 100644 --- a/src/db/db/dbShapeProcessor.cc +++ b/src/db/db/dbShapeProcessor.cc @@ -53,6 +53,12 @@ ShapeProcessor::reserve (size_t n) m_processor.reserve (n); } +size_t +ShapeProcessor::count () const +{ + return m_processor.count (); +} + void ShapeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) { diff --git a/src/db/db/dbShapeProcessor.h b/src/db/db/dbShapeProcessor.h index a7afde10c..333c53b69 100644 --- a/src/db/db/dbShapeProcessor.h +++ b/src/db/db/dbShapeProcessor.h @@ -196,6 +196,11 @@ public: */ void reserve (size_t n); + /** + * @brief Reports the number of edges stored in the processor + */ + size_t count () const; + /** * @brief Sets the base verbosity of the processor (see EdgeProcessor::set_base_verbosity for details) */ From 8947c9992fb534c12e7c1a354552717245d22c4f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 Feb 2024 21:20:52 +0100 Subject: [PATCH 02/32] Added configuration options for XOR tool to switch between with merge-before and without. --- .../tools/xor/lay_plugin/layXORToolDialog.cc | 169 ++++++++++-------- 1 file changed, 92 insertions(+), 77 deletions(-) diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc index d78cbc9cf..08558adc4 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc @@ -52,6 +52,12 @@ namespace lay { +bool merge_before_bool () +{ + // $KLAYOUT_XOR_MERGE_BEFORE_BOOLEAN + return tl::app_flag ("xor-merge-before-boolean"); +} + std::string cfg_xor_input_mode ("xor-input-mode"); std::string cfg_xor_output_mode ("xor-output-mode"); std::string cfg_xor_nworkers ("xor-num-workers"); @@ -864,107 +870,116 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) if (! mp_job->has_tiles ()) { tl::SelfTimer timer (tl::verbosity () >= 21, "Boolean part"); -#if 0 - // Straightforward implementation - sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - xor_results_cell.shapes (0), op, true, false, true); -#else - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); - if (!la.empty ()) { - sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - merge_helper_cell.shapes (0), true, 0, false, true); + if (! merge_before_bool ()) { +# + // Straightforward implementation + sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, + mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, + xor_results_cell.shapes (0), mp_job->op (), true, false, true); + + } else { + + // This implementation is faster when a lot of overlapping shapes are involved + db::Layout merge_helper; + db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); + merge_helper.insert_layer (0); + merge_helper.insert_layer (1); + + if (!la.empty ()) { + sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, + merge_helper_cell.shapes (0), true, 0, false, true); + } + if (!lb.empty ()) { + sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, + merge_helper_cell.shapes (1), true, 0, false, true); + } + sp.boolean (merge_helper, merge_helper_cell, 0, + merge_helper, merge_helper_cell, 1, + xor_results_cell.shapes (0), mp_job->op (), true, false, true); + } - if (!lb.empty ()) { - sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - merge_helper_cell.shapes (1), true, 0, false, true); - } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); -#endif } else { tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); size_t n; -#if 0 - // Straightforward implementation - sp.clear (); - - db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); - db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); - - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), mp_job->cva ().cell (), la, region_a); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2); - } - - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), mp_job->cvb ().cell (), lb, region_b); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1); - } - - db::BooleanOp bool_op (mp_job->op ()); - db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); - db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, mp_job->op ()); -#else - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - merge_helper.dbu (mp_job->dbu ()); - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); - - // This implementation is faster when a lot of overlapping shapes are involved - if (!la.empty ()) { + if (! merge_before_bool ()) { + // Straightforward implementation sp.clear (); - db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); + db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); + db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); n = 0; for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale * s.trans (), n); + sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2); } - db::MergeOp op (0); - db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/); - db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, op); - - } - - if (!lb.empty ()) { - - sp.clear (); - - db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); - n = 0; for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale * s.trans (), n); + sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1); } - db::MergeOp op (0); - db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/); + db::BooleanOp bool_op (mp_job->op ()); + db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, op); + sp.process (out, bool_op); + + } else { + + // This implementation is faster when a lot of overlapping shapes are involved + db::Layout merge_helper; + merge_helper.dbu (mp_job->dbu ()); + db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); + merge_helper.insert_layer (0); + merge_helper.insert_layer (1); + + // This implementation is faster when a lot of overlapping shapes are involved + if (!la.empty ()) { + + sp.clear (); + + db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); + + n = 0; + for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { + sp.insert (s.shape (), dbu_scale * s.trans (), n); + } + + db::MergeOp op (0); + db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/); + db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); + sp.process (out, op); + + } + + if (!lb.empty ()) { + + sp.clear (); + + db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); + + n = 0; + for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { + sp.insert (s.shape (), dbu_scale * s.trans (), n); + } + + db::MergeOp op (0); + db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/); + db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); + sp.process (out, op); + + } + + sp.boolean (merge_helper, merge_helper_cell, 0, + merge_helper, merge_helper_cell, 1, + xor_results_cell.shapes (0), mp_job->op (), true, false, true); } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); -#endif - } } else if (mp_job->op () == db::BooleanOp::Xor || From 7c4133b9e48b930372dae9a91f66d425dda5c8e8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 9 Mar 2024 21:54:17 +0100 Subject: [PATCH 03/32] Fixed issue #1632 (at least partially): introducing non-const versions of RDB iterators and access methods --- src/rdb/rdb/gsiDeclRdb.cc | 256 +++++++++++++++++++++++++++++++++++++- src/rdb/rdb/rdb.cc | 39 +++++- src/rdb/rdb/rdb.h | 119 ++++++++++-------- testdata/ruby/rdbTest.rb | 72 +++++++++++ 4 files changed, 425 insertions(+), 61 deletions(-) diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 00392affa..2b467f2ed 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -88,6 +88,49 @@ private: rdb::Database::const_item_ref_iterator m_iter; }; +class ItemRefUnwrappingNonConstIterator +{ +public: + typedef rdb::Database::const_item_ref_iterator::iterator_category iterator_category; + typedef rdb::Database::const_item_ref_iterator::difference_type difference_type; + typedef rdb::Item value_type; + typedef rdb::Item &reference; + typedef rdb::Item *pointer; + + ItemRefUnwrappingNonConstIterator (rdb::Database::item_ref_iterator i) + : m_iter (i) + { } + + bool operator== (const ItemRefUnwrappingNonConstIterator &d) const + { + return m_iter == d.m_iter; + } + + bool operator!= (const ItemRefUnwrappingNonConstIterator &d) const + { + return m_iter != d.m_iter; + } + + ItemRefUnwrappingNonConstIterator &operator++ () + { + ++m_iter; + return *this; + } + + rdb::Item &operator* () const + { + return (*m_iter).operator* (); + } + + rdb::Item *operator-> () const + { + return (*m_iter).operator-> (); + } + +private: + rdb::Database::item_ref_iterator m_iter; +}; + // --------------------------------------------------------------- // rdb::Reference binding @@ -105,7 +148,12 @@ Class decl_RdbReference ("rdb", "RdbReference", "\n" "This method has been introduced in version 0.23." ) + - gsi::method ("trans", &rdb::Reference::trans, + gsi::method ("database", (rdb::Database *(rdb::Reference::*)()) &rdb::Reference::database, + "@brief Gets the database object that category is associated with (non-const version)\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method ("trans", &rdb::Reference::trans, "@brief Gets the transformation for this reference\n" "The transformation describes the transformation of the child cell into the parent cell. In that sense that is the " "usual transformation of a cell reference.\n" @@ -141,6 +189,16 @@ static rdb::References::const_iterator end_references (const rdb::Cell *cell) return cell->references ().end (); } +static rdb::References::iterator begin_references_nc (rdb::Cell *cell) +{ + return cell->references ().begin (); +} + +static rdb::References::iterator end_references_nc (rdb::Cell *cell) +{ + return cell->references ().end (); +} + static void add_reference (rdb::Cell *cell, const rdb::Reference &ref) { cell->references ().insert (ref); @@ -163,6 +221,18 @@ ItemRefUnwrappingIterator cell_items_end (const rdb::Cell *cell) return cell->database ()->items_by_cell (cell->id ()).second; } +ItemRefUnwrappingNonConstIterator cell_items_begin_non_const (rdb::Cell *cell) +{ + tl_assert (cell->database ()); + return cell->database ()->items_by_cell (cell->id ()).first; +} + +ItemRefUnwrappingNonConstIterator cell_items_end_non_const (rdb::Cell *cell) +{ + tl_assert (cell->database ()); + return cell->database ()->items_by_cell (cell->id ()).second; +} + Class decl_RdbCell ("rdb", "RdbCell", gsi::method ("rdb_id", &rdb::Cell::id, "@brief Gets the cell ID\n" @@ -175,12 +245,22 @@ Class decl_RdbCell ("rdb", "RdbCell", "\n" "This method has been introduced in version 0.23." ) + + gsi::method ("database", (rdb::Database *(rdb::Cell::*)()) &rdb::Cell::database, + "@brief Gets the database object that category is associated with (non-const version)\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::iterator_ext ("each_item", &cell_items_begin, &cell_items_end, "@brief Iterates over all items inside the database which are associated with this cell\n" "\n" "This method has been introduced in version 0.23." ) + - gsi::method ("name", &rdb::Cell::name, + gsi::iterator_ext ("each_item", &cell_items_begin_non_const, &cell_items_end_non_const, + "@brief Iterates over all items inside the database which are associated with this cell (non-const version)\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method ("name", &rdb::Cell::name, "@brief Gets the cell name\n" "The cell name is an string that identifies the category in the database. " "Additionally, a cell may carry a variant identifier which is a string that uniquely identifies a cell " @@ -215,6 +295,11 @@ Class decl_RdbCell ("rdb", "RdbCell", ) + gsi::iterator_ext ("each_reference", &begin_references, &end_references, "@brief Iterates over all references\n" + ) + + gsi::iterator_ext ("each_reference", &begin_references_nc, &end_references_nc, + "@brief Iterates over all references (non-const version)\n" + "\n" + "This method has been introduced in version 0.23." ), "@brief A cell inside the report database\n" "This class represents a cell in the report database. There is not necessarily a 1:1 correspondence of RDB cells " @@ -226,12 +311,22 @@ Class decl_RdbCell ("rdb", "RdbCell", // --------------------------------------------------------------- // rdb::Category binding -static rdb::Categories::iterator begin_sub_categories (rdb::Category *cat) +static rdb::Categories::const_iterator begin_sub_categories (const rdb::Category *cat) { return cat->sub_categories ().begin (); } -static rdb::Categories::iterator end_sub_categories (rdb::Category *cat) +static rdb::Categories::const_iterator end_sub_categories (const rdb::Category *cat) +{ + return cat->sub_categories ().end (); +} + +static rdb::Categories::iterator begin_sub_categories_non_const (rdb::Category *cat) +{ + return cat->sub_categories ().begin (); +} + +static rdb::Categories::iterator end_sub_categories_non_const (rdb::Category *cat) { return cat->sub_categories ().end (); } @@ -248,6 +343,18 @@ ItemRefUnwrappingIterator category_items_end (const rdb::Category *cat) return cat->database ()->items_by_category (cat->id ()).second; } +ItemRefUnwrappingNonConstIterator category_items_begin_non_const (rdb::Category *cat) +{ + tl_assert (cat->database ()); + return cat->database ()->items_by_category (cat->id ()).first; +} + +ItemRefUnwrappingNonConstIterator category_items_end_non_const (rdb::Category *cat) +{ + tl_assert (cat->database ()); + return cat->database ()->items_by_category (cat->id ()).second; +} + static void scan_layer (rdb::Category *cat, const db::Layout &layout, unsigned int layer, const db::Cell *from_cell, int levels, bool with_properties) { rdb::scan_layer (cat, layout, layer, from_cell, levels, with_properties); @@ -299,6 +406,11 @@ Class decl_RdbCategory ("rdb", "RdbCategory", "\n" "This method has been introduced in version 0.23." ) + + gsi::iterator_ext ("each_item", &category_items_begin_non_const, &category_items_end_non_const, + "@brief Iterates over all items inside the database which are associated with this category (non-const version)\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method_ext ("scan_shapes", &scan_shapes, gsi::arg ("iter"), gsi::arg ("flat", false), gsi::arg ("with_properties", true), "@brief Scans the polygon or edge shapes from the shape iterator into the category\n" "Creates RDB items for each polygon or edge shape read from the iterator and puts them into this category.\n" @@ -379,12 +491,23 @@ Class decl_RdbCategory ("rdb", "RdbCategory", ) + gsi::iterator_ext ("each_sub_category", &begin_sub_categories, &end_sub_categories, "@brief Iterates over all sub-categories\n" + "\n" + "The const version has been added in version 0.29." ) + - gsi::method ("parent", (rdb::Category *(rdb::Category::*) ()) &rdb::Category::parent, + gsi::iterator_ext ("each_sub_category", &begin_sub_categories_non_const, &end_sub_categories_non_const, + "@brief Iterates over all sub-categories (non-const version)\n" + ) + + gsi::method ("parent", (const rdb::Category *(rdb::Category::*) () const) &rdb::Category::parent, "@brief Gets the parent category of this category\n" "@return The parent category or nil if this category is a top-level category\n" + "\n" + "The const version has been added in version 0.29." ) + - gsi::method ("num_items", &rdb::Category::num_items, + gsi::method ("parent", (rdb::Category *(rdb::Category::*) ()) &rdb::Category::parent, + "@brief Gets the parent category of this category (non-const version)\n" + "@return The parent category or nil if this category is a top-level category\n" + ) + + gsi::method ("num_items", &rdb::Category::num_items, "@brief Gets the number of items in this category\n" "The number of items includes the items in sub-categories of this category.\n" ) + @@ -923,6 +1046,16 @@ rdb::Items::const_iterator database_items_end (const rdb::Database *db) return db->items ().end (); } +rdb::Items::iterator database_items_begin_nc (rdb::Database *db) +{ + return db->items_non_const ().begin (); +} + +rdb::Items::iterator database_items_end_nc (rdb::Database *db) +{ + return db->items_non_const ().end (); +} + ItemRefUnwrappingIterator database_items_begin_cell (const rdb::Database *db, rdb::id_type cell_id) { return db->items_by_cell (cell_id).first; @@ -933,6 +1066,16 @@ ItemRefUnwrappingIterator database_items_end_cell (const rdb::Database *db, rdb: return db->items_by_cell (cell_id).second; } +ItemRefUnwrappingNonConstIterator database_items_begin_cell_nc (rdb::Database *db, rdb::id_type cell_id) +{ + return db->items_by_cell (cell_id).first; +} + +ItemRefUnwrappingNonConstIterator database_items_end_cell_nc (rdb::Database *db, rdb::id_type cell_id) +{ + return db->items_by_cell (cell_id).second; +} + ItemRefUnwrappingIterator database_items_begin_cat (const rdb::Database *db, rdb::id_type cat_id) { return db->items_by_category (cat_id).first; @@ -943,6 +1086,16 @@ ItemRefUnwrappingIterator database_items_end_cat (const rdb::Database *db, rdb:: return db->items_by_category (cat_id).second; } +ItemRefUnwrappingNonConstIterator database_items_begin_cat_nc (rdb::Database *db, rdb::id_type cat_id) +{ + return db->items_by_category (cat_id).first; +} + +ItemRefUnwrappingNonConstIterator database_items_end_cat_nc (rdb::Database *db, rdb::id_type cat_id) +{ + return db->items_by_category (cat_id).second; +} + ItemRefUnwrappingIterator database_items_begin_cc (const rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id) { return db->items_by_cell_and_category (cell_id, cat_id).first; @@ -953,6 +1106,16 @@ ItemRefUnwrappingIterator database_items_end_cc (const rdb::Database *db, rdb::i return db->items_by_cell_and_category (cell_id, cat_id).second; } +ItemRefUnwrappingNonConstIterator database_items_begin_cc_nc (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id) +{ + return db->items_by_cell_and_category (cell_id, cat_id).first; +} + +ItemRefUnwrappingNonConstIterator database_items_end_cc_nc (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id) +{ + return db->items_by_cell_and_category (cell_id, cat_id).second; +} + rdb::Categories::const_iterator database_begin_categories (const rdb::Database *db) { return db->categories ().begin (); @@ -963,6 +1126,16 @@ rdb::Categories::const_iterator database_end_categories (const rdb::Database *db return db->categories ().end (); } +rdb::Categories::iterator database_end_categories_nc (rdb::Database *db) +{ + return db->categories_non_const ().end (); +} + +rdb::Categories::iterator database_begin_categories_nc (rdb::Database *db) +{ + return db->categories_non_const ().begin (); +} + rdb::Cells::const_iterator database_begin_cells (const rdb::Database *db) { return db->cells ().begin (); @@ -973,6 +1146,16 @@ rdb::Cells::const_iterator database_end_cells (const rdb::Database *db) return db->cells ().end (); } +rdb::Cells::iterator database_begin_cells_nc (rdb::Database *db) +{ + return db->cells_non_const ().begin (); +} + +rdb::Cells::iterator database_end_cells_nc (rdb::Database *db) +{ + return db->cells_non_const ().end (); +} + const std::string &database_tag_name (const rdb::Database *db, rdb::id_type tag) { return db->tags ().tag (tag).name (); @@ -1121,6 +1304,11 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", gsi::iterator_ext ("each_category", &database_begin_categories, &database_end_categories, "@brief Iterates over all top-level categories\n" ) + + gsi::iterator_ext ("each_category", &database_begin_categories_nc, &database_end_categories_nc, + "@brief Iterates over all top-level categories (non-const version)\n" + "\n" + "The non-const variant has been added in version 0.29." + ) + gsi::method ("create_category", (rdb::Category *(rdb::Database::*) (const std::string &)) &rdb::Database::create_category, gsi::arg ("name"), "@brief Creates a new top level category\n" "@param name The name of the category\n" @@ -1135,10 +1323,23 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@param path The full path to the category starting from the top level (subcategories separated by dots)\n" "@return The (const) category object or nil if the name is not valid\n" ) + + gsi::method ("category_by_path", &rdb::Database::category_by_name_non_const, gsi::arg ("path"), + "@brief Gets a category by path (non-const version)\n" + "@param path The full path to the category starting from the top level (subcategories separated by dots)\n" + "@return The (const) category object or nil if the name is not valid\n" + "\n" + "This non-const variant has been introduced in version 0.29." + ) + gsi::method ("category_by_id", &rdb::Database::category_by_id, gsi::arg ("id"), "@brief Gets a category by ID\n" "@return The (const) category object or nil if the ID is not valid\n" ) + + gsi::method ("category_by_id", &rdb::Database::category_by_id_non_const, gsi::arg ("id"), + "@brief Gets a category by ID (non-const version)\n" + "@return The (const) category object or nil if the ID is not valid\n" + "\n" + "This non-const variant has been introduced in version 0.29." + ) + gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &)) &rdb::Database::create_cell, gsi::arg ("name"), "@brief Creates a new cell\n" "@param name The name of the cell\n" @@ -1158,14 +1359,33 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@param qname The qualified name of the cell (name plus variant name optionally)\n" "@return The cell object or nil if no such cell exists\n" ) + + gsi::method ("cell_by_qname", &rdb::Database::cell_by_qname_non_const, gsi::arg ("qname"), + "@brief Returns the cell for a given qualified name (non-const version)\n" + "@param qname The qualified name of the cell (name plus variant name optionally)\n" + "@return The cell object or nil if no such cell exists\n" + "\n" + "This non-const variant has been added version 0.29." + ) + gsi::method ("cell_by_id", &rdb::Database::cell_by_id, gsi::arg ("id"), "@brief Returns the cell for a given ID\n" "@param id The ID of the cell\n" "@return The cell object or nil if no cell with that ID exists\n" ) + + gsi::method ("cell_by_id", &rdb::Database::cell_by_id_non_const, gsi::arg ("id"), + "@brief Returns the cell for a given ID (non-const version)\n" + "@param id The ID of the cell\n" + "@return The cell object or nil if no cell with that ID exists\n" + "\n" + "This non-const variant has been added version 0.29." + ) + gsi::iterator_ext ("each_cell", &database_begin_cells, &database_end_cells, "@brief Iterates over all cells\n" ) + + gsi::iterator_ext ("each_cell", &database_begin_cells_nc, &database_end_cells_nc, + "@brief Iterates over all cells (non-const version)\n" + "\n" + "This non-const variant has been added version 0.29." + ) + gsi::method ("num_items", (size_t (rdb::Database::*) () const) &rdb::Database::num_items, "@brief Returns the number of items inside the database\n" "@return The total number of items\n" @@ -1351,19 +1571,43 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", gsi::iterator_ext ("each_item", &database_items_begin, &database_items_end, "@brief Iterates over all items inside the database\n" ) + + gsi::iterator_ext ("each_item", &database_items_begin_nc, &database_items_end_nc, + "@brief Iterates over all items inside the database (non-const version)\n" + "\n" + "This non-const variant has been added in version 0.29." + ) + gsi::iterator_ext ("each_item_per_cell", &database_items_begin_cell, &database_items_end_cell, gsi::arg ("cell_id"), "@brief Iterates over all items inside the database which are associated with the given cell\n" "@param cell_id The ID of the cell for which all associated items should be retrieved\n" ) + + gsi::iterator_ext ("each_item_per_cell", &database_items_begin_cell_nc, &database_items_end_cell_nc, gsi::arg ("cell_id"), + "@brief Iterates over all items inside the database which are associated with the given cell (non-const version)\n" + "@param cell_id The ID of the cell for which all associated items should be retrieved\n" + "\n" + "This non-const variant has been added in version 0.29." + ) + gsi::iterator_ext ("each_item_per_category", &database_items_begin_cat, &database_items_end_cat, gsi::arg ("category_id"), "@brief Iterates over all items inside the database which are associated with the given category\n" "@param category_id The ID of the category for which all associated items should be retrieved\n" ) + + gsi::iterator_ext ("each_item_per_category", &database_items_begin_cat_nc, &database_items_end_cat_nc, gsi::arg ("category_id"), + "@brief Iterates over all items inside the database which are associated with the given category (non-const version)\n" + "@param category_id The ID of the category for which all associated items should be retrieved\n" + "\n" + "This non-const variant has been added in version 0.29." + ) + gsi::iterator_ext ("each_item_per_cell_and_category", &database_items_begin_cc, &database_items_end_cc, gsi::arg ("cell_id"), gsi::arg ("category_id"), "@brief Iterates over all items inside the database which are associated with the given cell and category\n" "@param cell_id The ID of the cell for which all associated items should be retrieved\n" "@param category_id The ID of the category for which all associated items should be retrieved\n" ) + + gsi::iterator_ext ("each_item_per_cell_and_category", &database_items_begin_cc_nc, &database_items_end_cc_nc, gsi::arg ("cell_id"), gsi::arg ("category_id"), + "@brief Iterates over all items inside the database which are associated with the given cell and category\n" + "@param cell_id The ID of the cell for which all associated items should be retrieved\n" + "@param category_id The ID of the category for which all associated items should be retrieved\n" + "\n" + "This non-const variant has been added in version 0.29." + ) + gsi::method ("set_item_visited", &rdb::Database::set_item_visited, gsi::arg ("item"), gsi::arg ("visited"), "@brief Modifies the visited state of an item\n" "@param item The item to modify\n" diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 887383198..464d31409 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -1488,7 +1488,18 @@ Database::items_by_cell_and_category (id_type cell_id, id_type category_id) cons } } -std::pair +std::pair +Database::items_by_cell_and_category (id_type cell_id, id_type category_id) +{ + std::map , std::list >::iterator i = m_items_by_cell_and_category_id.find (std::make_pair (cell_id, category_id)); + if (i != m_items_by_cell_and_category_id.end ()) { + return std::make_pair (i->second.begin (), i->second.end ()); + } else { + return std::make_pair (empty_list.begin (), empty_list.end ()); + } +} + +std::pair Database::items_by_cell (id_type cell_id) const { std::map >::const_iterator i = m_items_by_cell_id.find (cell_id); @@ -1499,7 +1510,18 @@ Database::items_by_cell (id_type cell_id) const } } -std::pair +std::pair +Database::items_by_cell (id_type cell_id) +{ + std::map >::iterator i = m_items_by_cell_id.find (cell_id); + if (i != m_items_by_cell_id.end ()) { + return std::make_pair (i->second.begin (), i->second.end ()); + } else { + return std::make_pair (empty_list.begin (), empty_list.end ()); + } +} + +std::pair Database::items_by_category (id_type category_id) const { std::map >::const_iterator i = m_items_by_category_id.find (category_id); @@ -1510,7 +1532,18 @@ Database::items_by_category (id_type category_id) const } } -size_t +std::pair +Database::items_by_category (id_type category_id) +{ + std::map >::iterator i = m_items_by_category_id.find (category_id); + if (i != m_items_by_category_id.end ()) { + return std::make_pair (i->second.begin (), i->second.end ()); + } else { + return std::make_pair (empty_list.begin (), empty_list.end ()); + } +} + +size_t Database::num_items (id_type cell_id, id_type category_id) const { std::map , size_t>::const_iterator n = m_num_items_by_cell_and_category.find (std::make_pair (cell_id, category_id)); diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index a7859f60e..6b5069bda 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -2082,6 +2082,14 @@ public: return *mp_categories; } + /** + * @brief Get the reference to the categories collection (non-const version) + */ + Categories &categories_non_const () + { + return *mp_categories; + } + /** * @brief Import categories * @@ -2129,6 +2137,20 @@ public: return const_cast (this)->category_by_id_non_const (id); } + /** + * @brief Get the category pointer for a category name (non-const version) + * + * This method returns 0 if the category name is invalid. + */ + Category *category_by_name_non_const (const std::string &name); + + /** + * @brief Get the category pointer for a category id (non-const version) + * + * This method returns 0 if the category is invalid. + */ + Category *category_by_id_non_const (id_type id); + /** * @brief Access to the cell collection (const) */ @@ -2137,6 +2159,14 @@ public: return m_cells; } + /** + * @brief Access to the cell collection + */ + Cells &cells_non_const () + { + return m_cells; + } + /** * @brief Import cells * @@ -2190,6 +2220,20 @@ public: return const_cast (this)->cell_by_id_non_const (id); } + /** + * @brief Get the cell pointer for a cell name or name:variant combination (non-const version) + * + * This method returns 0 if the cell name or name:variant combination is invalid. + */ + Cell *cell_by_qname_non_const (const std::string &qname); + + /** + * @brief Get the cell pointer for a cell id (non-const version) + * + * This method returns 0 if the cell id is invalid. + */ + Cell *cell_by_id_non_const (id_type id); + /** * @brief Report the number of items in total */ @@ -2266,6 +2310,14 @@ public: return *mp_items; } + /** + * @brief Get the items collection (non-const version) + */ + Items &items_non_const () + { + return *mp_items; + } + /** * @brief Set the items collection * @@ -2279,16 +2331,31 @@ public: */ std::pair items_by_cell (id_type cell_id) const; + /** + * @brief Get an iterator pair that delivers the non-const items (ItemRef) for a given cell + */ + std::pair items_by_cell (id_type cell_id); + /** * @brief Get an iterator that delivers the const items (ItemRef) for a given category */ std::pair items_by_category (id_type category_id) const; + /** + * @brief Get an iterator that delivers the non-const items (ItemRef) for a given category + */ + std::pair items_by_category (id_type category_id); + /** * @brief Get an iterator that delivers the const items (ItemRef) for a given cell and category */ std::pair items_by_cell_and_category (id_type cell_id, id_type category_id) const; + /** + * @brief Get an iterator that delivers the non-const items (ItemRef) for a given cell and category + */ + std::pair items_by_cell_and_category (id_type cell_id, id_type category_id); + /** * @brief Returns true, if the database was modified */ @@ -2349,14 +2416,6 @@ private: m_modified = true; } - /** - * @brief Get the items collection (non-const version) - */ - Items &items_non_const () - { - return *mp_items; - } - /** * @brief Get the reference to the tags collection (non-const version) */ @@ -2364,50 +2423,6 @@ private: { return m_tags; } - - /** - * @brief Get the reference to the categories collection (non-const version) - */ - Categories &categories_non_const () - { - return *mp_categories; - } - - /** - * @brief Get the category pointer for a category name - * - * This method returns 0 if the category name is invalid. - */ - Category *category_by_name_non_const (const std::string &name); - - /** - * @brief Get the category pointer for a category id - * - * This method returns 0 if the category is invalid. - */ - Category *category_by_id_non_const (id_type id); - - /** - * @brief Access to the cell collection - */ - Cells &cells_non_const () - { - return m_cells; - } - - /** - * @brief Get the cell pointer for a cell name or name:variant combination (non-const version) - * - * This method returns 0 if the cell name or name:variant combination is invalid. - */ - Cell *cell_by_qname_non_const (const std::string &qname); - - /** - * @brief Get the cell pointer for a cell id (non-const version) - * - * This method returns 0 if the cell id is invalid. - */ - Cell *cell_by_id_non_const (id_type id); }; } diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index e6e1ace7d..8b1c2b13d 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -935,6 +935,78 @@ class RDB_TestClass < TestBase end + def test_13 + + # manipulations + rdb = RBA::ReportDatabase::new("") + + _cell = rdb.create_cell("CELL") + _cat = rdb.create_category("cat") + _subcat = rdb.create_category(_cat, "subcat") + _subcat.description = "subcat_d" + _item1 = rdb.create_item(_cell.rdb_id, _subcat.rdb_id) + _item1.add_value(17.5) + _item1.add_value("string") + _item2 = rdb.create_item(_cell.rdb_id, _subcat.rdb_id) + _item2.add_value("b") + _subsubcat = rdb.create_category(_subcat, "subsubcat") + _cat2 = rdb.create_category("cat2") + + cell = rdb.cell_by_id(_cell.rdb_id) + assert_equal(cell._is_const_object?, false) + assert_equal(rdb.each_cell.first._is_const_object?, false) + + cell = rdb.cell_by_qname("CELL") + assert_equal(cell._is_const_object?, false) + + cat = rdb.category_by_id(_cat.rdb_id) + assert_equal(cat._is_const_object?, false) + + cat = rdb.category_by_path("cat") + assert_equal(cat._is_const_object?, false) + subcat = rdb.category_by_path("cat.subcat") + + assert_equal(rdb.each_category.first._is_const_object?, false) + assert_equal(rdb.each_category.collect { |c| c.name }.join(","), "cat,cat2") + assert_equal(subcat._is_const_object?, false) + assert_equal(subcat.database._is_const_object?, false) + assert_equal(subcat.name, "subcat") + assert_equal(subcat.parent.name, "cat") + + assert_equal(subcat.description, "subcat_d") + subcat.description = "changed" + assert_equal(subcat.description, "changed") + + assert_equal(rdb.each_item_per_category(subcat.rdb_id).collect { |item| item.each_value.collect { |v| v.to_s }.join("/") }.join(";"), "float: 17.5/text: string;text: b") + + item1 = rdb.each_item_per_category(subcat.rdb_id).first + assert_equal(item1._is_const_object?, false) + item1.clear_values + assert_equal(rdb.each_item_per_category(subcat.rdb_id).collect { |item| item.each_value.collect { |v| v.to_s }.join("/") }.join(";"), ";text: b") + item1.add_value("x") + assert_equal(rdb.each_item_per_category(subcat.rdb_id).collect { |item| item.each_value.collect { |v| v.to_s }.join("/") }.join(";"), "text: x;text: b") + item1.add_tag(17) + assert_equal(item1.has_tag?(17), true) + assert_equal(item1.has_tag?(16), false) + + item1 = rdb.each_item.first + assert_equal(item1._is_const_object?, false) + assert_equal(item1.has_tag?(17), true) + + item1 = rdb.each_item_per_cell(cell.rdb_id).first + assert_equal(item1._is_const_object?, false) + assert_equal(item1.has_tag?(17), true) + + item1 = rdb.each_item_per_cell_and_category(cell.rdb_id, subcat.rdb_id).first + assert_equal(item1._is_const_object?, false) + assert_equal(item1.has_tag?(17), true) + + item1 = cell.each_item.first + assert_equal(item1._is_const_object?, false) + assert_equal(item1.has_tag?(17), true) + + end + end load("test_epilogue.rb") From f2d61e1dee6da22720978d63b39e996e8d146836 Mon Sep 17 00:00:00 2001 From: klayoutmatthias Date: Mon, 11 Mar 2024 22:39:59 +0000 Subject: [PATCH 04/32] Fixed a crash with Ruby 2.0.0 on CentOS7 --- testdata/ruby/rdbTest.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index 8b1c2b13d..abc40519f 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -954,7 +954,7 @@ class RDB_TestClass < TestBase cell = rdb.cell_by_id(_cell.rdb_id) assert_equal(cell._is_const_object?, false) - assert_equal(rdb.each_cell.first._is_const_object?, false) + assert_equal(rdb.each_cell.to_a[0]._is_const_object?, false) cell = rdb.cell_by_qname("CELL") assert_equal(cell._is_const_object?, false) @@ -966,7 +966,7 @@ class RDB_TestClass < TestBase assert_equal(cat._is_const_object?, false) subcat = rdb.category_by_path("cat.subcat") - assert_equal(rdb.each_category.first._is_const_object?, false) + assert_equal(rdb.each_category.to_a[0]._is_const_object?, false) assert_equal(rdb.each_category.collect { |c| c.name }.join(","), "cat,cat2") assert_equal(subcat._is_const_object?, false) assert_equal(subcat.database._is_const_object?, false) @@ -979,7 +979,7 @@ class RDB_TestClass < TestBase assert_equal(rdb.each_item_per_category(subcat.rdb_id).collect { |item| item.each_value.collect { |v| v.to_s }.join("/") }.join(";"), "float: 17.5/text: string;text: b") - item1 = rdb.each_item_per_category(subcat.rdb_id).first + item1 = rdb.each_item_per_category(subcat.rdb_id).to_a[0] assert_equal(item1._is_const_object?, false) item1.clear_values assert_equal(rdb.each_item_per_category(subcat.rdb_id).collect { |item| item.each_value.collect { |v| v.to_s }.join("/") }.join(";"), ";text: b") @@ -989,19 +989,19 @@ class RDB_TestClass < TestBase assert_equal(item1.has_tag?(17), true) assert_equal(item1.has_tag?(16), false) - item1 = rdb.each_item.first + item1 = rdb.each_item.to_a[0] assert_equal(item1._is_const_object?, false) assert_equal(item1.has_tag?(17), true) - item1 = rdb.each_item_per_cell(cell.rdb_id).first + item1 = rdb.each_item_per_cell(cell.rdb_id).to_a[0] assert_equal(item1._is_const_object?, false) assert_equal(item1.has_tag?(17), true) - item1 = rdb.each_item_per_cell_and_category(cell.rdb_id, subcat.rdb_id).first + item1 = rdb.each_item_per_cell_and_category(cell.rdb_id, subcat.rdb_id).to_a[0] assert_equal(item1._is_const_object?, false) assert_equal(item1.has_tag?(17), true) - item1 = cell.each_item.first + item1 = cell.each_item.to_a[0] assert_equal(item1._is_const_object?, false) assert_equal(item1.has_tag?(17), true) From b4d170fa66cb8b41bfb45454d311bb6716a85ffc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 09:07:26 +0100 Subject: [PATCH 05/32] Implemented issue #1656 (Display-->Goto Position dialog should accept + as well as - for number prefixes) --- src/db/unit_tests/dbTransTests.cc | 4 ++-- src/tl/tl/tlString.cc | 2 -- src/tl/unit_tests/tlStringTests.cc | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/db/unit_tests/dbTransTests.cc b/src/db/unit_tests/dbTransTests.cc index 85f9a5188..622b94428 100644 --- a/src/db/unit_tests/dbTransTests.cc +++ b/src/db/unit_tests/dbTransTests.cc @@ -353,9 +353,9 @@ TEST(11) EXPECT_EQ (x.try_read (tt2), true); EXPECT_EQ (x.test ("a"), true); EXPECT_EQ (tt2.to_string (), t2.to_string ()); - x = tl::Extractor ("m22.5 *0.55 12.4,-17 ++"); + x = tl::Extractor ("m22.5 *0.55 12.4,-17 ##"); EXPECT_EQ (x.try_read (tt2), true); - EXPECT_EQ (x.test ("++"), true); + EXPECT_EQ (x.test ("##"), true); EXPECT_EQ (tt2.to_string (), "m22.5 *0.55 12.4,-17"); EXPECT_EQ (tt2.to_string (), t3.to_string ()); } diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index ab327c00f..8a0379e6e 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -322,10 +322,8 @@ static double local_strtod (const char *cp, const char *&cp_new) if (*cp == '-') { s = -1.0; ++cp; - /* } else if (*cp == '+') { ++cp; - */ } // Extract upper digits diff --git a/src/tl/unit_tests/tlStringTests.cc b/src/tl/unit_tests/tlStringTests.cc index e6078ec3a..aafab46e6 100644 --- a/src/tl/unit_tests/tlStringTests.cc +++ b/src/tl/unit_tests/tlStringTests.cc @@ -305,6 +305,25 @@ TEST(6) EXPECT_EQ (x3.test (":"), true); } +TEST(6_double) +{ + Extractor x (" 5.5 -2.5 \n+0.125 (no number)"); + + EXPECT_EQ (x.at_end (), false); + + double d = 0.0; + + EXPECT_EQ (x.try_read (d), true); + EXPECT_EQ (d, 5.5); + + x.read (d); + EXPECT_EQ (d, -2.5); + x.read (d); + EXPECT_EQ (d, 0.125); + + x.expect ("("); +} + TEST(7) { EXPECT_EQ (tl::to_quoted_string ("a_word!"), "'a_word!'"); From 38a3b8305e0478235c9ab71a39821b151a0eb363 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 15:24:18 +0100 Subject: [PATCH 06/32] Fixing issue #1651 (errors when adding polygons with 4 points) - needs some testing --- src/pya/pya/pyaCallables.cc | 9 +++++---- src/pya/pya/pyaMarshal.cc | 26 +++++++++++++------------- src/pya/pya/pyaMarshal.h | 2 +- src/rba/rba/rba.cc | 4 ++-- src/rba/rba/rbaMarshal.cc | 20 ++++++++++---------- src/rba/rba/rbaMarshal.h | 2 +- testdata/python/dbPolygonTest.py | 10 ++++++++++ testdata/ruby/dbPolygonTest.rb | 12 ++++++++++++ 8 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index 72e4eb7d2..77f3c1728 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -350,9 +350,9 @@ match_method (int mid, PyObject *self, PyObject *args, PyObject *kwargs, bool st PythonPtr arg (i >= argc ? get_kwarg (*a, kwargs) : (is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i))); if (! arg) { is_valid = a->spec ()->has_default (); - } else if (test_arg (*a, arg.get (), false /*strict*/)) { + } else if (test_arg (*a, arg.get (), false /*strict*/, false /*object substitution*/)) { ++sc; - } else if (test_arg (*a, arg.get (), true /*loose*/)) { + } else if (test_arg (*a, arg.get (), true /*loose*/, true /*object substitution*/)) { // non-scoring match } else { is_valid = false; @@ -405,7 +405,7 @@ match_method (int mid, PyObject *self, PyObject *args, PyObject *kwargs, bool st int i = 0; for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments (); ++a, ++i) { PythonPtr arg (i >= argc ? get_kwarg (*a, kwargs) : (is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i))); - if (arg && ! test_arg (*a, arg.get (), true /*loose*/)) { + if (arg && ! test_arg (*a, arg.get (), true /*loose*/, true /*object substitution*/)) { return 0; } } @@ -1116,7 +1116,8 @@ property_setter_impl (int mid, PyObject *self, PyObject *value) // check arguments (count and type) bool is_valid = (*m)->compatible_with_num_args (1); - if (is_valid && ! test_arg (*(*m)->begin_arguments (), value, pass != 0 /*loose in the second pass*/)) { + bool loose = (pass != 0); // loose in the second pass + if (is_valid && ! test_arg (*(*m)->begin_arguments (), value, loose, loose)) { is_valid = false; } diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 41cc134c0..e6ebed0ed 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -1046,7 +1046,7 @@ size_t PythonBasedMapAdaptor::serial_size () const template struct test_arg_func { - void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) + void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { @@ -1083,7 +1083,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator() (bool *ret, PyObject *, const gsi::ArgType &, bool) + void operator() (bool *ret, PyObject *, const gsi::ArgType &, bool, bool) { // we assume we can convert everything into a variant *ret = true; @@ -1093,7 +1093,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator() (bool *ret, PyObject *arg, const gsi::ArgType &, bool) + void operator() (bool *ret, PyObject *arg, const gsi::ArgType &, bool, bool) { #if PY_MAJOR_VERSION < 3 if (PyString_Check (arg)) { @@ -1117,7 +1117,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) + void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { // for ptr or cptr, null is an allowed value @@ -1138,7 +1138,7 @@ struct test_arg_func size_t n = PyTuple_Size (arg); for (size_t i = 0; i < n && *ret; ++i) { - if (! test_arg (ainner, PyTuple_GetItem (arg, i), loose)) { + if (! test_arg (ainner, PyTuple_GetItem (arg, i), loose, true /*issue-1651*/)) { *ret = false; } } @@ -1147,7 +1147,7 @@ struct test_arg_func size_t n = PyList_Size (arg); for (size_t i = 0; i < n && *ret; ++i) { - if (! test_arg (ainner, PyList_GetItem (arg, i), loose)) { + if (! test_arg (ainner, PyList_GetItem (arg, i), loose, true /*issue-1651*/)) { *ret = false; } } @@ -1159,7 +1159,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { // for ptr or cptr, null is an allowed value @@ -1184,11 +1184,11 @@ struct test_arg_func PyObject *key, *value; Py_ssize_t pos = 0; while (PyDict_Next(arg, &pos, &key, &value)) { - if (! test_arg (ainner_k, key, loose)) { + if (! test_arg (ainner_k, key, loose, true /*issue-1651*/)) { *ret = false; break; } - if (! test_arg (ainner, value, loose)) { + if (! test_arg (ainner, value, loose, true /*issue-1651*/)) { *ret = false; break; } @@ -1199,7 +1199,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) + void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose, bool object_substitution) { const gsi::ClassBase *acls = atype.cls (); @@ -1209,7 +1209,7 @@ struct test_arg_func return; } - if (loose && (PyTuple_Check (arg) || PyList_Check (arg))) { + if (object_substitution && (PyTuple_Check (arg) || PyList_Check (arg))) { // we may implicitly convert a tuple into a constructor call of a target object - // for now we only check whether the number of arguments is compatible with the list given. @@ -1247,10 +1247,10 @@ struct test_arg_func }; bool -test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose) +test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose, bool object_substitution) { bool ret = false; - gsi::do_on_type () (atype.type (), &ret, arg, atype, loose); + gsi::do_on_type () (atype.type (), &ret, arg, atype, loose, object_substitution); return ret; } diff --git a/src/pya/pya/pyaMarshal.h b/src/pya/pya/pyaMarshal.h index ea61acaef..e0e529382 100644 --- a/src/pya/pya/pyaMarshal.h +++ b/src/pya/pya/pyaMarshal.h @@ -69,7 +69,7 @@ PythonRef pull_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObje * @return True, if the type match */ bool -test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose); +test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose, bool object_substitution); /** * @brief Correct constness if a reference is const and a non-const reference is required diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 8bd642f04..0cef54c62 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -479,9 +479,9 @@ private: VALUE arg = i >= argc ? get_kwarg (*a, kwargs) : argv[i]; if (arg == Qundef) { is_valid = a->spec ()->has_default (); - } else if (test_arg (*a, arg, false /*strict*/)) { + } else if (test_arg (*a, arg, false /*strict*/, false /*with object substitution*/)) { ++sc; - } else if (test_arg (*a, arg, true /*loose*/)) { + } else if (test_arg (*a, arg, true /*loose*/, true /*with object substitution*/)) { // non-scoring match } else { is_valid = false; diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index 5cacd22e1..7083fd1c7 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -1053,7 +1053,7 @@ pull_arg (const gsi::ArgType &atype, Proxy *self, gsi::SerialArgs &aserial, tl:: template struct test_arg_func { - void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { @@ -1101,7 +1101,7 @@ struct test_vector unsigned int len = RARRAY_LEN(arr); VALUE *el = RARRAY_PTR(arr); while (len-- > 0) { - if (! test_arg (ainner, *el++, loose)) { + if (! test_arg (ainner, *el++, loose, true /*issue-1651*/)) { *ret = false; break; } @@ -1112,7 +1112,7 @@ struct test_vector template <> struct test_arg_func { - void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { // for pointers to vectors, nil is a valid value @@ -1141,11 +1141,11 @@ struct HashTestKeyValueData static int hash_test_value_key (VALUE key, VALUE value, VALUE a) { HashTestKeyValueData *args = (HashTestKeyValueData *)a; - if (! test_arg (*args->ainner_k, key, args->loose)) { + if (! test_arg (*args->ainner_k, key, args->loose, true /*issue-1651*/)) { *(args->ret) = false; return ST_STOP; } - if (! test_arg (*args->ainner, value, args->loose)) { + if (! test_arg (*args->ainner, value, args->loose, true /*issue-1651*/)) { *(args->ret) = false; return ST_STOP; } @@ -1155,7 +1155,7 @@ static int hash_test_value_key (VALUE key, VALUE value, VALUE a) template <> struct test_arg_func { - void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { // for pointers to maps, nil is a valid value @@ -1183,14 +1183,14 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, VALUE arg, const gsi::ArgType &atype, bool loose, bool object_substitution) { if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { // for const X * or X *, nil is an allowed value *ret = true; - } else if (loose && TYPE (arg) == T_ARRAY) { + } else if (object_substitution && TYPE (arg) == T_ARRAY) { // we may implicitly convert an array into a constructor call of a target object - // for now we only check whether the number of arguments is compatible with the array given. @@ -1234,10 +1234,10 @@ struct test_arg_func }; bool -test_arg (const gsi::ArgType &atype, VALUE arg, bool loose) +test_arg (const gsi::ArgType &atype, VALUE arg, bool loose, bool object_substitution) { bool ret = false; - gsi::do_on_type () (atype.type (), &ret, arg, atype, loose); + gsi::do_on_type () (atype.type (), &ret, arg, atype, loose, object_substitution); return ret; } diff --git a/src/rba/rba/rbaMarshal.h b/src/rba/rba/rbaMarshal.h index 9e8ea4e07..436ef01cd 100644 --- a/src/rba/rba/rbaMarshal.h +++ b/src/rba/rba/rbaMarshal.h @@ -62,7 +62,7 @@ void push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, VALUE arg, t * otherwise: * argument must be of the requested type */ -bool test_arg (const gsi::ArgType &atype, VALUE arg, bool loose); +bool test_arg (const gsi::ArgType &atype, VALUE arg, bool loose, bool object_substitution); } diff --git a/testdata/python/dbPolygonTest.py b/testdata/python/dbPolygonTest.py index ec1c4a3b6..fb5349d45 100644 --- a/testdata/python/dbPolygonTest.py +++ b/testdata/python/dbPolygonTest.py @@ -746,6 +746,16 @@ class DBPolygonTests(unittest.TestCase): poly = pya.Polygon(hull).insert_hole(hole1).insert_hole(hole2) self.assertEqual(str(poly), "(0,0;0,3000;6000,3000;6000,0/1000,1000;2000,1000;2000,2000;1000,2000/3000,1000;4000,1000;4000,2000;3000,2000)") + def test_argumentShortcuts(self): + + # implicit conversion to a Point array: + poly = pya.Polygon([ (0,0), (0,1000), (1000,1000) ]) + self.assertEqual(str(poly), "(0,0;0,1000;1000,1000)") + + # issue 1651 - no binding to Box constructor + poly = pya.Polygon([ (0,0), (0,1000), (1000,1000), (1000,0) ]) + self.assertEqual(str(poly), "(0,0;0,1000;1000,1000;1000,0)") + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(DBPolygonTests) diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index bdd290f01..6643c3aef 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -834,6 +834,18 @@ class DBPolygon_TestClass < TestBase end + def test_argumentShortcuts + + # implicit conversion to a Point array: + poly = RBA::Polygon.new([ [0,0], [0,1000], [1000,1000] ]) + assert_equal(poly.to_s, "(0,0;0,1000;1000,1000)") + + # issue 1651 - no binding to Box constructor + poly = RBA::Polygon.new([ [0,0], [0,1000], [1000,1000], [1000,0] ]) + assert_equal(poly.to_s, "(0,0;0,1000;1000,1000;1000,0)") + + end + end load("test_epilogue.rb") From 0ef35b300aec715c16fb5ff0fd68aaafa687845a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 15:41:07 +0100 Subject: [PATCH 07/32] Added a dummy Changelog to make Debian builds pass --- Changelog.Debian | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.Debian b/Changelog.Debian index 8a6a53a23..735a1aaae 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.29.0-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Fri, 01 Apr 2024 12:00:00 +0100 + klayout (0.28.17-1) unstable; urgency=low * New features and bugfixes From b24d27cf732d19fd82a0f0181afe8e7378d8feb5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 15:41:07 +0100 Subject: [PATCH 08/32] Added a dummy Changelog to make Debian builds pass --- Changelog.Debian | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.Debian b/Changelog.Debian index 8a6a53a23..735a1aaae 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.29.0-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Fri, 01 Apr 2024 12:00:00 +0100 + klayout (0.28.17-1) unstable; urgency=low * New features and bugfixes From 20fd5a54a7160bc8ff474ca60dcd481767c728fd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 16:57:44 +0100 Subject: [PATCH 09/32] Tests --- Changelog | 3 +++ src/db/unit_tests/dbDeepRegionTests.cc | 29 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/Changelog b/Changelog index 5a9b54af4..2ac7bcff6 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,6 @@ +0.29.0 (2024-04-01): +* TODO + 0.28.17 (2024-02-16): * Enhancement: %GITHUB%/issues/1626 Technology specific grids diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 1037dc1c7..68c787fb8 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -2649,6 +2649,35 @@ TEST(101_DeepFlatCollaboration) db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_region_au101.gds"); } +TEST(102_SameInputs) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + + EXPECT_EQ ((r2 & r2).to_string (), "..."); + EXPECT_EQ ((r2 - r2).to_string (), "..."); + EXPECT_EQ ((r2 | r2).to_string (), "..."); + EXPECT_EQ ((r2 ^ r2).to_string (), "..."); + EXPECT_EQ ((r2 + r2).to_string (), "..."); +} + TEST(issue_277) { db::Layout ly; From a17b27453a08b8840ba50dff55edb7e86997593c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 16:59:24 +0100 Subject: [PATCH 10/32] Dummy Changelog --- Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog b/Changelog index 5a9b54af4..2ac7bcff6 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,6 @@ +0.29.0 (2024-04-01): +* TODO + 0.28.17 (2024-02-16): * Enhancement: %GITHUB%/issues/1626 Technology specific grids From 76345e207a0a52bf5208c39cee10163c33da195c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 16:59:24 +0100 Subject: [PATCH 11/32] Dummy Changelog --- Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog b/Changelog index 5a9b54af4..2ac7bcff6 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,6 @@ +0.29.0 (2024-04-01): +* TODO + 0.28.17 (2024-02-16): * Enhancement: %GITHUB%/issues/1626 Technology specific grids From e1b041113aa8d9d6d62cf7153e19b34ee12ccda2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 19:23:21 +0100 Subject: [PATCH 12/32] [consider merging] fixed a linker problem for debug builds --- src/tl/tl/tlOptional.cc | 2 +- src/tl/tl/tlOptional.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tl/tl/tlOptional.cc b/src/tl/tl/tlOptional.cc index ba2774439..d7ee370b2 100644 --- a/src/tl/tl/tlOptional.cc +++ b/src/tl/tl/tlOptional.cc @@ -25,6 +25,6 @@ namespace tl { -extern const nullopt_t nullopt = nullopt_t (); +const nullopt_t nullopt = nullopt_t (); } // namespace tl diff --git a/src/tl/tl/tlOptional.h b/src/tl/tl/tlOptional.h index c30fa6679..3893d6ac0 100644 --- a/src/tl/tl/tlOptional.h +++ b/src/tl/tl/tlOptional.h @@ -34,7 +34,7 @@ namespace tl struct nullopt_t {}; -extern const nullopt_t nullopt; +extern TL_PUBLIC const nullopt_t nullopt; /** * @brief Poor man's partial implementation of C++17's std::optional From a6d2930f805bea5fe3df997af43e27fad688f503 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 19:37:27 +0100 Subject: [PATCH 13/32] Fixed issue #1643 (Feeding the same layer to two-layer operations in deep mode does not render the desired result) by implementating the identical layer case as an exception for booleans and interactions --- src/db/db/dbDeepEdges.cc | 72 ++++++++++++++++++++++++-- src/db/db/dbDeepRegion.cc | 63 +++++++++++++++++++++- src/db/unit_tests/dbDeepEdgesTests.cc | 49 ++++++++++++++++++ src/db/unit_tests/dbDeepRegionTests.cc | 47 ++++++++++++++--- 4 files changed, 219 insertions(+), 12 deletions(-) diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index c94839621..21591eeda 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -1052,6 +1052,10 @@ EdgesDelegate *DeepEdges::and_with (const Edges &other) const return AsIfFlatEdges::and_with (other); + } else if (deep_layer () == other_deep->deep_layer ()) { + + return clone (); + } else { return new DeepEdges (and_or_not_with (other_deep, EdgeAnd).first); @@ -1071,6 +1075,10 @@ EdgesDelegate *DeepEdges::not_with (const Edges &other) const return AsIfFlatEdges::not_with (other); + } else if (deep_layer () == other_deep->deep_layer ()) { + + return new DeepEdges (deep_layer ().derived ()); + } else { return new DeepEdges (and_or_not_with (other_deep, EdgeNot).first); @@ -1164,6 +1172,10 @@ DeepEdges::andnot_with (const Edges &other) const return AsIfFlatEdges::andnot_with (other); + } else if (deep_layer () == other_deep->deep_layer ()) { + + return std::make_pair (clone (), new DeepEdges (deep_layer ().derived ())); + } else { auto res = and_or_not_with (other_deep, EdgeAndNot); @@ -1188,6 +1200,10 @@ EdgesDelegate *DeepEdges::xor_with (const Edges &other) const return AsIfFlatEdges::xor_with (other); + } else if (deep_layer () == other_deep->deep_layer ()) { + + return new DeepEdges (deep_layer ().derived ()); + } else { // Implement XOR as (A-B)+(B-A) - only this implementation @@ -1203,6 +1219,11 @@ EdgesDelegate *DeepEdges::xor_with (const Edges &other) const EdgesDelegate *DeepEdges::or_with (const Edges &other) const { + const DeepEdges *other_deep = dynamic_cast (other.delegate ()); + if (other_deep && other_deep->deep_layer () == deep_layer ()) { + return clone (); + } + // NOTE: in the hierarchical case we don't do a merge on "or": just map to add return add (other); } @@ -1503,8 +1524,19 @@ DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer () && !counting) { + if ((mode == EdgesOutside) == inverse) { + return clone (); + } else { + return new DeepEdges (deep_layer ().derived ()); + } + } + const db::DeepLayer &edges = merged_deep_layer (); + // NOTE: with counting the other region needs to be merged + const db::DeepLayer &other_edges = (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()); + DeepLayer dl_out (edges.derived ()); db::Edge2EdgeInteractingLocalOperation op (mode, inverse ? db::Edge2EdgeInteractingLocalOperation::Inverse : db::Edge2EdgeInteractingLocalOperation::Normal, min_count, max_count); @@ -1513,8 +1545,13 @@ DeepEdges::selected_interacting_generic (const Edges &other, EdgeInteractionMode proc.set_base_verbosity (base_verbosity ()); proc.set_threads (edges.store ()->threads ()); - // NOTE: with counting the other region needs to be merged - proc.run (&op, edges.layer (), (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), dl_out.layer ()); + if (edges == other_edges) { + // with counting and two identical inputs, a copy needs to be made + db::DeepLayer copy (other_edges.copy ()); + proc.run (&op, edges.layer (), copy.layer (), dl_out.layer ()); + } else { + proc.run (&op, edges.layer (), other_edges.layer (), dl_out.layer ()); + } return new db::DeepEdges (dl_out); } @@ -1533,8 +1570,19 @@ DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractio other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer () && !counting) { + if (mode != EdgesOutside) { + return std::make_pair (clone (), new DeepEdges (deep_layer ().derived ())); + } else { + return std::make_pair (new DeepEdges (deep_layer ().derived ()), clone ()); + } + } + const db::DeepLayer &edges = merged_deep_layer (); + // NOTE: with counting the other region needs to be merged + const db::DeepLayer &other_edges = (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()); + DeepLayer dl_out (edges.derived ()); DeepLayer dl_out2 (edges.derived ()); @@ -1550,7 +1598,13 @@ DeepEdges::selected_interacting_pair_generic (const Edges &other, EdgeInteractio proc.set_threads (edges.store ()->threads ()); // NOTE: with counting the other region needs to be merged - proc.run (&op, edges.layer (), (counting || mode != EdgesInteract ? other_deep->merged_deep_layer () : other_deep->deep_layer ()).layer (), output_layers); + if (edges == other_edges) { + // with counting and two identical inputs, a copy needs to be made + db::DeepLayer copy (other_edges.copy ()); + proc.run (&op, edges.layer (), copy.layer (), output_layers); + } else { + proc.run (&op, edges.layer (), other_edges.layer (), output_layers); + } return std::make_pair (new db::DeepEdges (dl_out), new db::DeepEdges (dl_out2)); } @@ -1591,6 +1645,10 @@ EdgesDelegate *DeepEdges::pull_generic (const Edges &other) const other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + return clone (); + } + const db::DeepLayer &edges = deep_layer (); const db::DeepLayer &other_edges = other_deep->merged_deep_layer (); @@ -1617,6 +1675,10 @@ EdgesDelegate *DeepEdges::in (const Edges &other, bool invert) const other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + return invert ? new db::DeepEdges (deep_layer ().derived ()) : clone (); + } + const db::DeepLayer &edges = merged_deep_layer (); DeepLayer dl_out (edges.derived ()); @@ -1646,6 +1708,10 @@ std::pair DeepEdges::in_and_out (const Edges & other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + return std::make_pair (clone (), new db::DeepEdges (deep_layer ().derived ())); + } + const db::DeepLayer &edges = merged_deep_layer (); DeepLayer dl_out (edges.derived ()); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 50173c49e..68146b114 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -797,6 +797,10 @@ DeepRegion::and_with (const Region &other, PropertyConstraint property_constrain return AsIfFlatRegion::and_with (other, property_constraint); + } else if (other_deep->deep_layer () == deep_layer ()) { + + return clone ()->remove_properties (pc_remove (property_constraint)); + } else { return new DeepRegion (and_or_not_with (other_deep, true, property_constraint)); @@ -817,6 +821,10 @@ DeepRegion::not_with (const Region &other, PropertyConstraint property_constrain return AsIfFlatRegion::not_with (other, property_constraint); + } else if (other_deep->deep_layer () == deep_layer ()) { + + return new DeepRegion (deep_layer ().derived ()); + } else { return new DeepRegion (and_or_not_with (other_deep, false, property_constraint)); @@ -827,6 +835,11 @@ DeepRegion::not_with (const Region &other, PropertyConstraint property_constrain RegionDelegate * DeepRegion::or_with (const Region &other, db::PropertyConstraint /*property_constraint*/) const { + const DeepRegion *other_deep = dynamic_cast (other.delegate ()); + if (other_deep && other_deep->deep_layer () == deep_layer ()) { + return clone (); + } + // TODO: implement property_constraint RegionDelegate *res = add (other); return res->merged_in_place (); @@ -849,6 +862,10 @@ DeepRegion::andnot_with (const Region &other, PropertyConstraint property_constr return AsIfFlatRegion::andnot_with (other, property_constraint); + } else if (other_deep->deep_layer () == deep_layer ()) { + + return std::make_pair (clone ()->remove_properties (pc_remove (property_constraint)), new DeepRegion (deep_layer ().derived ())); + } else { std::pair res = and_and_not_with (other_deep, property_constraint); @@ -962,6 +979,10 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const return AsIfFlatRegion::xor_with (other, property_constraint); + } else if (other_deep->deep_layer () == deep_layer ()) { + + return new DeepRegion (deep_layer ().derived ()); + } else { // Implement XOR as (A-B)+(B-A) - only this implementation @@ -2127,6 +2148,16 @@ DeepRegion::in_and_out_generic (const Region &other, InteractingOutputMode outpu other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + if (output_mode == PositiveAndNegative) { + return std::make_pair (clone (), new DeepRegion (deep_layer ().derived ())); + } else if (output_mode == Negative) { + return std::make_pair (new DeepRegion (deep_layer ().derived ()), (RegionDelegate *) 0); + } else { + return std::make_pair (clone (), (RegionDelegate *) 0); + } + } + const db::DeepLayer &polygons = merged_deep_layer (); const db::DeepLayer &other_polygons = other_deep->merged_deep_layer (); @@ -2188,6 +2219,26 @@ DeepRegion::selected_interacting_generic (const Region &other, int mode, bool to other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer () && !counting) { + if (mode <= 0 /*inside or interacting*/) { + if (output_mode == PositiveAndNegative) { + return std::make_pair (clone (), new DeepRegion (deep_layer ().derived ())); + } else if (output_mode == Negative) { + return std::make_pair (new DeepRegion (deep_layer ().derived ()), (RegionDelegate *) 0); + } else { + return std::make_pair (clone (), (RegionDelegate *) 0); + } + } else { + if (output_mode == PositiveAndNegative) { + return std::make_pair (new DeepRegion (deep_layer ().derived ()), clone ()); + } else if (output_mode == Negative) { + return std::make_pair (clone (), (RegionDelegate *) 0); + } else { + return std::make_pair (new DeepRegion (deep_layer ().derived ()), (RegionDelegate *) 0); + } + } + } + const db::DeepLayer &polygons = merged_deep_layer (); // NOTE: with counting, the other polygons must be merged const db::DeepLayer &other_polygons = counting ? other_deep->merged_deep_layer () : other_deep->deep_layer (); @@ -2205,7 +2256,13 @@ DeepRegion::selected_interacting_generic (const Region &other, int mode, bool to bool result_is_merged = (! split_after && (merged_semantics () || is_merged ())); InteractingResultHolder orh (output_mode, result_is_merged, polygons); - proc.run (&op, polygons.layer (), other_polygons.layer (), orh.layers ()); + if (polygons == other_polygons) { + // with counting and two identical inputs we need to create a layer copy + db::DeepLayer other_copy (other_polygons.copy ()); + proc.run (&op, polygons.layer (), other_copy.layer (), orh.layers ()); + } else { + proc.run (&op, polygons.layer (), other_polygons.layer (), orh.layers ()); + } return orh.result_pair (); } @@ -2285,6 +2342,10 @@ DeepRegion::pull_generic (const Region &other, int mode, bool touching) const other_deep = dr_holder.get (); } + if (deep_layer () == other_deep->deep_layer ()) { + return clone (); + } + // in "inside" mode, the first argument needs to be merged too const db::DeepLayer &polygons = mode < 0 ? merged_deep_layer () : deep_layer (); const db::DeepLayer &other_polygons = other_deep->merged_deep_layer (); diff --git a/src/db/unit_tests/dbDeepEdgesTests.cc b/src/db/unit_tests/dbDeepEdgesTests.cc index 7d992997e..381ae4fc7 100644 --- a/src/db/unit_tests/dbDeepEdgesTests.cc +++ b/src/db/unit_tests/dbDeepEdgesTests.cc @@ -1505,6 +1505,55 @@ TEST(22_InteractingWithCount) EXPECT_EQ (db::compare (e.selected_interacting_differential (r2, size_t (2), size_t(3)).second, "(0,0;200,0)"), true); } +TEST(23_SameInputs) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + db::Edges e2 = db::Edges ((db::Region (db::RecursiveShapeIterator (ly, top_cell, l2), dss)).edges ()); + + EXPECT_EQ ((e2 & e2).to_string (), e2.to_string ()); + EXPECT_EQ ((e2 - e2).to_string (), ""); + EXPECT_EQ (e2.andnot (e2).first.to_string (), e2.to_string ()); + EXPECT_EQ (e2.andnot (e2).second.to_string (), ""); + EXPECT_EQ ((e2 | e2).to_string (), e2.to_string ()); + EXPECT_EQ ((e2 ^ e2).to_string (), ""); + EXPECT_EQ (e2.in (e2).to_string (), e2.to_string ()); + EXPECT_EQ (e2.in (e2, true).to_string (), ""); + EXPECT_EQ (e2.in_and_out (e2).first.to_string (), e2.to_string ()); + EXPECT_EQ (e2.in_and_out (e2).second.to_string (), ""); + EXPECT_EQ (e2.selected_interacting (e2).to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_not_interacting (e2).to_string (), ""); + EXPECT_EQ (e2.selected_interacting_differential (e2).first.to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_interacting_differential (e2).second.to_string (), ""); + EXPECT_EQ ((e2.selected_interacting (e2, (size_t) 1, (size_t) 3) ^ e2).to_string (), ""); + EXPECT_EQ ((e2.selected_interacting_differential (e2, (size_t) 1, (size_t) 3).first ^ e2).to_string (), ""); + EXPECT_EQ (e2.selected_interacting_differential (e2, (size_t) 1, (size_t) 3).second.to_string (), ""); + EXPECT_EQ (e2.selected_interacting (e2, (size_t) 4).to_string (), ""); + EXPECT_EQ (e2.selected_interacting_differential (e2, (size_t) 4).first.to_string (), ""); + EXPECT_EQ ((e2.selected_interacting_differential (e2, (size_t) 4).second ^ e2).to_string (), ""); + EXPECT_EQ (e2.selected_inside (e2).to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_not_inside (e2).to_string (), ""); + EXPECT_EQ (e2.selected_inside_differential (e2).first.to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_inside_differential (e2).second.to_string (), ""); + EXPECT_EQ (e2.selected_outside (e2).to_string (), ""); + EXPECT_EQ (e2.selected_not_outside (e2).to_string (), e2.to_string ()); + EXPECT_EQ (e2.selected_outside_differential (e2).first.to_string (), ""); + EXPECT_EQ (e2.selected_outside_differential (e2).second.to_string (), e2.to_string ()); + EXPECT_EQ (e2.pull_interacting (e2).to_string (), e2.to_string ()); +} TEST(deep_edges_and_cheats) { diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 68c787fb8..9212846a5 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -2666,16 +2666,47 @@ TEST(102_SameInputs) db::DeepShapeStore dss; unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); - unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); - db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss); - db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); - EXPECT_EQ ((r2 & r2).to_string (), "..."); - EXPECT_EQ ((r2 - r2).to_string (), "..."); - EXPECT_EQ ((r2 | r2).to_string (), "..."); - EXPECT_EQ ((r2 ^ r2).to_string (), "..."); - EXPECT_EQ ((r2 + r2).to_string (), "..."); + EXPECT_EQ ((r2 & r2).to_string (), r2.to_string ()); + EXPECT_EQ ((r2 - r2).to_string (), ""); + EXPECT_EQ (r2.andnot (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.andnot (r2).second.to_string (), ""); + EXPECT_EQ ((r2 | r2).to_string (), r2.to_string ()); + EXPECT_EQ ((r2 ^ r2).to_string (), ""); + EXPECT_EQ (r2.in (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.in (r2, true).to_string (), ""); + EXPECT_EQ (r2.in_and_out (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.in_and_out (r2).second.to_string (), ""); + EXPECT_EQ (r2.selected_enclosing (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_not_enclosing (r2).to_string (), ""); + EXPECT_EQ (r2.selected_enclosing_differential (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_enclosing_differential (r2).second.to_string (), ""); + EXPECT_EQ (r2.selected_interacting (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_not_interacting (r2).to_string (), ""); + EXPECT_EQ (r2.selected_interacting_differential (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_interacting_differential (r2).second.to_string (), ""); + EXPECT_EQ (r2.selected_interacting (r2, (size_t) 1, (size_t) 2).to_string (), r2.merged ().to_string ()); + EXPECT_EQ (r2.selected_interacting_differential (r2, (size_t) 1, (size_t) 2).first.to_string (), r2.merged ().to_string ()); + EXPECT_EQ (r2.selected_interacting_differential (r2, (size_t) 1, (size_t) 2).second.to_string (), ""); + EXPECT_EQ (r2.selected_interacting (r2, (size_t) 2).to_string (), ""); + EXPECT_EQ (r2.selected_interacting_differential (r2, (size_t) 2).first.to_string (), ""); + EXPECT_EQ (r2.selected_interacting_differential (r2, (size_t) 2).second.to_string (), r2.merged ().to_string ()); + EXPECT_EQ (r2.selected_inside (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_not_inside (r2).to_string (), ""); + EXPECT_EQ (r2.selected_inside_differential (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_inside_differential (r2).second.to_string (), ""); + EXPECT_EQ (r2.selected_outside (r2).to_string (), ""); + EXPECT_EQ (r2.selected_not_outside (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_outside_differential (r2).first.to_string (), ""); + EXPECT_EQ (r2.selected_outside_differential (r2).second.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_overlapping (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_not_overlapping (r2).to_string (), ""); + EXPECT_EQ (r2.selected_overlapping_differential (r2).first.to_string (), r2.to_string ()); + EXPECT_EQ (r2.selected_overlapping_differential (r2).second.to_string (), ""); + EXPECT_EQ (r2.pull_inside (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.pull_overlapping (r2).to_string (), r2.to_string ()); + EXPECT_EQ (r2.pull_interacting (r2).to_string (), r2.to_string ()); } TEST(issue_277) From 4cacb60f26beb7656d2b9eb02381426a27d23c3a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 19:55:16 +0100 Subject: [PATCH 14/32] Fixed an issue with property constraints --- src/db/db/dbDeepRegion.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 68146b114..d52df7f9c 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -797,9 +797,9 @@ DeepRegion::and_with (const Region &other, PropertyConstraint property_constrain return AsIfFlatRegion::and_with (other, property_constraint); - } else if (other_deep->deep_layer () == deep_layer ()) { + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { - return clone ()->remove_properties (pc_remove (property_constraint)); + return clone (); } else { @@ -821,7 +821,7 @@ DeepRegion::not_with (const Region &other, PropertyConstraint property_constrain return AsIfFlatRegion::not_with (other, property_constraint); - } else if (other_deep->deep_layer () == deep_layer ()) { + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { return new DeepRegion (deep_layer ().derived ()); @@ -833,10 +833,10 @@ DeepRegion::not_with (const Region &other, PropertyConstraint property_constrain } RegionDelegate * -DeepRegion::or_with (const Region &other, db::PropertyConstraint /*property_constraint*/) const +DeepRegion::or_with (const Region &other, db::PropertyConstraint property_constraint) const { const DeepRegion *other_deep = dynamic_cast (other.delegate ()); - if (other_deep && other_deep->deep_layer () == deep_layer ()) { + if (other_deep && other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { return clone (); } @@ -862,9 +862,9 @@ DeepRegion::andnot_with (const Region &other, PropertyConstraint property_constr return AsIfFlatRegion::andnot_with (other, property_constraint); - } else if (other_deep->deep_layer () == deep_layer ()) { + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { - return std::make_pair (clone ()->remove_properties (pc_remove (property_constraint)), new DeepRegion (deep_layer ().derived ())); + return std::make_pair (clone (), new DeepRegion (deep_layer ().derived ())); } else { @@ -979,7 +979,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const return AsIfFlatRegion::xor_with (other, property_constraint); - } else if (other_deep->deep_layer () == deep_layer ()) { + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { return new DeepRegion (deep_layer ().derived ()); From e2df385f2dc893895cbd2cc4face057ce6236f0f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 11:05:48 +0100 Subject: [PATCH 15/32] First version of fix for issue-1638 (let klayout marker browser read strmxor .. xor.gds.gz, xor.oas result files) --- src/db/db/dbStream.cc | 36 +++ src/db/db/dbStream.h | 5 + src/lay/lay/layMainWindow.cc | 31 +-- src/layui/layui/rdbMarkerBrowserDialog.cc | 285 ++++++++++++---------- src/layui/layui/rdbMarkerBrowserDialog.h | 10 + 5 files changed, 214 insertions(+), 153 deletions(-) diff --git a/src/db/db/dbStream.cc b/src/db/db/dbStream.cc index 3d7cf935d..878198943 100644 --- a/src/db/db/dbStream.cc +++ b/src/db/db/dbStream.cc @@ -30,6 +30,42 @@ namespace db { +// ------------------------------------------------------------------ +// Implementation of StreamFormatDeclaration + +std::string StreamFormatDeclaration::all_formats_string () +{ + std::string fmts = tl::to_string (tr ("All layout files (")); + + for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { + if (rdr != tl::Registrar::begin ()) { + fmts += " "; + } + std::string f = rdr->file_format (); + if (!f.empty ()) { + const char *fp = f.c_str (); + while (*fp && *fp != '(') { + ++fp; + } + if (*fp) { + ++fp; + } + while (*fp && *fp != ')') { + fmts += *fp++; + } + } + } + fmts += ")"; + for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { + if (!rdr->file_format ().empty ()) { + fmts += ";;"; + fmts += rdr->file_format (); + } + } + + return fmts; +} + // ------------------------------------------------------------------ // Implementation of load_options_xml_element_list diff --git a/src/db/db/dbStream.h b/src/db/db/dbStream.h index 06ce1755d..97dfee6ef 100644 --- a/src/db/db/dbStream.h +++ b/src/db/db/dbStream.h @@ -136,6 +136,11 @@ public: { return 0; } + + /** + * @brief Returns a string for the file dialogs that describes all formats + */ + static std::string all_formats_string (); }; /** diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 0eb2eb397..bda513a58 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -498,34 +498,9 @@ MainWindow::~MainWindow () std::string MainWindow::all_layout_file_formats () const { - std::string fmts = tl::to_string (QObject::tr ("All layout files (")); - for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { - if (rdr != tl::Registrar::begin ()) { - fmts += " "; - } - std::string f = rdr->file_format (); - if (!f.empty ()) { - const char *fp = f.c_str (); - while (*fp && *fp != '(') { - ++fp; - } - if (*fp) { - ++fp; - } - while (*fp && *fp != ')') { - fmts += *fp++; - } - } - } - fmts += ");;"; - for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { - if (!rdr->file_format ().empty ()) { - fmts += rdr->file_format (); - fmts += ";;"; - } - } - fmts += tl::to_string (QObject::tr ("All files (*)")); - + std::string fmts = db::StreamFormatDeclaration::all_formats_string (); + fmts += ";;"; + fmts += tl::to_string (tr ("All files (*)")); return fmts; } diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index 459803767..7502734ad 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -35,6 +35,7 @@ #include "layConfigurationDialog.h" #include "dbLayoutUtils.h" #include "dbRecursiveShapeIterator.h" +#include "dbStream.h" #include "ui_MarkerBrowserDialog.h" @@ -412,6 +413,26 @@ BEGIN_PROTECTED END_PROTECTED } +void +MarkerBrowserDialog::read_db_from_layout (rdb::Database *db, const std::string &filename) +{ + // try reading a layout file + db::Layout layout; + tl::InputStream is (m_open_filename); + db::Reader reader (is); + + reader.read (layout); + + std::vector > layers; + for (auto l = layout.begin_layers (); l != layout.end_layers (); ++l) { + layers.push_back (std::make_pair ((*l).first, std::string ())); + } + + if (layout.begin_top_down () != layout.end_top_down ()) { + scan_layout (db, layout, *layout.begin_top_down (), layers, false /*hierarchical*/); + } +} + void MarkerBrowserDialog::open_clicked () { @@ -420,15 +441,34 @@ BEGIN_PROTECTED // collect the formats available ... std::string fmts = tl::to_string (QObject::tr ("All files (*)")); for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end (); ++rdr) { - fmts += ";;" + rdr->file_format (); + fmts += ";;"; + fmts += rdr->file_format (); } + // also provide the stream formats + fmts += ";;"; + fmts += db::StreamFormatDeclaration::all_formats_string (); + // prepare and open the file dialog lay::FileDialog open_dialog (this, tl::to_string (QObject::tr ("Load Marker Database File")), fmts); + if (open_dialog.get_open (m_open_filename)) { std::unique_ptr db (new rdb::Database ()); - db->load (m_open_filename); + + bool ok = false; + try { + // try reading a stream file + read_db_from_layout (db.get (), m_open_filename); + ok = true; + } catch (tl::Exception &ex) { + // continue + } + + if (! ok) { + // try reading database directly. + db->load (m_open_filename); + } int rdb_index = view ()->add_rdb (db.release ()); mp_ui->rdb_cb->setCurrentIndex (rdb_index); @@ -731,111 +771,17 @@ MarkerBrowserDialog::deactivated () void MarkerBrowserDialog::scan_layer () { - std::vector layers = view ()->selected_layers (); - if (layers.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No layer selected to get shapes from"))); - } - - int cv_index = -1; - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - if (!(*l)->has_children ()) { - if (cv_index < 0) { - cv_index = (*l)->cellview_index (); - } else if ((*l)->cellview_index () >= 0) { - if (cv_index != (*l)->cellview_index ()) { - throw tl::Exception (tl::to_string (QObject::tr ("All layers must originate from the same layout"))); - } - } - } - } - - if (cv_index < 0) { - throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected"))); - } - - tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Shapes To Markers")), 10000); - progress.set_format (tl::to_string (QObject::tr ("%.0f0000 markers"))); - progress.set_unit (10000); - - const lay::CellView &cv = view ()->cellview (cv_index); - const db::Layout &layout = cv->layout (); - - std::unique_ptr rdb (new rdb::Database ()); - rdb->set_name ("Shapes"); - rdb->set_top_cell_name (layout.cell_name (cv.cell_index ())); - rdb::Cell *rdb_top_cell = rdb->create_cell (rdb->top_cell_name ()); - - std::string desc; - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { - if (! desc.empty ()) { - desc += ", "; - } - desc += layout.get_properties ((*l)->layer_index ()).to_string (); - } - } - desc = tl::to_string (tr ("Hierarchical shapes of layer(s) ")) + desc; - desc += " "; - desc += tl::to_string (tr ("from cell ")); - desc += cv->layout ().cell_name (cv.cell_index ()); - rdb->set_description (desc); - - std::set called_cells; - called_cells.insert (cv.cell_index ()); - cv->layout ().cell (cv.cell_index ()).collect_called_cells (called_cells); - - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - - if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { - - rdb::Category *cat = rdb->create_category (layout.get_properties ((*l)->layer_index ()).to_string ()); - - for (db::Layout::const_iterator cid = layout.begin (); cid != layout.end (); ++cid) { - - if (called_cells.find (cid->cell_index ()) == called_cells.end ()) { - continue; - } - - const db::Cell &cell = *cid; - if (cell.shapes ((*l)->layer_index ()).size () > 0) { - - std::string cn = layout.cell_name (cell.cell_index ()); - const rdb::Cell *rdb_cell = rdb->cell_by_qname (cn); - if (! rdb_cell) { - - rdb::Cell *rdb_cell_nc = rdb->create_cell (cn); - rdb_cell = rdb_cell_nc; - - std::pair ctx = db::find_layout_context (layout, cell.cell_index (), cv.cell_index ()); - if (ctx.first) { - db::DCplxTrans t = db::DCplxTrans (layout.dbu ()) * db::DCplxTrans (ctx.second) * db::DCplxTrans (1.0 / layout.dbu ()); - rdb_cell_nc->references ().insert (Reference (t, rdb_top_cell->id ())); - } - - } - - for (db::ShapeIterator shape = cell.shapes ((*l)->layer_index ()).begin (db::ShapeIterator::All); ! shape.at_end (); ++shape) { - - rdb::create_item_from_shape (rdb.get (), rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), *shape); - - ++progress; - - } - - } - - } - - } - - } - - unsigned int rdb_index = view ()->add_rdb (rdb.release ()); - view ()->open_rdb_browser (rdb_index, cv_index); + scan_layer_flat_or_hierarchical (false); } -void +void MarkerBrowserDialog::scan_layer_flat () +{ + scan_layer_flat_or_hierarchical (true); +} + +void +MarkerBrowserDialog::scan_layer_flat_or_hierarchical (bool flat) { std::vector layers = view ()->selected_layers (); if (layers.empty ()) { @@ -859,43 +805,87 @@ MarkerBrowserDialog::scan_layer_flat () throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected"))); } + const lay::CellView &cv = view ()->cellview (cv_index); + const db::Layout &layout = cv->layout (); + + std::vector > layer_indexes; + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { + layer_indexes.push_back (std::make_pair ((*l)->layer_index (), (*l)->name ())); + } + } + + std::unique_ptr rdb (new rdb::Database ()); + + scan_layout (rdb.get (), layout, cv.cell_index (), layer_indexes, flat); + + unsigned int rdb_index = view ()->add_rdb (rdb.release ()); + view ()->open_rdb_browser (rdb_index, cv_index); +} + +void +MarkerBrowserDialog::scan_layout (rdb::Database *rdb, const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat) +{ tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Shapes To Markers")), 10000); progress.set_format (tl::to_string (QObject::tr ("%.0f0000 markers"))); progress.set_unit (10000); - const lay::CellView &cv = view ()->cellview (cv_index); - const db::Layout &layout = cv->layout (); - - std::unique_ptr rdb (new rdb::Database ()); rdb->set_name ("Shapes"); - rdb->set_top_cell_name (layout.cell_name (cv.cell_index ())); + rdb->set_top_cell_name (layout.cell_name (cell_index)); rdb::Cell *rdb_top_cell = rdb->create_cell (rdb->top_cell_name ()); std::string desc; - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { - if (! desc.empty ()) { - desc += ", "; - } - desc += layout.get_properties ((*l)->layer_index ()).to_string (); + + if (layers_and_descriptions.size () == 1) { + + if (flat) { + desc = tl::to_string (tr ("Flat shapes of layer ")); + } else { + desc = tl::to_string (tr ("Hierarchical shapes of layer ")); } + + desc += layout.get_properties (layers_and_descriptions.front ().first).to_string (); + + } else if (layers_and_descriptions.size () < 4 && layers_and_descriptions.size () > 0) { + + if (flat) { + desc = tl::to_string (tr ("Flat shapes of layers ")); + } else { + desc = tl::to_string (tr ("Hierarchical shapes of layers ")); + } + + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { + if (l != layers_and_descriptions.begin ()) { + desc += ","; + } + desc += layout.get_properties (l->first).to_string (); + } + + } else { + + if (flat) { + desc = tl::sprintf (tl::to_string (tr ("Flat shapes of %d layers")), int (layers_and_descriptions.size ())); + } else { + desc = tl::sprintf (tl::to_string (tr ("Hierarchical shapes of %d layers")), int (layers_and_descriptions.size ())); + } + } - desc = tl::to_string (tr ("Flat shapes of layer(s) ")) + desc; + desc += " "; desc += tl::to_string (tr ("from cell ")); - desc += cv->layout ().cell_name (cv.cell_index ()); + desc += layout.cell_name (cell_index); rdb->set_description (desc); - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + if (flat) { - if (!(*l)->has_children () && (*l)->cellview_index () == cv_index && layout.is_valid_layer ((*l)->layer_index ())) { + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { - rdb::Category *cat = rdb->create_category (layout.get_properties ((*l)->layer_index ()).to_string ()); + rdb::Category *cat = rdb->create_category (l->second.empty () ? layout.get_properties (l->first).to_string () : l->second); - db::RecursiveShapeIterator shape (layout, *cv.cell (), (*l)->layer_index ()); + db::RecursiveShapeIterator shape (layout, layout.cell (cell_index), l->first); while (! shape.at_end ()) { - rdb::create_item_from_shape (rdb.get (), rdb_top_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()) * shape.trans (), *shape); + rdb::create_item_from_shape (rdb, rdb_top_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()) * shape.trans (), *shape); ++progress; ++shape; @@ -904,10 +894,55 @@ MarkerBrowserDialog::scan_layer_flat () } - } + } else { - unsigned int rdb_index = view ()->add_rdb (rdb.release ()); - view ()->open_rdb_browser (rdb_index, cv_index); + std::set called_cells; + called_cells.insert (cell_index); + layout.cell (cell_index).collect_called_cells (called_cells); + + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { + + rdb::Category *cat = rdb->create_category (l->second.empty () ? layout.get_properties (l->first).to_string () : l->second); + + for (db::Layout::const_iterator cid = layout.begin (); cid != layout.end (); ++cid) { + + if (called_cells.find (cid->cell_index ()) == called_cells.end ()) { + continue; + } + + const db::Cell &cell = *cid; + if (! cell.shapes (l->first).empty ()) { + + std::string cn = layout.cell_name (cell.cell_index ()); + const rdb::Cell *rdb_cell = rdb->cell_by_qname (cn); + if (! rdb_cell) { + + rdb::Cell *rdb_cell_nc = rdb->create_cell (cn); + rdb_cell = rdb_cell_nc; + + std::pair ctx = db::find_layout_context (layout, cell.cell_index (), cell_index); + if (ctx.first) { + db::DCplxTrans t = db::DCplxTrans (layout.dbu ()) * db::DCplxTrans (ctx.second) * db::DCplxTrans (1.0 / layout.dbu ()); + rdb_cell_nc->references ().insert (Reference (t, rdb_top_cell->id ())); + } + + } + + for (db::ShapeIterator shape = cell.shapes (l->first).begin (db::ShapeIterator::All); ! shape.at_end (); ++shape) { + + rdb::create_item_from_shape (rdb, rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), *shape); + + ++progress; + + } + + } + + } + + } + + } } void diff --git a/src/layui/layui/rdbMarkerBrowserDialog.h b/src/layui/layui/rdbMarkerBrowserDialog.h index 7988d6070..3b50f1fa7 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.h +++ b/src/layui/layui/rdbMarkerBrowserDialog.h @@ -36,9 +36,16 @@ namespace Ui class MarkerBrowserDialog; } +namespace db +{ + class Layout; +} + namespace rdb { +class Database; + class LAYUI_PUBLIC MarkerBrowserDialog : public lay::Browser { @@ -101,6 +108,9 @@ private: void update_content (); void scan_layer (); void scan_layer_flat (); + void scan_layer_flat_or_hierarchical (bool flat); + void scan_layout (rdb::Database *db, const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat); + void read_db_from_layout (rdb::Database *db, const std::string &filename); }; } From 8a0a6cad04ea80e77f0504c5cc672283051f217d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 11:29:18 +0100 Subject: [PATCH 16/32] Refactoring solution such that loading a layout file into a marker database also works from command line (-m) and scripts --- src/layui/layui/rdbMarkerBrowserDialog.cc | 159 +------------------- src/layui/layui/rdbMarkerBrowserDialog.h | 2 - src/rdb/rdb/rdb.cc | 170 +++++++++++++++++++++- src/rdb/rdb/rdb.h | 11 ++ 4 files changed, 179 insertions(+), 163 deletions(-) diff --git a/src/layui/layui/rdbMarkerBrowserDialog.cc b/src/layui/layui/rdbMarkerBrowserDialog.cc index 7502734ad..800025a13 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.cc +++ b/src/layui/layui/rdbMarkerBrowserDialog.cc @@ -413,26 +413,6 @@ BEGIN_PROTECTED END_PROTECTED } -void -MarkerBrowserDialog::read_db_from_layout (rdb::Database *db, const std::string &filename) -{ - // try reading a layout file - db::Layout layout; - tl::InputStream is (m_open_filename); - db::Reader reader (is); - - reader.read (layout); - - std::vector > layers; - for (auto l = layout.begin_layers (); l != layout.end_layers (); ++l) { - layers.push_back (std::make_pair ((*l).first, std::string ())); - } - - if (layout.begin_top_down () != layout.end_top_down ()) { - scan_layout (db, layout, *layout.begin_top_down (), layers, false /*hierarchical*/); - } -} - void MarkerBrowserDialog::open_clicked () { @@ -455,20 +435,7 @@ BEGIN_PROTECTED if (open_dialog.get_open (m_open_filename)) { std::unique_ptr db (new rdb::Database ()); - - bool ok = false; - try { - // try reading a stream file - read_db_from_layout (db.get (), m_open_filename); - ok = true; - } catch (tl::Exception &ex) { - // continue - } - - if (! ok) { - // try reading database directly. - db->load (m_open_filename); - } + db->load (m_open_filename); int rdb_index = view ()->add_rdb (db.release ()); mp_ui->rdb_cb->setCurrentIndex (rdb_index); @@ -817,134 +784,12 @@ MarkerBrowserDialog::scan_layer_flat_or_hierarchical (bool flat) std::unique_ptr rdb (new rdb::Database ()); - scan_layout (rdb.get (), layout, cv.cell_index (), layer_indexes, flat); + rdb->scan_layout (layout, cv.cell_index (), layer_indexes, flat); unsigned int rdb_index = view ()->add_rdb (rdb.release ()); view ()->open_rdb_browser (rdb_index, cv_index); } -void -MarkerBrowserDialog::scan_layout (rdb::Database *rdb, const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat) -{ - tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Shapes To Markers")), 10000); - progress.set_format (tl::to_string (QObject::tr ("%.0f0000 markers"))); - progress.set_unit (10000); - - rdb->set_name ("Shapes"); - rdb->set_top_cell_name (layout.cell_name (cell_index)); - rdb::Cell *rdb_top_cell = rdb->create_cell (rdb->top_cell_name ()); - - std::string desc; - - if (layers_and_descriptions.size () == 1) { - - if (flat) { - desc = tl::to_string (tr ("Flat shapes of layer ")); - } else { - desc = tl::to_string (tr ("Hierarchical shapes of layer ")); - } - - desc += layout.get_properties (layers_and_descriptions.front ().first).to_string (); - - } else if (layers_and_descriptions.size () < 4 && layers_and_descriptions.size () > 0) { - - if (flat) { - desc = tl::to_string (tr ("Flat shapes of layers ")); - } else { - desc = tl::to_string (tr ("Hierarchical shapes of layers ")); - } - - for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { - if (l != layers_and_descriptions.begin ()) { - desc += ","; - } - desc += layout.get_properties (l->first).to_string (); - } - - } else { - - if (flat) { - desc = tl::sprintf (tl::to_string (tr ("Flat shapes of %d layers")), int (layers_and_descriptions.size ())); - } else { - desc = tl::sprintf (tl::to_string (tr ("Hierarchical shapes of %d layers")), int (layers_and_descriptions.size ())); - } - - } - - desc += " "; - desc += tl::to_string (tr ("from cell ")); - desc += layout.cell_name (cell_index); - rdb->set_description (desc); - - if (flat) { - - for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { - - rdb::Category *cat = rdb->create_category (l->second.empty () ? layout.get_properties (l->first).to_string () : l->second); - - db::RecursiveShapeIterator shape (layout, layout.cell (cell_index), l->first); - while (! shape.at_end ()) { - - rdb::create_item_from_shape (rdb, rdb_top_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()) * shape.trans (), *shape); - - ++progress; - ++shape; - - } - - } - - } else { - - std::set called_cells; - called_cells.insert (cell_index); - layout.cell (cell_index).collect_called_cells (called_cells); - - for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { - - rdb::Category *cat = rdb->create_category (l->second.empty () ? layout.get_properties (l->first).to_string () : l->second); - - for (db::Layout::const_iterator cid = layout.begin (); cid != layout.end (); ++cid) { - - if (called_cells.find (cid->cell_index ()) == called_cells.end ()) { - continue; - } - - const db::Cell &cell = *cid; - if (! cell.shapes (l->first).empty ()) { - - std::string cn = layout.cell_name (cell.cell_index ()); - const rdb::Cell *rdb_cell = rdb->cell_by_qname (cn); - if (! rdb_cell) { - - rdb::Cell *rdb_cell_nc = rdb->create_cell (cn); - rdb_cell = rdb_cell_nc; - - std::pair ctx = db::find_layout_context (layout, cell.cell_index (), cell_index); - if (ctx.first) { - db::DCplxTrans t = db::DCplxTrans (layout.dbu ()) * db::DCplxTrans (ctx.second) * db::DCplxTrans (1.0 / layout.dbu ()); - rdb_cell_nc->references ().insert (Reference (t, rdb_top_cell->id ())); - } - - } - - for (db::ShapeIterator shape = cell.shapes (l->first).begin (db::ShapeIterator::All); ! shape.at_end (); ++shape) { - - rdb::create_item_from_shape (rdb, rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), *shape); - - ++progress; - - } - - } - - } - - } - - } -} - void MarkerBrowserDialog::menu_activated (const std::string &symbol) { diff --git a/src/layui/layui/rdbMarkerBrowserDialog.h b/src/layui/layui/rdbMarkerBrowserDialog.h index 3b50f1fa7..482913f6a 100644 --- a/src/layui/layui/rdbMarkerBrowserDialog.h +++ b/src/layui/layui/rdbMarkerBrowserDialog.h @@ -109,8 +109,6 @@ private: void scan_layer (); void scan_layer_flat (); void scan_layer_flat_or_hierarchical (bool flat); - void scan_layout (rdb::Database *db, const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat); - void read_db_from_layout (rdb::Database *db, const std::string &filename); }; } diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 887383198..e219dfc52 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -23,11 +23,13 @@ #include "rdb.h" #include "rdbReader.h" +#include "rdbUtils.h" #include "tlString.h" #include "tlAssert.h" #include "tlStream.h" #include "tlLog.h" #include "tlBase64.h" +#include "tlProgress.h" #include "dbPolygon.h" #include "dbBox.h" #include "dbEdge.h" @@ -35,6 +37,10 @@ #include "dbPath.h" #include "dbText.h" #include "dbShape.h" +#include "dbLayout.h" +#include "dbLayoutUtils.h" +#include "dbReader.h" +#include "dbRecursiveShapeIterator.h" #if defined(HAVE_QT) # include @@ -1566,16 +1572,49 @@ Database::clear () mp_categories->set_database (this); } +static void +read_db_from_layout (rdb::Database *db, tl::InputStream &is) +{ + // try reading a layout file + db::Layout layout; + db::Reader reader (is); + + reader.read (layout); + + std::vector > layers; + for (auto l = layout.begin_layers (); l != layout.end_layers (); ++l) { + layers.push_back (std::make_pair ((*l).first, std::string ())); + } + + if (layout.begin_top_down () != layout.end_top_down ()) { + db->scan_layout (layout, *layout.begin_top_down (), layers, false /*hierarchical*/); + } +} + void Database::load (const std::string &fn) { tl::log << "Loading RDB from " << fn; - tl::InputStream stream (fn); - rdb::Reader reader (stream); - clear (); - reader.read (*this); + + tl::InputStream stream (fn); + + bool ok = false; + try { + // try reading a stream file + read_db_from_layout (this, stream); + ok = true; + } catch (tl::Exception &) { + stream.reset (); + } + + if (! ok) { + // try reading a DB file + clear (); + rdb::Reader reader (stream); + reader.read (*this); + } set_filename (stream.absolute_path ()); set_name (stream.filename ()); @@ -1587,5 +1626,128 @@ Database::load (const std::string &fn) } } +void +Database::scan_layout (const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat) +{ + tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Shapes To Markers")), 10000); + progress.set_format (tl::to_string (QObject::tr ("%.0f0000 markers"))); + progress.set_unit (10000); + + set_name ("Shapes"); + set_top_cell_name (layout.cell_name (cell_index)); + rdb::Cell *rdb_top_cell = create_cell (top_cell_name ()); + + std::string desc; + + if (layers_and_descriptions.size () == 1) { + + if (flat) { + desc = tl::to_string (tr ("Flat shapes of layer ")); + } else { + desc = tl::to_string (tr ("Hierarchical shapes of layer ")); + } + + desc += layout.get_properties (layers_and_descriptions.front ().first).to_string (); + + } else if (layers_and_descriptions.size () < 4 && layers_and_descriptions.size () > 0) { + + if (flat) { + desc = tl::to_string (tr ("Flat shapes of layers ")); + } else { + desc = tl::to_string (tr ("Hierarchical shapes of layers ")); + } + + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { + if (l != layers_and_descriptions.begin ()) { + desc += ","; + } + desc += layout.get_properties (l->first).to_string (); + } + + } else { + + if (flat) { + desc = tl::sprintf (tl::to_string (tr ("Flat shapes of %d layers")), int (layers_and_descriptions.size ())); + } else { + desc = tl::sprintf (tl::to_string (tr ("Hierarchical shapes of %d layers")), int (layers_and_descriptions.size ())); + } + + } + + desc += " "; + desc += tl::to_string (tr ("from cell ")); + desc += layout.cell_name (cell_index); + set_description (desc); + + if (flat) { + + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { + + rdb::Category *cat = create_category (l->second.empty () ? layout.get_properties (l->first).to_string () : l->second); + + db::RecursiveShapeIterator shape (layout, layout.cell (cell_index), l->first); + while (! shape.at_end ()) { + + rdb::create_item_from_shape (this, rdb_top_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()) * shape.trans (), *shape); + + ++progress; + ++shape; + + } + + } + + } else { + + std::set called_cells; + called_cells.insert (cell_index); + layout.cell (cell_index).collect_called_cells (called_cells); + + for (auto l = layers_and_descriptions.begin (); l != layers_and_descriptions.end (); ++l) { + + rdb::Category *cat = create_category (l->second.empty () ? layout.get_properties (l->first).to_string () : l->second); + + for (db::Layout::const_iterator cid = layout.begin (); cid != layout.end (); ++cid) { + + if (called_cells.find (cid->cell_index ()) == called_cells.end ()) { + continue; + } + + const db::Cell &cell = *cid; + if (! cell.shapes (l->first).empty ()) { + + std::string cn = layout.cell_name (cell.cell_index ()); + const rdb::Cell *rdb_cell = cell_by_qname (cn); + if (! rdb_cell) { + + rdb::Cell *rdb_cell_nc = create_cell (cn); + rdb_cell = rdb_cell_nc; + + std::pair ctx = db::find_layout_context (layout, cell.cell_index (), cell_index); + if (ctx.first) { + db::DCplxTrans t = db::DCplxTrans (layout.dbu ()) * db::DCplxTrans (ctx.second) * db::DCplxTrans (1.0 / layout.dbu ()); + rdb_cell_nc->references ().insert (Reference (t, rdb_top_cell->id ())); + } + + } + + for (db::ShapeIterator shape = cell.shapes (l->first).begin (db::ShapeIterator::All); ! shape.at_end (); ++shape) { + + rdb::create_item_from_shape (this, rdb_cell->id (), cat->id (), db::CplxTrans (layout.dbu ()), *shape); + + ++progress; + + } + + } + + } + + } + + } +} + + } // namespace rdb diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index a7859f60e..22970cd3c 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -50,6 +50,7 @@ namespace tl namespace db { class Shape; + class Layout; } namespace rdb @@ -2317,6 +2318,16 @@ public: */ void load (const std::string &filename); + /** + * @brief Scans a layout into this RDB + * + * @param layout The layout to scan + * @param cell_index The top cell to scan + * @param layers_and_descriptions The layers and (optional) descriptions/names of the layer to scan + * @param flat True, to perform a flat scan + */ + void scan_layout (const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat); + private: std::string m_generator; std::string m_filename; From 9125ed703533204fbd70170a3236a818184e320e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 19:23:21 +0100 Subject: [PATCH 17/32] [consider merging] fixed a linker problem for debug builds --- src/tl/tl/tlOptional.cc | 2 +- src/tl/tl/tlOptional.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tl/tl/tlOptional.cc b/src/tl/tl/tlOptional.cc index ba2774439..d7ee370b2 100644 --- a/src/tl/tl/tlOptional.cc +++ b/src/tl/tl/tlOptional.cc @@ -25,6 +25,6 @@ namespace tl { -extern const nullopt_t nullopt = nullopt_t (); +const nullopt_t nullopt = nullopt_t (); } // namespace tl diff --git a/src/tl/tl/tlOptional.h b/src/tl/tl/tlOptional.h index c30fa6679..3893d6ac0 100644 --- a/src/tl/tl/tlOptional.h +++ b/src/tl/tl/tlOptional.h @@ -34,7 +34,7 @@ namespace tl struct nullopt_t {}; -extern const nullopt_t nullopt; +extern TL_PUBLIC const nullopt_t nullopt; /** * @brief Poor man's partial implementation of C++17's std::optional From 3b0a34b9559e7eea4042543df90fda3d1b9130dd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 12:31:44 +0100 Subject: [PATCH 18/32] [consider merging] Avoid a segfault when changing the default grids. --- src/lay/lay/layMainConfigPages.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index a7634e4e5..95e724196 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -525,7 +525,7 @@ CustomizeMenuConfigPage::commit (lay::Dispatcher *dispatcher) std::map::iterator cb = m_current_bindings.find (kb->first); if (cb != m_current_bindings.end ()) { lay::Action *a = dispatcher->menu ()->action (kb->first); - if (cb->second != a->get_default_shortcut ()) { + if (a && cb->second != a->get_default_shortcut ()) { if (cb->second.empty ()) { kb->second = lay::Action::no_shortcut (); } else { From 376058f34bb0852d0aea6f259116d8bb083734ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 12:48:53 +0100 Subject: [PATCH 19/32] Implemented fix for issue-1662 (Strong default grids) --- src/db/db/dbTechnology.cc | 28 ++++++++-- src/db/db/dbTechnology.h | 9 ++++ src/db/db/gsiDeclDbTechnologies.cc | 31 ++++++++++- src/lay/lay/MainConfigPage3.ui | 82 ++++++++++++++++-------------- src/lay/lay/TechBaseEditorPage.ui | 10 +++- src/lay/lay/layMainWindow.cc | 44 ++++++++++++++-- src/lay/lay/layMainWindow.h | 3 ++ testdata/ruby/dbTechnologies.rb | 19 +++++++ 8 files changed, 178 insertions(+), 48 deletions(-) diff --git a/src/db/db/dbTechnology.cc b/src/db/db/dbTechnology.cc index 2910fbbab..84bd4118e 100644 --- a/src/db/db/dbTechnology.cc +++ b/src/db/db/dbTechnology.cc @@ -347,12 +347,10 @@ Technology::get_display_string () const return d; } -std::vector -Technology::default_grid_list () const +static void +parse_default_grids (const std::string &s, std::vector &grids, double &default_grid) { - tl::Extractor ex (m_default_grids.c_str ()); - - std::vector grids; + tl::Extractor ex (s.c_str ()); // convert the list of grids to a list of doubles while (! ex.at_end ()) { @@ -361,12 +359,32 @@ Technology::default_grid_list () const break; } grids.push_back (g); + if (ex.test ("!")) { + default_grid = g; + } ex.test (","); } +} +std::vector +Technology::default_grid_list () const +{ + std::vector grids; + double default_grid = 0.0; + parse_default_grids (m_default_grids, grids, default_grid); return grids; } +double +Technology::default_grid () const +{ + std::vector grids; + double default_grid = 0.0; + parse_default_grids (m_default_grids, grids, default_grid); + return default_grid; +} + + tl::XMLElementList Technology::xml_elements () { diff --git a/src/db/db/dbTechnology.h b/src/db/db/dbTechnology.h index e06ddca14..2fa1c6f5e 100644 --- a/src/db/db/dbTechnology.h +++ b/src/db/db/dbTechnology.h @@ -480,6 +480,15 @@ public: */ std::vector default_grid_list () const; + /** + * @brief Gets the default grid (strong grid), parsed from the list + * + * The default grid is the one marked with an exclamation mark in the + * grid list (e.g. "0.01!,0.02,0.05"). If there is not such default + * grid, this method returns zero. + */ + double default_grid () const; + /** * @brief Sets the default default grids */ diff --git a/src/db/db/gsiDeclDbTechnologies.cc b/src/db/db/gsiDeclDbTechnologies.cc index de670fa4f..bc3d6f9d0 100644 --- a/src/db/db/gsiDeclDbTechnologies.cc +++ b/src/db/db/gsiDeclDbTechnologies.cc @@ -136,7 +136,7 @@ gsi::Class technology_component_decl ("db", "Technology DB_PUBLIC gsi::Class &decl_dbTechnologyComponent () { return technology_component_decl; } static void -set_default_grid_list (db::Technology *tech, const std::vector &grids) +set_default_grid_list2 (db::Technology *tech, const std::vector &grids, double default_grid) { std::string r; for (auto g = grids.begin (); g != grids.end (); ++g) { @@ -144,10 +144,19 @@ set_default_grid_list (db::Technology *tech, const std::vector &grids) r += ","; } r += tl::micron_to_string (*g); + if (db::coord_traits::equals (*g, default_grid)) { + r += "!"; + } } tech->set_default_grids (r); } +static void +set_default_grid_list (db::Technology *tech, const std::vector &grids) +{ + set_default_grid_list2 (tech, grids, 0.0); +} + gsi::Class technology_decl ("db", "Technology", gsi::method ("name", &db::Technology::name, "@brief Gets the name of the technology" @@ -238,12 +247,32 @@ gsi::Class technology_decl ("db", "Technology", "\n" "This property has been introduced in version 0.28.17." ) + + gsi::method ("default_grid", &db::Technology::default_grid, + "@brief Gets the default grid\n" + "\n" + "The default grid is a specific one from the default grid list.\n" + "It indicates the one that is taken if the current grid is not matching one of " + "the default grids.\n" + "\n" + "To set the default grid, use \\set_default_grids.\n" + "\n" + "This property has been introduced in version 0.29." + ) + gsi::method_ext ("default_grids=", &set_default_grid_list, gsi::arg ("grids"), "@brief Sets the default grids\n" "If not empty, this list replaces the global grid list for this technology.\n" + "Note that this method will reset the default grid (see \\default_grid). Use " + "\\set_default_grids to set the default grids and the strong default one.\n" "\n" "This property has been introduced in version 0.28.17." ) + + gsi::method_ext ("set_default_grids", &set_default_grid_list2, gsi::arg ("grids"), gsi::arg ("default_grid", 0.0), + "@brief Sets the default grids and the strong default one\n" + "See \\default_grids and \\default_grid for a description of this property.\n" + "Note that the default grid has to be a member of the 'grids' array to become active.\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method ("layer_properties_file", &db::Technology::layer_properties_file, "@brief Gets the path of the layer properties file\n" "\n" diff --git a/src/lay/lay/MainConfigPage3.ui b/src/lay/lay/MainConfigPage3.ui index 54420b273..614606a58 100644 --- a/src/lay/lay/MainConfigPage3.ui +++ b/src/lay/lay/MainConfigPage3.ui @@ -1,67 +1,75 @@ - + + MainConfigPage3 - - + + 0 0 - 475 - 81 + 504 + 180 - + Settings - - - 9 - - + + 6 + + 9 + - - + + Default Grids - - + + 9 - + 6 - - - + + + + + 0 + 0 + + + + Grids for "View" menu + + + + + + µm (g1,g2,...) - - - - - 7 - 0 + + + + 0 0 - - - - - 5 - 5 - 0 - 0 - + + + + <html>You can declare one grid a strong default to enforce an editing grid from this list. To do so, add an exclamation mark - e.g. "0.01!,0.02,0.05". +<br/><b>Note</b>: the general default grids can be overridden by technology specific default grids.</html> - - Grids for "View" menu + + true @@ -70,7 +78,7 @@ - + diff --git a/src/lay/lay/TechBaseEditorPage.ui b/src/lay/lay/TechBaseEditorPage.ui index 668e04ffe..17d89749d 100644 --- a/src/lay/lay/TechBaseEditorPage.ui +++ b/src/lay/lay/TechBaseEditorPage.ui @@ -7,7 +7,7 @@ 0 0 625 - 587 + 616 @@ -331,6 +331,9 @@ properties The default database unit is used as database unit for freshly created layouts + + true + @@ -352,7 +355,10 @@ properties - These grids are available for selection from the "View" menu + These grids are available for selection from the "View" menu and will override the general ones. You can declare one grid as a strong default to enforce an editing grid from this list. To do so, add an exclamation mark to the grid - e.g. "0.01!,0.02,0.05". + + + true diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 0eb2eb397..b5083ae8b 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -175,9 +175,11 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) m_disable_tab_selected (false), m_exited (false), dm_do_update_menu (this, &MainWindow::do_update_menu), + dm_do_update_grids (this, &MainWindow::do_update_grids), dm_do_update_mru_menus (this, &MainWindow::do_update_mru_menus), dm_exit (this, &MainWindow::exit), m_grid_micron (0.001), + m_default_grid (0.0), m_default_grids_updated (true), m_new_layout_current_panel (false), m_synchronized_views (false), @@ -583,7 +585,7 @@ MainWindow::technology_changed () } m_default_grids_updated = true; // potentially ... - dm_do_update_menu (); + dm_do_update_grids (); } void @@ -939,7 +941,7 @@ MainWindow::config_finalize () // Update the default grids menu if necessary if (m_default_grids_updated) { - dm_do_update_menu (); + dm_do_update_grids (); } // make the changes visible in the setup form if the form is visible @@ -972,6 +974,7 @@ MainWindow::configure (const std::string &name, const std::string &value) tl::Extractor ex (value.c_str ()); m_default_grids.clear (); + m_default_grid = 0.0; m_default_grids_updated = true; // convert the list of grids to a list of doubles @@ -980,6 +983,9 @@ MainWindow::configure (const std::string &name, const std::string &value) if (! ex.try_read (g)) { break; } + if (ex.test ("!")) { + m_default_grid = g; + } m_default_grids.push_back (g); ex.test (","); } @@ -4041,6 +4047,38 @@ MainWindow::menu_changed () dm_do_update_menu (); } +void +MainWindow::do_update_grids () +{ + const std::vector *grids = &m_default_grids; + double default_grid = m_default_grid; + + std::vector tech_grids; + lay::TechnologyController *tc = lay::TechnologyController::instance (); + if (tc && tc->active_technology ()) { + tech_grids = tc->active_technology ()->default_grid_list (); + if (! tech_grids.empty ()) { + grids = &tech_grids; + default_grid = tc->active_technology ()->default_grid (); + } + } + + if (default_grid > db::epsilon) { + for (auto g = grids->begin (); g != grids->end (); ++g) { + if (db::coord_traits::equals (*g, m_grid_micron)) { + default_grid = 0.0; + break; + } + } + } + + if (default_grid > db::epsilon) { + dispatcher ()->config_set (cfg_grid, default_grid); + } + + do_update_menu (); +} + void MainWindow::do_update_menu () { @@ -4082,7 +4120,7 @@ MainWindow::do_update_menu () lay::Action *ga = new lay::ConfigureAction (gs, cfg_grid, tl::to_string (*g)); ga->set_checkable (true); - ga->set_checked (fabs (*g - m_grid_micron) < 1e-10); + ga->set_checked (db::coord_traits::equals (*g, m_grid_micron)); for (std::vector::const_iterator t = group.begin (); t != group.end (); ++t) { menu ()->insert_item (*t + ".end", name, ga); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 326bc3c07..64e457c89 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -706,6 +706,7 @@ protected slots: protected: void update_content (); void do_update_menu (); + void do_update_grids (); void do_update_mru_menus (); bool eventFilter (QObject *watched, QEvent *event); @@ -753,6 +754,7 @@ private: bool m_disable_tab_selected; bool m_exited; tl::DeferredMethod dm_do_update_menu; + tl::DeferredMethod dm_do_update_grids; tl::DeferredMethod dm_do_update_mru_menus; tl::DeferredMethod dm_exit; QTimer m_message_timer; @@ -765,6 +767,7 @@ private: std::string m_initial_technology; double m_grid_micron; std::vector m_default_grids; + double m_default_grid; bool m_default_grids_updated; std::vector > m_key_bindings; bool m_new_layout_current_panel; diff --git a/testdata/ruby/dbTechnologies.rb b/testdata/ruby/dbTechnologies.rb index d1dc9cf93..3305eae6a 100644 --- a/testdata/ruby/dbTechnologies.rb +++ b/testdata/ruby/dbTechnologies.rb @@ -105,10 +105,29 @@ END tech.default_grids = [] assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "") + assert_equal("%.12g" % tech.default_grid, "0") tech.default_grids = [0.001, 0.01, 0.2] assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "0.001,0.01,0.2") tech.default_grids = [1] assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "1") + assert_equal("%.12g" % tech.default_grid, "0") + + # setting the default grid + tech.set_default_grids([0.01,0.02,0.05], 0.02) + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "0.01,0.02,0.05") + assert_equal("%.12g" % tech.default_grid, "0.02") + + # default grid must be a member of the default grid list + tech.set_default_grids([0.01,0.02,0.05], 0.2) + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "0.01,0.02,0.05") + assert_equal("%.12g" % tech.default_grid, "0") + + # "default_grids=" resets the default grid + tech.set_default_grids([0.01,0.02,0.05], 0.02) + assert_equal("%.12g" % tech.default_grid, "0.02") + tech.default_grids = [1] + assert_equal(tech.default_grids.collect { |g| "%.12g" % g }.join(","), "1") + assert_equal("%.12g" % tech.default_grid, "0") tech.default_base_path = "/default/path" assert_equal(tech.default_base_path, "/default/path") From 735d2101fd1e31e0ee4d8647390b83ee475b4172 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 12:53:18 +0100 Subject: [PATCH 20/32] Fixed Qt-less builds --- src/rdb/rdb/rdb.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index e219dfc52..390e6f88a 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -1629,8 +1629,8 @@ Database::load (const std::string &fn) void Database::scan_layout (const db::Layout &layout, db::cell_index_type cell_index, const std::vector > &layers_and_descriptions, bool flat) { - tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Shapes To Markers")), 10000); - progress.set_format (tl::to_string (QObject::tr ("%.0f0000 markers"))); + tl::AbsoluteProgress progress (tl::to_string (tr ("Shapes To Markers")), 10000); + progress.set_format (tl::to_string (tr ("%.0f0000 markers"))); progress.set_unit (10000); set_name ("Shapes"); From 1673c472f2a7d9eb51142c10f3f41ce2a3705f8e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 19:23:21 +0100 Subject: [PATCH 21/32] [consider merging] fixed a linker problem for debug builds --- src/tl/tl/tlOptional.cc | 2 +- src/tl/tl/tlOptional.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tl/tl/tlOptional.cc b/src/tl/tl/tlOptional.cc index ba2774439..d7ee370b2 100644 --- a/src/tl/tl/tlOptional.cc +++ b/src/tl/tl/tlOptional.cc @@ -25,6 +25,6 @@ namespace tl { -extern const nullopt_t nullopt = nullopt_t (); +const nullopt_t nullopt = nullopt_t (); } // namespace tl diff --git a/src/tl/tl/tlOptional.h b/src/tl/tl/tlOptional.h index c30fa6679..3893d6ac0 100644 --- a/src/tl/tl/tlOptional.h +++ b/src/tl/tl/tlOptional.h @@ -34,7 +34,7 @@ namespace tl struct nullopt_t {}; -extern const nullopt_t nullopt; +extern TL_PUBLIC const nullopt_t nullopt; /** * @brief Poor man's partial implementation of C++17's std::optional From b9bdcf6facb65c87b76ebf1dd1cc17c4ccfa58e6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 19:01:36 +0100 Subject: [PATCH 22/32] Preparations: recursive shape iterator shortcuts if hierarchy traversal, needs testing. --- src/db/db/dbAsIfFlatRegion.cc | 2 + src/db/db/dbFlatRegion.cc | 12 ++- src/db/db/dbFlatRegion.h | 3 +- src/db/db/dbRecursiveShapeIterator.cc | 103 +++++++++++++++---- src/db/db/dbRecursiveShapeIterator.h | 51 +++++++-- src/db/db/dbRegion.cc | 36 ++++++- src/db/db/dbRegion.h | 20 +++- src/db/db/gsiDeclDbRecursiveShapeIterator.cc | 18 +++- src/db/db/gsiDeclDbRegion.cc | 54 ++++++---- src/db/unit_tests/dbRegionTests.cc | 29 ++++++ testdata/ruby/dbRegionTest.rb | 41 ++++++++ 11 files changed, 307 insertions(+), 62 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 9ee803c2a..2171c4f37 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -219,6 +219,8 @@ AsIfFlatRegion::area (const db::Box &box) const for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { if (box.empty () || p->box ().inside (box)) { a += p->area (); + } else if (p->is_box ()) { + a += (p->box () & box).area (); } else { std::vector clipped; clip_poly (*p, box, clipped); diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index d96bb8a22..0d79dbe3e 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -43,7 +43,6 @@ FlatRegion::FlatRegion (const FlatRegion &other) : MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons), mp_properties_repository (other.mp_properties_repository) { init (); - m_is_merged = other.m_is_merged; m_merged_polygons_valid = other.m_merged_polygons_valid; } @@ -52,15 +51,22 @@ FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); - m_is_merged = is_merged; } +FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) +{ + init (); + m_is_merged = is_merged; + transform_generic (trans); + set_merged_semantics (merged_semantics); +} + FlatRegion::FlatRegion (bool is_merged) : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); - m_is_merged = is_merged; } diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index d89e53173..55805a8a5 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -52,7 +52,8 @@ public: typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type; FlatRegion (); - FlatRegion (const db::Shapes &polygons, bool is_merged); + FlatRegion (const db::Shapes &polygons, bool is_merged = false); + FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); FlatRegion (bool is_merged); FlatRegion (const FlatRegion &other); diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 9a8f6052f..0534ebd3f 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -48,6 +48,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI mp_shape_prop_sel = d.mp_shape_prop_sel; m_shape_inv_prop_sel = d.m_shape_inv_prop_sel; m_overlapping = d.m_overlapping; + m_for_merged_input = d.m_for_merged_input; + m_start = d.m_start; m_stop = d.m_stop; @@ -99,6 +101,7 @@ RecursiveShapeIterator::RecursiveShapeIterator () mp_cell = 0; m_current_layer = 0; m_overlapping = false; + m_for_merged_input = false; m_max_depth = std::numeric_limits::max (); // all m_min_depth = 0; m_shape_flags = shape_iterator::All; @@ -116,6 +119,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes) mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = false; + m_for_merged_input = false; init (); init_region (box_type::world ()); } @@ -127,6 +131,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes, const mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = overlapping; + m_for_merged_input = false; init (); init_region (region); } @@ -138,11 +143,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes, const mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = overlapping; + m_for_merged_input = false; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -151,11 +157,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -164,11 +171,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -177,11 +185,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -191,11 +200,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -205,11 +215,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -219,11 +230,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -233,11 +245,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -247,11 +260,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -261,6 +275,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } @@ -721,13 +736,9 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const } - if (is_empty) { - + if (is_empty || !down (receiver)) { ++m_inst; new_inst (receiver); - - } else { - down (receiver); } } else { @@ -755,7 +766,7 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const } } -void +bool RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { tl_assert (mp_layout); @@ -783,6 +794,40 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const new_region = m_trans.inverted () * m_region; new_region &= cell_bbox (cell_index ()); } + + // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: + // - single layer + // - less than 32 shapes to consider + // - total shape bbox in current region covers at least a third of it + // - total area of shapes in current region is at least a third of it + // TODO: the current implementation does not touch the complex search region + + if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! new_region.empty ()) { + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + const shapes_type &shapes = m_cells.back ()->shapes (l); + box_type region_in_parent = m_inst->complex_trans (*m_inst_array) * new_region; + + // NOTE: new_region is already in the coordinate system of the child cell + + if (shapes.size () < 32 && + 3 * (shapes.bbox () & region_in_parent).area () > region_in_parent.area ()) { + + region_type shapes_region (shapes); + if (3 * shapes_region.area (region_in_parent) > region_in_parent.area ()) { + + shapes_region.transform (m_inst->complex_trans (*m_inst_array).inverted ()); + + // reduce the search region for less instances to look up + region_type new_complex_region = region_type (new_region) - shapes_region; + new_region = new_complex_region.bbox (); + + } + + } + + } + m_local_region_stack.push_back (new_region); if (! m_local_complex_region_stack.empty ()) { @@ -817,11 +862,25 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } - if (receiver) { - receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); - } + // do not descend if the box is empty - new_cell (receiver); + if (m_local_region_stack.back ().empty ()) { + + pop (); + + return false; + + } else { + + if (receiver) { + receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + } + + new_cell (receiver); + + return true; + + } } void @@ -831,6 +890,12 @@ RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const receiver->leave_cell (this, cell ()); } + pop (); +} + +void +RecursiveShapeIterator::pop () const +{ m_shape = shape_iterator (); m_shape_quad_id = 0; diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 43f913b1a..b8e6a640a 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -122,12 +122,13 @@ public: * @param layer The layer from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor @@ -137,13 +138,14 @@ public: * @param layer The layer from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration @@ -153,8 +155,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layer The layer from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -164,12 +167,13 @@ public: * @param layers The layers from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -179,13 +183,14 @@ public: * @param layers The layers from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -195,12 +200,13 @@ public: * @param layers The layers from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -210,13 +216,14 @@ public: * @param layers The layers from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration with a layer set @@ -226,8 +233,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layers The layers from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration with a layer set @@ -237,8 +245,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layers The layers from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, bool for_merged_input = false); /** * @brief Destructor @@ -427,6 +436,25 @@ public: } } + /** + * @brief Gets a flag indicating whether optimizing for merged input + */ + bool for_merged_input () const + { + return m_for_merged_input; + } + + /** + * @brief Sets a flag indicating whether optimizing for merged input + */ + void set_for_merged_input (bool f) + { + if (m_for_merged_input != f) { + m_for_merged_input = f; + m_needs_reinit = true; + } + } + /** * @brief Sets a global transformation * @@ -812,7 +840,7 @@ private: unsigned int m_shape_flags; const shape_iterator::property_selector *mp_shape_prop_sel; bool m_shape_inv_prop_sel; - bool m_overlapping; + bool m_overlapping, m_for_merged_input; std::set m_start, m_stop; cplx_trans_type m_global_trans; db::PropertiesTranslator m_property_translator; @@ -858,7 +886,8 @@ private: void new_cell (RecursiveShapeReceiver *receiver) const; void new_layer () const; void up (RecursiveShapeReceiver *receiver) const; - void down (RecursiveShapeReceiver *receiver) const; + bool down (RecursiveShapeReceiver *receiver) const; + void pop () const; bool is_outside_complex_region (const db::Box &box) const; diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 488fbc460..c05f9d338 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -74,14 +74,42 @@ Region &Region::operator= (const Region &other) return *this; } -Region::Region (const RecursiveShapeIterator &si) +Region::Region (const RecursiveShapeIterator &si, bool merged_semantics, bool is_merged) { - mp_delegate = new OriginalLayerRegion (si); + mp_delegate = new OriginalLayerRegion (si, db::ICplxTrans (), merged_semantics, is_merged); } -Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) +Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) { - mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics); + mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics, is_merged); +} + +Region::Region (const Shapes &shapes, bool merged_semantics, bool is_merged) +{ + db::FlatRegion *flat_region = new FlatRegion (is_merged); + flat_region->reserve (shapes.size (db::ShapeIterator::Regions)); + + // NOTE: we need to normalize the shapes to polygons because this is what the flat region expects + for (auto s = shapes.begin (db::ShapeIterator::Regions); ! s.at_end (); ++s) { + flat_region->insert (*s); + } + + mp_delegate = flat_region; + mp_delegate->set_merged_semantics (merged_semantics); +} + +Region::Region (const Shapes &shapes, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) +{ + db::FlatRegion *flat_region = new FlatRegion (is_merged); + flat_region->reserve (shapes.size (db::ShapeIterator::Regions)); + + // NOTE: we need to normalize the shapes to polygons because this is what the flat region expects + for (auto s = shapes.begin (db::ShapeIterator::Regions); ! s.at_end (); ++s) { + flat_region->insert (*s, trans); + } + + mp_delegate = flat_region; + mp_delegate->set_merged_semantics (merged_semantics); } Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 39880b2ca..1329adcc8 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -199,7 +199,7 @@ public: * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. */ - explicit Region (const RecursiveShapeIterator &si); + explicit Region (const RecursiveShapeIterator &si, bool merged_semantics = true, bool is_merged = false); /** * @brief Constructor from a RecursiveShapeIterator with a transformation @@ -208,7 +208,23 @@ public: * from a hierarchy of cells. The transformation is useful to scale to a specific * DBU for example. */ - explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true, bool is_merged = false); + + /** + * @brief Constructor from a Shapes container + * + * Creates a region from a shapes container. + */ + explicit Region (const Shapes &si, bool merged_semantics = true, bool is_merged = false); + + /** + * @brief Constructor from a Shapes container with a transformation + * + * Creates a region from a recursive shape iterator. This allows feeding a region + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + explicit Region (const Shapes &si, const db::ICplxTrans &trans, bool merged_semantics = true, bool is_merged = false); /** * @brief Constructor from a RecursiveShapeIterator providing a deep representation diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index bc702af0f..50f840eee 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -476,13 +476,29 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This method has been introduced in version 0.23.\n" ) + - gsi::method ("overlapping=", &db::RecursiveShapeIterator::set_overlapping, gsi::arg ("region"), + gsi::method ("overlapping=", &db::RecursiveShapeIterator::set_overlapping, gsi::arg ("flag"), "@brief Sets a flag indicating whether overlapping shapes are selected when a region is used\n" "\n" "If this flag is false, shapes touching the search region are returned.\n" "\n" "This method has been introduced in version 0.23.\n" ) + + gsi::method ("for_merged_input?", &db::RecursiveShapeIterator::for_merged_input, + "@brief Gets a flag indicating whether iterator optimizes for merged input\n" + "\n" + "see \\for_merged_input= for details of this attribute.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + gsi::method ("for_merged_input=", &db::RecursiveShapeIterator::set_for_merged_input, gsi::arg ("flag"), + "@brief Sets a flag indicating whether iterator optimizes for merged input\n" + "\n" + "If this flag is set to true, the iterator is allowed to skip shapes it deems irrelevant " + "because they are covered entirely by other shapes. This allows shortcutting hierarchy traversal in " + "some cases.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + gsi::method ("unselect_all_cells", &db::RecursiveShapeIterator::unselect_all_cells, "@brief Unselects all cells.\n" "\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index f978a7c4f..ad941cc27 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -270,15 +270,6 @@ static db::Region *new_path (const db::Path &o) return new db::Region (o); } -static db::Region *new_shapes (const db::Shapes &s) -{ - db::Region *r = new db::Region (); - for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end (); ++i) { - r->insert (*i); - } - return r; -} - static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl) { return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl)); @@ -329,16 +320,26 @@ static db::Region *new_si (const db::RecursiveShapeIterator &si) return new db::Region (si); } -static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) -{ - return new db::Region (si, dss, area_ratio, max_vertex_count); -} - static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) { return new db::Region (si, trans); } +static db::Region *new_sis (const db::Shapes &si) +{ + return new db::Region (si); +} + +static db::Region *new_sis2 (const db::Shapes &si, const db::ICplxTrans &trans) +{ + return new db::Region (si, trans); +} + +static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) +{ + return new db::Region (si, dss, area_ratio, max_vertex_count); +} + static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count) { return new db::Region (si, dss, trans, true, area_ratio, max_vertex_count); @@ -1088,13 +1089,6 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This constructor creates a region from a path.\n" ) + - constructor ("new", &new_shapes, gsi::arg ("shapes"), - "@brief Shapes constructor\n" - "\n" - "This constructor creates a region from a \\Shapes collection.\n" - "\n" - "This constructor has been introduced in version 0.25." - ) + constructor ("new", &new_si, gsi::arg ("shape_iterator"), "@brief Constructor from a hierarchical shape set\n" "\n" @@ -1126,6 +1120,24 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + constructor ("new", &new_sis, gsi::arg ("shapes"), + "@brief Constructor from a shapes container\n" + "\n" + "This constructor creates a region from the shapes container.\n" + "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" + "This method allows feeding the shapes from a hierarchy of cells into the region.\n" + "\n" + "This constructor has been introduced in version 0.25 and extended in version 0.29." + ) + + constructor ("new", &new_sis2, gsi::arg ("shapes"), gsi::arg ("trans"), + "@brief Constructor from a shapes container with a transformation\n" + "\n" + "This constructor creates a region from the shapes container after applying the transformation.\n" + "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" + "This method allows feeding the shapes from a hierarchy of cells into the region.\n" + "\n" + "This constructor variant has been introduced in version 0.29." + ) + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)), "@brief Constructor for a deep region from a hierarchical shape set\n" "\n" diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 554f25956..70ac453b2 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2537,6 +2537,35 @@ TEST(55_PropertiesFilterFlat) EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); } +TEST(56_RegionsFromShapes) +{ + db::Shapes shapes; + + shapes.insert (db::Box (0, 0, 100, 200)); + shapes.insert (db::Box (50, 50, 150, 250)); + + EXPECT_EQ (db::Region (shapes).area (), 32500); + EXPECT_EQ (db::Region (shapes, false).area (), 40000); + EXPECT_EQ (db::Region (shapes, db::ICplxTrans (0.5)).area (), 8125); + EXPECT_EQ (db::Region (shapes, db::ICplxTrans (0.5), false).area (), 10000); + + // for cross-checking: same for RecursiveShapeIterator + + db::Layout layout; + unsigned int l1 = layout.insert_layer (); + db::Cell &top = layout.cell (layout.add_cell ("TOP")); + + top.shapes (l1).insert (db::Box (0, 0, 100, 200)); + top.shapes (l1).insert (db::Box (50, 50, 150, 250)); + + db::RecursiveShapeIterator si (layout, top, l1); + + EXPECT_EQ (db::Region (si).area (), 32500); + EXPECT_EQ (db::Region (si, false).area (), 40000); + EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5)).area (), 8125); + EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5), false).area (), 10000); +} + TEST(100_Processors) { db::Region r; diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 0d81b5efd..8c59d587c 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1066,6 +1066,47 @@ class DBRegion_TestClass < TestBase end + # regions from Shapes + def test_regions_from_shapes + + shapes = RBA::Shapes::new; + + shapes.insert(RBA::Box::new(0, 0, 100, 200)) + shapes.insert(RBA::Box::new(50, 50, 150, 250)) + + assert_equal(RBA::Region::new(shapes).area, 32500) + region = RBA::Region::new(shapes) + region.merged_semantics = false + assert_equal(region.area, 40000) + + assert_equal(RBA::Region::new(shapes, RBA::ICplxTrans::new(0.5)).area, 8125) + region = RBA::Region::new(shapes, RBA::ICplxTrans::new(0.5)) + region.merged_semantics = false + assert_equal(region.area, 10000) + + # for cross-checking: same for RecursiveShapeIterator + + layout = RBA::Layout::new + l1 = layout.insert_layer(RBA::LayerInfo::new(1, 0)) + top = layout.create_cell("TOP") + + top.shapes(l1).insert (RBA::Box::new(0, 0, 100, 200)) + top.shapes(l1).insert (RBA::Box::new(50, 50, 150, 250)) + + si = RBA::RecursiveShapeIterator::new(layout, top, l1) + + assert_equal(RBA::Region::new(si).area, 32500) + region = RBA::Region::new(si) + region.merged_semantics = false + assert_equal(region.area, 40000) + + assert_equal(RBA::Region::new(si, RBA::ICplxTrans::new(0.5)).area, 8125) + region = RBA::Region::new(si, RBA::ICplxTrans::new(0.5)) + region.merged_semantics = false + assert_equal(region.area, 10000) + + end + # deep region tests def test_deep1 From 3cf8b29699a1e883fbec8ca102e3a635517e28fc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 21:57:39 +0100 Subject: [PATCH 23/32] RecursiveShapeIterator debugging --- src/db/db/dbRecursiveShapeIterator.cc | 97 ++++++++++++------- .../dbRecursiveShapeIteratorTests.cc | 72 ++++++++++++++ 2 files changed, 132 insertions(+), 37 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 0534ebd3f..33f6c0448 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -790,44 +790,11 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const box_type new_region = box_type::world (); // compute the region inside the new cell - if (new_region != m_region) { - new_region = m_trans.inverted () * m_region; + if (new_region != m_local_region_stack.back ()) { + new_region = m_inst->complex_trans (*m_inst_array).inverted () * m_local_region_stack.back (); new_region &= cell_bbox (cell_index ()); } - // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: - // - single layer - // - less than 32 shapes to consider - // - total shape bbox in current region covers at least a third of it - // - total area of shapes in current region is at least a third of it - // TODO: the current implementation does not touch the complex search region - - if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! new_region.empty ()) { - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - const shapes_type &shapes = m_cells.back ()->shapes (l); - box_type region_in_parent = m_inst->complex_trans (*m_inst_array) * new_region; - - // NOTE: new_region is already in the coordinate system of the child cell - - if (shapes.size () < 32 && - 3 * (shapes.bbox () & region_in_parent).area () > region_in_parent.area ()) { - - region_type shapes_region (shapes); - if (3 * shapes_region.area (region_in_parent) > region_in_parent.area ()) { - - shapes_region.transform (m_inst->complex_trans (*m_inst_array).inverted ()); - - // reduce the search region for less instances to look up - region_type new_complex_region = region_type (new_region) - shapes_region; - new_region = new_complex_region.bbox (); - - } - - } - - } - m_local_region_stack.push_back (new_region); if (! m_local_complex_region_stack.empty ()) { @@ -966,7 +933,59 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_layer (); - m_inst = cell ()->begin_touching (m_local_region_stack.back ()); + // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: + // - single layer + // - less than 32 shapes to consider + // - total shape bbox in current region covers at least a third of it + // - total area of shapes in current region is at least a third of it + // + // NOTE that this implementation can modify the search box on the box stack + // because we did "new_layer()" already and this function is not going to + // be called, because we do so only for single layers. + + const box_type ®ion = m_local_region_stack.back (); + + if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! region.empty ()) { + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + const shapes_type &shapes = cell ()->shapes (l); + + if (! shapes.empty () && shapes.size () < 32 && + 3 * (shapes.bbox () & region).area () > region.area ()) { + + region_type shapes_region (shapes); + if (3 * shapes_region.area (region) > region.area ()) { + + // Need to enlarge the empty area somewhat so we really exclude instances + // entirely enclosed by the shape - also the ones at the border. + box_type::vector_type bias; + if (! m_overlapping) { + bias = box_type::vector_type (1, 1); + } + + // reduce the search region for less instances to look up + // NOTE: because we use "touching" for the instances below, we + region_type new_complex_region; + if (region == box_type::world ()) { + new_complex_region = region_type (cell ()->bbox ()) - shapes_region; + } else { + new_complex_region = region_type (cell ()->bbox () & region.enlarged (bias)) - shapes_region; + } + + // TODO: the current implementation does not touch the complex search region + m_local_region_stack.back () = new_complex_region.bbox ().enlarged (-bias); + + } + + } + + } + + if (m_overlapping) { + m_inst = cell ()->begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1))); + } else { + m_inst = cell ()->begin_touching (m_local_region_stack.back ()); + } m_inst_quad_id = 0; @@ -1015,7 +1034,11 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const // 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); + if (m_overlapping) { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1)), m_box_convert); + } else { + 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 (); } diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index c89af7605..c4fec7476 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1554,3 +1554,75 @@ TEST(11_LayoutIsWeakPointer) x = collect(i1, *g); EXPECT_EQ (x, ""); } + +TEST(12_ForMerged) +{ + std::unique_ptr g (new db::Layout ()); + g->insert_layer (0); + g->insert_layer (1); + db::Cell &c0 (g->cell (g->add_cell ())); + db::Cell &c1 (g->cell (g->add_cell ())); + db::Cell &c2 (g->cell (g->add_cell ())); + db::Cell &c3 (g->cell (g->add_cell ())); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::Box (0, 0, 3000, 2000)); + c1.shapes (0).insert (b); + c2.shapes (0).insert (b); + c3.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (100, -100)))); + c0.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (1))); + c2.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (db::Vector (1100, 0)))); + + std::string x; + + db::RecursiveShapeIterator i1 (*g, c0, 0); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + std::vector lv; + lv.push_back (0); + i1 = db::RecursiveShapeIterator (*g, c0, lv); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + lv.push_back (1); // empty, but kills "for merged" optimization + i1 = db::RecursiveShapeIterator (*g, c0, lv); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + // no longer optimized + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50)); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50)); + i1.set_overlapping (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + // ... +} From ab93dde25c09b2f53500e1e9f28165183540430c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 22:11:07 +0100 Subject: [PATCH 24/32] Tests for GSI binding --- testdata/ruby/dbRecursiveShapeIterator.rb | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/testdata/ruby/dbRecursiveShapeIterator.rb b/testdata/ruby/dbRecursiveShapeIterator.rb index 29724541c..f4d2d9b9b 100644 --- a/testdata/ruby/dbRecursiveShapeIterator.rb +++ b/testdata/ruby/dbRecursiveShapeIterator.rb @@ -322,6 +322,38 @@ END end + def test_3 + + l = RBA::Layout.new + l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + c2 = l.cell(l.add_cell("c2")) + c3 = l.cell(l.add_cell("c3")) + + b = RBA::Box.new(0, 100, 1000, 1200) + c0.shapes(0).insert(RBA::Box.new(0, 0, 3000, 2000)) + c1.shapes(0).insert(b) + c2.shapes(0).insert(b) + c3.shapes(0).insert(b) + + tt = RBA::Trans.new + c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) + c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Vector.new(100, -100)))) + c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) + c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Vector.new(1100, 0)))) + + ii = RBA::RecursiveShapeIterator.new(l, c0, 0) + assert_equal(ii.for_merged_input, false) + assert_equal(collect(ii, l), "[c0](0,0;3000,2000)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") + + ii.for_merged_input = true + assert_equal(ii.for_merged_input, true) + assert_equal(collect(ii, l), "[c0](0,0;3000,2000)/[c3](-1200,0;-100,1000)") + + end + end load("test_epilogue.rb") From b4c7176c52117b04bac158048fc18bac7f525919 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 22:45:58 +0100 Subject: [PATCH 25/32] Bug fixing --- src/db/db/dbRecursiveShapeIterator.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 33f6c0448..7fe8b99ed 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -967,9 +967,9 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const // NOTE: because we use "touching" for the instances below, we region_type new_complex_region; if (region == box_type::world ()) { - new_complex_region = region_type (cell ()->bbox ()) - shapes_region; + new_complex_region = region_type (cell ()->bbox (l)) - shapes_region; } else { - new_complex_region = region_type (cell ()->bbox () & region.enlarged (bias)) - shapes_region; + new_complex_region = region_type (cell ()->bbox (l) & region.enlarged (bias)) - shapes_region; } // TODO: the current implementation does not touch the complex search region From 254f598a087fa5329bfd77c83a2d8ee2db36067d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 23:03:08 +0100 Subject: [PATCH 26/32] Deploying solution for XOR tool. Needs testing. --- .../tools/xor/lay_plugin/layXORToolDialog.cc | 184 +++++++++--------- 1 file changed, 91 insertions(+), 93 deletions(-) diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc index 08558adc4..83d2c66e1 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc @@ -772,6 +772,9 @@ XORWorker::do_perform_deep (const XORTask *xor_task) db::RecursiveShapeIterator s_a (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, xor_task->region_a ()); db::RecursiveShapeIterator s_b (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, xor_task->region_b ()); + s_a.set_for_merged_input (true); + s_b.set_for_merged_input (true); + db::Region ra (s_a, dss, db::ICplxTrans (mp_job->cva ()->layout ().dbu () / mp_job->dbu ())); db::Region rb (s_b, dss, db::ICplxTrans (mp_job->cvb ()->layout ().dbu () / mp_job->dbu ())); @@ -800,6 +803,8 @@ XORWorker::do_perform_deep (const XORTask *xor_task) dbu_scale = db::ICplxTrans (mp_job->cvb ()->layout ().dbu () / mp_job->dbu ()); } + s.set_for_merged_input (true); + rr = db::Region (s, dss, dbu_scale); } @@ -867,119 +872,110 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) if ((!la.empty () && !lb.empty ()) || mp_job->el_handling () == XORJob::EL_process) { - if (! mp_job->has_tiles ()) { + tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); + size_t n; - tl::SelfTimer timer (tl::verbosity () >= 21, "Boolean part"); + if (! merge_before_bool ()) { - if (! merge_before_bool ()) { -# - // Straightforward implementation - sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); + // Straightforward implementation + sp.clear (); + db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); + db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); + + n = 0; + db::RecursiveShapeIterator s_a; + if (mp_job->has_tiles ()) { + s_a = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); } else { - - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); - - if (!la.empty ()) { - sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - merge_helper_cell.shapes (0), true, 0, false, true); - } - if (!lb.empty ()) { - sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - merge_helper_cell.shapes (1), true, 0, false, true); - } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); - + s_a = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la); } + s_a.set_for_merged_input (true); + for ( ; ! s_a.at_end (); ++s_a, ++n) { + sp.insert (s_a.shape (), dbu_scale_a * s_a.trans (), n * 2); + } + + n = 0; + db::RecursiveShapeIterator s_b; + if (mp_job->has_tiles ()) { + s_b = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); + } else { + s_b = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb); + } + s_b.set_for_merged_input (true); + for (; ! s_b.at_end (); ++s_b, ++n) { + sp.insert (s_b.shape (), dbu_scale_b * s_b.trans (), n * 2 + 1); + } + + db::BooleanOp bool_op (mp_job->op ()); + db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); + db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); + sp.process (out, bool_op); } else { - tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); - size_t n; + // This implementation is faster when a lot of overlapping shapes are involved + db::Layout merge_helper; + merge_helper.dbu (mp_job->dbu ()); + db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); + merge_helper.insert_layer (0); + merge_helper.insert_layer (1); - if (! merge_before_bool ()) { + // This implementation is faster when a lot of overlapping shapes are involved + if (!la.empty ()) { - // Straightforward implementation sp.clear (); - db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); - db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); + db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2); + db::RecursiveShapeIterator s; + if (mp_job->has_tiles ()) { + s = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); + } else { + s = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la); + } + s.set_for_merged_input (true); + for ( ; ! s.at_end (); ++s, ++n) { + sp.insert (s.shape (), dbu_scale * s.trans (), n); } - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1); - } - - db::BooleanOp bool_op (mp_job->op ()); - db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); + db::MergeOp op (0); + db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/); db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, bool_op); - - } else { - - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - merge_helper.dbu (mp_job->dbu ()); - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); - - // This implementation is faster when a lot of overlapping shapes are involved - if (!la.empty ()) { - - sp.clear (); - - db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); - - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale * s.trans (), n); - } - - db::MergeOp op (0); - db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/); - db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, op); - - } - - if (!lb.empty ()) { - - sp.clear (); - - db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); - - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale * s.trans (), n); - } - - db::MergeOp op (0); - db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/); - db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, op); - - } - - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); + sp.process (out, op); } + if (!lb.empty ()) { + + sp.clear (); + + db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); + + n = 0; + db::RecursiveShapeIterator s; + if (mp_job->has_tiles ()) { + s = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); + } else { + s = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb); + } + s.set_for_merged_input (true); + for ( ; ! s.at_end (); ++s, ++n) { + sp.insert (s.shape (), dbu_scale * s.trans (), n); + } + + db::MergeOp op (0); + db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/); + db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); + sp.process (out, op); + + } + + sp.boolean (merge_helper, merge_helper_cell, 0, + merge_helper, merge_helper_cell, 1, + xor_results_cell.shapes (0), mp_job->op (), true, false, true); + } } else if (mp_job->op () == db::BooleanOp::Xor || @@ -1005,6 +1001,8 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) dbu_scale = db::CplxTrans (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); } + s.set_for_merged_input (true); + for (; ! s.at_end (); ++s) { if (s->is_polygon () || s->is_box () || s->is_path ()) { db::Polygon p; From 40a8f21f9c3510b58d62a10c4f1e2388b4073a7b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2024 01:05:35 +0100 Subject: [PATCH 27/32] Simplified optimization as performance was bad. --- src/db/db/dbRecursiveShapeIterator.cc | 105 ++++++++++++++++++-------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 7fe8b99ed..60c9601cc 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -918,6 +918,64 @@ RecursiveShapeIterator::new_layer () const } } +static +RecursiveShapeIterator::box_type +shape_box (const RecursiveShapeIterator::shape_type &shape) +{ + if (shape.is_box ()) { + return shape.box (); + } + + switch (shape.type ()) { + case db::Shape::Polygon: + return shape.polygon ().is_box () ? shape.polygon ().box () : RecursiveShapeIterator::box_type (); + case db::Shape::PolygonRef: + case db::Shape::PolygonPtrArrayMember: + return shape.polygon_ref ().is_box () ? shape.polygon_ref ().box () : RecursiveShapeIterator::box_type (); + case db::Shape::SimplePolygon: + return shape.simple_polygon ().is_box () ? shape.simple_polygon ().box () : RecursiveShapeIterator::box_type (); + case db::Shape::SimplePolygonRef: + case db::Shape::SimplePolygonPtrArrayMember: + return shape.simple_polygon_ref ().is_box () ? shape.simple_polygon_ref ().box () : RecursiveShapeIterator::box_type (); + default: + return RecursiveShapeIterator::box_type (); + } +} + +static +RecursiveShapeIterator::box_type +subtract_box (const RecursiveShapeIterator::box_type &from, const RecursiveShapeIterator::box_type &box) +{ + RecursiveShapeIterator::box_type res (from); + if (box.empty ()) { + return res; + } + + if (! res.empty ()) { + if (box.bottom () <= res.bottom () && box.top () >= res.top ()) { + if (box.left () <= res.left ()) { + res.set_left (std::max (box.right (), res.left ())); + } + if (box.right () >= res.right ()) { + res.set_right (std::min (box.left (), res.right ())); + } + } + } + + if (! res.empty ()) { + if (box.left () <= res.left () && box.right () >= res.right ()) { + if (box.bottom () <= res.bottom ()) { + res.set_bottom (std::max (box.top (), res.bottom ())); + } + if (box.top () >= res.top ()) { + res.set_top (std::min (box.bottom (), res.top ())); + } + } + } + + return res; +} + void RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const { @@ -935,48 +993,29 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: // - single layer - // - less than 32 shapes to consider - // - total shape bbox in current region covers at least a third of it - // - total area of shapes in current region is at least a third of it + // - at least one shape to consider and it is a box + // - that box clips the region entirely on one side // // NOTE that this implementation can modify the search box on the box stack // because we did "new_layer()" already and this function is not going to // be called, because we do so only for single layers. - const box_type ®ion = m_local_region_stack.back (); + if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! m_shape.at_end ()) { - if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! region.empty ()) { - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - const shapes_type &shapes = cell ()->shapes (l); - - if (! shapes.empty () && shapes.size () < 32 && - 3 * (shapes.bbox () & region).area () > region.area ()) { - - region_type shapes_region (shapes); - if (3 * shapes_region.area (region) > region.area ()) { - - // Need to enlarge the empty area somewhat so we really exclude instances - // entirely enclosed by the shape - also the ones at the border. - box_type::vector_type bias; - if (! m_overlapping) { - bias = box_type::vector_type (1, 1); - } - - // reduce the search region for less instances to look up - // NOTE: because we use "touching" for the instances below, we - region_type new_complex_region; - if (region == box_type::world ()) { - new_complex_region = region_type (cell ()->bbox (l)) - shapes_region; - } else { - new_complex_region = region_type (cell ()->bbox (l) & region.enlarged (bias)) - shapes_region; - } - - // TODO: the current implementation does not touch the complex search region - m_local_region_stack.back () = new_complex_region.bbox ().enlarged (-bias); + box_type box = shape_box (*m_shape); + if (! box.empty ()) { + // Need to enlarge the empty area somewhat so we really exclude instances + // entirely enclosed by the shape - also the ones at the border. + if (! m_overlapping) { + box.enlarge (box_type::vector_type (1, 1)); } + const box_type ®ion = m_local_region_stack.back (); + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + box = subtract_box (cell ()->bbox (l) & region, box); + m_local_region_stack.back () = box; + } } From 3fc32e77c3f9b64da6a863938459874fd9a252b6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2024 19:15:50 +0100 Subject: [PATCH 28/32] Added full-circuit test for recursive shape iterator --- .../dbRecursiveShapeIteratorTests.cc | 166 +++++++++++++++++- 1 file changed, 164 insertions(+), 2 deletions(-) diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index c4fec7476..2719312bf 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -26,6 +26,10 @@ #include "dbLayoutDiff.h" #include "tlString.h" #include "tlUnitTest.h" +#include "tlFileUtils.h" +#include "tlStream.h" +#include "dbReader.h" +#include "dbWriter.h" #include @@ -1623,6 +1627,164 @@ TEST(12_ForMerged) i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); - - // ... } + + +static void write (const db::Region ®ion, const std::string &fn) +{ + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("TOP")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + region.insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + +TEST(13_ForMergedPerformance) +{ + test_is_long_runner (); + + std::string fn (tl::combine_path (tl::testdata_private (), "oasis/caravel.oas.gz")); + + db::Layout ly; + + { + tl::InputStream is (fn); + db::Reader reader (is); + reader.read (ly); + } + + unsigned l1 = ly.get_layer (db::LayerProperties (66, 20)); + unsigned l2 = ly.get_layer (db::LayerProperties (235, 4)); + + db::RecursiveShapeIterator si1 (ly, ly.cell (*ly.begin_top_down ()), l1); + db::RecursiveShapeIterator si2 (ly, ly.cell (*ly.begin_top_down ()), l2); + + { + tl::SelfTimer timer ("Standard loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (1218378)); + } + + { + tl::SelfTimer timer ("Standard loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (57462)); + } + + si1.set_for_merged_input (true); + si2.set_for_merged_input (true); + + { + tl::SelfTimer timer ("'for_merged' loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (1217072)); + } + + { + tl::SelfTimer timer ("'for_merged' loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (919)); + } + + si1.set_for_merged_input (false); + si1.set_region (db::Box (0, 0, 1000000, 1000000)); + si2.set_for_merged_input (false); + si2.set_region (db::Box (0, 0, 1000000, 1000000)); + + { + tl::SelfTimer timer ("Standard loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (218823)); + } + + { + tl::SelfTimer timer ("Standard loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (2578)); + } + + si1.set_for_merged_input (true); + si2.set_for_merged_input (true); + + { + tl::SelfTimer timer ("'for_merged' loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (218736)); + } + + { + tl::SelfTimer timer ("'for_merged' loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (1)); + } + + { + tl::SelfTimer timer ("XOR on tile of 66/20"); + si1.set_for_merged_input (false); + db::Region r1 (si1); + si1.set_for_merged_input (true); + db::Region r2 (si1); + + EXPECT_EQ (r1.count (), size_t (218823)); + EXPECT_EQ (r2.count (), size_t (218736)); + EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); + } + + { + tl::SelfTimer timer ("XOR on tile of 235/4"); + si2.set_for_merged_input (false); + db::Region r1 (si2); + si2.set_for_merged_input (true); + db::Region r2 (si2); + + EXPECT_EQ (r1.count (), size_t (2578)); + EXPECT_EQ (r2.count (), size_t (1)); + EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); + } +} + From 5699c91d3f1ad7244d374fc190320e51074ce1d0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2024 21:48:38 +0100 Subject: [PATCH 29/32] Some utility features derived from the latest code changes - Subtraction of boxes (pya/RBA: Box minus operator) - Shape#rectangle, Shape#drectangle - EdgePairs#write, Edges#write, Texts#write, Region#write for debugging --- src/db/db/dbBox.h | 82 ++++++++++++++++++ src/db/db/dbEdgePairs.cc | 20 +++++ src/db/db/dbEdgePairs.h | 8 ++ src/db/db/dbEdges.cc | 20 +++++ src/db/db/dbEdges.h | 8 ++ src/db/db/dbRecursiveShapeIterator.cc | 62 +------------ src/db/db/dbRegion.cc | 20 +++++ src/db/db/dbRegion.h | 8 ++ src/db/db/dbShape.cc | 55 ++++++++++++ src/db/db/dbShape.h | 10 +++ src/db/db/dbTexts.cc | 21 ++++- src/db/db/dbTexts.h | 8 ++ src/db/db/gsiDeclDbBox.cc | 15 ++++ src/db/db/gsiDeclDbEdgePairs.cc | 6 ++ src/db/db/gsiDeclDbEdges.cc | 6 ++ src/db/db/gsiDeclDbRegion.cc | 6 ++ src/db/db/gsiDeclDbShape.cc | 36 ++++++++ src/db/db/gsiDeclDbTexts.cc | 6 ++ src/db/unit_tests/dbBoxTests.cc | 11 +++ .../dbRecursiveShapeIteratorTests.cc | 14 --- src/db/unit_tests/dbShapeTests.cc | 86 +++++++++++++++++++ testdata/ruby/dbBoxTest.rb | 3 + testdata/ruby/dbShapesTest.rb | 4 + 23 files changed, 440 insertions(+), 75 deletions(-) diff --git a/src/db/db/dbBox.h b/src/db/db/dbBox.h index 94e2ac58c..cf7d4b233 100644 --- a/src/db/db/dbBox.h +++ b/src/db/db/dbBox.h @@ -254,6 +254,27 @@ struct DB_PUBLIC_TEMPLATE box */ box &operator+= (const point &p); + /** + * @brief Subtraction of boxes. + * + * The -= operator subtracts the argument box from *this. + * Subtraction leaves the bounding box of the region resulting + * from the geometrical NOT of *this and the argument box. + * Subtracting a box from itself gives an empty box. + * Subtracting a box that does not cover a full side of + * *this will not modify the box. + * + * @param b The box to subtract from *this. + * + * @return The result box. + */ + box &operator-= (const box &b); + + /** + * @brief A method version for operator- (mainly for automation purposes) + */ + box subtracted (const box &b) const; + /** * @brief Intersection of boxes. * @@ -784,6 +805,50 @@ box::operator+= (const point &p) return *this; } +template +inline box +box::subtracted (const box &b) const +{ + box r (*this); + r -= b; + return r; +} + +template +inline box & +box::operator-= (const box &bx) +{ + if (bx.empty () || empty ()) { + return *this; + } + + coord_type l = m_p1.x (), r = m_p2.x (); + coord_type b = m_p1.y (), t = m_p2.y (); + + if (bx.bottom () <= bottom () && bx.top () >= top ()) { + if (bx.left () <= left ()) { + l = std::max (bx.right (), left ()); + } + if (bx.right () >= right ()) { + r = std::min (bx.left (), right ()); + } + } + + if (bx.left () <= left () && bx.right () >= right ()) { + if (bx.bottom () <= bottom ()) { + b = std::max (bx.top (), bottom ()); + } + if (bx.top () >= top ()) { + t = std::min (bx.bottom (), top ()); + } + } + + m_p1 = point_type (l, b); + m_p2 = point_type (r, t); + + return *this; +} + template inline box & box::operator&= (const box &b) @@ -1363,6 +1428,23 @@ operator+ (const box &b1, const box &b2) return bb; } +/** + * @brief Box subtraction mapped on the - operator + * + * @param b1 The first box + * @param b2 The second box to subtract from the first + * + * @return The bounding box of the region formed but subtracting b2 from b1 + */ +template +inline box +operator- (const box &b1, const box &b2) +{ + box bb (b1); + bb -= b2; + return bb; +} + /** * @brief "Folding" of two boxes * diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index fcfd405f9..6e0d9059e 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -30,6 +30,9 @@ #include "dbOriginalLayerEdgePairs.h" #include "dbEdges.h" #include "dbRegion.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlVariant.h" @@ -93,6 +96,23 @@ EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, con mp_delegate = new DeepEdgePairs (si, dss, trans); } +void +EdgePairs::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("EDGE_PAIRS")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + template void EdgePairs::insert (const Sh &shape) { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 9d1b1aee2..53a499689 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -185,6 +185,14 @@ public: */ explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Writes the edge pair collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index e5a6cf007..5cdefe414 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -28,6 +28,9 @@ #include "dbFlatEdges.h" #include "dbEdgesUtils.h" #include "dbRegion.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" namespace db { @@ -141,6 +144,23 @@ Edges::set_delegate (EdgesDelegate *delegate, bool keep_attributes) } } +void +Edges::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("EDGES")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + void Edges::clear () { diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 28decbbb6..ffb8ab6ff 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -833,6 +833,14 @@ public: return *this; } + /** + * @brief Writes the edge collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Intersections with other edges * Intersections are similar to "AND", but will also report diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 60c9601cc..c918068ff 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -918,64 +918,6 @@ RecursiveShapeIterator::new_layer () const } } -static -RecursiveShapeIterator::box_type -shape_box (const RecursiveShapeIterator::shape_type &shape) -{ - if (shape.is_box ()) { - return shape.box (); - } - - switch (shape.type ()) { - case db::Shape::Polygon: - return shape.polygon ().is_box () ? shape.polygon ().box () : RecursiveShapeIterator::box_type (); - case db::Shape::PolygonRef: - case db::Shape::PolygonPtrArrayMember: - return shape.polygon_ref ().is_box () ? shape.polygon_ref ().box () : RecursiveShapeIterator::box_type (); - case db::Shape::SimplePolygon: - return shape.simple_polygon ().is_box () ? shape.simple_polygon ().box () : RecursiveShapeIterator::box_type (); - case db::Shape::SimplePolygonRef: - case db::Shape::SimplePolygonPtrArrayMember: - return shape.simple_polygon_ref ().is_box () ? shape.simple_polygon_ref ().box () : RecursiveShapeIterator::box_type (); - default: - return RecursiveShapeIterator::box_type (); - } -} - -static -RecursiveShapeIterator::box_type -subtract_box (const RecursiveShapeIterator::box_type &from, const RecursiveShapeIterator::box_type &box) -{ - RecursiveShapeIterator::box_type res (from); - if (box.empty ()) { - return res; - } - - if (! res.empty ()) { - if (box.bottom () <= res.bottom () && box.top () >= res.top ()) { - if (box.left () <= res.left ()) { - res.set_left (std::max (box.right (), res.left ())); - } - if (box.right () >= res.right ()) { - res.set_right (std::min (box.left (), res.right ())); - } - } - } - - if (! res.empty ()) { - if (box.left () <= res.left () && box.right () >= res.right ()) { - if (box.bottom () <= res.bottom ()) { - res.set_bottom (std::max (box.top (), res.bottom ())); - } - if (box.top () >= res.top ()) { - res.set_top (std::min (box.bottom (), res.top ())); - } - } - } - - return res; -} - void RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const { @@ -1002,7 +944,7 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! m_shape.at_end ()) { - box_type box = shape_box (*m_shape); + box_type box = m_shape->rectangle (); if (! box.empty ()) { // Need to enlarge the empty area somewhat so we really exclude instances @@ -1013,7 +955,7 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const const box_type ®ion = m_local_region_stack.back (); unsigned int l = m_has_layers ? m_layers.front () : m_layer; - box = subtract_box (cell ()->bbox (l) & region, box); + box = (cell ()->bbox (l) & region) - box; m_local_region_stack.back () = box; } diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index c05f9d338..2d04a2758 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -31,6 +31,9 @@ #include "dbFlatEdges.h" #include "dbPolygonTools.h" #include "dbCompoundOperation.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlGlobPattern.h" // NOTE: include this to provide the symbols for "make_variant" @@ -129,6 +132,23 @@ Region::Region (DeepShapeStore &dss) mp_delegate = new db::DeepRegion (db::DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); } +void +Region::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("REGION")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + const db::RecursiveShapeIterator & Region::iter () const { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 1329adcc8..145018ef0 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -248,6 +248,14 @@ public: */ explicit Region (DeepShapeStore &dss); + /** + * @brief Writes the region to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index 47fc28776..e5c1d7820 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -807,6 +807,61 @@ Shape::box_type Shape::bbox () const } } +Shape::box_type Shape::rectangle () const +{ + if (is_box ()) { + return box (); + } + + switch (m_type) { + case db::Shape::Polygon: + return polygon ().is_box () ? polygon ().box () : box_type (); + case db::Shape::PolygonRef: + case db::Shape::PolygonPtrArrayMember: + return polygon_ref ().is_box () ? polygon_ref ().box () : box_type (); + case db::Shape::SimplePolygon: + return simple_polygon ().is_box () ? simple_polygon ().box () : box_type (); + case db::Shape::SimplePolygonRef: + case db::Shape::SimplePolygonPtrArrayMember: + return simple_polygon_ref ().is_box () ? simple_polygon_ref ().box () : box_type (); + case db::Shape::Path: + { + const path_type &p = path (); + if (! p.round () && p.points () <= 2 && p.points () > 0) { + point_type p1 = *p.begin (); + point_type p2 = p1; + if (p.points () == 2) { + p2 = *++p.begin (); + } + if (p1.x () == p2.x () || p1.y () == p2.y ()) { + return p.box (); + } + } + } + break; + case db::Shape::PathRef: + case db::Shape::PathPtrArrayMember: + { + const path_ref_type &p = path_ref (); + if (! p.ptr ()->round () && p.ptr ()->points () <= 2 && p.ptr ()->points () > 0) { + point_type p1 = *p.begin (); + point_type p2 = p1; + if (p.ptr ()->points () == 2) { + p2 = *++p.begin (); + } + if (p1.x () == p2.x () || p1.y () == p2.y ()) { + return p.box (); + } + } + } + break; + default: + break; + } + + return box_type (); +} + std::string Shape::to_string () const { diff --git a/src/db/db/dbShape.h b/src/db/db/dbShape.h index 5ab336667..dd003b47e 100644 --- a/src/db/db/dbShape.h +++ b/src/db/db/dbShape.h @@ -2651,6 +2651,16 @@ public: */ box_type bbox () const; + /** + * @brief Returns the box if the object represents a rectangle or an empty box if not + * + * This method returns the rectangle (aka box) the shape represents a polygon + * that is a rectangle, a path with two points and no rounded ends or an actual box. + * + * If not, an empty box is returned. + */ + box_type rectangle () const; + /** * @brief Compute the area of the shape */ diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 0b90c8897..388c89696 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -30,7 +30,9 @@ #include "dbOriginalLayerTexts.h" #include "dbEdges.h" #include "dbRegion.h" - +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlVariant.h" #include @@ -90,6 +92,23 @@ Texts::Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::I mp_delegate = new DeepTexts (si, dss, trans); } +void +Texts::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("TEXTS")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + template void Texts::insert (const Sh &shape) { diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 82d017187..cacda8589 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -181,6 +181,14 @@ public: */ explicit Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Writes the text collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index 50dc0d883..d2c5fcc8f 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -324,6 +324,21 @@ struct box_defs "\n" "@return The joined box\n" ) + + method ("-", &C::subtracted, gsi::arg ("box"), + "@brief Subtraction of boxes\n" + "\n" + "\n" + "The - operator subtracts the argument box from self.\n" + "This will return the bounding box of the are covered by self, but not by argument box. " + "Subtracting a box from itself will render an empty box. Subtracting another box from " + "self will modify the first box only if the argument box covers one side entirely.\n" + "\n" + "@param box The box to subtract from this box.\n" + "\n" + "@return The result box\n" + "\n" + "This feature has been introduced in version 0.29." + ) + method ("&", &C::intersection, gsi::arg ("box"), "@brief Returns the intersection of this box with another box\n" "\n" diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 4a22f7045..b4f6b5447 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -602,6 +602,12 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This constructor has been introduced in version 0.26." ) + + method ("write", &db::EdgePairs::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("insert_into", &db::EdgePairs::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Inserts this edge pairs into the given layout, below the given cell and into the given layer.\n" "If the edge pair collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index d325dfa26..5fc83ec49 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -1567,6 +1567,12 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been added in version 0.28.\n" ) + + method ("write", &db::Edges::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("clear", &db::Edges::clear, "@brief Clears the edge collection\n" ) + diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index ad941cc27..c5d6c6570 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1221,6 +1221,12 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.26." ) + + method ("write", &db::Region::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + factory_ext ("texts", &texts_as_boxes1, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), gsi::arg ("enl", 1), "@hide\n" "This method is provided for DRC implementation only." diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index c6671d9b7..68f6ff4fb 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -669,6 +669,26 @@ static tl::Variant get_dbox (const db::Shape *s) } } +static tl::Variant get_rectangle (const db::Shape *s) +{ + db::Shape::box_type b = s->rectangle (); + if (! b.empty ()) { + return tl::Variant (b); + } else { + return tl::Variant (); + } +} + +static tl::Variant get_drectangle (const db::Shape *s) +{ + db::Shape::box_type b = s->rectangle (); + if (! b.empty ()) { + return tl::Variant (db::CplxTrans (shape_dbu (s)) * b); + } else { + return tl::Variant (); + } +} + static tl::Variant get_edge (const db::Shape *s) { db::Shape::edge_type p; @@ -1982,6 +2002,22 @@ Class decl_Shape ("db", "Shape", "\n" "This method has been added in version 0.25.\n" ) + + gsi::method_ext ("rectangle", &get_rectangle, + "@brief Gets the rectangle if the object represents one or nil if not\n" + "\n" + "If the shape represents a rectangle - i.e. a box or box polygon, a path with two points and no round ends - " + "this method returns the box. If not, nil is returned.\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method_ext ("drectangle", &get_drectangle, + "@brief Gets the rectangle in micron units if the object represents one or nil if not\n" + "\n" + "If the shape represents a rectangle - i.e. a box or box polygon, a path with two points and no round ends - " + "this method returns the box. If not, nil is returned.\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method ("is_user_object?", &db::Shape::is_user_object, "@brief Returns true if the shape is a user defined object\n" ) + diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 8be9238b8..359249795 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -436,6 +436,12 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "r = RBA::Texts::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + method ("write", &db::Texts::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("insert_into", &db::Texts::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Inserts this texts into the given layout, below the given cell and into the given layer.\n" "If the text collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " diff --git a/src/db/unit_tests/dbBoxTests.cc b/src/db/unit_tests/dbBoxTests.cc index d70e942fc..1ac60f799 100644 --- a/src/db/unit_tests/dbBoxTests.cc +++ b/src/db/unit_tests/dbBoxTests.cc @@ -49,6 +49,17 @@ TEST(2) EXPECT_EQ (b & db::Box (110, 220, 120, 250), empty); EXPECT_EQ (b & db::Box (50, 100, 120, 250), db::Box (50, 100, 100, 200)); EXPECT_EQ (b & db::Box (50, 100, 60, 120), db::Box (50, 100, 60, 120)); + EXPECT_EQ (b - b, db::Box ()); + EXPECT_EQ (b - db::Box (), b); + EXPECT_EQ (db::Box () - b, db::Box ()); + EXPECT_EQ (db::Box () - db::Box (), db::Box ()); + EXPECT_EQ (b - db::Box (0, 0, 50, 50), b); + EXPECT_EQ (b - db::Box (0, 0, 50, 200), db::Box (50, 0, 100, 200)); + EXPECT_EQ (b - db::Box (50, 0, 100, 200), db::Box (0, 0, 50, 200)); + EXPECT_EQ (b - db::Box (0, 0, 100, 100), db::Box (0, 100, 100, 200)); + EXPECT_EQ (b - db::Box (0, 100, 100, 200), db::Box (0, 0, 100, 100)); + EXPECT_EQ (db::Box::world () - b, db::Box::world ()); + EXPECT_EQ (b - db::Box::world (), db::Box ()); empty.move (db::Vector (10, 20)); EXPECT_EQ (empty == db::Box (), true); diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 2719312bf..4dda034db 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1630,20 +1630,6 @@ TEST(12_ForMerged) } -static void write (const db::Region ®ion, const std::string &fn) -{ - db::Layout layout; - const db::Cell &top = layout.cell (layout.add_cell ("TOP")); - unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); - region.insert_into (&layout, top.cell_index (), li); - - tl::OutputStream os (fn); - db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); - db::Writer writer (opt); - writer.write (layout, os); -} - TEST(13_ForMergedPerformance) { test_is_long_runner (); diff --git a/src/db/unit_tests/dbShapeTests.cc b/src/db/unit_tests/dbShapeTests.cc index b6f8b514c..1d4a19003 100644 --- a/src/db/unit_tests/dbShapeTests.cc +++ b/src/db/unit_tests/dbShapeTests.cc @@ -948,3 +948,89 @@ TEST(9) EXPECT_EQ (si.at_end (), true); } +// Rectangle +TEST(10) +{ + db::Manager m (true); + db::Shapes s (&m, 0, db::default_editable_mode ()); + db::ShapeIterator si; + + s.insert (db::Point (100, 200)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Edge (db::Point (100, 200), db::Point (200, 400))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::EdgePair (db::Edge (db::Point (100, 200), db::Point (200, 400)), db::Edge (db::Point (0, 300), db::Point (100, 500)))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Box (0, 0, 1000, 2000)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::ShortBox (0, 0, 1000, 2000)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::Polygon (db::Box (0, 0, 1000, 2000))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::Polygon ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::SimplePolygon ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + db::Point pts1 [1] = { db::Point (0, 0) }; + db::Point pts2 [2] = { db::Point (0, 0), db::Point (1000, 0) }; + db::Point pts2b [2] = { db::Point (0, 0), db::Point (1000, 1000) }; + db::Point pts3 [3] = { db::Point (0, 0), db::Point (1000, 0), db::Point (1000, 1000) }; + + s.clear (); + s.insert (db::Path (pts1 + 0, pts1 + 1, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (-500, -500, 500, 500)); + + s.clear (); + s.insert (db::Path (pts2 + 0, pts2 + 2, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (-500, -500, 1500, 500)); + + s.clear (); + s.insert (db::Path (pts2 + 0, pts2 + 2, 1000, 500, 500, true)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path (pts2b + 0, pts2b + 2, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path (pts3 + 0, pts3 + 3, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); +} diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index 8e9a916e9..1a207ef81 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -146,6 +146,9 @@ class DBBox_TestClass < TestBase assert_equal( a + b, b ) assert_equal( (b + c).to_s, "(1,-10;22,22)" ) + assert_equal( b - a, b ) + assert_equal( (b - c).to_s, "(1,-1;17,22)" ) + assert_equal( a + RBA::DPoint::new( 1, -5 ), RBA::DBox::new( 1, -5, 1, -5 ) ) assert_equal( (b + RBA::DPoint::new( 1, -5 )).to_s, "(1,-5;17,22)" ) diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 3417c8688..bca7672cf 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -178,6 +178,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].is_polygon?, false ) assert_equal( arr[0].is_box?, true ) assert_equal( arr[0].box.to_s, "(10,-10;50,40)" ) + assert_equal( arr[0].rectangle.to_s, "(10,-10;50,40)" ) assert_equal( arr[0].bbox.to_s, "(10,-10;50,40)" ) # edges @@ -198,6 +199,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].edge.to_s, "(-1,2;5,2)" ) assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) + assert_equal( arr[0].rectangle.inspect, "nil" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) assert_equal( arr[0].edge == a, true ) @@ -533,6 +535,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dedge.inspect, "nil" ) assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.to_s, "(0.01,-0.01;0.05,0.04)" ) + assert_equal( arr[0].drectangle.to_s, "(0.01,-0.01;0.05,0.04)" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) assert_equal( arr[0].is_polygon?, false ) @@ -557,6 +560,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dedge.to_s, "(-0.001,0.002;0.005,0.002)" ) assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) + assert_equal( arr[0].drectangle.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) assert_equal( arr[0].dbbox.to_s, "(-0.001,0.002;0.005,0.002)" ) From cb5a1f7d3edbd413aeb3abc3858ad9fde264d95f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 27 Mar 2024 23:46:33 +0100 Subject: [PATCH 30/32] Refining shape iterator optimization, so it checks instances for overlap with shapes rather the other way round. This suits better to real test cases. --- src/buddies/src/bd/strmxor.cc | 10 +++- src/db/db/dbRecursiveShapeIterator.cc | 56 +++++++++---------- .../dbRecursiveShapeIteratorTests.cc | 12 ++-- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 9bd72e860..de1f265e4 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -680,13 +680,17 @@ bool run_tiled_xor (const XORData &xor_data) if (ll->second.first < 0) { proc.input (in_a, db::RecursiveShapeIterator ()); } else { - proc.input (in_a, db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first)); + db::RecursiveShapeIterator si (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); + si.set_for_merged_input (true); + proc.input (in_a, si); } if (ll->second.second < 0) { proc.input (in_b, db::RecursiveShapeIterator ()); } else { - proc.input (in_b, db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second)); + db::RecursiveShapeIterator si (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); + si.set_for_merged_input (true); + proc.input (in_b, si); } std::string expr = "var x=" + in_a + "^" + in_b + "; "; @@ -805,10 +809,12 @@ bool run_deep_xor (const XORData &xor_data) if (ll->second.first >= 0) { ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); + ri_a.set_for_merged_input (true); } if (ll->second.second >= 0) { ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); + ri_b.set_for_merged_input (true); } db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu)); diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index c918068ff..b86f5d010 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -933,35 +933,6 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_layer (); - // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: - // - single layer - // - at least one shape to consider and it is a box - // - that box clips the region entirely on one side - // - // NOTE that this implementation can modify the search box on the box stack - // because we did "new_layer()" already and this function is not going to - // be called, because we do so only for single layers. - - if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! m_shape.at_end ()) { - - box_type box = m_shape->rectangle (); - if (! box.empty ()) { - - // Need to enlarge the empty area somewhat so we really exclude instances - // entirely enclosed by the shape - also the ones at the border. - if (! m_overlapping) { - box.enlarge (box_type::vector_type (1, 1)); - } - - const box_type ®ion = m_local_region_stack.back (); - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - box = (cell ()->bbox (l) & region) - box; - m_local_region_stack.back () = box; - - } - - } - if (m_overlapping) { m_inst = cell ()->begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1))); } else { @@ -994,6 +965,33 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const } } + if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1)) { + + // Try some optimization: if the instance we're looking at is entirely covered + // by a rectangle (other objects are too expensive to check), then wil skip it + // + // We check 10 shapes max. + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + box_type inst_bx = m_inst->bbox (m_box_convert); + auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); + bool skip = false; + size_t nmax = 10; + while (! skip && ! si.at_end () && nmax-- > 0) { + if (inst_bx.inside (si->rectangle ())) { + skip = true; + break; + } + ++si; + } + + if (skip) { + ++m_inst; + continue; + } + + } + bool all_of_instance = false; bool with_region = false; diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 4dda034db..0e4a7430a 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1683,7 +1683,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (1217072)); + EXPECT_EQ (n, size_t (1212844)); } { @@ -1694,7 +1694,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 235/4"; - EXPECT_EQ (n, size_t (919)); + EXPECT_EQ (n, size_t (10)); } si1.set_for_merged_input (false); @@ -1735,7 +1735,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (218736)); + EXPECT_EQ (n, size_t (218552)); } { @@ -1746,7 +1746,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 235/4"; - EXPECT_EQ (n, size_t (1)); + EXPECT_EQ (n, size_t (2)); } { @@ -1757,7 +1757,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si1); EXPECT_EQ (r1.count (), size_t (218823)); - EXPECT_EQ (r2.count (), size_t (218736)); + EXPECT_EQ (r2.count (), size_t (218552)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } @@ -1769,7 +1769,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si2); EXPECT_EQ (r1.count (), size_t (2578)); - EXPECT_EQ (r2.count (), size_t (1)); + EXPECT_EQ (r2.count (), size_t (2)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } } From 7080ed9a0c4e5b20bed5adb3b1fd6925fe8d41d7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 28 Mar 2024 20:57:10 +0100 Subject: [PATCH 31/32] Refined XOR optimization solution such that it is compatible with deep mode and 'wants_all_cells', added more tests --- src/db/db/dbRecursiveShapeIterator.cc | 87 +++++++++++------- src/db/db/dbRecursiveShapeIterator.h | 1 + src/db/unit_tests/dbHierProcessorTests.cc | 88 +++++++++++++++++++ .../dbRecursiveShapeIteratorTests.cc | 6 +- 4 files changed, 148 insertions(+), 34 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index b86f5d010..cc02f75a9 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -82,6 +82,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_cells = d.m_cells; m_local_complex_region_stack = d.m_local_complex_region_stack; m_local_region_stack = d.m_local_region_stack; + m_skip_shapes_stack = d.m_skip_shapes_stack; m_needs_reinit = d.m_needs_reinit; m_inst_quad_id = d.m_inst_quad_id; m_inst_quad_id_stack = d.m_inst_quad_id_stack; @@ -462,6 +463,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const m_local_region_stack.clear (); m_local_region_stack.push_back (m_global_trans.inverted () * m_region); + m_skip_shapes_stack.clear (); + m_skip_shapes_stack.push_back (false); m_local_complex_region_stack.clear (); if (mp_complex_region.get ()) { @@ -736,9 +739,23 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const } - if (is_empty || !down (receiver)) { + if (is_empty) { + + // skip entire cell ++m_inst; new_inst (receiver); + + } else if (!down (receiver)) { + + // skip this instance array member + ++m_inst_array; + new_inst_member (receiver); + + if (m_inst_array.at_end ()) { + ++m_inst; + new_inst (receiver); + } + } } else { @@ -769,6 +786,39 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const bool RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { + bool skip_shapes = false; + + if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) { + + // Try some optimization: if the instance we're looking at is entirely covered + // by a rectangle (other objects are too expensive to check), then we skip it + // + // We check 10 shapes max. + + box_type inst_bx; + if (m_inst->size () == 1) { + inst_bx = m_inst->bbox (m_box_convert); + } else { + inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); + } + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); + size_t nmax = 10; + while (! si.at_end () && nmax-- > 0) { + if (inst_bx.inside (si->rectangle ())) { + skip_shapes = true; + break; + } + ++si; + } + + } + + if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) { + return false; + } + tl_assert (mp_layout); m_trans_stack.push_back (m_trans); @@ -796,6 +846,7 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } m_local_region_stack.push_back (new_region); + m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes); if (! m_local_complex_region_stack.empty ()) { @@ -878,6 +929,7 @@ RecursiveShapeIterator::pop () const mp_cell = m_cells.back (); m_cells.pop_back (); m_local_region_stack.pop_back (); + m_skip_shapes_stack.pop_back (); if (! m_local_complex_region_stack.empty ()) { m_local_complex_region_stack.pop_back (); } @@ -902,7 +954,7 @@ RecursiveShapeIterator::start_shapes () const void RecursiveShapeIterator::new_layer () const { - if (int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { + if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { m_shape = shape_iterator (); } else if (! m_overlapping) { m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); @@ -942,7 +994,7 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const m_inst_quad_id = 0; // skip instance quad if possible - if (! m_local_complex_region_stack.empty ()) { + if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) { skip_inst_iter_for_complex_region (); } @@ -958,40 +1010,13 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const while (! m_inst.at_end ()) { // skip instance quad if possible - if (! m_local_complex_region_stack.empty ()) { + if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) { skip_inst_iter_for_complex_region (); if (m_inst.at_end ()) { break; } } - if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1)) { - - // Try some optimization: if the instance we're looking at is entirely covered - // by a rectangle (other objects are too expensive to check), then wil skip it - // - // We check 10 shapes max. - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - box_type inst_bx = m_inst->bbox (m_box_convert); - auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); - bool skip = false; - size_t nmax = 10; - while (! skip && ! si.at_end () && nmax-- > 0) { - if (inst_bx.inside (si->rectangle ())) { - skip = true; - break; - } - ++si; - } - - if (skip) { - ++m_inst; - continue; - } - - } - bool all_of_instance = false; bool with_region = false; diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index b8e6a640a..532fdd247 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -867,6 +867,7 @@ private: mutable std::vector m_cells; mutable std::vector m_local_complex_region_stack; mutable std::vector m_local_region_stack; + mutable std::vector m_skip_shapes_stack; mutable bool m_needs_reinit; mutable size_t m_inst_quad_id; mutable std::vector m_inst_quad_id_stack; diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 23833d2ff..4679c5951 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -23,6 +23,7 @@ #include "tlUnitTest.h" #include "tlStream.h" +#include "tlFileUtils.h" #include "dbHierProcessor.h" #include "dbTestSupport.h" #include "dbReader.h" @@ -32,6 +33,9 @@ #include "dbLocalOperationUtils.h" #include "dbRegionLocalOperations.h" #include "dbPolygon.h" +#include "dbRecursiveInstanceIterator.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" static std::string testdata (const std::string &fn) { @@ -1284,3 +1288,87 @@ TEST(Arrays) run_test_bool2 (_this, "hlp18.oas", TMNot, 100); } +TEST(XORTool) +{ + test_is_long_runner (); + + std::string fna (tl::combine_path (tl::testdata_private (), "xor/a.gds.gz")); + std::string fnb (tl::combine_path (tl::testdata_private (), "xor/b.gds.gz")); + std::string fn_au (tl::combine_path (tl::testdata_private (), "xor/xor_au.oas.gz")); + + db::Layout lya, lyb; + + unsigned int l1, l2; + + db::LayerMap lmap; + + lmap.map (db::LDPair (1, 0), l1 = lya.insert_layer ()); + lyb.insert_layer (); + + lmap.map (db::LDPair (2, 0), l2 = lya.insert_layer ()); + lyb.insert_layer (); + + { + tl::InputStream stream (fna); + db::Reader reader (stream); + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (lya, options); + } + + { + tl::InputStream stream (fnb); + db::Reader reader (stream); + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (lyb, options); + } + + db::Layout ly_out; + db::cell_index_type top_out = ly_out.add_cell ("TOP"); + unsigned int l1_out = ly_out.insert_layer (db::LayerProperties (1, 0)); + unsigned int l2_out = ly_out.insert_layer (db::LayerProperties (2, 0)); + + db::DeepShapeStore dss; + dss.set_wants_all_cells (true); // saves time for less cell mapping operations + + { + db::RecursiveShapeIterator ri_a, ri_b; + + ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l1); + ri_a.set_for_merged_input (true); + + ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l1); + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, dss, db::ICplxTrans (1.0)); + db::Region in_b (ri_b, dss, db::ICplxTrans (1.0)); + + db::Region xor_res = in_a ^ in_b; + EXPECT_EQ (xor_res.count (), size_t (12)); + + xor_res.insert_into (&ly_out, top_out, l1_out); + } + + { + db::RecursiveShapeIterator ri_a, ri_b; + + ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l2); + ri_a.set_for_merged_input (true); + + ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l2); + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, dss, db::ICplxTrans (1.0)); + db::Region in_b (ri_b, dss, db::ICplxTrans (1.0)); + + db::Region xor_res = in_a ^ in_b; + EXPECT_EQ (xor_res.count (), size_t (15984)); + + xor_res.insert_into (&ly_out, top_out, l2_out); + } + + db::compare_layouts (_this, ly_out, fn_au, db::WriteOAS); +} diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 0e4a7430a..4c109fef0 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1683,7 +1683,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (1212844)); + EXPECT_EQ (n, size_t (1203078)); } { @@ -1735,7 +1735,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (218552)); + EXPECT_EQ (n, size_t (218069)); } { @@ -1757,7 +1757,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si1); EXPECT_EQ (r1.count (), size_t (218823)); - EXPECT_EQ (r2.count (), size_t (218552)); + EXPECT_EQ (r2.count (), size_t (218069)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } From e0e6017a80686bf478a7dfba1ef4829b136710dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 28 Mar 2024 21:06:00 +0100 Subject: [PATCH 32/32] Need to differentiate test results between editable and non-editable mode --- .../dbRecursiveShapeIteratorTests.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 4c109fef0..6184176db 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1650,6 +1650,9 @@ TEST(13_ForMergedPerformance) db::RecursiveShapeIterator si1 (ly, ly.cell (*ly.begin_top_down ()), l1); db::RecursiveShapeIterator si2 (ly, ly.cell (*ly.begin_top_down ()), l2); + size_t n1_expected_full = db::default_editable_mode () ? 1203072 : 1203078; + size_t n2_expected_full = 10; + { tl::SelfTimer timer ("Standard loop on 66/20"); size_t n = 0; @@ -1683,7 +1686,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (1203078)); + EXPECT_EQ (n, size_t (n1_expected_full)); } { @@ -1694,7 +1697,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 235/4"; - EXPECT_EQ (n, size_t (10)); + EXPECT_EQ (n, size_t (n2_expected_full)); } si1.set_for_merged_input (false); @@ -1727,6 +1730,9 @@ TEST(13_ForMergedPerformance) si1.set_for_merged_input (true); si2.set_for_merged_input (true); + size_t n1_expected = db::default_editable_mode () ? 218068 : 218069; + size_t n2_expected = 2; + { tl::SelfTimer timer ("'for_merged' loop on 66/20"); size_t n = 0; @@ -1735,7 +1741,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (218069)); + EXPECT_EQ (n, size_t (n1_expected)); } { @@ -1746,7 +1752,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 235/4"; - EXPECT_EQ (n, size_t (2)); + EXPECT_EQ (n, size_t (n2_expected)); } { @@ -1757,7 +1763,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si1); EXPECT_EQ (r1.count (), size_t (218823)); - EXPECT_EQ (r2.count (), size_t (218069)); + EXPECT_EQ (r2.count (), size_t (n1_expected)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } @@ -1769,7 +1775,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si2); EXPECT_EQ (r1.count (), size_t (2578)); - EXPECT_EQ (r2.count (), size_t (2)); + EXPECT_EQ (r2.count (), size_t (n2_expected)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } }