From 878a494abbbcb7ad21ff19c0d5e315d15b759870 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 15 May 2020 22:24:18 +0200 Subject: [PATCH] AND and NOT for texts vs. region, DRC generalization. --- src/db/db/gsiDeclDbTexts.cc | 8 +- src/drc/drc/built-in-macros/_drc_engine.rb | 32 ++++-- src/drc/drc/built-in-macros/_drc_layer.rb | 118 ++++++++++++++++++--- src/drc/drc/built-in-macros/_drc_source.rb | 69 ++++++++++-- 4 files changed, 193 insertions(+), 34 deletions(-) diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 365990f14..003179a9d 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -404,12 +404,12 @@ Class decl_Texts ("db", "Texts", "If \"inverse\" is false, this method returns the texts matching the pattern.\n" "If \"inverse\" is true, this method returns the texts not matching the pattern.\n" ) + - method ("interacting", (db::Texts (db::Texts::*) (const db::Region &) const) &db::Texts::selected_interacting, gsi::arg ("other"), + method ("interacting|&", (db::Texts (db::Texts::*) (const db::Region &) const) &db::Texts::selected_interacting, gsi::arg ("other"), "@brief Returns the texts from this text collection which are inside or on the edge of polygons from the given region\n" "\n" "@return A new text collection containing the texts inside or on the edge of polygons from the region\n" ) + - method ("not_interacting", (db::Texts (db::Texts::*) (const db::Region &) const) &db::Texts::selected_not_interacting, gsi::arg ("other"), + method ("not_interacting|-", (db::Texts (db::Texts::*) (const db::Region &) const) &db::Texts::selected_not_interacting, gsi::arg ("other"), "@brief Returns the texts from this text collection which are not inside or on the edge of polygons from the given region\n" "\n" "@return A new text collection containing the texts not inside or on the edge of polygons from the region\n" @@ -500,10 +500,10 @@ Class decl_Texts ("db", "Texts", "@brief Texts (a collection of texts)\n" "\n" "Text objects are useful as labels for net names, to identify certain regions and to specify specific locations in general. " - "Text collections provide a way to store - also in a hierarchical fashion - and manipulate collection of text objects.\n" + "Text collections provide a way to store - also in a hierarchical fashion - and manipulate a collection of text objects.\n" "\n" "Text objects can be turned into polygons by creating small boxes around the texts (\\polygons). Texts can also be turned into dot-like " - "edges (\\edges). Texts can be filtered by string, either by matching against a fixed string (\\with_string) or a glob-style pattern (\\with_pattern).\n" + "edges (\\edges). Texts can be filtered by string, either by matching against a fixed string (\\with_text) or a glob-style pattern (\\with_match).\n" "\n" "Text collections can be filtered geometrically against a polygon \\Region using \\interacting or \\non-interacting. " "Vice versa, texts can be used to select polygons from a \\Region using \\pull_interacting.\n" diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 7ffcc1b28..5e1ce030d 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -1083,13 +1083,33 @@ module DRC # %DRC% # @name labels # @brief Gets the labels (text) from an original layer - # @synopsis labels + # @synopsis labels(args) # See \Source#labels for a description of that function. def labels(*args) layout.labels(*args) end + # %DRC% + # @name edges + # @brief Gets the edges from an original layer + # @synopsis edges(args) + # See \Source#edges for a description of that function. + + def edges(*args) + layout.edges(*args) + end + + # %DRC% + # @name edge_pairs + # @brief Gets the edges from an original layer + # @synopsis edge_pairs(args) + # See \Source#edge_pairs for a description of that function. + + def edge_pairs(*args) + layout.edge_pairs(*args) + end + # %DRC% # @name output # @brief Outputs a layer to the report database or output layout @@ -1757,11 +1777,11 @@ CODE end end - def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags) + def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, cls) if layers.empty? && ! @deep - r = RBA::Region::new + r = cls.new else @@ -1795,13 +1815,13 @@ CODE # object which keeps the DSS. @dss.text_property_name = "LABEL" @dss.text_enlargement = 1 - r = RBA::Region::new(iter, @dss, RBA::ICplxTrans::new(sf.to_f)) + r = cls.new(iter, @dss, RBA::ICplxTrans::new(sf.to_f)) else - r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f)) + r = cls.new(iter, RBA::ICplxTrans::new(sf.to_f)) end # clip if a box is specified - if box && clip + if box && clip && (cls == RBA::Region || cls == RBA::Edge) r &= RBA::Region::new(box) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 7b42e7a02..363234287 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -593,6 +593,10 @@ CODE # selected text. By using the "as_dots" option, degenerated point-like edges will be # produced. # + # This method can also be applied to true text layers obtained with \labels. + # In this case, without specifying "as_dots" or "as_boxes" retains the text + # objects as such. Only text filtering is applied. + # # Texts can be selected either by exact match string or a pattern match with a # glob-style pattern. By default, glob-style pattern are used. # The options available are: @@ -615,14 +619,39 @@ CODE # # Selects all texts whose string is "A*" # t = input(1, 0).texts(text("A*")) # @/code + # def texts(*args) + requires_texts_or_region("texts") + self._texts_impl(false, *args) + end - requires_region("texts") + # %DRC% + # @name texts_not + # @brief Selects texts from an original layer not matching a specific selection + # @synopsis layer.texts_not + # @synopsis layer.texts_not(p) + # @synopsis layer.texts_not([ options ]) + # + # This method can be applied to true text layers obtained with \labels. + # In this case, without specifying "as_dots" or "as_boxes" retains the text + # objects as such. Only text filtering is applied. + # + # Beside that this method acts like \texts, but will select the text objects + # not matching the filter. + + def texts_not(*args) + requires_texts("texts_not") + self._texts_impl(true, *args) + end + + # Implementation of texts or texts_not + + def _texts_impl(invert, *args) as_pattern = true pattern = "*" - as_dots = false + as_dots = nil args.each do |a| if a.is_a?(String) @@ -637,15 +666,28 @@ CODE raise("Invalid argument for 'texts' method") end end - - if as_dots - DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :texts_dots, pattern, as_pattern)) - else - DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :texts, pattern, as_pattern)) + + if @data.is_a?(RBA::Texts) + if as_pattern + DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Texts, :with_match, pattern, invert)) + else + DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Texts, :with_text, pattern, invert)) + end + if as_dots + DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :edges)) + elsif as_dots == false + DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :polygons)) + end + else + if as_dots + DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :texts_dots, pattern, as_pattern)) + else + DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :texts, pattern, as_pattern)) + end end end - + # %DRC% # @name corners # @brief Selects corners of polygons @@ -1695,7 +1737,35 @@ CODE requires_region("#{f}") other.requires_region("#{f}") else - requires_edges_or_region("#{f}") + requires_edges_texts_or_region("#{f}") + if @data.is_a?(RBA::Text) + other.requires_region("#{f}") + else + other.requires_edges_or_region("#{f}") + end + end + DRCLayer::new(@engine, @engine._tcmd(@data, 0, other.data.class, :#{f}, other.data)) + end +CODE + end + + %w(| ^ overlapping not_overlapping inside not_inside outside not_outside in not_in).each do |f| + eval <<"CODE" + def #{f}(other) + requires_same_type(other, "#{f}") + requires_edges_or_region("#{f}") + DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data)) + end +CODE + end + + %w(& -).each do |f| + eval <<"CODE" + def #{f}(other) + other.requires_edges_texts_or_region("#{f}") + if @data.is_a?(RBA::Texts) + other.requires_region("#{f}") + else other.requires_edges_or_region("#{f}") end DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data)) @@ -1703,15 +1773,22 @@ CODE CODE end - %w(& | ^ - + interacting not_interacting overlapping not_overlapping inside not_inside outside not_outside in not_in).each do |f| + %w(+).each do |f| eval <<"CODE" def #{f}(other) - if :#{f} != :interacting && :#{f} != :not_interacting && :#{f} != :& && :#{f} != :- - requires_same_type(other, "#{f}") + requires_same_type(other, "#{f}") + DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data)) + end +CODE + end + + %w(interacting not_interacting).each do |f| + eval <<"CODE" + def #{f}(other) + other.requires_edges_texts_or_region("#{f}") + if @data.is_a?(RBA::Texts) + other.requires_region("#{f}") else - other.requires_edges_or_region("#{f}") - end - if :#{f} != :+ requires_edges_or_region("#{f}") end DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data)) @@ -2207,7 +2284,7 @@ CODE # If the layer already is a flat one, this method does nothing. # If the layer is a hierarchical layer (an original layer or # a derived layer in deep mode), this method will convert it - # to a flat collection of polygons, edges or edge pairs. + # to a flat collection of texts, polygons, edges or edge pairs. def flatten DRC::DRCLayer::new(@engine, @engine._cmd(@data, :flatten)) @@ -2235,7 +2312,6 @@ CODE # @synopsis layer.is_empty? def is_empty? - requires_edges_or_region("is_empty?") @data.is_empty? end @@ -3006,6 +3082,10 @@ CODE @data.is_a?(RBA::Region) || raise("#{f}: Requires a polygon layer") end + def requires_texts_or_region(f) + @data.is_a?(RBA::Region) || @data.is_a?(RBA::Texts) || raise("#{f}: Requires a polygon or text layer") + end + def requires_edge_pairs(f) @data.is_a?(RBA::EdgePairs) || raise("#{f}: Requires a edge pair layer") end @@ -3018,6 +3098,10 @@ CODE @data.is_a?(RBA::Edges) || @data.is_a?(RBA::Region) || raise("#{f}: Requires an edge or polygon layer") end + def requires_edges_texts_or_region(f) + @data.is_a?(RBA::Edges) || @data.is_a?(RBA::Region) || @data.is_a?(RBA::Texts) || raise("#{f}: Requires an edge, text or polygon layer") + end + def requires_same_type(other, f) @data.class == other.data.class || raise("#{f}: Requires input of the same kind") end diff --git a/src/drc/drc/built-in-macros/_drc_source.rb b/src/drc/drc/built-in-macros/_drc_source.rb index f047ff4a2..c7c846692 100644 --- a/src/drc/drc/built-in-macros/_drc_source.rb +++ b/src/drc/drc/built-in-macros/_drc_source.rb @@ -262,7 +262,7 @@ CODE # not have names)@/li # @/ul # - # Layers created with "input" contain both texts and polygons. There is a subtle + # Layers created with "input" may contain both texts (labels) and polygons. There is a subtle # difference between flat and deep mode: in flat mode, texts are not visible in polygon # operations. In deep mode, texts appear as small 2x2 DBU rectangles. In flat mode, # some operations such as clipping are not fully supported for texts. Also, texts will @@ -273,11 +273,16 @@ CODE # If you don't want to see texts, use \polygons to create an input layer with polygon data # only. If you only want to see texts, use \labels to create an input layer with texts only. # + # \labels also produces a true "text layer" which contains text objects. A variety of + # operations is available for these objects, such as boolean "and" and "not" with a polygon layer. + # True text layers should be preferred over mixed polygon/text layers if text object processing + # is required. + # # Use the global version of "input" without a source object to address the default source. def input(*args) layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, RBA::Region)) end # %DRC% @@ -288,16 +293,18 @@ CODE # @synopsis source.labels(layer_into) # @synopsis source.labels(filter, ...) # - # Creates a layer with the labels from the given layer of the source. + # Creates a true text layer with the labels from the given layer of the source. # # This method is identical to \input, but takes only texts from the given input - # layer. + # layer. Starting with version 0.27, the result is no longer a polygon layer that tries + # to provide text support but a layer type which is provided for carrying text objects + # explicitly. # # Use the global version of "labels" without a source object to address the default source. def labels(*args) layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, RBA::Texts)) end # %DRC% @@ -318,7 +325,55 @@ CODE def polygons(*args) layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, RBA::Region)) + end + + # %DRC% + # @name edges + # @brief Gets the edge shapes (or shapes that can be converted edges) from an input layer + # @synopsis source.edges(layer) + # @synopsis source.edges(layer, datatype) + # @synopsis source.edges(layer_into) + # @synopsis source.edges(filter, ...) + # + # Creates a layer with the edges from the given layer of the source. + # Edge layers are formed from shapes by decomposing the shapes into edges: polygons + # for example are decomposed into their outline edges. Some file formats support egdes + # as native objects. + # + # This method is identical to \input with respect to the options supported. + # + # Use the global version of "edges" without a source object to address the default source. + # + # This method has been introduced in version 0.27. + + def edges(*args) + layers = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, RBA::Edges)) + end + + # %DRC% + # @name edge_pairs + # @brief Gets the edge pairs from an input layer + # @synopsis source.edge_pairs(layer) + # @synopsis source.edge_pairs(layer, datatype) + # @synopsis source.edge_pairs(layer_into) + # @synopsis source.edge_pairs(filter, ...) + # + # Creates a layer with the edge_pairs from the given layer of the source. + # Edge pairs are not supported by layout formats so far. So except if the source is + # a custom-built layout object, this method has little use. It is provided for future + # extensions which may include edge pairs in file streams. + # + # This method is identical to \input with respect to the options supported. + # + # Use the global version of "edge_pairs" without a source object to address the default source. + # + # This method has been introduced in version 0.27. + + def edge_pairs(*args) + layers = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, RBA::EdgePairs)) end # %DRC% @@ -329,7 +384,7 @@ CODE def make_layer layers = [] - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, RBA::Region)) end # %DRC%