diff --git a/src/db/dbRegion.h b/src/db/dbRegion.h index cccf3e86c..b26cfc71f 100644 --- a/src/db/dbRegion.h +++ b/src/db/dbRegion.h @@ -1493,6 +1493,16 @@ public: return db::RecursiveShapeIterator (m_iter).at_end (); } + /** + * @brief Gets the internal iterator + * + * This method is intended for users who know what they are doing + */ + const db::RecursiveShapeIterator &iter () const + { + return m_iter; + } + /** * @brief Ensures the region has valid polygons * diff --git a/src/db/gsiDeclDbRegion.cc b/src/db/gsiDeclDbRegion.cc index 25dff930b..39ebc5122 100644 --- a/src/db/gsiDeclDbRegion.cc +++ b/src/db/gsiDeclDbRegion.cc @@ -26,6 +26,9 @@ #include "dbPolygonTools.h" #include "dbLayoutUtils.h" #include "dbShapes.h" +#include "tlGlobPattern.h" + +#include namespace gsi { @@ -69,6 +72,43 @@ static db::Region *new_shapes (const db::Shapes &s) return r; } +static db::Region *new_si_texts (const db::RecursiveShapeIterator &si_in, const std::string &pat, bool pattern) +{ + db::RecursiveShapeIterator si (si_in); + si.shape_flags (db::ShapeIterator::Texts); + + tl::GlobPattern glob_pat; + bool all = false; + if (pattern) { + if (pat == "*") { + all = true; + } else { + glob_pat = tl::GlobPattern (pat); + } + } + + std::auto_ptr r (new db::Region ()); + + while (! si.at_end ()) { + if (si.shape ().is_text () && + (all || (pattern && glob_pat.match (si.shape ().text_string ())) || (!pattern && si.shape ().text_string () == pat))) { + db::Text t; + si.shape ().text (t); + t.transform (si.trans ()); + r->insert (db::Box (t.box ().enlarged (db::Vector (1, 1)))); + } + si.next (); + } + + return r.release (); +} + +static db::Region texts (const db::Region *r, const std::string &pat, bool pattern) +{ + std::auto_ptr o (new_si_texts (r->iter (), pat, pattern)); + return *o; +} + static db::Region *new_si (const db::RecursiveShapeIterator &si) { return new db::Region (si); @@ -595,6 +635,30 @@ Class decl_Region ("Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + constructor ("new", &new_si_texts, gsi::arg("shape_iterator"), gsi::arg ("expr"), gsi::arg ("as_pattern", true), + "@brief Constructor from a text set\n" + "\n" + "@param shape_iterator The iterator from which to derive the texts\n" + "@param expr The selection string\n" + "@param as_pattern If true, the selection string is treated as a glob pattern. Otherwise the match is exact.\n" + "\n" + "This special constructor will create a region from the text objects delivered by the shape iterator. " + "Each text object will deliver a small (non-empty) box that represents the text origin.\n" + "Texts can be selected by their strings - either through a glob pattern or by exact comparison with " + "the given string. The following options are available:\n" + "\n" + "@code\n" + "region = RBA::Region::new(iter, \"*\") # all texts\n" + "region = RBA::Region::new(iter, \"A*\") # all texts starting with an 'A'\n" + "region = RBA::Region::new(iter, \"A*\", false) # all texts exactly matchin 'A*'\n" + "@/code\n" + "\n" + "This method has been introduced in version 0.25." + ) + + method_ext ("texts", &texts, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), + "@hide\n" + "This method is provided for DRC implementation only." + ) + method ("merged_semantics=", &db::Region::set_merged_semantics, "@brief Enables or disables merged semantics\n" "@args f\n" diff --git a/src/lay/built_in_macros/drc.lym b/src/lay/built_in_macros/drc.lym index 27e367904..e6baff795 100644 --- a/src/lay/built_in_macros/drc.lym +++ b/src/lay/built_in_macros/drc.lym @@ -801,6 +801,35 @@ CODE DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :smoothed, prep_value(d))) end + # %DRC% + # @name texts + # @brief Selects texts from an original layer + # @synopsis layer.texts + # @synopsis layer.texts(text) + # @synopsis layer.texts(text, true) + # @synopsis layer.texts(text, false) + # This method can be applied to original layers - i.e. ones that have + # been created with \input. It will produce a small box (2x2 DBU) on each + # selected texts. + # + # Texts can be selected either by exact match or pattern match with a + # glob-style pattern. The second argument selects pattern style (true) + # or exact match (false). + # + # @code + # # Selects all texts + # t = input(1, 0).texts + # # Selects all texts beginning with an "A" + # t = input(1, 0).texts("A*") + # t = input(1, 0).texts("A*", true) + # # Selects all texts whose string is "A*" + # t = input(1, 0).texts("A*", false) + # @/code + + def texts(expr = "*", as_pattern = true) + DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :texts, expr, as_pattern)) + end + # %DRC% # @name odd_polygons # @brief Checks for odd polygons (self-overlapping, non-orientable) diff --git a/testdata/drc/drctest.drc b/testdata/drc/drctest.drc index cab2b23a7..de4ee2647 100644 --- a/testdata/drc/drctest.drc +++ b/testdata/drc/drctest.drc @@ -497,6 +497,9 @@ cell("TOP") c1.rounded_corners(0.5, 0.5, 16).output(1010, 0) c1.smoothed(1.5).output(1011, 0) +a1.texts.output(1020, 0) +a1.texts("A*").output(1021, 0) +a1.texts("A*", false).output(1022, 0) puts "=== Single-cell testsuite ===" diff --git a/testdata/drc/drctest.gds b/testdata/drc/drctest.gds index 9026772e6..daa662289 100644 Binary files a/testdata/drc/drctest.gds and b/testdata/drc/drctest.gds differ diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 5d8b537d5..cb9ef787e 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -115,12 +115,15 @@ class DBRegion_TestClass < TestBase ly = RBA::Layout::new l1 = ly.layer("l1") + l2 = ly.layer("l2") c1 = ly.create_cell("C1") c2 = ly.create_cell("C2") c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 0))) c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(0, 100))) c1.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new(200, 100))) c2.shapes(l1).insert(RBA::Box::new(-10, -20, 10, 20)) + c2.shapes(l2).insert(RBA::Text::new("AA", RBA::Vector::new(-10, -20))) + c2.shapes(l2).insert(RBA::Text::new("BB", RBA::Vector::new(10, 20))) r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l1)) assert_equal(r.to_s, "(-10,-20;-10,20;10,20;10,-20);(-10,80;-10,120;10,120;10,80);(190,80;190,120;210,120;210,80)") @@ -131,6 +134,15 @@ class DBRegion_TestClass < TestBase assert_equal(r.bbox.to_s, "(-10,-20;210,120)") assert_equal(r.is_merged?, false) + r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") + assert_equal(r.to_s, "(-11,-21;-11,-19;-9,-19;-9,-21);(9,19;9,21;11,21;11,19);(-11,79;-11,81;-9,81;-9,79);(9,119;9,121;11,121;11,119);(189,79;189,81;191,81;191,79);(209,119;209,121;211,121;211,119)") + r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "A*") + assert_equal(r.to_s, "(-11,-21;-11,-19;-9,-19;-9,-21);(-11,79;-11,81;-9,81;-9,79);(189,79;189,81;191,81;191,79)") + r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "A*", false) + assert_equal(r.to_s, "") + r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "AA", false) + assert_equal(r.to_s, "(-11,-21;-11,-19;-9,-19;-9,-21);(-11,79;-11,81;-9,81;-9,79);(189,79;189,81;191,81;191,79)") + r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l1), RBA::ICplxTrans::new(10, 20)) assert_equal(r.to_s, "(0,0;0,40;20,40;20,0);(0,100;0,140;20,140;20,100);(200,100;200,140;220,140;220,100)") assert_equal(r.extents.to_s, "(0,0;0,40;20,40;20,0);(0,100;0,140;20,140;20,100);(200,100;200,140;220,140;220,100)")