AND and NOT for texts vs. region, DRC generalization.

This commit is contained in:
Matthias Koefferlein 2020-05-15 22:24:18 +02:00
parent 8a2742d436
commit 878a494abb
4 changed files with 193 additions and 34 deletions

View File

@ -404,12 +404,12 @@ Class<db::Texts> 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<db::Texts> 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"

View File

@ -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

View File

@ -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

View File

@ -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%