mirror of https://github.com/KLayout/klayout.git
1099 lines
29 KiB
XML
1099 lines
29 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<klayout-macro>
|
|
<description/>
|
|
<version/>
|
|
<category/>
|
|
<prolog/>
|
|
<epilog/>
|
|
<doc/>
|
|
<autorun>false</autorun>
|
|
<autorun-early>false</autorun-early>
|
|
<shortcut/>
|
|
<show-in-menu>false</show-in-menu>
|
|
<group-name/>
|
|
<menu-path/>
|
|
<interpreter>ruby</interpreter>
|
|
<dsl-interpreter-name/>
|
|
<text>
|
|
module DRC
|
|
|
|
class DRCLayer
|
|
|
|
def drc(op)
|
|
requires_region("drc")
|
|
return DRCLayer::new(@engine, self.data.complex_op(op.create_node({})))
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNode
|
|
|
|
attr_accessor :description
|
|
attr_accessor :engine
|
|
|
|
def initialize(engine, node = nil)
|
|
@node = node
|
|
self.engine = engine
|
|
self.description = "Basic"
|
|
end
|
|
|
|
def create_node(cache)
|
|
n = cache[self.object_id]
|
|
if !n
|
|
n = self.do_create_node(cache)
|
|
cache[self.object_id] = n
|
|
end
|
|
n
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
@node
|
|
end
|
|
|
|
def dump(indent)
|
|
return indent + self.description
|
|
end
|
|
|
|
def _build_geo_bool_node(other, op)
|
|
if ! other.is_a?(DRCOpNode)
|
|
raise("Second argument to #{op.to_s} must be a DRC expression")
|
|
end
|
|
DRCOpNodeBool::new(@engine, op, self, other)
|
|
end
|
|
|
|
%w(& - ^ | +).each do |f|
|
|
eval <<"CODE"
|
|
def #
|
|
#{f}(other)
|
|
self._build_geo_bool_node(other, :#{f})
|
|
end
|
|
CODE
|
|
end
|
|
|
|
def !()
|
|
if self.respond_to?(:inverted)
|
|
return self.inverted
|
|
else
|
|
empty = RBA::CompoundRegionOperationNode::new_empty(RBA::CompoundRegionOperationNode::ResultType::Region)
|
|
DRCOpNodeCase::new(@engine, [ self, DRCOpNode::new(@engine, empty), @engine.primary ])
|
|
end
|
|
end
|
|
|
|
def _check_numeric(v, symbol)
|
|
if ! v.is_a?(Float) && ! v.is_a?(1.class)
|
|
if symbol
|
|
raise Exception("Argument '#{symbol}' (#{v.inspect}) isn't numeric in operation '#{self.description}'")
|
|
else
|
|
raise Exception("Argument (#{v.inspect}) isn't numeric in operation '#{self.description}'")
|
|
end
|
|
end
|
|
end
|
|
|
|
def _make_value(v, symbol)
|
|
self._check_numeric(v, symbol)
|
|
@engine._prep_value(v)
|
|
end
|
|
|
|
def _make_area_value(v, symbol)
|
|
self._check_numeric(v, symbol)
|
|
@engine._prep_area_value(v)
|
|
end
|
|
|
|
def area
|
|
DRCOpNodeAreaFilter::new(@engine, self)
|
|
end
|
|
|
|
def perimeter
|
|
DRCOpNodePerimeterFilter::new(@engine, self)
|
|
end
|
|
|
|
def bbox_min
|
|
DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxMinDim, self)
|
|
end
|
|
|
|
def bbox_max
|
|
DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxMaxDim, self)
|
|
end
|
|
|
|
def bbox_width
|
|
DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxWidth, self)
|
|
end
|
|
|
|
def bbox_height
|
|
DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxHeight, self)
|
|
end
|
|
|
|
def length
|
|
DRCOpNodeEdgeLengthFilter::new(@engine, self)
|
|
end
|
|
|
|
def angle
|
|
DRCOpNodeEdgeOrientationFilter::new(@engine, self)
|
|
end
|
|
|
|
def rounded_corners(inner, outer, n)
|
|
self._check_numeric(n, :n)
|
|
DRCOpNodeFilter::new(@engine, self, :new_rounded_corners, "rounded_corners", self.make_value(inner, :inner), self.make_value(outer, :outer), n)
|
|
end
|
|
|
|
def smoothed(d)
|
|
DRCOpNodeFilter::new(@engine, self, :new_smoothed, "smoothed", self.make_value(d, :d))
|
|
end
|
|
|
|
def corners(as_dots = DRCAsDots::new(false))
|
|
if as_dots.is_a?(DRCAsDots)
|
|
as_dots = as_dots.value
|
|
else
|
|
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
|
|
end
|
|
DRCOpNodeCornersFilter::new(@engine, self, as_dots)
|
|
end
|
|
|
|
%w(middle extent_refs).each do |f|
|
|
eval <<"CODE"
|
|
def #{f}(*args)
|
|
|
|
f = []
|
|
as_edges = false
|
|
|
|
@@std_refs ||= {
|
|
:center => [0.5] * 4,
|
|
:c => [0.5] * 4,
|
|
:bottom_center => [ 0.5, 0.0, 0.5, 0.0 ],
|
|
:bc => [ 0.5, 0.0, 0.5, 0.0 ],
|
|
:bottom_left => [ 0.0, 0.0, 0.0, 0.0 ],
|
|
:bl => [ 0.0, 0.0, 0.0, 0.0 ],
|
|
:bottom_right => [ 1.0, 0.0, 1.0, 0.0 ],
|
|
:br => [ 1.0, 0.0, 1.0, 0.0 ],
|
|
:top_center => [ 0.5, 1.0, 0.5, 1.0 ],
|
|
:tc => [ 0.5, 1.0, 0.5, 1.0 ],
|
|
:top_left => [ 0.0, 1.0, 0.0, 1.0 ],
|
|
:tl => [ 0.0, 1.0, 0.0, 1.0 ],
|
|
:top_right => [ 1.0, 1.0, 1.0, 1.0 ],
|
|
:tr => [ 1.0, 1.0, 1.0, 1.0 ],
|
|
:left_center => [ 0.0, 0.5, 0.0, 0.5 ],
|
|
:lc => [ 0.0, 0.5, 0.0, 0.5 ],
|
|
:right_center => [ 1.0, 0.5, 1.0, 0.5 ],
|
|
:rc => [ 1.0, 0.5, 1.0, 0.5 ],
|
|
:south => [ 0.5, 0.0, 0.5, 0.0 ],
|
|
:s => [ 0.5, 0.0, 0.5, 0.0 ],
|
|
:left => [ 0.0, 0.0, 0.0, 1.0 ],
|
|
:l => [ 0.0, 0.0, 0.0, 1.0 ],
|
|
:bottom => [ 1.0, 0.0, 0.0, 0.0 ],
|
|
:b => [ 1.0, 0.0, 0.0, 0.0 ],
|
|
:right => [ 1.0, 1.0, 1.0, 0.0 ],
|
|
:r => [ 1.0, 1.0, 1.0, 0.0 ],
|
|
:top => [ 0.0, 1.0, 1.0, 1.0 ],
|
|
:t => [ 0.0, 1.0, 1.0, 1.0 ]
|
|
}
|
|
|
|
args.each_with_index do |a,ia|
|
|
if a.is_a?(1.0.class) && :#{f} != :middle
|
|
f << a
|
|
elsif a.is_a?(DRCAsDots)
|
|
as_edges = a.value
|
|
elsif @@std_refs[a] && :#{f} != :middle
|
|
f = @@std_refs[a]
|
|
else
|
|
raise("Invalid argument #" + (ia + 1).to_s + " (" + a.inspect + ") for '#{f}' method on operation '" + self.description + "' - needs to be numeric or 'as_dots/as_edges'")
|
|
end
|
|
end
|
|
|
|
if f.size == 2
|
|
f = f + f
|
|
else
|
|
f = (f + [0.5] * 4)[0..3]
|
|
end
|
|
|
|
if as_edges
|
|
return DRCOpNodeRelativeExtents::new(self, true, *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
|
|
return DRCOpNodeRelativeExtents::new(self, false, *f)
|
|
end
|
|
|
|
end
|
|
CODE
|
|
end
|
|
|
|
def odd_polygon
|
|
return DRCOpNodeFilter::new(@engine, self, :new_strange_polygons_filter, "odd_polygon")
|
|
end
|
|
|
|
def rectangle
|
|
return DRCOpNodeFilter::new(@engine, self, :new_rectangle_filter, "rectangle")
|
|
end
|
|
|
|
def rectilinear
|
|
return DRCOpNodeFilter::new(@engine, self, :new_rectilinear_filter, "rectilinear")
|
|
end
|
|
|
|
def holes
|
|
return DRCOpNodeFilter::new(@engine, self, :new_holes, "holes")
|
|
end
|
|
|
|
def hull
|
|
return DRCOpNodeFilter::new(@engine, self, :new_hulls, "hull")
|
|
end
|
|
|
|
def edges
|
|
return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges")
|
|
end
|
|
|
|
def sized(*args)
|
|
|
|
dist = 0
|
|
|
|
mode = 2
|
|
values = []
|
|
args.each_with_index do |a,ia|
|
|
if a.is_a?(1.class) || a.is_a?(Float)
|
|
v = self._make_value(a, "argument ##{ia + 1}")
|
|
v.abs > dist && dist = v.abs
|
|
values.push(v)
|
|
elsif a.is_a?(DRCSizingMode)
|
|
mode = a.value
|
|
end
|
|
end
|
|
|
|
args = []
|
|
if values.size < 1
|
|
raise("sized: Method requires one or two sizing values")
|
|
elsif values.size > 2
|
|
raise("sized: Method must not have more than two values")
|
|
else
|
|
args << values[0]
|
|
args << values[-1]
|
|
end
|
|
args << mode
|
|
|
|
return DRCOpNodeFilter::new(@engine, self, :new_sized, "sized", *args)
|
|
|
|
end
|
|
|
|
def extents(e = 0)
|
|
return DRCOpNodeFilter::new(@engine, self, :new_extents, "extents", self._make_value(e, :e))
|
|
end
|
|
|
|
def first_edges
|
|
return DRCOpNodeFilter::new(@engine, self, :new_edge_pair_to_first_edges, "first_edges")
|
|
end
|
|
|
|
def second_edges
|
|
return DRCOpNodeFilter::new(@engine, self, :new_edge_pair_to_second_edges, "second_edges")
|
|
end
|
|
|
|
def end_segments(length, fraction = 0.0)
|
|
self._check_numeric(fraction, :fraction)
|
|
return DRCOpNodeFilter::new(@engine, self, :new_end_segments, "end_segments", self._make_value(length, :length), fraction)
|
|
end
|
|
|
|
def start_segments(length, fraction = 0.0)
|
|
self._check_numeric(fraction, :fraction)
|
|
return DRCOpNodeFilter::new(@engine, self, :new_start_segments, "start_segments", self._make_value(length, :length), fraction)
|
|
end
|
|
|
|
def centers(length, fraction = 0.0)
|
|
self._check_numeric(fraction, :fraction)
|
|
return DRCOpNodeFilter::new(@engine, self, :new_centers, "centers", self._make_value(length, :length), fraction)
|
|
end
|
|
|
|
def extended(*args)
|
|
|
|
av = [ 0, 0, 0, 0 ]
|
|
args.each_with_index do |a,i|
|
|
if a.is_a?(Hash)
|
|
a[:begin] && av[0] = self._make_value(a[:begin], :begin)
|
|
a[:end] && av[1] = self._make_value(a[:end], :end)
|
|
a[:out] && av[2] = self._make_value(a[:out], :out)
|
|
a[:in] && av[3] = self._make_value(a[:in], :in)
|
|
a[:joined] && av[4] = true
|
|
elsif i < 4
|
|
av[i] = self._make_value(a, "argument " + (i+1).to_s)
|
|
else
|
|
raise("Too many arguments for method '#{f}' (1 to 5 expected)")
|
|
end
|
|
end
|
|
|
|
return DRCOpNodeFilter::new(@engine, self, :new_extended, "extended", *args)
|
|
|
|
end
|
|
|
|
def extended_in(e)
|
|
return DRCOpNodeFilter::new(@engine, self, :new_extended_in, "extended_in", self._make_value(e))
|
|
end
|
|
|
|
def extended_out(e)
|
|
return DRCOpNodeFilter::new(@engine, self, :new_extended_out, "extended_out", self._make_value(e))
|
|
end
|
|
|
|
def polygons
|
|
return DRCOpNodeFilter::new(@engine, self, :new_polygons, "polygons")
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeLogicalBool < DRCOpNode
|
|
|
|
attr_accessor :children
|
|
attr_accessor :op
|
|
|
|
def initialize(engine, op)
|
|
super(engine)
|
|
self.children = []
|
|
self.op = op
|
|
self.description = "Logical #{op.to_s}"
|
|
end
|
|
|
|
def dump(indent)
|
|
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
log_op = { :land => RBA::CompoundRegionOperationNode::LogicalOp::LogAnd, :lor => RBA::CompoundRegionOperationNode::LogicalOp::LogOr }[self.op]
|
|
RBA::CompoundRegionOperationNode::new_logical_boolean(log_op, false, self.children.collect { |c| c.create_node(cache) })
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeBool < DRCOpNode
|
|
|
|
attr_accessor :children
|
|
attr_accessor :op
|
|
|
|
def initialize(engine, op, a, b)
|
|
super(engine)
|
|
self.children = [a, b]
|
|
self.op = op
|
|
self.description = "Geometrical #{op.to_s}"
|
|
end
|
|
|
|
def dump(indent)
|
|
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
bool_op = { :& => RBA::CompoundRegionOperationNode::GeometricalOp::And,
|
|
:+ => RBA::CompoundRegionOperationNode::GeometricalOp::Or,
|
|
:| => RBA::CompoundRegionOperationNode::GeometricalOp::Or,
|
|
:- => RBA::CompoundRegionOperationNode::GeometricalOp::Not,
|
|
:^ => RBA::CompoundRegionOperationNode::GeometricalOp::Xor }[self.op]
|
|
nodes = self.children.collect do |c|
|
|
n = c.create_node(cache)
|
|
if n.result_type == RBA::CompoundRegionOperationNode::ResultType::EdgePairs
|
|
n = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(n)
|
|
end
|
|
n
|
|
end
|
|
RBA::CompoundRegionOperationNode::new_geometrical_boolean(bool_op, *nodes)
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeCase < DRCOpNode
|
|
|
|
attr_accessor :children
|
|
|
|
def initialize(engine, children)
|
|
super(engine)
|
|
self.children = children
|
|
self.description = "switch"
|
|
end
|
|
|
|
def dump(indent)
|
|
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
RBA::CompoundRegionOperationNode::new_case(self.children.collect { |c| c.create_node(cache) })
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeWithCompare < DRCOpNode
|
|
|
|
attr_accessor :reverse
|
|
attr_accessor :original
|
|
attr_accessor :lt, :le, :gt, :ge, :arg
|
|
|
|
def initialize(engine, original = nil, reverse = false)
|
|
super(engine)
|
|
self.reverse = reverse
|
|
self.original = original
|
|
self.description = original ? original.description : "BasicWithCompare"
|
|
end
|
|
|
|
def _description_for_dump
|
|
self.description
|
|
end
|
|
|
|
def dump(indent = "")
|
|
if self.original
|
|
return "@temp (should not happen)"
|
|
else
|
|
cmp = []
|
|
self.lt && (cmp << ("<%.12g" % self.lt))
|
|
self.le && (cmp << ("<=%.12g" % self.le))
|
|
self.gt && (cmp << (">%.12g" % self.gt))
|
|
self.ge && (cmp << (">=%.12g" % self.ge))
|
|
return indent + self.description + " " + cmp.join(" ")
|
|
end
|
|
end
|
|
|
|
def _check_bounds
|
|
if (self.lt || self.le) && (self.gt || self.ge)
|
|
epsilon = 1e-10
|
|
lower = self.ge ? self.ge - epsilon : self.gt + epsilon
|
|
upper = self.le ? self.le + epsilon : self.lt - epsilon
|
|
if lower > upper - epsilon
|
|
raise("Lower bound is larger than upper bound")
|
|
end
|
|
end
|
|
end
|
|
|
|
def set_lt(value)
|
|
(self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
|
|
self.lt = value
|
|
self._check_bounds
|
|
end
|
|
|
|
def set_le(value)
|
|
(self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
|
|
self.le = value
|
|
self._check_bounds
|
|
end
|
|
|
|
def set_gt(value)
|
|
(self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
|
|
self.gt = value
|
|
self._check_bounds
|
|
end
|
|
|
|
def set_ge(value)
|
|
(self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
|
|
self.ge = value
|
|
self._check_bounds
|
|
end
|
|
|
|
def coerce(something)
|
|
[ DRCOpNodeWithCompare::new(self.engine, self, true), something ]
|
|
end
|
|
|
|
def _self_or_original
|
|
return self.original || self
|
|
end
|
|
|
|
def ==(other)
|
|
if !(other.is_a?(Float) || other.is_a?(Integer))
|
|
raise("== operator needs a numerical argument for '" + self.description + "' argument")
|
|
end
|
|
res = self._self_or_original
|
|
res.set_le(other)
|
|
res.set_ge(other)
|
|
return res
|
|
end
|
|
|
|
def <(other)
|
|
if !(other.is_a?(Float) || other.is_a?(Integer))
|
|
raise("< operator needs a numerical argument for '" + self.description + "' argument")
|
|
end
|
|
res = self._self_or_original
|
|
if reverse
|
|
res.set_gt(other)
|
|
else
|
|
res.set_lt(other)
|
|
end
|
|
return res
|
|
end
|
|
|
|
def <=(other)
|
|
if !(other.is_a?(Float) || other.is_a?(Integer))
|
|
raise("<= operator needs a numerical argument for '" + self.description + "' argument")
|
|
end
|
|
res = self._self_or_original
|
|
if reverse
|
|
res.set_ge(other)
|
|
else
|
|
res.set_le(other)
|
|
end
|
|
return res
|
|
end
|
|
|
|
def >(other)
|
|
if !(other.is_a?(Float) || other.is_a?(Integer))
|
|
raise("> operator needs a numerical argument for '" + self.description + "' argument")
|
|
end
|
|
res = self._self_or_original
|
|
if reverse
|
|
res.set_lt(other)
|
|
else
|
|
res.set_gt(other)
|
|
end
|
|
return res
|
|
end
|
|
|
|
def >=(other)
|
|
if !(other.is_a?(Float) || other.is_a?(Integer))
|
|
raise(">= operator needs a numerical argument for '" + self.description + "' argument")
|
|
end
|
|
res = self._self_or_original
|
|
if reverse
|
|
res.set_le(other)
|
|
else
|
|
res.set_ge(other)
|
|
end
|
|
return res
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeAreaFilter < DRCOpNodeWithCompare
|
|
|
|
attr_accessor :input
|
|
attr_accessor :inverted
|
|
|
|
def initialize(engine, input)
|
|
super(engine)
|
|
self.input = input
|
|
self.inverted = false
|
|
self.description = "area"
|
|
end
|
|
|
|
def _description_for_dump
|
|
self.inverted ? "area" : "not_area"
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
args = [ self.input.create_node(cache), self.inverse ]
|
|
args << (self.gt ? make_area_value(self.gt) + 1 : (self.ge ? make_area_value(self.ge) : 0))
|
|
if self.lt || self.le
|
|
args << self.lt ? make_area_value(self.lt) : make_area_value(self.le) - 1
|
|
end
|
|
RBA::CompoundRegionOperationNode::new_area_filter(*args)
|
|
end
|
|
|
|
def inverted
|
|
res = self.dup
|
|
res.inverted = !res.inverted
|
|
return res
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeEdgeLengthFilter < DRCOpNodeWithCompare
|
|
|
|
attr_accessor :input
|
|
attr_accessor :inverted
|
|
|
|
def initialize(engine, input)
|
|
super(engine)
|
|
self.input = input
|
|
self.inverted = false
|
|
self.description = "length"
|
|
end
|
|
|
|
def _description_for_dump
|
|
self.inverted ? "length" : "not_length"
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
args = [ self.input.create_node(cache), self.inverse ]
|
|
args << (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
|
|
if self.lt || self.le
|
|
args << self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
|
|
end
|
|
RBA::CompoundRegionOperationNode::new_edge_length_filter(*args)
|
|
end
|
|
|
|
def inverted
|
|
res = self.dup
|
|
res.inverted = !res.inverted
|
|
return res
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare
|
|
|
|
attr_accessor :input
|
|
attr_accessor :inverted
|
|
|
|
def initialize(engine, input)
|
|
super(engine)
|
|
self.input = input
|
|
self.inverted = false
|
|
self.description = "angle"
|
|
end
|
|
|
|
def _description_for_dump
|
|
self.inverted ? "angle" : "not_angle"
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
args = [ self.input.create_node(cache), self.inverse ]
|
|
angle_delta = 1e-6
|
|
args << (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0))
|
|
args << (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0))
|
|
RBA::CompoundRegionOperationNode::new_edge_orientation_filter(*args)
|
|
end
|
|
|
|
def inverted
|
|
res = self.dup
|
|
res.inverted = !res.inverted
|
|
return res
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodePerimeterFilter < DRCOpNodeWithCompare
|
|
|
|
attr_accessor :input
|
|
attr_accessor :inverted
|
|
|
|
def initialize(engine, input)
|
|
super(engine)
|
|
self.input = input
|
|
self.inverted = false
|
|
self.description = "perimeter"
|
|
end
|
|
|
|
def _description_for_dump
|
|
self.inverted ? "perimeter" : "not_perimeter"
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
args = [ self.input.create_node(cache), self.inverse ]
|
|
args << (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
|
|
if self.lt || self.le
|
|
args << self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
|
|
end
|
|
RBA::CompoundRegionOperationNode::new_perimeter_filter(*args)
|
|
end
|
|
|
|
def inverted
|
|
res = self.dup
|
|
res.inverted = !res.inverted
|
|
return res
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeInteractingWithCount < DRCOpNodeWithCompare
|
|
|
|
attr_accessor :a, :b
|
|
attr_accessor :inverted
|
|
attr_accessor :op
|
|
|
|
def initialize(engine, a, b, op)
|
|
super(engine)
|
|
self.a = a
|
|
self.b = b
|
|
self.op = op
|
|
self.inverted = false
|
|
self.description = (self.inverted ? "" : "not_") + self.op.to_s
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
args = [ self.a.create_node(cache), self.b.create_node(cache), self.inverse ]
|
|
args << (self.gt ? self.gt + 1 : (self.ge ? self.ge : 0))
|
|
if self.lt || self.le
|
|
args << self.lt ? self.lt : self.le - 1
|
|
end
|
|
factory = { :covering => :new_enclosing,
|
|
:overlapping => :new_overlapping,
|
|
:interacting => :new_interacting }[self.op]
|
|
RBA::CompoundRegionOperationNode::send(factory, *args)
|
|
end
|
|
|
|
def inverted
|
|
res = self.dup
|
|
res.inverted = !res.inverted
|
|
return res
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeInteracting < DRCOpNode
|
|
|
|
attr_accessor :a, :b
|
|
attr_accessor :inverted
|
|
attr_accessor :op
|
|
|
|
def initialize(engine, a, b, op)
|
|
super(engine)
|
|
self.a = a
|
|
self.b = b
|
|
self.op = op
|
|
self.inverted = false
|
|
self.description = (self.inverted ? "" : "not_") + self.op.to_s
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
factory = { :inside => :new_inside,
|
|
:outside => :new_outside }[self.op]
|
|
RBA::CompoundRegionOperationNode::send(factory, self.a.create_node(cache), self.b.create_node(cache), self.inverse)
|
|
end
|
|
|
|
def inverted
|
|
res = self.dup
|
|
res.inverted = !res.inverted
|
|
return res
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeFilter < DRCOpNode
|
|
|
|
attr_accessor :input
|
|
attr_accessor :factory
|
|
attr_accessor :args
|
|
|
|
def initialize(engine, input, factory, description, *args)
|
|
super(engine)
|
|
self.input = input
|
|
self.factory = factory
|
|
self.args = args
|
|
self.description = description
|
|
end
|
|
|
|
def dump(indent)
|
|
if self.args.size > 0
|
|
return self.description + "(" + self.args.collect { |a| a.inspect }.join(",") + ")\n" + input.dump(" " + indent)
|
|
else
|
|
return self.description + "\n" + input.dump(" " + indent)
|
|
end
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
RBA::CompoundRegionOperationNode::send(self.factory, self.input.create_node(cache), *args)
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeCheck < DRCOpNodeWithCompare
|
|
|
|
attr_accessor :other
|
|
attr_accessor :check
|
|
attr_accessor :args
|
|
|
|
def initialize(engine, check, other, *args)
|
|
super(engine)
|
|
self.check = check
|
|
self.other = other
|
|
self.args = args
|
|
self.description = check.to_s
|
|
end
|
|
|
|
def _description_for_dump
|
|
if self.args.size > 0
|
|
return self.description + "(" + self.args.collect { |a| a.inspect }.join(",") + ")"
|
|
else
|
|
return self.description
|
|
end
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
|
|
if !(self.lt || self.le) && !(self.gt || self.ge)
|
|
raise("No value given for check #{self.check}")
|
|
end
|
|
|
|
factory = { :width => :new_width_check, :space => :new_space_check,
|
|
:notch => :new_notch_check, :separation => :new_separation_check,
|
|
:isolated => :new_isolated_check, :overlap => :new_overlap_check,
|
|
:enclosing => :new_inside_check }[self.check]
|
|
|
|
if self.lt || self.le
|
|
dmin = self.le ? self._make_value(self.le, :le) + 1 : self._make_value(self.lt, :lt)
|
|
res = RBA::CompoundRegionOperationNode::send(factory, dmin, *self.args)
|
|
else
|
|
res = nil
|
|
end
|
|
|
|
if self.gt || self.ge
|
|
dmax = self.ge ? self._make_value(self.ge, :ge) : self._make_value(self.gt, :gt) + 1
|
|
max_check = RBA::CompoundRegionOperationNode::send(factory, dmax, *self.args + [ true ])
|
|
res_max = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(max_check)
|
|
if res
|
|
if self.check == :width || self.check == :notch
|
|
# Same polygon check - we need to take both edges of the result
|
|
and_with = RBA::CompoundRegionOperationNode::new_edges(res)
|
|
else
|
|
and_with = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(res)
|
|
end
|
|
res = RBA::CompoundRegionOperationNode::new_geometrical_boolean(RBA::CompoundRegionOperationNode::GeometricalOp::And, and_with, res_max)
|
|
else
|
|
res = res_max
|
|
end
|
|
end
|
|
|
|
return res
|
|
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeBBoxParameterFilter < DRCOpNodeWithCompare
|
|
|
|
attr_accessor :input
|
|
attr_accessor :parameter
|
|
attr_accessor :inverted
|
|
|
|
def initialize(engine, parameter, input, description)
|
|
super(engine)
|
|
self.parameter = parameter
|
|
self.input = input
|
|
self.inverted = false
|
|
self.description = description
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
args = [ self.input.create_node(cache), self.inverse ]
|
|
args << (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
|
|
if self.lt || self.le
|
|
args << self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
|
|
end
|
|
RBA::CompoundRegionOperationNode::new_perimeter_filter(*args)
|
|
end
|
|
|
|
def inverted
|
|
res = self.dup
|
|
res.inverted = !res.inverted
|
|
return res
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
|
|
|
|
attr_accessor :input
|
|
attr_accessor :parameter
|
|
attr_accessor :inverted
|
|
|
|
def initialize(engine, as_dots, input)
|
|
super(engine)
|
|
self.as_dots = as_dots
|
|
self.input = input
|
|
self.description = "corners"
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
args = [ self.input.create_node(cache) ]
|
|
angle_delta = 1e-6
|
|
args << (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0))
|
|
args << (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0))
|
|
if self.as_dots
|
|
RBA::CompoundRegionOperationNode::new_corners_as_dots_node(*args)
|
|
else
|
|
args << 2 # dimension is 2x2 DBU
|
|
RBA::CompoundRegionOperationNode::new_corners_as_rectangles_node(*args)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
class DRCOpNodeRelativeExtents < DRCOpNode
|
|
|
|
attr_accessor :input
|
|
attr_accessor :as_edges, :fx1, :fx2, :fy1, :fy2, :dx, :dy
|
|
|
|
def initialize(engine, input, as_edges, fx1, fx2, fy1, fy2, dx = 0, dy = 0)
|
|
super(engine)
|
|
self.input = input
|
|
self.as_edges = as_edges
|
|
self.description = "extents"
|
|
end
|
|
|
|
def dump(indent)
|
|
if !self.as_edges
|
|
return "extents(%.12g,%.12g,%.12g,%.12g,%12g,%.12g)" % [self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy]
|
|
else
|
|
return "extents_as_edges(%.12g,%.12g,%.12g,%.12g)" % [self.fx1, self.fx2, self.fy1, self.fy2]
|
|
end
|
|
end
|
|
|
|
def do_create_node(cache)
|
|
if !self.as_edges
|
|
RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy)
|
|
else
|
|
RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
class DRCEngine
|
|
|
|
def switch(*args)
|
|
anum = 1
|
|
args.each do |a|
|
|
if !a.is_a?(DRCOpNode)
|
|
raise("All inputs to 'switch' need to be valid compound operation expressions (#{anum} isn't)")
|
|
end
|
|
anum += 1
|
|
end
|
|
DRCOpNodeCase::new(self, args)
|
|
end
|
|
|
|
def secondary(layer)
|
|
layer.requires_region("secondary")
|
|
res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_secondary(layer.data))
|
|
res.description = "secondary"
|
|
return res
|
|
end
|
|
|
|
def primary
|
|
res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_primary)
|
|
res.description = "primary"
|
|
return res
|
|
end
|
|
|
|
%w(land lor).each do |f|
|
|
eval <<"CODE"
|
|
def #{f}(*args)
|
|
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")
|
|
end
|
|
end
|
|
res = DRCOpNodeLogicalBool::new(self, :#{f})
|
|
res.children = args
|
|
res
|
|
end
|
|
CODE
|
|
end
|
|
|
|
%w(area perimeter bbox_min bbox_max bbox_width bbox_height rectangle rectilinear edges odd_polygon hull holes).each do |f|
|
|
eval <<"CODE"
|
|
def #{f}
|
|
primary.#{f}
|
|
end
|
|
CODE
|
|
end
|
|
|
|
def corners(as_dots = DRCAsDots::new(false))
|
|
return primary.corners(as_dots)
|
|
end
|
|
|
|
%w(middle extent_refs rounded_corners smoothed sized extents).each do |f|
|
|
eval <<"CODE"
|
|
def #{f}(*args)
|
|
primary.#{f}(*args)
|
|
end
|
|
CODE
|
|
end
|
|
|
|
%w(covering overlapping inside outside interacting).each do |f|
|
|
eval <<"CODE"
|
|
def #{f}(other)
|
|
primary.#{f}(other)
|
|
end
|
|
CODE
|
|
end
|
|
|
|
def covering(input)
|
|
return DRCOpNodeInteractingWithCount::new(self, primary, input, :enclosing)
|
|
end
|
|
|
|
def overlapping
|
|
return DRCOpNodeInteractingWithCount::new(self, primary, input, :overlapping)
|
|
end
|
|
|
|
def inside
|
|
return DRCOpNodeInteracting::new(self, primary, input, :inside)
|
|
end
|
|
|
|
def outside
|
|
return DRCOpNodeInteracting::new(self, primary, input, :outside)
|
|
end
|
|
|
|
def interacting
|
|
return DRCOpNodeInteractingWithCount::new(self, primary, input, :interacting)
|
|
end
|
|
|
|
%w(width space overlap enclosing separation isolated notch).each do |f|
|
|
eval <<"CODE"
|
|
def #{f}(*args)
|
|
|
|
metrics = RBA::Region::Euclidian
|
|
minp = nil
|
|
maxp = nil
|
|
alim = nil
|
|
whole_edges = false
|
|
other = nil
|
|
shielded = nil
|
|
opposite_filter = RBA::Region::NoOppositeFilter
|
|
rect_filter = RBA::Region::NoRectFilter
|
|
|
|
n = 1
|
|
args.each do |a|
|
|
if a.is_a?(DRCMetrics)
|
|
metrics = a.value
|
|
elsif a.is_a?(DRCWholeEdges)
|
|
whole_edges = a.value
|
|
elsif a.is_a?(DRCOppositeErrorFilter)
|
|
opposite_filter = a.value
|
|
elsif a.is_a?(DRCRectangleErrorFilter)
|
|
rect_filter = RBA::Region::RectFilter::new(a.value.to_i | rect_filter.to_i)
|
|
elsif a.is_a?(DRCAngleLimit)
|
|
alim = a.value
|
|
elsif a.is_a?(DRCOpNode)
|
|
other = a
|
|
elsif a.is_a?(DRCProjectionLimits)
|
|
minp = self._prep_value(a.min)
|
|
maxp = self._prep_value(a.max)
|
|
elsif a.is_a?(DRCShielded)
|
|
shielded = a.value
|
|
else
|
|
raise("#{f}: Parameter #" + n.to_s + " does not have an expected type")
|
|
end
|
|
n += 1
|
|
end
|
|
|
|
args = [ whole_edges, metrics, alim, minp, maxp ]
|
|
|
|
args << (shielded == nil ? true : shielded)
|
|
if :#{f} != :width && :#{f} != :notch
|
|
args << opposite_filter
|
|
args << rect_filter
|
|
elsif opposite_filter != RBA::Region::NoOppositeFilter
|
|
raise("#{f}: an opposite error filter cannot be used with this check")
|
|
elsif rect_filter != RBA::Region::NoRectFilter
|
|
raise("#{f}: a rectangle error filter cannot be used with this check")
|
|
end
|
|
|
|
if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated
|
|
if other
|
|
raise("No other layer must be specified for a single-layer check (here: #{f})")
|
|
end
|
|
else
|
|
if !other
|
|
raise("The other layer must be specified for a two-layer check (here: #{f})")
|
|
end
|
|
end
|
|
|
|
DRCOpNodeCheck::new(self, :#{f}, other, *args)
|
|
|
|
end
|
|
CODE
|
|
end
|
|
|
|
def iso(*args)
|
|
isolated(*args)
|
|
end
|
|
|
|
def sep(*args)
|
|
separation(*args)
|
|
end
|
|
|
|
def enc(*args)
|
|
separation(*args)
|
|
end
|
|
|
|
end
|
|
|
|
end</text>
|
|
</klayout-macro>
|