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.
This commit is contained in:
Matthias Koefferlein 2025-08-01 22:59:38 +02:00
parent 786c60a28e
commit 4b79c4c362
2 changed files with 192 additions and 3 deletions

View File

@ -525,7 +525,7 @@ class ClusterMerger
{
public:
ClusterMerger (unsigned int layer, db::Layout &layout, const db::hier_clusters<db::PolygonRef> &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<db::PolygonRef> *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<std::pair<size_t, db::cell_index_type>, 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<db::PolygonRef> &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<db::PolygonRef> &cc = hc.clusters_per_cell (c->cell_index ());
for (db::connected_clusters<db::PolygonRef>::all_iterator cl = cc.begin_all (); ! cl.at_end (); ++cl) {

View File

@ -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)");
}