DRC enhancements: max_vertex_count, max_area_ratio and forget

This commit is contained in:
Matthias Koefferlein 2020-09-15 00:12:55 +02:00
parent ce9c7e848a
commit e75333ea41
10 changed files with 436 additions and 158 deletions

View File

@ -576,7 +576,7 @@ ReducingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db:
void
ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box &region, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
{
if (poly.vertices () > m_max_vertex_count || poly.area_ratio () > m_area_ratio) {
if ((m_max_vertex_count >= 4 && poly.vertices () > m_max_vertex_count) || (m_area_ratio > 2.0 && poly.area_ratio () > m_area_ratio)) {
std::vector <db::Polygon> split_polygons;
db::split_polygon (poly, split_polygons);

View File

@ -48,6 +48,12 @@ module DRC
@deep = false
@netter = nil
@netter_data = nil
# initialize the defaults for max_area_ratio, max_vertex_count
dss = RBA::DeepShapeStore::new
@max_area_ratio = dss.max_area_ratio
@max_vertex_count = dss.max_vertex_count
dss._destroy
@verbose = false
@ -337,6 +343,10 @@ module DRC
self.verbose = f
end
def verbose=(f)
@verbose = f
end
# %DRC%
# @name silent
# @brief Resets verbose mode
@ -347,10 +357,6 @@ module DRC
self.verbose = !f
end
def verbose=(f)
@verbose = f
end
# %DRC%
# @name info
# @brief Outputs as message to the logger window
@ -420,7 +426,7 @@ module DRC
# %DRC%
# @name dbu
# @brief Gets or sets the database unit to use
# @synopsis dbu(dbu)
# @synopsis dbu(dbu_value)
# @synopsis dbu
# Without any argument, this method gets the database unit
# used inside the DRC engine.
@ -442,6 +448,10 @@ module DRC
@dbu_read = true
@dbu
end
def dbu=(d)
self.dbu(d)
end
# %DRC%
# @name tiles
@ -470,6 +480,47 @@ module DRC
@ty = (ty || tx).to_f
@deep = false
end
def tiles=(t)
self.tiles(t)
end
# %DRC%
# @name tile_borders
# @brief Specifies a minimum tile border
# @synopsis tile_borders(b)
# @synopsis tile_borders(bx, by)
# The tile border specifies the distance to which shapes are collected into the
# tile. In order words, when processing a tile, shapes within the border distance
# participate in the operations.
#
# For some operations such as booleans (\and, \or, ...), \size and the DRC functions (\width, \space, ...)
# a tile border is automatically established. For other operations such as \with_area
# or \edges, the exact distance is unknown, because such operations may have a long range.
# In that cases, no border is used. The tile_borders function may be used to specify a minimum border
# which is used in that case. That allows taking into account at least shapes within the
# given range, although not necessarily all.
#
# To reset the tile borders, use \no_borders or "tile_borders(nil)".
def tile_borders(bx, by = nil)
@bx = bx.to_f
@by = (by || bx).to_f
end
def tile_borders=(b)
self.tile_borders(b)
end
# %DRC%
# @name no_borders
# @brief Reset the tile borders
# @synopsis no_borders
# Resets the tile borders - see \tile_borders for a description of tile borders.
def no_borders
@bx = @by = nil
end
# %DRC%
# @name is_tiled?
@ -512,39 +563,6 @@ module DRC
@deep
end
# %DRC%
# @name tile_borders
# @brief Specifies a minimum tile border
# @synopsis tile_border(b)
# @synopsis tile_border(bx, by)
# The tile border specifies the distance to which shapes are collected into the
# tile. In order words, when processing a tile, shapes within the border distance
# participate in the operations.
#
# For some operations such as booleans (\and, \or, ...), \size and the DRC functions (\width, \space, ...)
# a tile border is automatically established. For other operations such as \with_area
# or \edges, the exact distance is unknown, because such operations may have a long range.
# In that cases, no border is used. The tile_borders function may be used to specify a minimum border
# which is used in that case. That allows taking into account at least shapes within the
# given range, although not necessarily all.
#
# To reset the tile borders, use \no_borders or "tile_borders(nil)".
def tile_borders(bx, by = nil)
@bx = bx.to_f
@by = (by || bx).to_f
end
# %DRC%
# @name no_borders
# @brief Reset the tile borders
# @synopsis no_borders
# Resets the tile borders - see \tile_borders for a description of tile borders.
def no_borders
@bx = @by = nil
end
# %DRC%
# @name flat
# @brief Disables tiling mode
@ -560,14 +578,84 @@ module DRC
# @name threads
# @brief Specifies the number of CPU cores to use in tiling mode
# @synopsis threads(n)
# @synopsis threads
# If using threads, tiles are distributed on multiple CPU cores for
# parallelization. Still, all tiles must be processed before the
# operation proceeds with the next statement.
# operation proceeds with the next statement.
#
# Without an argument, "threads" will return the current number of
# threads
def threads(n)
@tt = n.to_i
def threads(n = nil)
if n
@tt = n.to_i
end
@tt
end
def threads=(n)
self.threads(n)
end
# %DRC%
# @name max_vertex_count
# @brief Gets or sets the maximum vertex count for deep mode fragmentation
# @synopsis max_vertex_count(count)
# @synopsis max_vertex_count
#
# In deep mode, polygons with more than the given number of vertexes will be split into
# smaller chunks to optimize performance (which is better or less complex polygons).
# The default threshold is 16 vertexes. Use this method with a vertex count to set the
# value and without an argument to get the current maximum vertex count.
# Set the value to zero to disable splitting by vertex count.
#
# See also \max_area_ratio for the other option affecting polygon splitting.
def max_vertex_count(count = nil)
if count
if count.is_a?(1.class)
@max_vertex_count = count.to_i
else
raise("Argument is not an integer number in max_vertex_count")
end
end
@max_vertex_count
end
def max_vertex_count=(count)
self.max_vertex_count(count)
end
# %DRC%
# @name max_area_ratio
# @brief Gets or sets the maximum bounding box to polygon area ratio for deep mode fragmentation
# @synopsis max_area_ratio(ratio)
# @synopsis max_area_ratio
#
# In deep mode, polygons with a bounding box to polygon area ratio bigger than the given number
# will be split into smaller chunks to optimize performance (which gets better if the polygon's
# bounding boxes do not cover a lot of empty space).
# The default threshold is 3.0 which means fairly compact polygons. Use this method with a numeric
# argument to set the value and without an argument to get the current maximum area ratio.
# Set the value to zero to disable splitting by area ratio.
#
# See also \max_vertex_count for the other option affecting polygon splitting.
def max_area_ratio(ratio = nil)
if ratio
if ratio.is_a?(1.0.class) || ratio.is_a?(1.class)
@max_area_ratio = ratio.to_f
else
raise("Argument is not a number in max_area_ratio")
end
end
@max_area_ratio
end
def max_area_ratio=(ratio)
self.max_area_ratio(ratio)
end
# %DRC%
# @name make_layer
# @brief Creates an empty polygon layer based on the hierarchical scheme selected
@ -1900,12 +1988,18 @@ CODE
sf = layout.dbu / self.dbu
if @deep
@dss ||= RBA::DeepShapeStore::new
# TODO: align with LayoutToNetlist by using a "master" L2N
# object which keeps the DSS.
@dss.text_property_name = "LABEL"
@dss.text_enlargement = 1
@dss.max_vertex_count = @max_vertex_count
@dss.max_area_ratio = @max_area_ratio
r = cls.new(iter, @dss, RBA::ICplxTrans::new(sf.to_f))
else
r = cls.new(iter, RBA::ICplxTrans::new(sf.to_f))
end

View File

@ -18,10 +18,6 @@ module DRC
@data = data
end
def data
@data
end
# %DRC%
# @name insert
# @brief Inserts one or many objects into the layer
@ -46,15 +42,15 @@ module DRC
requires_edges_or_region("insert")
args.each do |a|
if a.is_a?(RBA::DBox)
@data.insert(RBA::Box::from_dbox(a * (1.0 / @engine.dbu)))
self.data.insert(RBA::Box::from_dbox(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::DPolygon)
@data.insert(RBA::Polygon::from_dpoly(a * (1.0 / @engine.dbu)))
self.data.insert(RBA::Polygon::from_dpoly(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::DSimplePolygon)
@data.insert(RBA::SimplePolygon::from_dpoly(a * (1.0 / @engine.dbu)))
self.data.insert(RBA::SimplePolygon::from_dpoly(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::DPath)
@data.insert(RBA::Path::from_dpath(a * (1.0 / @engine.dbu)))
self.data.insert(RBA::Path::from_dpath(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::DEdge)
@data.insert(RBA::Edge::from_dedge(a * (1.0 / @engine.dbu)))
self.data.insert(RBA::Edge::from_dedge(a * (1.0 / @engine.dbu)))
elsif a.is_a?(Array)
insert(*a)
else
@ -80,7 +76,7 @@ module DRC
def strict
requires_region("strict")
@data.strict_handling = true
self.data.strict_handling = true
self
end
@ -95,7 +91,7 @@ module DRC
def non_strict
requires_region("non_strict")
@data.strict_handling = false
self.data.strict_handling = false
self
end
@ -110,7 +106,7 @@ module DRC
def is_strict?
requires_region("is_strict?")
@data.strict_handling?
self.data.strict_handling?
end
# %DRC%
@ -128,7 +124,7 @@ module DRC
def clean
requires_edges_or_region("clean")
@data.merged_semantics = true
self.data.merged_semantics = true
self
end
@ -159,7 +155,7 @@ module DRC
def raw
requires_edges_or_region("raw")
@data.merged_semantics = false
self.data.merged_semantics = false
self
end
@ -172,7 +168,7 @@ module DRC
def is_clean?
requires_edges_or_region("is_clean?")
@data.merged_semantics?
self.data.merged_semantics?
end
# %DRC%
@ -184,7 +180,44 @@ module DRC
def is_raw?
requires_edges_or_region("is_raw?")
!@data.merged_semantics?
!self.data.merged_semantics?
end
# %DRC%
# @name forget
# @brief Cleans up memory for this layer
# @synopsis forget
#
# KLayout's DRC engine is imperative. This means, every command is executed immediately
# rather than being compiled and executed later. The advantage of this approach is that
# it allows decisions to be taken depending on the content of a layer and to code
# functions that operate directly on the layer's content.
#
# However, one drawback is that the engine cannot decide when a layer is no longer
# required - it may still be used later in the script. So a layer's data is not cleaned
# up automatically.
#
# In order to save memory for DRC scripts intended for bigger layouts, the DRC script
# should clean up layers as soon as they are no longer required. The "forget" method
# will free the memory used for the layer's information.
#
# The recommended approach is:
#
# @code
# l = ... # compute some layer
# ...
# # once you're done with l:
# l.forget
# l = nil
# @/code
#
# By setting the layer to nil, it is ensured that it can no longer be accessed.
def forget
if @data
@data._destroy
@data = nil
end
end
# %DRC%
@ -197,7 +230,7 @@ module DRC
# on input layers.
def size
@data.size
self.data.size
end
# %DRC%
@ -216,7 +249,7 @@ module DRC
# and performing the deep copy may be expensive in terms of CPU time.
def dup
DRCLayer::new(@engine, @data.dup)
DRCLayer::new(@engine, self.data.dup)
end
# %DRC%
@ -253,12 +286,12 @@ module DRC
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a.first), @engine._prep_value_area(a.last), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a.first), @engine._prep_value_area(a.last), #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(args[0]), @engine._prep_value_area(args[1]), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(args[0]), @engine._prep_value_area(args[1]), #{inv.inspect}))
else
raise("Invalid number of arguments for method '#{mn}'")
end
@ -403,12 +436,12 @@ CODE
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect}))
else
raise("Invalid number of arguments for method '#{mn}'")
end
@ -452,12 +485,12 @@ CODE
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect}))
else
raise("Invalid number of arguments for method '#{mn}'")
end
@ -516,16 +549,16 @@ CODE
eval <<"CODE"
def #{mn}(*args)
requires_edges_or_region("#{mn}")
result_class = @data.is_a?(RBA::Region) ? RBA::EdgePairs : RBA::Edges
result_class = self.data.is_a?(RBA::Region) ? RBA::EdgePairs : RBA::Edges
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(@data, 0, result_class, :with_angle, a.first, a.last, #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a.first, a.last, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(@data, 0, result_class, :with_angle, a, #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a, #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(@data, 0, result_class, :with_angle, args[0], args[1], #{inv.inspect}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, args[0], args[1], #{inv.inspect}))
else
raise("Invalid number of arguments for method '#{mn}'")
end
@ -562,7 +595,7 @@ CODE
def rounded_corners(inner, outer, n)
requires_region("rounded_corners")
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :rounded_corners, @engine._prep_value(inner), @engine._prep_value(outer), n))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :rounded_corners, @engine._prep_value(inner), @engine._prep_value(outer), n))
end
# %DRC%
@ -579,7 +612,7 @@ CODE
def smoothed(d)
requires_region("smoothed")
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :smoothed, @engine._prep_value(d)))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :smoothed, @engine._prep_value(d)))
end
# %DRC%
@ -677,11 +710,11 @@ CODE
end
end
if @data.is_a?(RBA::Texts)
if self.data.is_a?(RBA::Texts)
if as_pattern
result = @engine._tcmd(@data, 0, RBA::Texts, :with_match, pattern, invert)
result = @engine._tcmd(self.data, 0, RBA::Texts, :with_match, pattern, invert)
else
result = @engine._tcmd(@data, 0, RBA::Texts, :with_text, pattern, invert)
result = @engine._tcmd(self.data, 0, RBA::Texts, :with_text, pattern, invert)
end
if as_dots
return DRCLayer::new(@engine, @engine._tcmd(result, 0, RBA::Region, :edges))
@ -692,9 +725,9 @@ CODE
end
else
if as_dots
return DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :texts_dots, pattern, as_pattern))
return DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :texts_dots, pattern, as_pattern))
else
return DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :texts, pattern, as_pattern))
return DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :texts, pattern, as_pattern))
end
end
@ -756,7 +789,7 @@ CODE
end
end
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax))
end
@ -937,12 +970,12 @@ CODE
end
if as_edges
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :extent_refs_edges, *f))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :extent_refs_edges, *f))
else
# add oversize for point- and edge-like regions
zero_area = (f[0] - f[2]).abs < 1e-7 || (f[1] - f[3]).abs < 1e-7
f += [ zero_area ? 1 : 0 ] * 2
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :extent_refs, *f))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :extent_refs, *f))
end
end
@ -967,10 +1000,10 @@ CODE
# @/code
def select(&block)
new_data = @data.class.new
new_data = self.data.class.new
t = RBA::CplxTrans::new(@engine.dbu)
@engine.run_timed("\"select\" in: #{@engine.src_line}", @data) do
@data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
@engine.run_timed("\"select\" in: #{@engine.src_line}", self.data) do
self.data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
block.call(object.transformed(t)) && new_data.insert(object)
end
end
@ -989,8 +1022,8 @@ CODE
def each(&block)
t = RBA::CplxTrans::new(@engine.dbu)
@engine.run_timed("\"select\" in: #{@engine.src_line}", @data) do
@data.send(@data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
@engine.run_timed("\"select\" in: #{@engine.src_line}", self.data) do
self.data.send(self.data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
block.call(object.transformed(t))
end
end
@ -1055,7 +1088,7 @@ CODE
def #{f}(&block)
if :#{f} == :collect
new_data = @data.class.new
new_data = self.data.class.new
elsif :#{f} == :collect_to_region
new_data = RBA::Region.new
elsif :#{f} == :collect_to_edges
@ -1067,8 +1100,8 @@ CODE
t = RBA::CplxTrans::new(@engine.dbu)
dbu_trans = RBA::VCplxTrans::new(1.0 / @engine.dbu)
@engine.run_timed("\\"select\\" in: " + @engine.src_line, @data) do
@data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
@engine.run_timed("\\"select\\" in: " + @engine.src_line, self.data) do
self.data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object|
insert_object_into(new_data, block.call(object.transformed(t)), dbu_trans)
end
end
@ -1088,7 +1121,7 @@ CODE
def odd_polygons
requires_region("ongrid")
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :strange_polygon_check))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :strange_polygon_check))
end
# %DRC%
@ -1106,9 +1139,9 @@ CODE
def ongrid(*args)
requires_region("ongrid")
if args.size == 1
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[0])))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[0])))
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[1])))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[1])))
else
raise("Invalid number of arguments for method 'ongrid'")
end
@ -1153,13 +1186,13 @@ CODE
aa = args.collect { |a| @engine._prep_value(a) }
if :#{f} == :snap && @engine.is_tiled?
# in tiled mode, no modifying versions are available
@data = @engine._tcmd(@data, 0, @data.class, :snapped, gx, gy)
self.data = @engine._tcmd(self.data, 0, self.data.class, :snapped, gx, gy)
self
elsif :#{f} == :snap
@engine._tcmd(@data, 0, @data.class, :#{f}, gx, gy)
@engine._tcmd(self.data, 0, self.data.class, :#{f}, gx, gy)
self
else
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, gx, gy))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, gx, gy))
end
end
CODE
@ -1331,7 +1364,7 @@ CODE
requires_region("andnot")
other.requires_region("andnot")
res = @engine._tcmd_a2(@data, 0, @data.class, @data.class, :andnot, other.data)
res = @engine._tcmd_a2(self.data, 0, self.data.class, self.data.class, :andnot, other.data)
[ DRCLayer::new(@engine, res[0]), DRCLayer::new(@engine, res[1]) ]
@ -1802,15 +1835,15 @@ CODE
other.requires_region("#{f}")
else
requires_edges_texts_or_region("#{f}")
if @data.is_a?(RBA::Text)
if self.data.is_a?(RBA::Text)
other.requires_region("#{f}")
elsif @data.is_a?(RBA::Region)
elsif self.data.is_a?(RBA::Region)
other.requires_edges_texts_or_region("#{f}")
else
other.requires_edges_or_region("#{f}")
end
end
DRCLayer::new(@engine, @engine._tcmd(@data, 0, other.data.class, :#{f}, other.data))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, other.data.class, :#{f}, other.data))
end
CODE
end
@ -1820,7 +1853,7 @@ 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))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data))
end
CODE
end
@ -1829,12 +1862,12 @@ CODE
eval <<"CODE"
def #{f}(other)
other.requires_edges_texts_or_region("#{f}")
if @data.is_a?(RBA::Texts)
if self.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))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data))
end
CODE
end
@ -1843,7 +1876,7 @@ CODE
eval <<"CODE"
def #{f}(other)
requires_same_type(other, "#{f}")
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data))
end
CODE
end
@ -1852,14 +1885,14 @@ CODE
eval <<"CODE"
def #{f}(other)
other.requires_edges_texts_or_region("#{f}")
if @data.is_a?(RBA::Text)
if self.data.is_a?(RBA::Text)
other.requires_region("#{f}")
elsif @data.is_a?(RBA::Region)
elsif self.data.is_a?(RBA::Region)
other.requires_edges_texts_or_region("#{f}")
else
other.requires_edges_or_region("#{f}")
end
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data))
end
CODE
end
@ -1874,19 +1907,19 @@ CODE
requires_same_type(other, "#{f}")
else
requires_edges_texts_or_region("#{f}")
if @data.is_a?(RBA::Text)
if self.data.is_a?(RBA::Text)
other.requires_region("#{f}")
elsif @data.is_a?(RBA::Region)
elsif self.data.is_a?(RBA::Region)
other.requires_edges_texts_or_region("#{f}")
else
other.requires_edges_or_region("#{f}")
end
end
if @engine.is_tiled?
@data = @engine._tcmd(@data, 0, @data.class, :#{fi}, other.data)
DRCLayer::new(@engine, @data)
self.data = @engine._tcmd(self.data, 0, self.data.class, :#{fi}, other.data)
DRCLayer::new(@engine, self.data)
else
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data))
end
end
CODE
@ -1897,7 +1930,7 @@ CODE
def #{f}(other)
other.requires_region("#{f}")
requires_edges("#{f}")
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data))
end
CODE
end
@ -1907,7 +1940,7 @@ CODE
def #{f}(other)
other.requires_edges("#{f}")
requires_edges("#{f}")
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :#{f}, other.data))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data))
end
CODE
end
@ -1987,7 +2020,7 @@ CODE
eval <<"CODE"
def #{f}
requires_region("#{f}")
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :#{f}))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f}))
end
CODE
end
@ -2069,7 +2102,7 @@ CODE
def #{f}(length, fraction = 0.0)
requires_edges("#{f}")
length = @engine._prep_value(length)
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :#{f}, length, fraction))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f}, length, fraction))
end
CODE
end
@ -2137,7 +2170,7 @@ CODE
end
end
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :#{f}, *av))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f}, *av))
end
CODE
@ -2171,7 +2204,7 @@ CODE
eval <<"CODE"
def #{f}(dist)
requires_edges("#{f}")
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :#{f}, @engine._prep_value(dist)))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f}, @engine._prep_value(dist)))
end
CODE
end
@ -2191,10 +2224,10 @@ CODE
%w(edges).each do |f|
eval <<"CODE"
def #{f}
if @data.is_a?(RBA::Region)
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :#{f}))
elsif @data.is_a?(RBA::EdgePairs)
DRCLayer::new(@engine, @engine._cmd(@data, :#{f}))
if self.data.is_a?(RBA::Region)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f}))
elsif self.data.is_a?(RBA::EdgePairs)
DRCLayer::new(@engine, @engine._cmd(self.data, :#{f}))
else
raise "#{f}: Layer must be a polygon or edge pair layer"
end
@ -2222,7 +2255,7 @@ CODE
eval <<"CODE"
def #{f}
requires_edge_pairs("#{f}")
DRCLayer::new(@engine, @engine._cmd(@data, :#{f}))
DRCLayer::new(@engine, @engine._cmd(self.data, :#{f}))
end
CODE
end
@ -2235,7 +2268,7 @@ CODE
# micrometer units.
def bbox
RBA::DBox::from_ibox(@data.bbox) * @engine.dbu.to_f
RBA::DBox::from_ibox(self.data.bbox) * @engine.dbu.to_f
end
# %DRC%
@ -2244,7 +2277,7 @@ CODE
# @synopsis layer.polygons?
def polygons?
@data.is_a?(RBA::Region)
self.data.is_a?(RBA::Region)
end
# %DRC%
@ -2253,7 +2286,7 @@ CODE
# @synopsis layer.edges?
def edges?
@data.is_a?(RBA::Edges)
self.data.is_a?(RBA::Edges)
end
# %DRC%
@ -2262,7 +2295,7 @@ CODE
# @synopsis layer.edge_pairs?
def edge_pairs?
@data.is_a?(RBA::EdgePairs)
self.data.is_a?(RBA::EdgePairs)
end
# %DRC%
@ -2271,7 +2304,7 @@ CODE
# @synopsis layer.is_deep?
def is_deep?
@data.respond_to?(:is_deep?) && @data.is_deep?
self.data.respond_to?(:is_deep?) && self.data.is_deep?
end
# %DRC%
@ -2289,7 +2322,7 @@ CODE
def area
requires_region("area")
@engine._tdcmd(@data, 0, :area) * (@engine.dbu.to_f * @engine.dbu.to_f)
@engine._tdcmd(self.data, 0, :area) * (@engine.dbu.to_f * @engine.dbu.to_f)
end
# %DRC%
@ -2308,7 +2341,7 @@ CODE
requires_region("perimeter")
# Note: we have to add 1 DBU border to collect the neighbors. It's important
# to know then since they tell us whether an edge is an outside edge.
@engine._tdcmd(@data, 1, :perimeter) * @engine.dbu.to_f
@engine._tdcmd(self.data, 1, :perimeter) * @engine.dbu.to_f
end
# %DRC%
@ -2322,7 +2355,7 @@ CODE
def is_box?
requires_region("is_box?")
@engine._cmd(@data, :is_box?)
@engine._cmd(self.data, :is_box?)
end
# %DRC%
@ -2338,7 +2371,7 @@ CODE
def length
requires_edges("length")
@engine._cmd(@data, :length) * @engine.dbu.to_f
@engine._cmd(self.data, :length) * @engine.dbu.to_f
end
# %DRC%
@ -2352,7 +2385,7 @@ CODE
# to a flat collection of texts, polygons, edges or edge pairs.
def flatten
DRC::DRCLayer::new(@engine, @engine._cmd(@data, :flatten))
DRC::DRCLayer::new(@engine, @engine._cmd(self.data, :flatten))
end
# %DRC%
@ -2368,7 +2401,7 @@ CODE
def is_merged?
requires_edges_or_region("is_merged?")
@data.is_merged?
self.data.is_merged?
end
# %DRC%
@ -2377,7 +2410,7 @@ CODE
# @synopsis layer.is_empty?
def is_empty?
@data.is_empty?
self.data.is_empty?
end
# %DRC%
@ -2685,13 +2718,13 @@ CODE
if other
raise("No other layer must be specified for single-layer checks (i.e. width)")
end
DRCLayer::new(@engine, @engine._tcmd(@data, border, RBA::EdgePairs, :#{f}_check, value, whole_edges, metrics, alim, minp, maxp))
DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, value, whole_edges, metrics, alim, minp, maxp))
else
if !other
raise("The other layer must be specified for two-layer checks (i.e. overlap)")
end
requires_same_type(other, "#{f}")
DRCLayer::new(@engine, @engine._tcmd(@data, border, RBA::EdgePairs, :#{f}_check, other.data, value, whole_edges, metrics, alim, minp, maxp))
DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, other.data, value, whole_edges, metrics, alim, minp, maxp))
end
end
@ -2744,12 +2777,12 @@ CODE
if other
raise("#{f}: No other layer must be specified for single-layer checks (i.e. width)")
end
DRCLayer::new(@engine, @engine._tcmd(@data, border, RBA::EdgePairs, :#{f}_check, value, whole_edges, metrics, alim, minp, maxp))
DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, value, whole_edges, metrics, alim, minp, maxp))
else
if !other
raise("#{f}: The other layer must be specified for two-layer checks (i.e. overlap)")
end
DRCLayer::new(@engine, @engine._tcmd(@data, border, RBA::EdgePairs, :#{f}_check, other.data, value, whole_edges, metrics, alim, minp, maxp))
DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, other.data, value, whole_edges, metrics, alim, minp, maxp))
end
end
@ -2919,13 +2952,13 @@ CODE
if :#{f} == :size && @engine.is_tiled?
# in tiled mode, no modifying versions are available
@data = @engine._tcmd(@data, dist, RBA::Region, :sized, *aa)
self.data = @engine._tcmd(self.data, dist, RBA::Region, :sized, *aa)
self
elsif :#{f} == :size
@engine._tcmd(@data, dist, RBA::Region, :#{f}, *aa)
@engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa)
self
else
DRCLayer::new(@engine, @engine._tcmd(@data, dist, RBA::Region, :#{f}, *aa))
DRCLayer::new(@engine, @engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa))
end
end
@ -2948,7 +2981,7 @@ CODE
requires_edge_pairs("polygons")
args.size <= 1 || raise("polygons: Method requires 0 or 1 arguments")
aa = args.collect { |a| @engine._prep_value(a) }
DRCLayer::new(@engine, @engine._cmd(@data, :polygons, *aa))
DRCLayer::new(@engine, @engine._cmd(self.data, :polygons, *aa))
end
# %DRC%
@ -3037,7 +3070,7 @@ CODE
eval <<"CODE"
def #{f}(*args)
aa = args.collect { |a| @engine._prep_value(a) }
DRCLayer::new(@engine, @engine._cmd(@data, :#{f}, *aa))
DRCLayer::new(@engine, @engine._cmd(self.data, :#{f}, *aa))
end
CODE
end
@ -3046,7 +3079,7 @@ CODE
eval <<"CODE"
def #{f}(*args)
aa = args.collect { |a| @engine._prep_value(a) }
@engine._cmd(@data, :#{f}, *aa)
@engine._cmd(self.data, :#{f}, *aa)
self
end
CODE
@ -3092,7 +3125,7 @@ CODE
def merged(*args)
requires_edges_or_region("merged")
aa = args.collect { |a| @engine._prep_value(a) }
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :merged, *aa))
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :merged, *aa))
end
def merge(*args)
@ -3100,9 +3133,9 @@ CODE
aa = args.collect { |a| @engine._prep_value(a) }
if @engine.is_tiled?
# in tiled mode, no modifying versions are available
@data = @engine._tcmd(@data, 0, @data.class, :merged, *aa)
self.data = @engine._tcmd(self.data, 0, self.data.class, :merged, *aa)
else
@engine._tcmd(@data, 0, @data.class, :merge, *aa)
@engine._tcmd(self.data, 0, self.data.class, :merge, *aa)
end
self
end
@ -3126,7 +3159,7 @@ CODE
# or report database.
def output(*args)
@engine._vcmd(@engine, :_output, @data, *args)
@engine._vcmd(@engine, :_output, self.data, *args)
end
# %DRC%
@ -3140,39 +3173,40 @@ CODE
# of the layer's data.
def data
@data || raise("Trying to access an invalid layer (did you use 'forget' on it?)")
@data
end
def requires_region(f)
@data.is_a?(RBA::Region) || raise("#{f}: Requires a polygon layer")
self.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")
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise("#{f}: Requires a polygon or text layer")
end
def requires_texts(f)
@data.is_a?(RBA::Texts) || raise("#{f}: Requires a text layer")
self.data.is_a?(RBA::Texts) || raise("#{f}: Requires a text layer")
end
def requires_edge_pairs(f)
@data.is_a?(RBA::EdgePairs) || raise("#{f}: Requires a edge pair layer")
self.data.is_a?(RBA::EdgePairs) || raise("#{f}: Requires a edge pair layer")
end
def requires_edges(f)
@data.is_a?(RBA::Edges) || raise("#{f}: Requires an edge layer")
self.data.is_a?(RBA::Edges) || raise("#{f}: Requires an edge layer")
end
def requires_edges_or_region(f)
@data.is_a?(RBA::Edges) || @data.is_a?(RBA::Region) || raise("#{f}: Requires an edge or polygon layer")
self.data.is_a?(RBA::Edges) || self.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")
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || self.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")
self.data.class == other.data.class || raise("#{f}: Requires input of the same kind")
end
private

View File

@ -755,3 +755,84 @@ TEST(16_issue570)
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
TEST(17_breaking)
{
std::string rs = tl::testsrc ();
rs += "/testdata/drc/drcSimpleTests_17.drc";
std::string input = tl::testsrc ();
input += "/testdata/drc/drcSimpleTests_17.gds";
std::string au = tl::testsrc ();
au += "/testdata/drc/drcSimpleTests_au17.gds";
std::string output = this->tmp_file ("tmp.gds");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
TEST(18_breaking)
{
std::string rs = tl::testsrc ();
rs += "/testdata/drc/drcSimpleTests_18.drc";
std::string input = tl::testsrc ();
input += "/testdata/drc/drcSimpleTests_18.gds";
std::string au = tl::testsrc ();
au += "/testdata/drc/drcSimpleTests_au18.gds";
std::string output = this->tmp_file ("tmp.gds");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (_this, layout, au, db::NoNormalization);
}

36
testdata/drc/drcSimpleTests_17.drc vendored Normal file
View File

@ -0,0 +1,36 @@
# Hierarchical antenna check
source($drc_test_source, "TOP")
target($drc_test_target)
deep
max_area_ratio(0)
max_vertex_count(0)
l1 = input(1, 0)
max_area_ratio(3.0)
if max_area_ratio != 3.0
raise("Area ratio is not 3.0!")
end
max_vertex_count(0)
l2 = input(1, 0)
max_area_ratio(0)
max_vertex_count(6)
if max_vertex_count != 6
raise("Max vertex count is not 6!")
end
l3 = input(1, 0)
# alternative notation
self.max_area_ratio = 3
self.max_vertex_count = 6
l4 = input(1, 0)
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
l4.output(4, 0)

BIN
testdata/drc/drcSimpleTests_17.gds vendored Normal file

Binary file not shown.

33
testdata/drc/drcSimpleTests_18.drc vendored Normal file
View File

@ -0,0 +1,33 @@
# Hierarchical antenna check
source($drc_test_source, "TOP")
target($drc_test_target)
deep
max_area_ratio(0)
max_vertex_count(0)
l1 = input(1, 0)
l2 = input(2, 0)
l3 = l1 & l2
l1.output(1, 0)
l1.forget
begin
l1.data
raise("l1.data did not raise an error")
rescue
end
l2.output(2, 0)
l2.forget
begin
l2.data
raise("l1.data did not raise an error")
rescue
end
l3.output(3, 0)

BIN
testdata/drc/drcSimpleTests_18.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au17.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au18.gds vendored Normal file

Binary file not shown.