From 37d62f97e10c2b704ea03fabe826ba1c72dcc327 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 6 Jan 2021 01:27:02 +0100 Subject: [PATCH] WIP: More documentation for complex DRC ops --- .../drc/built-in-macros/_drc_complex_ops.rb | 229 +++++++-- .../built-in-macros/_drc_cop_integration.rb | 460 +++++++++++++++++- src/drc/drc/built-in-macros/_drc_engine.rb | 31 +- 3 files changed, 676 insertions(+), 44 deletions(-) diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb index ff4739e41..df3e9c9f9 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -55,6 +55,7 @@ class DRCOpNode end def _build_geo_bool_node(other, op) + other = @engine._make_node(other) if ! other.is_a?(DRCOpNode) raise("Second argument to #{op.to_s} must be a DRC expression") end @@ -70,7 +71,7 @@ class DRCOpNode # %DRC% # @name & # @brief Boolean AND between the results of two expressions - # @synopsis expr & expr + # @synopsis expression & expression # # The & operator will compute the boolean AND between the results # of two expressions. The expression types need to be edge or polygon. @@ -85,7 +86,7 @@ class DRCOpNode # %DRC% # @name | # @brief Boolean OR between the results of two expressions - # @synopsis expr | expr + # @synopsis expression | expression # # The | operator will compute the boolean OR between the results of # two expressions. '+' is basically a synonym. Both expressions @@ -94,14 +95,14 @@ class DRCOpNode # %DRC% # @name + # @brief Boolean OR between the results of two expressions - # @synopsis expr + expr + # @synopsis expression + expression # # The + operator will join the results of two expressions. # %DRC% # @name - # @brief Boolean NOT between the results of two expressions - # @synopsis expr - expr + # @synopsis expression - expression # # The - operator will compute the difference between the results # of two expressions. The NOT operation is defined for polygons, @@ -119,7 +120,7 @@ class DRCOpNode # %DRC% # @name ^ # @brief Boolean XOR between the results of two expressions - # @synopsis expr - expr + # @synopsis expression - expression # # The ^ operator will compute the XOR (symmetric difference) between the results # of two expressions. The XOR operation is defined for polygons and edges. @@ -138,7 +139,7 @@ CODE # %DRC% # @name ! # @brief Logical not - # @synopsis ! expr + # @synopsis ! expression # # This operator will evaluate the expression after. If this expression renders # an empty result, the operator will return the primary shape. Otherwise it will @@ -168,7 +169,7 @@ CODE # %DRC% # @name area # @brief Selects the primary shape if the area is meeting the condition - # @synopsis area (in condition) + # @synopsis expression.area (in condition) # # This operation is used in conditions to select shapes based on their area. # It is applicable on polygon expressions. The result will be the input @@ -182,6 +183,9 @@ CODE # out = in.drc(area < 2.0) # out = in.drc(primary.area < 2.0) # equivalent # @/code + # + # The area method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.area". def area DRCOpNodeAreaFilter::new(@engine, self) @@ -190,7 +194,7 @@ CODE # %DRC% # @name count # @brief Selects a expression result based on the number of (local) shapes - # @synopsis count (in condition) + # @synopsis expression.count (in condition) # # This operation is used in conditions to select expression results based on their # count. "count" is used as a method on a expression. It will evaluate the expression locally @@ -219,7 +223,7 @@ CODE # %DRC% # @name perimeter # @brief Selects the primary shape if the perimeter is meeting the condition - # @synopsis perimeter (in condition) + # @synopsis expression.perimeter (in condition) # # This operation is used in conditions to select shapes based on their perimeter. # It is applicable on polygon expressions. The result will be the input @@ -233,6 +237,9 @@ CODE # out = in.drc(perimeter < 10.0) # out = in.drc(primary.perimeter < 10.0) # equivalent # @/code + # + # The perimeter method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.perimeter". def perimeter DRCOpNodePerimeterFilter::new(@engine, self) @@ -241,7 +248,7 @@ CODE # %DRC% # @name bbox_min # @brief Selects the primary shape if its bounding box smaller dimension is meeting the condition - # @synopsis bbox_min (in condition) + # @synopsis expression.bbox_min (in condition) # # This operation is used in conditions to select shapes based on smaller dimension of their bounding boxes. # It is applicable on polygon expressions. The result will be the input @@ -256,6 +263,9 @@ CODE # out = in.drc(bbox_min > 200.nm) # out = in.drc(primary.bbox_min > 200.nm) # equivalent # @/code + # + # The "bbox_min" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.bbox_min". def bbox_min DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxMinDim, self) @@ -264,10 +274,13 @@ CODE # %DRC% # @name bbox_max # @brief Selects the primary shape if its bounding box larger dimension is meeting the condition - # @synopsis bbox_max (in condition) + # @synopsis expression.bbox_max (in condition) # # This operation acts similar to \DRC#bbox_min, but takes the larger dimension of the shape's # bounding box. + # + # The "bbox_max" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.bbox_max". def bbox_max DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxMaxDim, self) @@ -276,13 +289,16 @@ CODE # %DRC% # @name bbox_width # @brief Selects the primary shape if its bounding box width is meeting the condition - # @synopsis bbox_width (in condition) + # @synopsis expression.bbox_width (in condition) # # This operation acts similar to \DRC#bbox_min, but takes the width of the shape's # bounding box. In general, it's more advisable to use \DRC#bbox_min or \DRC#bbox_max # because bbox_width implies a certain orientation. This can imply variant formation in # hierarchical contexts: cells rotated by 90 degree have to be treated differently from # ones not rotated. This usually results in a larger computation effort and larger result files. + # + # The "bbox_width" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.bbox_width". def bbox_width DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxWidth, self) @@ -291,13 +307,16 @@ CODE # %DRC% # @name bbox_height # @brief Selects the primary shape if its bounding box height is meeting the condition - # @synopsis bbox_height (in condition) + # @synopsis expression.bbox_height (in condition) # # This operation acts similar to \DRC#bbox_min, but takes the height of the shape's # bounding box. In general, it's more advisable to use \DRC#bbox_min or \DRC#bbox_max # because bbox_height implies a certain orientation. This can imply variant formation in # hierarchical contexts: cells rotated by 90 degree have to be treated differently from # ones not rotated. This usually results in a larger computation effort and larger result files. + # + # The "bbox_height" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.bbox_height". def bbox_height DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxHeight, self) @@ -306,7 +325,7 @@ CODE # %DRC% # @name length # @brief Selects edges based on their length - # @synopsis length (in condition) + # @synopsis expression.length (in condition) # # This operation will select those edges which are meeting the length # criterion. Non-edge shapes (polygons, edge pairs) will be converted to edges before. @@ -318,6 +337,9 @@ CODE # out = in.drc(length >= 1.um) # out = in.drc(primary.length >= 1.um) # equivalent # @/code + # + # The "length" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.length". def length DRCOpNodeEdgeLengthFilter::new(@engine, self) @@ -326,7 +348,7 @@ CODE # %DRC% # @name angle # @brief Selects edges based on their angle - # @synopsis angle (in condition) + # @synopsis expression.angle (in condition) # # This operation selects edges by their angle, measured against the horizontal # axis in the mathematical sense. @@ -350,6 +372,9 @@ CODE # are placed unrotated and rotated by 90 degree cannot be considered identical. This imposes # a performance penalty in hierarchical mode. If possible, consider using \DRC#rectilinear for # example to detect shapes with non-manhattan geometry instead of using angle checks. + # + # The "angle" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.angle". def angle DRCOpNodeEdgeOrientationFilter::new(@engine, self) @@ -358,10 +383,13 @@ CODE # %DRC% # @name rounded_corners # @brief Applies corner rounding - # @synopsis rounded_corners(inner, outer, n) + # @synopsis expression.rounded_corners(inner, outer, n) # # This operation acts on polygons and applies corner rounding the the given inner - # and outer corner radius and the number of points n per full circle. + # and outer corner radius and the number of points n per full circle. See \Layer#rounded_corners for more details. + # + # The "rounded_corners" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.rounded_corners". def rounded_corners(inner, outer, n) @engine._context("rounded_corners") do @@ -373,9 +401,12 @@ CODE # %DRC% # @name smoothed # @brief Applies smoothing - # @synopsis smoothed(d) + # @synopsis expression.smoothed(d) # - # This operation acts on polygons and applies polygon smoothing with the tolerance d. + # This operation acts on polygons and applies polygon smoothing with the tolerance d. See \Layer#smoothed for more details. + # + # The "smoothed" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.smoothed". def smoothed(d) @engine._context("smoothed") do @@ -386,9 +417,9 @@ CODE # %DRC% # @name corners (in condition) # @brief Applies smoothing - # @synopsis corners - # @synopsis corners(as_dots) - # @synopsis corners(as_boxes) + # @synopsis expression.corners + # @synopsis expression.corners(as_dots) + # @synopsis expression.corners(as_boxes) # # This operation acts on polygons and selects the corners of the polygons. # It can be put into a condition to select corners by their angles. The angle of @@ -412,6 +443,9 @@ CODE # out = in.drc(corners < 0) # out = in.drc(primary.corners < 0) # equivalent # @/code + # + # The "corners" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.corners". def corners(as_dots = DRCAsDots::new(false)) @engine._context("corners") do @@ -427,7 +461,7 @@ CODE # %DRC% # @name middle # @brief Returns the centers of polygon bounding boxes - # @synopsis middle([ options ]) + # @synopsis expression.middle([ options ]) # # The middle operation acts on polygons and has the same effect than \Layer#middle. # It takes the same arguments. It is available as a method on \DRC expressions or @@ -436,7 +470,7 @@ CODE # %DRC% # @name extent_refs # @brief Returns partial references to the boundings boxes of the polygons - # @synopsis extent_refs([ options ]) + # @synopsis expression.extent_refs([ options ]) # # The extent_refs operation acts on polygons and has the same effect than \Layer#extent_refs. # It takes the same arguments. It is available as a method on \DRC expressions or @@ -518,7 +552,7 @@ CODE # %DRC% # @name odd_polygons # @brief Selects all polygons which are non-orientable - # @synopsis odd_polygons + # @synopsis expression.odd_polygons # # Non-orientable polygons are for example "8"-shape polygons. Such polygons are # usually considered harmful as their definition of covered area is depending on the @@ -534,7 +568,7 @@ CODE # %DRC% # @name rectangles # @brief Selects all polygons which are rectangles - # @synopsis rectangles + # @synopsis expression.rectangles # # This operation can be used as a plain function in which case it acts on primary # shapes or can be used as method on another DRC expression. @@ -552,7 +586,7 @@ CODE # %DRC% # @name rectilinear # @brief Selects all polygons which are rectilinear - # @synopsis rectilinear + # @synopsis expression.rectilinear # # Rectilinear polygons only have vertical and horizontal edges. Such polygons are also # called manhattan polygons. @@ -573,7 +607,7 @@ CODE # %DRC% # @name holes # @brief Selects all holes from the input polygons - # @synopsis holes + # @synopsis expression.holes # # This operation can be used as a plain function in which case it acts on primary # shapes or can be used as method on another DRC expression. @@ -591,7 +625,7 @@ CODE # %DRC% # @name hulls # @brief Selects all hulls from the input polygons - # @synopsis hulls + # @synopsis expression.hulls # # The hulls are the outer contours of the input polygons. By selecting hulls only, # all holes will be closed. @@ -612,7 +646,7 @@ CODE # %DRC% # @name edges # @brief Converts the input shapes into edges - # @synopsis edges + # @synopsis expression.edges # # Polygons will be separated into edges forming their contours. Edge pairs will be # decomposed into individual edges. @@ -633,8 +667,8 @@ CODE # %DRC% # @name merged # @brief Returns the merged input polygons, optionally selecting multi-overlap - # @synopsis merged - # @synopsis merged(min_count) + # @synopsis expression.merged + # @synopsis expression.merged(min_count) # # This operation will act on polygons. Without a min_count argument, the merged # polygons will be returned. @@ -642,6 +676,9 @@ CODE # With a min_count argument, the result will include only those parts where more # than the given number of polygons overlap. As the primary input is merged already, # it will always contribute as one. + # + # The "merged" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.merged". def merged(*args) @@ -664,7 +701,17 @@ CODE end - # .... + # %DRC% + # @name sized + # @brief Returns the sized version of the input + # @synopsis expression.sized(d [, mode]) + # @synopsis expression.sized(dx, dy [, mode])) + # + # This method provides the same functionality as \Layer#sized and takes the same + # arguments. It acts on polygon expressions. + # + # The "sized" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.sized". def sized(*args) @@ -701,12 +748,40 @@ CODE end + # %DRC% + # @name extents + # @brief Returns the bounding box of each input object + # @synopsis expression.extents([ enlargement ]) + # + # This method provides the same functionality as \Layer#extents and takes the same + # arguments. It returns the bounding boxes of the input objects. It acts on edge + # edge pair and polygon expressions. + # + # The "extents" method is available as a plain function or as a method on \DRC expressions. + # The plain function is equivalent to "primary.extents". + def extents(e = 0) @engine._context("extents") do DRCOpNodeFilter::new(@engine, self, :new_extents, "extents", @engine._make_value(e)) end end + # %DRC% + # @name first_edges + # @brief Returns the first edges of edge pairs + # @synopsis expression.extents([ enlargement ]) + # + # This method acts on edge pair expressions and returns the first edges of the + # edge pairs delivered by the expression. + + # %DRC% + # @name second_edges + # @brief Returns the second edges of edge pairs + # @synopsis expression.extents([ enlargement ]) + # + # This method acts on edge pair expressions and returns the second edges of the + # edge pairs delivered by the expression. + def first_edges DRCOpNodeFilter::new(@engine, self, :new_edge_pair_to_first_edges, "first_edges") end @@ -715,6 +790,33 @@ CODE DRCOpNodeFilter::new(@engine, self, :new_edge_pair_to_second_edges, "second_edges") end + # %DRC% + # @name end_segments + # @brief Returns the part at the end of each edge of the input + # @synopsis expression.end_segments(length) + # @synopsis expression.end_segments(length, fraction) + # + # This method acts on edge expressions and delivers a specific part of each edge. + # See \layer#end_segments for details about this functionality. + + # %DRC% + # @name start_segments + # @brief Returns the part at the beginning of each edge of the input + # @synopsis expression.start_segments(length) + # @synopsis expression.start_segments(length, fraction) + # + # This method acts on edge expressions and delivers a specific part of each edge. + # See \layer#start_segments for details about this functionality. + + # %DRC% + # @name centers + # @brief Returns the part at the center of each edge of the input + # @synopsis expression.centers(length) + # @synopsis expression.end_segments(length, fraction) + # + # This method acts on edge expressions and delivers a specific part of each edge. + # See \layer#centers for details about this functionality. + def end_segments(length, fraction = 0.0) @engine._context("end_segments") do @engine._check_numeric(fraction) @@ -735,6 +837,18 @@ CODE DRCOpNodeFilter::new(@engine, self, :new_centers, "centers", @engine._make_value(length), fraction) end end + + # %DRC% + # @name extended + # @brief Returns polygons describing an area along the edges of the input + # @synopsis expression.extended([:begin => b,] [:end => e,] [:out => o,] [:in => i], [:joined => true]) + # @synopsis expression.extended(b, e, o, i) + # + # This method acts on edge expressions. + # It will create a polygon for each edge + # tracing the edge with certain offsets to the edge. "o" is the offset applied to the + # outer side of the edge, "i" is the offset applied to the inner side of the edge. + # "b" is the offset applied at the beginning and "e" is the offset applied at the end. def extended(*args) @@ -761,6 +875,30 @@ CODE end + # %DRC% + # @name extended_in + # @brief Returns polygons describing an area along the edges of the input + # @synopsis expression.extended_in(d) + # + # This method acts on edge expressions. Polygons are generated for + # each edge describing the edge drawn with a certain width extending into + # the "inside" (the right side when looking from start to end). + # This method is basically equivalent to the \extended method: + # "extended(0, 0, 0, dist)". + # A version extending to the outside is \extended_out. + + # %DRC% + # @name extended_out + # @brief Returns polygons describing an area along the edges of the input + # @synopsis expression.extended_out(d) + # + # This method acts on edge expressions. Polygons are generated for + # each edge describing the edge drawn with a certain width extending into + # the "outside" (the left side when looking from start to end). + # This method is basically equivalent to the \extended method: + # "extended(0, 0, dist, 0)". + # A version extending to the inside is \extended_in. + def extended_in(e) @engine._context("extended_in") do DRCOpNodeFilter::new(@engine, self, :new_extended_in, "extended_in", self._make_value(e)) @@ -773,9 +911,30 @@ CODE end end - def polygons + # %DRC% + # @name polygons + # @brief Converts the input shapes into polygons + # @synopsis expression.polygons([ enlargement ]) + # + # Generates polygons from the input shapes. Polygons stay polygons. Edges and edge pairs + # are converted to polygons. For this, the enlargement parameter will specify the + # edge thickness or augmentation applied to edge pairs. With the default enlargement + # of zero edges will not be converted to valid polygons and degenerated edge pairs + # will not become valid polygons as well. + # + # Contrary most other operations, "polygons" does not have a plain function equivalent + # as this is reserved for the function generating a polygon layer. + # + # This method is useful for generating polygons from DRC violation markers as shown in + # the following example: + # + # @code + # out = in.drc((width < 0.5.um).polygons) + # @/code + + def polygons(e = 0) @engine._context("polygons") do - DRCOpNodeFilter::new(@engine, self, :new_polygons, "polygons") + DRCOpNodeFilter::new(@engine, self, :new_polygons, "polygons", self._make_value(e)) end end diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb index 27b982caf..90123d186 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -6,14 +6,73 @@ module DRC # %DRC% # @name drc - # @brief Universal DRC function - # @synopsis drc(...) + # @brief Provides a generic DRC function for use with \DRC expressions + # @synopsis layer.drc(expression) # - # TODO: add doc + # This method implement the universal DRC which offers enhanced abilities, + # improved performance in some applications and better readability. + # + # The key concept for this method are DRC expressions. DRC expressions + # are formed by using predefined keywords like "width", operators like "&" + # and method to build an abstract definition of the operations to perform + # within the DRC. + # + # When the DRC function is executed, it will basically visit all shapes + # from the input layer (the one, the "drc" method is called on), collect + # the neighbor shapes from all involved other inputs and run the requested + # operations on each cluster. Currently, "drc" is only available for polygon + # layers. + # + # The nature of the "drc" operation is that of the loop over all (merged) input + # polygons. Within the operation executed on each shape, it's possible to make + # decisions such as "if the shape has an area larger than something, apply this + # operation" etc. This can be achieved with conventional DRC functions too, + # but involves potentially complex and heavy operations such as booleans, interact + # etc. For this reason, the "drc" function may provide a performance benefit. + # + # In addition, within the loop, a single shape from the input layer is presented to + # execution engine which runs the operations. + # This allows using operations such as "size" without having to consider + # neigbor polygons growing into the area of the initial shape. In this sense, + # the "drc" function allows seeing the layer as individual polygons rather than + # a global "sea of polygons". This enables new applications which are otherwise + # difficult to implement. + # + # An important concept in the context of "drc" expressions is the "primary". + # This expression represents a single primary shape. "Secondaries" are shapes + # from other inputs. Primary shapes guide the operation - secondaries without + # primaries are not seen. The "drc" operation will look for secondaries within + # a certain distance which is determined from the operations within the + # expression to execute. The secondaries collected in this step will not be + # merged, so the secondary polygons may be partial. This is important when + # using measurement operations like "area" on secondary polygons. + # + # Here is an example for a generic DRC operation which performs a width + # check for less than 0.5.um on the primary shapes. + # + # @code + # out = in.drc(width < 0.5.um) + # @/code + # + # Another example computes a boolean AND between two layers before selecting + # the ones with an area larger than 1 square micrometer: + # + # @code + # other = ... another layer .. + # out = in.drc((primary & other).area > 1.0) + # @/code + # + # The last example shows how the "drc" operation can improve performance: as the + # boolean operation is computed locally and the result is discarded when no longer required, + # less shapes need to be stored hence reducing the memory overhead and CPU time required + # to manage these shapes. + # + # For more details about the expression see the \DRC class documentation. def drc(op) @engine._context("drc") do requires_region + expr.is_a?(DRCOpNode) || raise("A DRC expression is required for the argument (got #{expr.inspect})") return DRCLayer::new(@engine, self.data.complex_op(op.create_node({}))) end end @@ -52,6 +111,8 @@ module DRC anum = 1 + args = args.collect { |a| self._make_node(a) } + types = [] args.each do |a| if !a.is_a?(DRCOpNode) @@ -116,6 +177,33 @@ module DRC return res end + # %DRC% + # @name foreign + # @brief Represents all other polygons from primary except the current one + # @synopsis foreign + # + # The primary input of the universal DRC function is the layer the \drc function + # is called on. This operation represents all "other" primary polygons while + # \primary represents the current polygon. + # + # This feature opens new options for processing layouts beyond the + # abilities of the classical DRC concept. For classic DRC, intra-layer interactions + # are always symmetric: a polygon cannot be considered separated from it's neighbors + # on the same layer. + # + # The following example computes every part of the input which is closer than + # 0.5 micrometers to other (disconnected) polygons on the same layer: + # + # @code + # out = in.drc(primary & foreign.sized(0.5.um)) + # @/code + + def primary + res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_primary) + res.description = "primary" + return res + end + # %DRC% # @name if_all # @brief Evaluates to the primary shape when all condition expression results are non-empty @@ -160,6 +248,8 @@ module DRC self._context("#{f}") do + args = args.collect { |a| self._make_node(a) } + args.each_with_index do |a,ia| if ! a.is_a?(DRCOpNode) raise("Argument #" + (ia + 1).to_s + " to #{f} must be a DRC expression") @@ -223,6 +313,83 @@ CODE CODE end + # %DRC% + # @name area + # @brief Selects the primary shape if the area is meeting the condition + # @synopsis area (in condition) + # @synopsis area(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.area" (see \Layer#area). Without a layer + # argument, "area" represents an area filter for primary shapes in + # \DRC expressions (see \drc and \DRC#area for more details). + + # %DRC% + # @name hulls + # @brief Selects all hulls from the input polygons + # @synopsis hulls + # @synopsis hulls(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.hulls" (see \Layer#hulls). Without a layer + # argument, "hulls" represents a hull contour extractor for primary shapes in + # \DRC expressions (see \drc and \DRC#hulls for more details). + + # %DRC% + # @name holes + # @brief Selects all holes from the input polygons + # @synopsis holes + # @synopsis holes(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.holes" (see \Layer#hulls). Without a layer + # argument, "holes" represents a hole extractor for primary shapes in + # \DRC expressions (see \drc and \DRC#hulls for more details). + + # %DRC% + # @name odd_polygons + # @brief Selects all polygons which are non-orientable + # @synopsis odd_polygons + # @synopsis odd_polygons(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.odd_polygons" (see \Layer#odd_polygons). Without a layer + # argument, "odd_polygons" represents an odd polygon filter for primary shapes in + # \DRC expressions (see \drc and \DRC#odd_polygons for more details). + + # %DRC% + # @name perimeter + # @brief Selects the primary shape if the perimeter is meeting the condition + # @synopsis perimeter (in condition) + # @synopsis perimeter(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.perimeter" (see \Layer#perimeter). Without a layer + # argument, "perimeter" represents a perimeter filter for primary shapes in + # \DRC expressions (see \drc and \DRC#perimeter for more details). + + # %DRC% + # @name rectangles + # @brief Selects all polygons which are rectangles + # @synopsis rectangles + # @synopsis rectangles(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.rectangles" (see \Layer#rectangles). Without a layer + # argument, "rectangles" represents the rectangles filter for primary shapes in + # \DRC expressions (see \drc and \DRC#rectangles for more details). + + # %DRC% + # @name rectilinear + # @brief Selects all polygons which are rectilinear + # @synopsis rectilinear + # @synopsis rectilinear(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.rectilinear" (see \Layer#rectilinear). Without a layer + # argument, "rectilinear" represents the rectilinear polygons filter for primary shapes in + # \DRC expressions (see \drc and \DRC#rectilinear for more details). + %w( area holes @@ -240,11 +407,90 @@ CODE CODE end + # %DRC% + # @name corners + # @brief Selects all polygons which are rectilinear + # @synopsis corners (in condition) + # @synopsis corners(layer) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.corners" (see \Layer#corners). Without a layer + # argument, "corners" represents the corner generator/filter in primary shapes for + # \DRC expressions (see \drc and \DRC#corners for more details). + def _cop_corners(as_dots = DRCAsDots::new(false)) # NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here. return primary.corners(as_dots) end + # %DRC% + # @name extent_refs + # @brief Returns partial references to the boundings boxes of the polygons + # @synopsis extent_refs([ options ]) + # @synopsis extent_refs(layer, [ options ]) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.extent_refs" (see \Layer#extent_refs). Without a layer + # argument, "extent_refs" represents the partial extents extractor on primary shapes within + # \DRC expressions (see \drc and \DRC#extent_refs for more details). + + # %DRC% + # @name extents + # @brief Returns the bounding box of each input object + # @synopsis extents([ enlargement ]) + # @synopsis extents(layer, [ enlargement ]) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.extents" (see \Layer#extents). Without a layer + # argument, "extents" represents the extents generator on primary shapes within + # \DRC expressions (see \drc and \DRC#extents for more details). + + # %DRC% + # @name middle + # @brief Returns the centers of polygon bounding boxes + # @synopsis middle([ options ]) + # @synopsis middle(layer, [ options ]) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.middle" (see \Layer#middle). Without a layer + # argument, "middle" represents the bounding box center marker generator on primary shapes within + # \DRC expressions (see \drc and \DRC#middle for more details). + + # %DRC% + # @name rounded_corners + # @brief Applies corner rounding + # @synopsis rounded_corners(inner, outer, n) + # @synopsis rounded_corners(layer, inner, outer, n) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.rounded_corners" (see \Layer#rounded_corners). Without a layer + # argument, "rounded_corners" represents the corner rounding algorithm on primary shapes within + # \DRC expressions (see \drc and \DRC#rounded_corners for more details). + + # %DRC% + # @name sized + # @brief Returns the sized version of the input + # @synopsis sized(d [, mode]) + # @synopsis sized(dx, dy [, mode])) + # @synopsis sized(layer, d [, mode]) + # @synopsis sized(layer, dx, dy [, mode])) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.sized" (see \Layer#sized). Without a layer + # argument, "sized" represents the polygon sizer on primary shapes within + # \DRC expressions (see \drc and \DRC#sized for more details). + + # %DRC% + # @name smoothed + # @brief Applies smoothing + # @synopsis smoothed(d) + # @synopsis smoothed(layer, d) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.smoothed" (see \Layer#smoothed). Without a layer + # argument, "smoothed" represents the polygon smoother on primary shapes within + # \DRC expressions (see \drc and \DRC#smoothed for more details). + %w( extent_refs extents @@ -261,6 +507,81 @@ CODE CODE end + # %DRC% + # @name covering + # @brief Selects shapes entirely covering other shapes + # @synopsis covering(other) (in conditions) + # @synopsis covering(layer, other [, options ]) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.covering" (see \Layer#covering). + # + # Without a layer argument, this method represents the selector of primary shapes + # which entirely cover shapes from the other layer. This version can be put into + # a condition indicating how many shapes of the other layer need to be covered. + # Use this variant within \DRC expressions (also see \drc). + # + # For example, the following statement selects all input shapes which entirely + # cover shapes from the "other" layer: + # + # @code + # out = in.drc(covering(other)) + # @/code + # + # This example selects all input shapes which entire cover shapes from + # the other layer and there are more than two shapes from "other" inside + # primary shapes: + # + # @code + # out = in.drc(covering(other) > 2) + # @/code + + # %DRC% + # @name interacting + # @brief Selects shapes interacting with other shapes + # @synopsis interacting(other) (in conditions) + # @synopsis interacting(layer, other [, options ]) + # + # See \covering for a description of the use cases for this function. + # When using "interacting", shapes are selected when the interact (overlap, touch) + # shapes from the other layer. + + # %DRC% + # @name overlapping + # @brief Selects shapes overlapping with other shapes + # @synopsis overlapping(other) (in conditions) + # @synopsis overlapping(layer, other [, options ]) + # + # See \covering for a description of the use cases for this function. + # When using "overlapping", shapes are selected when the overlap + # shapes from the other layer. + + # %DRC% + # @name inside + # @brief Selects shapes entirely inside other shapes + # @synopsis inside(other) + # @synopsis inside(layer, other) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.inside" (see \Layer#inside). + # + # Without a layer argument, this method represents the selector of primary shapes + # which are entirely inside shapes from the other layer. + # Use this variant within \DRC expressions (also see \drc). + + # %DRC% + # @name outside + # @brief Selects shapes entirely outside other shapes + # @synopsis outside(other) + # @synopsis outside(layer, other) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.outside" (see \Layer#outside). + # + # Without a layer argument, this method represents the selector of primary shapes + # which are entirely outside shapes from the other layer. + # Use this variant within \DRC expressions (also see \drc). + %w( covering inside @@ -276,6 +597,106 @@ CODE CODE end + # %DRC% + # @name enclosing + # @brief Performs an enclosing check + # @synopsis enclosing(other [, options ]) (in conditions) + # @synopsis enclosing(layer, other [, options ]) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.enclosing" (see \Layer#enclosing). + # + # The version without the first layer is intended for use within \DRC expressions + # together with the \drc method. In this case, this function needs to be + # put into a condition to specify the check constraints. The other options + # of \Layer#enclosing (e.g. metrics, projection constraints, angle limits etc.) + # apply to this version too. + # + # The conditions can involve an upper and lower limit. The following examples + # illustrate the use of this function with conditions: + # + # @code + # out = in.drc(enclosing(other) < 0.2.um) + # out = in.drc(enclosing(other) <= 0.2.um) + # out = in.drc(enclosing(other) > 0.2.um) + # out = in.drc(enclosing(other) >= 0.2.um) + # out = in.drc(enclosing(other) == 0.2.um) + # out = in.drc(0.1.um <= enclosing(other) < 0.2.um) + # @/code + # + # The result of the enclosing check are edge pairs forming the violation + # markers. With a lower limit, these markers are formed by two, identical but opposite edges attached to + # the primary shape. Without a lower limit, the first edge of the marker is attached to the + # primary shape while the second edge is attached to the shape of the "other" layer. + + # %DRC% + # @name separation + # @brief Performs a separation check + # @synopsis separation(other [, options ]) (in conditions) + # @synopsis separation(layer, other [, options ]) + # + # Provides a separation check (primary layer vs. another layer). Like \enclosing this + # function provides a two-layer check. See there for details how to use this function. + + # %DRC% + # @name overlap + # @brief Performs an overlap check + # @synopsis overlap(other [, options ]) (in conditions) + # @synopsis overlap(layer, other [, options ]) + # + # Provides an overlap check (primary layer vs. another layer). Like \enclosing this + # function provides a two-layer check. See there for details how to use this function. + + # %DRC% + # @name width + # @brief Performs a width check + # @synopsis width([ options ]) (in conditions) + # @synopsis width(layer [, options ]) + # + # This function can be used with a layer argument in which case it + # is equivalent to "layer.width" (see \Layer#width). + # + # The version without a layer it intended for use within \DRC expressions + # together with the \drc method. In this case, this function needs to be + # put into a condition to specify the check constraints. The other options + # of \Layer#width (e.g. metrics, projection constraints, angle limits etc.) + # apply to this version too. + # + # The conditions can involve an upper and lower limit. The following examples + # illustrate the use of this function with conditions: + # + # @code + # out = in.drc(width < 0.2.um) + # out = in.drc(width <= 0.2.um) + # out = in.drc(width > 0.2.um) + # out = in.drc(width >= 0.2.um) + # out = in.drc(width == 0.2.um) + # out = in.drc(0.1.um <= width < 0.2.um) + # @/code + # + # The result of the width check are edge pairs forming the violation + # markers. With a lower limit, these markers are formed by two, identical but opposite edges attached to + # the primary shape. Without a lower limit, both edges are attached to different sides of the primary + # shape. + + # %DRC% + # @name space + # @brief Performs a space check + # @synopsis space([ options ]) (in conditions) + # @synopsis space(layer [, options ]) + # + # Provides a space check on the primary layer. Like \width this + # function provides a single-layer check. See there for details how to use this function. + + # %DRC% + # @name notch + # @brief Performs a notch (intra-polygon space) check + # @synopsis notch([ options ]) (in conditions) + # @synopsis notch(layer [, options ]) + # + # Provides a intra-polygon space check for polygons from the primary layer. Like \width this + # function provides a single-layer check. See there for details how to use this function. + %w( enclosing isolated @@ -313,6 +734,8 @@ CODE alim = a.value elsif a.is_a?(DRCOpNode) other = a + elsif a.is_a?(DRCLayer) + other = self._make_node(a) elsif a.is_a?(DRCProjectionLimits) minp = self._make_value(a.min) maxp = self._make_value(a.max) @@ -352,6 +775,27 @@ CODE CODE end + # %DRC% + # @name iso + # @brief Synonym for "isolated" + # @synopsis iso(...) + # + # "iso" is the short form for \isolated. + + # %DRC% + # @name sep + # @brief Synonym for "separation" + # @synopsis sep(...) + # + # "sep" is the short form for \separation. + + # %DRC% + # @name enc + # @brief Synonym for "enclosing" + # @synopsis enc(...) + # + # "enc" is the short form for \enclosing. + def _cop_iso(*args) # NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here. _cop_isolated(*args) @@ -364,7 +808,15 @@ CODE def _cop_enc(*args) # NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here. - _cop_separation(*args) + _cop_enclosing(*args) + end + + def _make_node(arg) + if arg.is_a?(DRCLayer) + arg = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_secondary(arg.data)) + arg.description = "secondary" + end + arg end end diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index c00a9aefe..b65a55bc7 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -1518,6 +1518,32 @@ CODE end end + # make some DRCLayer methods available as functions + # for the engine + %w( + enc + enclosing + overlapping + sep + separation + ).each do |f| + eval <<"CODE" + def #{f}(*args) + self._context("#{f}") do + if args[0].is_a?(DRCLayer) && args[1].is_a?(DRCLayer) + obj = args.shift + return obj.#{f}(*args) + elsif self.respond_to?(:_cop_#{f}) + # forward to _cop_ implementation for complex DRC operations + return self._cop_#{f}(*args) + else + raise("Function requires at a layer expression for the first two arguments") + end + end + end +CODE + end + # make some DRCLayer methods available as functions # for the engine %w( @@ -1528,8 +1554,6 @@ CODE centers corners covering - enc - enclosing end_segments extended extended_in @@ -1572,7 +1596,6 @@ CODE outside outside_part overlap - overlapping perimeter pull_inside pull_interacting @@ -1596,8 +1619,6 @@ CODE select_outside select_overlapping select_touching - sep - separation size sized smoothed