From 4b79c4c362c4f65381e694f365c6baea60503e4e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 1 Aug 2025 22:59:38 +0200 Subject: [PATCH] Fixing backward compatibility issue with pseudo-labels Problem: pseudo-labels are represented by polygons with properties. If those get merged onto normal polygons with "join_properties_on_merge", these are regarded labels and translated to texts later. We avoid this by skipping all objects with pseudo-label properties on region merge in "join_properties_on_merge" mode. --- src/db/db/dbDeepRegion.cc | 52 ++++++++- src/db/unit_tests/dbDeepRegionTests.cc | 143 +++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 3546efd82..09ecc2bc4 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -525,7 +525,7 @@ class ClusterMerger { public: ClusterMerger (unsigned int layer, db::Layout &layout, const db::hier_clusters &hc, bool min_coherence, bool report_progress, const std::string &progress_desc) - : m_layer (layer), mp_layout (&layout), mp_hc (&hc), m_min_coherence (min_coherence), m_ep (report_progress, progress_desc) + : m_layer (layer), mp_layout (&layout), mp_hc (&hc), m_min_coherence (min_coherence), m_text_name_id (0), m_ep (report_progress, progress_desc) { // .. nothing yet .. } @@ -535,6 +535,19 @@ public: m_ep.set_base_verbosity (vb); } + /** + * @brief Specifies the label property name ID for pseudo-label polygons + * If set to 0, the merge does not skip pseudo-labels. + */ + void set_text_name (const tl::Variant &n) + { + if (n.is_nil ()) { + m_text_name_id = 0; + } else { + m_text_name_id = db::property_names_id (n); + } + } + db::Shapes &merged (size_t cid, db::cell_index_type ci, unsigned int min_wc = 0) { return compute_merged (cid, ci, true, min_wc); @@ -553,8 +566,19 @@ private: db::Layout *mp_layout; const db::hier_clusters *mp_hc; bool m_min_coherence; + db::property_names_id_type m_text_name_id; db::EdgeProcessor m_ep; + bool skip_pseudo_label (db::properties_id_type prop_id) + { + if (prop_id == 0 || m_text_name_id == 0) { + return false; + } else { + const db::PropertiesSet &ps = db::properties (prop_id); + return (ps.size () == 1 && ps.begin ()->first == m_text_name_id); + } + } + db::properties_id_type property_id (size_t cid, db::cell_index_type ci, bool initial) { std::map, db::properties_id_type>::iterator s = m_property_id_per_cluster.find (std::make_pair (cid, ci)); @@ -591,7 +615,11 @@ private: db::PropertiesSet ps; for (auto a = attrs.begin (); a != attrs.end (); ++a) { - if (ps.empty ()) { + if (skip_pseudo_label (*a)) { + + // skip attributes for pseudo-labels + + } else if (ps.empty ()) { ps = db::properties (*a); @@ -616,8 +644,10 @@ private: s->second = db::properties_id (ps); - } else if (! attrs.empty ()) { + } else if (! attrs.empty () && ! skip_pseudo_label (*attrs.begin ())) { + s->second = *attrs.begin (); + } return s->second; @@ -750,6 +780,14 @@ DeepRegion::ensure_merged_polygons_valid () const ClusterMerger cm (deep_layer ().layer (), layout, hc, min_coherence (), report_progress (), progress_desc ()); cm.set_base_verbosity (base_verbosity () + 10); + // Specify the property name ID for the pseudo-labels, so we can filter out those properties + // (for backward compatibility only if join_properties_on_merge is true - if we don't with + // join_properties_on_merge, the pseudo-label properties may get attached to other shapes which + // will be taken as texts then) + if (join_properties_on_merge ()) { + cm.set_text_name (deep_layer ().store ()->text_property_name ()); + } + // TODO: iterate only over the called cells? for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { const db::connected_clusters &cc = hc.clusters_per_cell (c->cell_index ()); @@ -1835,6 +1873,14 @@ DeepRegion::merged (bool min_coherence, unsigned int min_wc, bool join_propertie ClusterMerger cm (deep_layer ().layer (), layout, hc, min_coherence, report_progress (), progress_desc ()); cm.set_base_verbosity (base_verbosity () + 10); + // Specify the property name ID for the pseudo-labels, so we can filter out those properties + // (for backward compatibility only if join_properties_on_merge is true - if we don't with + // join_properties_on_merge, the pseudo-label properties may get attached to other shapes which + // will be taken as texts then) + if (join_properties_on_merge) { + cm.set_text_name (deep_layer ().store ()->text_property_name ()); + } + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { const db::connected_clusters &cc = hc.clusters_per_cell (c->cell_index ()); for (db::connected_clusters::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) { diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 0d4fd72de..fd714701f 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -3013,3 +3013,146 @@ TEST(deep_region_and_cheats) CHECKPOINT(); db::compare_layouts (_this, ly, tl::testdata () + "/algo/cheats_au.gds"); } + +TEST(deep_region_merged_with_properties) +{ + db::Layout ly; + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + + top.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans (db::Vector ()))); + top.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (db::Vector (0, 1000)))); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::PropertiesSet ps1; + ps1.insert ("A", 17); + auto ps1_id = db::properties_id (ps1); + + db::PropertiesSet ps2; + ps2.insert ("B", 42); + auto ps2_id = db::properties_id (ps2); + + db::PropertiesSet ps3; + ps3.insert ("C", 18); + auto ps3_id = db::properties_id (ps3); + + db::PropertiesSet ps4; + ps4.insert ("D", 42); + auto ps4_id = db::properties_id (ps4); + + db::PropertiesSet ps5; + ps5.insert ("A", 18); + ps5.insert ("B", 41); + ps5.insert ("E", 43); + auto ps5_id = db::properties_id (ps5); + + a.shapes (l1).insert (db::BoxWithProperties (db::Box (0, 0, 1000, 1000), ps1_id)); + a.shapes (l1).insert (db::BoxWithProperties (db::Box (500, 0, 1500, 1000), ps1_id)); + a.shapes (l1).insert (db::BoxWithProperties (db::Box (1000, 0, 2000, 1000), ps2_id)); + + b.shapes (l1).insert (db::BoxWithProperties (db::Box (0, 0, 1000, 1000), ps3_id)); + b.shapes (l1).insert (db::BoxWithProperties (db::Box (1000, 0, 2000, 1000), ps4_id)); + + top.shapes (l1).insert (db::BoxWithProperties (db::Box (0, 2000, 1000, 3000), ps5_id)); + + db::DeepShapeStore dss; + + db::RecursiveShapeIterator iter (ly, top, l1); + // enable properties + iter.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + + db::Region r1 (iter, dss); + r1.set_join_properties_on_merge (false); + + db::Region rr1; + + EXPECT_EQ (r1.merged ().to_string (), "(0,2000;0,3000;1000,3000;1000,2000){A=>18,B=>41,E=>43};(0,0;0,1000;1500,1000;1500,0){A=>17};(1000,0;1000,1000;2000,1000;2000,0){B=>42};(0,1000;0,2000;1000,2000;1000,1000){C=>18};(1000,1000;1000,2000;2000,2000;2000,1000){D=>42}"); + rr1 = r1; + rr1.merge (); + EXPECT_EQ (rr1.to_string (), "(0,2000;0,3000;1000,3000;1000,2000){A=>18,B=>41,E=>43};(0,0;0,1000;1500,1000;1500,0){A=>17};(1000,0;1000,1000;2000,1000;2000,0){B=>42};(0,1000;0,2000;1000,2000;1000,1000){C=>18};(1000,1000;1000,2000;2000,2000;2000,1000){D=>42}"); + + r1.set_join_properties_on_merge (true); + + EXPECT_EQ (r1.merged ().to_string (), "(0,0;0,3000;1000,3000;1000,2000;2000,2000;2000,0){A=>18,B=>42,C=>18,D=>42,E=>43}"); + rr1 = r1; + rr1.merge (); + EXPECT_EQ (rr1.to_string (), "(0,0;0,3000;1000,3000;1000,2000;2000,2000;2000,0){A=>18,B=>42,C=>18,D=>42,E=>43}"); + + EXPECT_EQ (r1.join_properties_on_merge (), true); + + // force merge with "join_properties_on_nerge" = false + EXPECT_EQ (r1.merged (false, 0, false).to_string (), "(0,2000;0,3000;1000,3000;1000,2000){A=>18,B=>41,E=>43};(0,0;0,1000;1500,1000;1500,0){A=>17};(1000,0;1000,1000;2000,1000;2000,0){B=>42};(0,1000;0,2000;1000,2000;1000,1000){C=>18};(1000,1000;1000,2000;2000,2000;2000,1000){D=>42}"); + rr1 = r1; + rr1.merge (false, 0, false); + EXPECT_EQ (rr1.to_string (), "(0,2000;0,3000;1000,3000;1000,2000){A=>18,B=>41,E=>43};(0,0;0,1000;1500,1000;1500,0){A=>17};(1000,0;1000,1000;2000,1000;2000,0){B=>42};(0,1000;0,2000;1000,2000;1000,1000){C=>18};(1000,1000;1000,2000;2000,2000;2000,1000){D=>42}"); +} + +TEST(deep_region_merged_with_pseudo_labels) +{ + db::Layout ly; + + db::Cell &top = ly.cell (ly.add_cell ("TOP")); + db::Cell &a = ly.cell (ly.add_cell ("A")); + + top.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans (db::Vector ()))); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::PropertiesSet ps1; + ps1.insert ("A", 17); + auto ps1_id = db::properties_id (ps1); + + db::PropertiesSet ps2; + ps2.insert ("A", 18); + auto ps2_id = db::properties_id (ps2); + + db::PropertiesSet ps3; + ps3.insert ("B", 42); + auto ps3_id = db::properties_id (ps3); + + a.shapes (l1).insert (db::Text ("FAR_OUT", db::Trans (db::Vector (1000, 3000)))); + a.shapes (l1).insert (db::TextWithProperties (db::Text ("OUT", db::Trans (db::Vector (1000, 1000))), ps2_id)); + a.shapes (l1).insert (db::BoxWithProperties (db::Box (500, 500, 1500, 1500), ps3_id)); + + top.shapes (l1).insert (db::BoxWithProperties (db::Box (0, 0, 2000, 2000), ps1_id)); + + db::DeepShapeStore dss; + + db::RecursiveShapeIterator iter (ly, top, l1); + + // enable properties + iter.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + + db::Region r1 (iter, dss); + r1.set_join_properties_on_merge (false); + + db::Region rr1; + + EXPECT_EQ (r1.merged ().to_string (), "(0,0;0,2000;2000,2000;2000,0){A=>17};(500,500;500,1500;1500,1500;1500,500){B=>42}"); + rr1 = r1; + rr1.merge (); + EXPECT_EQ (rr1.to_string (), "(0,0;0,2000;2000,2000;2000,0){A=>17};(500,500;500,1500;1500,1500;1500,500){B=>42}"); + + dss.set_text_property_name ("LABEL"); + dss.set_text_enlargement (2); + + db::Region r2 (iter, dss); + r2.set_join_properties_on_merge (false); + + db::Region rr2; + + EXPECT_EQ (r2.merged ().to_string (), "(0,0;0,2000;2000,2000;2000,0){A=>17};(500,500;500,1500;1500,1500;1500,500){B=>42};(998,2998;998,3002;1002,3002;1002,2998){LABEL=>FAR_OUT};(998,998;998,1002;1002,1002;1002,998){LABEL=>OUT}"); + rr2 = r2; + rr2.merge (); + EXPECT_EQ (rr2.to_string (), "(0,0;0,2000;2000,2000;2000,0){A=>17};(500,500;500,1500;1500,1500;1500,500){B=>42};(998,2998;998,3002;1002,3002;1002,2998){LABEL=>FAR_OUT};(998,998;998,1002;1002,1002;1002,998){LABEL=>OUT}"); + + r2.set_join_properties_on_merge (true); + + EXPECT_EQ (r2.merged ().to_string (), "(0,0;0,2000;2000,2000;2000,0){A=>17,B=>42};(998,2998;998,3002;1002,3002;1002,2998)"); + rr2 = r2; + rr2.merge (); + EXPECT_EQ (rr2.to_string (), "(0,0;0,2000;2000,2000;2000,0){A=>17,B=>42};(998,2998;998,3002;1002,3002;1002,2998)"); +}