mirror of https://github.com/KLayout/klayout.git
WIP: refactoring of DRC Ruby code
This commit is contained in:
parent
43302dbe68
commit
f3d8fb4a43
|
|
@ -397,6 +397,12 @@ CompoundRegionLogicalBoolOperationNode::CompoundRegionLogicalBoolOperationNode (
|
|||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
CompoundRegionLogicalBoolOperationNode::ResultType
|
||||
CompoundRegionLogicalBoolOperationNode::result_type () const
|
||||
{
|
||||
return Region;
|
||||
}
|
||||
|
||||
std::string CompoundRegionLogicalBoolOperationNode::generated_description () const
|
||||
{
|
||||
std::string r;
|
||||
|
|
|
|||
|
|
@ -409,14 +409,14 @@ class DB_PUBLIC CompoundRegionLogicalBoolOperationNode
|
|||
: public CompoundRegionMultiInputOperationNode
|
||||
{
|
||||
public:
|
||||
enum LogicalOp { And, Or };
|
||||
enum LogicalOp { And, Or };
|
||||
|
||||
CompoundRegionLogicalBoolOperationNode (LogicalOp op, bool invert, const std::vector<CompoundRegionOperationNode *> &inputs);
|
||||
|
||||
virtual std::string generated_description () const;
|
||||
|
||||
// specifies the result type
|
||||
virtual ResultType result_type () const { return Region; }
|
||||
virtual ResultType result_type () const;
|
||||
|
||||
// the different computation slots
|
||||
virtual void do_compute_local (db::Layout *layout, const shape_interactions<db::Polygon, db::Polygon> &interactions, std::vector<std::unordered_set<db::Polygon> > &results, size_t max_vertex_count, double area_ratio) const
|
||||
|
|
|
|||
|
|
@ -0,0 +1,947 @@
|
|||
# $autorun-early
|
||||
|
||||
module DRC
|
||||
|
||||
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.engine._context("#{f}") do
|
||||
self._build_geo_bool_node(other, :#{f})
|
||||
end
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
def !()
|
||||
self.engine._context("!") do
|
||||
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
|
||||
end
|
||||
|
||||
def _check_numeric(v, symbol)
|
||||
if ! v.is_a?(Float) && ! v.is_a?(1.class)
|
||||
if symbol
|
||||
raise("Argument '#{symbol}' (#{v.inspect}) isn't numeric in operation '#{self.description}'")
|
||||
else
|
||||
raise("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.engine._context("rounded_corners") do
|
||||
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
|
||||
end
|
||||
|
||||
def smoothed(d)
|
||||
self.engine._context("smoothed") do
|
||||
DRCOpNodeFilter::new(@engine, self, :new_smoothed, "smoothed", self.make_value(d, :d))
|
||||
end
|
||||
end
|
||||
|
||||
def corners(as_dots = DRCAsDots::new(false))
|
||||
self.engine._context("corners") do
|
||||
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
|
||||
end
|
||||
|
||||
%w(middle extent_refs).each do |f|
|
||||
eval <<"CODE"
|
||||
def #{f}(*args)
|
||||
|
||||
self.engine._context("#{f}") do
|
||||
|
||||
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
|
||||
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
def odd_polygons
|
||||
return DRCOpNodeFilter::new(@engine, self, :new_strange_polygons_filter, "odd_polygon")
|
||||
end
|
||||
|
||||
def rectangles
|
||||
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 hulls
|
||||
return DRCOpNodeFilter::new(@engine, self, :new_hulls, "hull")
|
||||
end
|
||||
|
||||
def edges
|
||||
return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges")
|
||||
end
|
||||
|
||||
def sized(*args)
|
||||
|
||||
self.engine._context("sized") do
|
||||
|
||||
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
|
||||
|
||||
DRCOpNodeFilter::new(@engine, self, :new_sized, "sized", *args)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def extents(e = 0)
|
||||
self.engine._context("extents") do
|
||||
DRCOpNodeFilter::new(@engine, self, :new_extents, "extents", self._make_value(e, :e))
|
||||
end
|
||||
end
|
||||
|
||||
def first_edges
|
||||
DRCOpNodeFilter::new(@engine, self, :new_edge_pair_to_first_edges, "first_edges")
|
||||
end
|
||||
|
||||
def second_edges
|
||||
DRCOpNodeFilter::new(@engine, self, :new_edge_pair_to_second_edges, "second_edges")
|
||||
end
|
||||
|
||||
def end_segments(length, fraction = 0.0)
|
||||
self.engine._context("end_segments") do
|
||||
self._check_numeric(fraction, :fraction)
|
||||
DRCOpNodeFilter::new(@engine, self, :new_end_segments, "end_segments", self._make_value(length, :length), fraction)
|
||||
end
|
||||
end
|
||||
|
||||
def start_segments(length, fraction = 0.0)
|
||||
self.engine._context("start_segments") do
|
||||
self._check_numeric(fraction, :fraction)
|
||||
DRCOpNodeFilter::new(@engine, self, :new_start_segments, "start_segments", self._make_value(length, :length), fraction)
|
||||
end
|
||||
end
|
||||
|
||||
def centers(length, fraction = 0.0)
|
||||
self.engine._context("centers") do
|
||||
self._check_numeric(fraction, :fraction)
|
||||
DRCOpNodeFilter::new(@engine, self, :new_centers, "centers", self._make_value(length, :length), fraction)
|
||||
end
|
||||
end
|
||||
|
||||
def extended(*args)
|
||||
|
||||
self.engine._context("extended") do
|
||||
|
||||
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
|
||||
|
||||
DRCOpNodeFilter::new(@engine, self, :new_extended, "extended", *args)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def extended_in(e)
|
||||
self.engine._context("extended_in") do
|
||||
DRCOpNodeFilter::new(@engine, self, :new_extended_in, "extended_in", self._make_value(e))
|
||||
end
|
||||
end
|
||||
|
||||
def extended_out(e)
|
||||
self.engine._context("extended_out") do
|
||||
DRCOpNodeFilter::new(@engine, self, :new_extended_out, "extended_out", self._make_value(e))
|
||||
end
|
||||
end
|
||||
|
||||
def polygons
|
||||
self.engine._context("polygons") do
|
||||
DRCOpNodeFilter::new(@engine, self, :new_polygons, "polygons")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class DRCOpNodeLogicalBool < DRCOpNode
|
||||
|
||||
attr_accessor :children
|
||||
attr_accessor :op
|
||||
|
||||
def initialize(engine, op)
|
||||
super(engine)
|
||||
self.children = []
|
||||
self.op = op
|
||||
self.description = 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 = {
|
||||
:if_all => RBA::CompoundRegionOperationNode::LogicalOp::LogAnd,
|
||||
:if_any => RBA::CompoundRegionOperationNode::LogicalOp::LogOr,
|
||||
:if_none => RBA::CompoundRegionOperationNode::LogicalOp::LogOr
|
||||
} [self.op]
|
||||
invert = {
|
||||
:if_all => false,
|
||||
:if_any => false,
|
||||
:if_none => true
|
||||
} [self.op]
|
||||
RBA::CompoundRegionOperationNode::new_logical_boolean(log_op, invert, 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
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,372 @@
|
|||
# $autorun-early
|
||||
|
||||
module DRC
|
||||
|
||||
class DRCLayer
|
||||
|
||||
# %DRC%
|
||||
# @name drc
|
||||
# @brief Universal DRC function
|
||||
# @synopsis drc(...)
|
||||
#
|
||||
# TODO: add doc
|
||||
|
||||
def drc(op)
|
||||
@engine._context("drc") do
|
||||
requires_region
|
||||
return DRCLayer::new(@engine, self.data.complex_op(op.create_node({})))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class DRCEngine
|
||||
|
||||
# %DRC%
|
||||
# @name case
|
||||
# @brief A conditional selector for the "drc" universal DRC function
|
||||
# @synopsis case(...)
|
||||
#
|
||||
# This function provides a conditional selector for the "drc" function.
|
||||
# It is used this way:
|
||||
#
|
||||
# @code
|
||||
# out = in.drc(case(c1, r1, c2, r2, ..., cn, rn)
|
||||
# out = in.drc(case(c1, r1, c2, r2, ..., cn, rn, rdef)
|
||||
# @/code
|
||||
#
|
||||
# This function will evaluate c1 which is a universal DRC expression (see \drc).
|
||||
# If the result is not empty, "case" will evaluate and return r1. Otherwise it
|
||||
# will continue with c2 and the result of this expression is not empty it will
|
||||
# return r2. Otherwise it will continue with c3/r3 etc.
|
||||
#
|
||||
# If an odd number of arguments is given, the last expression is evaluated if
|
||||
# none of the conditions c1..cn gives a non-empty result.
|
||||
#
|
||||
# As a requirement, the result types of all r1..rn expressions and the rdef
|
||||
# needs to be the same - i.e. all need to render polygons or edges or edge pairs.
|
||||
|
||||
def case(*args)
|
||||
|
||||
self._context("case") do
|
||||
|
||||
anum = 1
|
||||
|
||||
types = []
|
||||
args.each do |a|
|
||||
if !a.is_a?(DRCOpNode)
|
||||
raise("All inputs need to be valid compound operation expressions (argument ##{anum} isn't)")
|
||||
end
|
||||
if a % 2 == 0
|
||||
types << a.result_type
|
||||
end
|
||||
anum += 1
|
||||
end
|
||||
|
||||
if types.sort.uniq.size > 1
|
||||
raise("All result arguments need to have the same type (we got '" + types.collect(:to_s).join(",") + "')")
|
||||
end
|
||||
|
||||
DRCOpNodeCase::new(self, args)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
# @name secondary
|
||||
# @brief Provides secondary input for the "drc" universal DRC function
|
||||
# @synopsis secondary(layer)
|
||||
#
|
||||
# To supply additional input for the universal DRC expressions (see \drc), use
|
||||
# "secondary" with a layer argument. This example provides a boolean AND
|
||||
# between l1 and l2:
|
||||
#
|
||||
# @code
|
||||
# l1 = layer(1, 0)
|
||||
# l2 = layer(2, 0)
|
||||
# out = l1.drc(primary & secondary(l2))
|
||||
# @/code
|
||||
|
||||
def secondary(layer)
|
||||
|
||||
self._context("secondary") do
|
||||
|
||||
layer.requires_region
|
||||
|
||||
res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_secondary(layer.data))
|
||||
res.description = "secondary"
|
||||
return res
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
# @name primary
|
||||
# @brief Represents the primary input of the universal DRC function
|
||||
# @synopsis primary
|
||||
#
|
||||
# The primary input of the universal DRC function is the layer the \drc function
|
||||
# is called on.
|
||||
|
||||
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
|
||||
# @synopsis if_all(c1, ... cn)
|
||||
#
|
||||
# This function will evaluate the conditions c1 to cn and return the
|
||||
# current primary shape if all conditions render a non-empty result.
|
||||
# The following example selects all shapes which are rectangles and
|
||||
# whose area is larger than 0.5 square micrometers:
|
||||
#
|
||||
# @code
|
||||
# out = in.drc(if_all(area > 0.5, rectangle))
|
||||
# @/code
|
||||
#
|
||||
# The condition expressions can be of any type (edges, edge pairs and polygons).
|
||||
|
||||
# %DRC%
|
||||
# @name if_any
|
||||
# @brief Evaluates to the primary shape when any condition expression results is non-empty
|
||||
# @synopsis if_any(c1, ... cn)
|
||||
#
|
||||
# This function will evaluate the conditions c1 to cn and return the
|
||||
# current primary shape if at least one condition renders a non-empty result.
|
||||
# See \if_all for an example how to use the if_... functions.
|
||||
|
||||
# %DRC%
|
||||
# @name if_none
|
||||
# @brief Evaluates to the primary shape when all of the condition expression results are empty
|
||||
# @synopsis if_none(c1, ... cn)
|
||||
#
|
||||
# This function will evaluate the conditions c1 to cn and return the
|
||||
# current primary shape if all conditions renders an empty result.
|
||||
# See \if_all for an example how to use the if_... functions.
|
||||
|
||||
%w(
|
||||
if_all
|
||||
if_any
|
||||
if_none
|
||||
).each do |f|
|
||||
eval <<"CODE"
|
||||
def #{f}(*args)
|
||||
|
||||
self._context("#{f}") do
|
||||
|
||||
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
|
||||
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
# @name bbox_height
|
||||
# @brief Selects primary shapes based on their bounding box height
|
||||
# @synopsis bbox_height (in condition)
|
||||
#
|
||||
# This method creates a universal DRC expression (see \drc) to select primary shapes whose
|
||||
# bounding box height satisfies the condition. Conditions can be written as arithmetic comparisons
|
||||
# against numeric values. For example, "bbox_height < 2.0" will select all primary shapes whose
|
||||
# bounding box height is less than 2 micrometers. See \drc for more details about comparison
|
||||
# specs.
|
||||
|
||||
# %DRC%
|
||||
# @name bbox_width
|
||||
# @brief Selects primary shapes based on their bounding box width
|
||||
# @synopsis bbox_max (in condition)
|
||||
#
|
||||
# See \bbox_height for more details.
|
||||
|
||||
# %DRC%
|
||||
# @name bbox_max
|
||||
# @brief Selects primary shapes based on their bounding box height or width, whichever is larger
|
||||
# @synopsis bbox_max (in condition)
|
||||
#
|
||||
# See \bbox_height for more details.
|
||||
|
||||
# %DRC%
|
||||
# @name bbox_min
|
||||
# @brief Selects primary shapes based on their bounding box height or width, whichever is smaller
|
||||
# @synopsis bbox_max (in condition)
|
||||
#
|
||||
# See \bbox_height for more details.
|
||||
|
||||
%w(
|
||||
bbox_height
|
||||
bbox_max
|
||||
bbox_min
|
||||
bbox_width
|
||||
).each do |f|
|
||||
eval <<"CODE"
|
||||
def #{f}
|
||||
self._context("#{f}") do
|
||||
primary.#{f}
|
||||
end
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
%w(
|
||||
area
|
||||
holes
|
||||
hulls
|
||||
odd_polygons
|
||||
perimeter
|
||||
rectangles
|
||||
rectilinear
|
||||
).each do |f|
|
||||
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
|
||||
eval <<"CODE"
|
||||
def _cop_#{f}
|
||||
primary.#{f}
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
%w(
|
||||
extent_refs
|
||||
extents
|
||||
middle
|
||||
rounded_corners
|
||||
sized
|
||||
smoothed
|
||||
).each do |f|
|
||||
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
|
||||
eval <<"CODE"
|
||||
def _cop_#{f}(*args)
|
||||
primary.#{f}(*args)
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
%w(
|
||||
covering
|
||||
inside
|
||||
interacting
|
||||
outside
|
||||
overlapping
|
||||
).each do |f|
|
||||
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
|
||||
eval <<"CODE"
|
||||
def _cop_#{f}(other)
|
||||
primary.#{f}(other)
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
%w(
|
||||
enclosing
|
||||
isolated
|
||||
notch
|
||||
overlap
|
||||
separation
|
||||
space
|
||||
width
|
||||
).each do |f|
|
||||
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
|
||||
eval <<"CODE"
|
||||
def _cop_#{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("Parameter #" + n.to_s + " does not have an expected type (is " + a.inspect + ")")
|
||||
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("An opposite error filter cannot be used with this check")
|
||||
elsif rect_filter != RBA::Region::NoRectFilter
|
||||
raise("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")
|
||||
end
|
||||
else
|
||||
if !other
|
||||
raise("The other layer must be specified for a two-layer check")
|
||||
end
|
||||
end
|
||||
|
||||
DRCOpNodeCheck::new(self, :#{f}, other, *args)
|
||||
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
def _cop_iso(*args)
|
||||
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
|
||||
_cop_isolated(*args)
|
||||
end
|
||||
|
||||
def _cop_sep(*args)
|
||||
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
|
||||
_cop_separation(*args)
|
||||
end
|
||||
|
||||
def _cop_enc(*args)
|
||||
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
|
||||
_cop_separation(*args)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -98,16 +98,20 @@ module DRC
|
|||
|
||||
def connect(a, b)
|
||||
|
||||
a.is_a?(DRC::DRCLayer) || raise("First argument of Netter#connect must be a layer")
|
||||
b.is_a?(DRC::DRCLayer) || raise("Second argument of Netter#connect must be a layer")
|
||||
a.requires_texts_or_region("Netter#connect (first argument)")
|
||||
b.requires_texts_or_region("Netter#connect (second argument)")
|
||||
@engine._context("connect") do
|
||||
|
||||
register_layer(a.data)
|
||||
register_layer(b.data)
|
||||
a.data.is_a?(RBA::Region) && @l2n.connect(a.data)
|
||||
b.data.is_a?(RBA::Region) && @l2n.connect(b.data)
|
||||
@l2n.connect(a.data, b.data)
|
||||
a.is_a?(DRC::DRCLayer) || raise("First argument must be a layer")
|
||||
b.is_a?(DRC::DRCLayer) || raise("Second argument must be a layer")
|
||||
a.requires_texts_or_region
|
||||
b.requires_texts_or_region
|
||||
|
||||
register_layer(a.data)
|
||||
register_layer(b.data)
|
||||
a.data.is_a?(RBA::Region) && @l2n.connect(a.data)
|
||||
b.data.is_a?(RBA::Region) && @l2n.connect(b.data)
|
||||
@l2n.connect(a.data, b.data)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -122,12 +126,16 @@ module DRC
|
|||
|
||||
def connect_global(l, name)
|
||||
|
||||
l.is_a?(DRC::DRCLayer) || raise("Layer argument of Netter#connect_global must be a layer")
|
||||
l.requires_texts_or_region("Netter#connect_global (layer argument)")
|
||||
@engine._context("connect_global") do
|
||||
|
||||
register_layer(l.data)
|
||||
l.data.is_a?(RBA::Region) && @l2n.connect(l.data)
|
||||
@l2n.connect_global(l.data, name)
|
||||
l.is_a?(DRC::DRCLayer) || raise("Layer argument must be a layer")
|
||||
l.requires_texts_or_region
|
||||
|
||||
register_layer(l.data)
|
||||
l.data.is_a?(RBA::Region) && @l2n.connect(l.data)
|
||||
@l2n.connect_global(l.data, name)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -186,22 +194,26 @@ module DRC
|
|||
|
||||
def extract_devices(devex, layer_selection)
|
||||
|
||||
ensure_data
|
||||
@engine._context("extract_devices") do
|
||||
|
||||
devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of Netter#extract_devices must be a device extractor instance in the two-arguments form")
|
||||
ensure_data
|
||||
|
||||
layer_selection.is_a?(Hash) || raise("Second argument of Netter#extract_devices must be a hash")
|
||||
devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of must be a device extractor instance in the two-arguments form")
|
||||
|
||||
layer_selection.is_a?(Hash) || raise("Second argument must be a hash")
|
||||
|
||||
ls = {}
|
||||
layer_selection.keys.sort.each do |n|
|
||||
l = layer_selection[n]
|
||||
l.requires_texts_or_region
|
||||
register_layer(l.data)
|
||||
ls[n.to_s] = l.data
|
||||
end
|
||||
|
||||
@engine._cmd(@l2n, :extract_devices, devex, ls)
|
||||
|
||||
ls = {}
|
||||
layer_selection.keys.sort.each do |n|
|
||||
l = layer_selection[n]
|
||||
l.requires_texts_or_region("Netter#extract_devices (#{n} layer)")
|
||||
register_layer(l.data)
|
||||
ls[n.to_s] = l.data
|
||||
end
|
||||
|
||||
@engine._cmd(@l2n, :extract_devices, devex, ls)
|
||||
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -213,8 +225,10 @@ module DRC
|
|||
# does not correspond to the physical dimensions.
|
||||
|
||||
def device_scaling(factor)
|
||||
@device_scaling = factor
|
||||
@l2n && @l2n.device_scaling = factor
|
||||
@engine._context("device_scaling") do
|
||||
@device_scaling = factor
|
||||
@l2n && @l2n.device_scaling = factor
|
||||
end
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -254,16 +268,22 @@ module DRC
|
|||
# on "clear_connections".
|
||||
|
||||
def connect_implicit(arg1, arg2 = nil)
|
||||
cleanup
|
||||
if arg2
|
||||
(arg2.is_a?(String) && arg2 != "") || raise("The second argument of 'connect_implicit' has to be a non-empty string")
|
||||
arg1.is_a?(String) || raise("The first argument of 'connect_implicit' has to be a string")
|
||||
@connect_implicit_per_cell[arg1] ||= []
|
||||
@connect_implicit_per_cell[arg1] << arg2
|
||||
else
|
||||
arg1.is_a?(String) || raise("The argument of 'connect_implicit' has to be a string")
|
||||
@connect_implicit << arg1
|
||||
|
||||
@engine._context("connect_implicit") do
|
||||
|
||||
cleanup
|
||||
if arg2
|
||||
(arg2.is_a?(String) && arg2 != "") || raise("The second argument has to be a non-empty string")
|
||||
arg1.is_a?(String) || raise("The first argument has to be a string")
|
||||
@connect_implicit_per_cell[arg1] ||= []
|
||||
@connect_implicit_per_cell[arg1] << arg2
|
||||
else
|
||||
arg1.is_a?(String) || raise("The argument has to be a string")
|
||||
@connect_implicit << arg1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -388,56 +408,60 @@ module DRC
|
|||
|
||||
def antenna_check(agate, ametal, ratio, *diodes)
|
||||
|
||||
gate_perimeter_factor = 0.0
|
||||
gate_area_factor = 1.0
|
||||
if agate.is_a?(DRC::DRCLayer)
|
||||
gate = agate
|
||||
elsif agate.is_a?(DRC::DRCAreaAndPerimeter)
|
||||
gate = agate.region
|
||||
gate_perimeter_factor = agate.perimeter_factor
|
||||
gate_area_factor = agate.area_factor
|
||||
if ! gate.is_a?(DRC::DRCLayer)
|
||||
raise("gate with area or area_and_perimeter: input argument must be a layer")
|
||||
@engine._context("antenna_check") do
|
||||
|
||||
gate_perimeter_factor = 0.0
|
||||
gate_area_factor = 1.0
|
||||
if agate.is_a?(DRC::DRCLayer)
|
||||
gate = agate
|
||||
elsif agate.is_a?(DRC::DRCAreaAndPerimeter)
|
||||
gate = agate.region
|
||||
gate_perimeter_factor = agate.perimeter_factor
|
||||
gate_area_factor = agate.area_factor
|
||||
if ! gate.is_a?(DRC::DRCLayer)
|
||||
raise("Gate with area or area_and_perimeter: input argument must be a layer")
|
||||
end
|
||||
else
|
||||
raise("Gate argument must be a layer ")
|
||||
end
|
||||
else
|
||||
raise("gate argument of Netter#antenna_check must be a layer ")
|
||||
end
|
||||
|
||||
gate.requires_region("Netter#antenna_check (gate argument)")
|
||||
gate.requires_region
|
||||
|
||||
metal_perimeter_factor = 0.0
|
||||
metal_area_factor = 1.0
|
||||
if ametal.is_a?(DRC::DRCLayer)
|
||||
metal = ametal
|
||||
elsif ametal.is_a?(DRC::DRCAreaAndPerimeter)
|
||||
metal = ametal.region
|
||||
metal_perimeter_factor = ametal.perimeter_factor
|
||||
metal_area_factor = ametal.area_factor
|
||||
if ! metal.is_a?(DRC::DRCLayer)
|
||||
raise("metal with area or area_and_perimeter: input argument must be a layer")
|
||||
metal_perimeter_factor = 0.0
|
||||
metal_area_factor = 1.0
|
||||
if ametal.is_a?(DRC::DRCLayer)
|
||||
metal = ametal
|
||||
elsif ametal.is_a?(DRC::DRCAreaAndPerimeter)
|
||||
metal = ametal.region
|
||||
metal_perimeter_factor = ametal.perimeter_factor
|
||||
metal_area_factor = ametal.area_factor
|
||||
if ! metal.is_a?(DRC::DRCLayer)
|
||||
raise("Metal with area or area_and_perimeter: input argument must be a layer")
|
||||
end
|
||||
else
|
||||
raise("Metal argument must be a layer")
|
||||
end
|
||||
else
|
||||
raise("metal argument of Netter#antenna_check must be a layer")
|
||||
end
|
||||
|
||||
metal.requires_region("Netter#antenna_check (metal argument)")
|
||||
metal.requires_region
|
||||
|
||||
if !ratio.is_a?(1.class) && !ratio.is_a?(Float)
|
||||
raise("ratio argument Netter#antenna_check is not a number")
|
||||
end
|
||||
|
||||
dl = diodes.collect do |d|
|
||||
if d.is_a?(Array)
|
||||
d.size == 2 || raise("diode specification pair expects two elements")
|
||||
d[0].requires_region("Netter#antenna_check (diode layer)")
|
||||
[ d[0].data, d[1].to_f ]
|
||||
else
|
||||
d.requires_region("Netter#antenna_check (diode layer)")
|
||||
[ d.data, 0.0 ]
|
||||
if !ratio.is_a?(1.class) && !ratio.is_a?(Float)
|
||||
raise("Ratio argument is not a number")
|
||||
end
|
||||
end
|
||||
|
||||
DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_area_factor, gate_perimeter_factor, metal.data, metal_area_factor, metal_perimeter_factor, ratio, dl))
|
||||
dl = diodes.collect do |d|
|
||||
if d.is_a?(Array)
|
||||
d.size == 2 || raise("Diode specification pair expects two elements")
|
||||
d[0].requires_region
|
||||
[ d[0].data, d[1].to_f ]
|
||||
else
|
||||
d.requires_region
|
||||
[ d.data, 0.0 ]
|
||||
end
|
||||
end
|
||||
|
||||
DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_area_factor, gate_perimeter_factor, metal.data, metal_area_factor, metal_perimeter_factor, ratio, dl))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -450,27 +474,31 @@ module DRC
|
|||
|
||||
def l2n_data
|
||||
|
||||
ensure_data
|
||||
@engine._context("l2n_data") do
|
||||
|
||||
# run extraction in a timed environment
|
||||
if ! @netlisted
|
||||
ensure_data
|
||||
|
||||
# build a glob expression from the parts
|
||||
expr = _join_glob_pattern(@connect_implicit)
|
||||
# run extraction in a timed environment
|
||||
if ! @netlisted
|
||||
|
||||
# build a glob expression from the parts
|
||||
expr = _join_glob_pattern(@connect_implicit)
|
||||
|
||||
# build cell-pattern specific glob expressions from the parts
|
||||
per_cell_expr = {}
|
||||
@connect_implicit_per_cell.each do |cell_pattern,label_pattern|
|
||||
per_cell_expr[cell_pattern] = _join_glob_pattern(label_pattern)
|
||||
end
|
||||
|
||||
@engine._cmd(@l2n, :extract_netlist, expr, per_cell_expr)
|
||||
@netlisted = true
|
||||
|
||||
# build cell-pattern specific glob expressions from the parts
|
||||
per_cell_expr = {}
|
||||
@connect_implicit_per_cell.each do |cell_pattern,label_pattern|
|
||||
per_cell_expr[cell_pattern] = _join_glob_pattern(label_pattern)
|
||||
end
|
||||
|
||||
@engine._cmd(@l2n, :extract_netlist, expr, per_cell_expr)
|
||||
@netlisted = true
|
||||
@l2n
|
||||
|
||||
end
|
||||
|
||||
@l2n
|
||||
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -482,7 +510,9 @@ module DRC
|
|||
# calls must have been made before this method is used. Further \connect
|
||||
# statements will clear the netlist and re-extract it again.
|
||||
def netlist
|
||||
l2n_data && @l2n.netlist
|
||||
@engine._context("netlist") do
|
||||
l2n_data && @l2n.netlist
|
||||
end
|
||||
end
|
||||
|
||||
def _finish
|
||||
|
|
|
|||
|
|
@ -78,20 +78,27 @@ module DRC
|
|||
end
|
||||
|
||||
def set_box(method, *args)
|
||||
box = nil
|
||||
if args.size == 1
|
||||
box = args[0]
|
||||
box.is_a?(RBA::DBox) || raise("'#{method}' method requires a box specification")
|
||||
elsif args.size == 2
|
||||
(args[0].is_a?(RBA::DPoint) && args[1].is_a?(RBA::DPoint)) || raise("'#{method}' method requires a box specification with two points")
|
||||
box = RBA::DBox::new(args[0], args[1])
|
||||
elsif args.size == 4
|
||||
box = RBA::DBox::new(*args)
|
||||
else
|
||||
raise("Invalid number of arguments for '#{method}' method")
|
||||
|
||||
@engine._context(method) do
|
||||
|
||||
box = nil
|
||||
if args.size == 1
|
||||
box = args[0]
|
||||
box.is_a?(RBA::DBox) || raise("Method requires a box specification")
|
||||
elsif args.size == 2
|
||||
(args[0].is_a?(RBA::DPoint) && args[1].is_a?(RBA::DPoint)) || raise("Method requires a box specification with two points")
|
||||
box = RBA::DBox::new(args[0], args[1])
|
||||
elsif args.size == 4
|
||||
box = RBA::DBox::new(*args)
|
||||
else
|
||||
raise("Invalid number of arguments (1, 2 or 4 expected)")
|
||||
end
|
||||
@box = RBA::Box::from_dbox(box * (1.0 / @layout.dbu))
|
||||
|
||||
self
|
||||
|
||||
end
|
||||
@box = RBA::Box::from_dbox(box * (1.0 / @layout.dbu))
|
||||
self
|
||||
|
||||
end
|
||||
|
||||
def inplace_clip(*args)
|
||||
|
|
@ -113,17 +120,21 @@ module DRC
|
|||
end
|
||||
|
||||
def inplace_cell(arg)
|
||||
@cell = layout.cell(arg)
|
||||
@cell ||= layout.create_cell(arg)
|
||||
self
|
||||
@engine._context("inplace_cell") do
|
||||
@cell = layout.cell(arg)
|
||||
@cell ||= layout.create_cell(arg)
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
def inplace_select(*args)
|
||||
args.each do |a|
|
||||
a.is_a?(String) || raise("Invalid arguments to 'select' method - must be strings")
|
||||
@sel.push(a)
|
||||
@engine._context("inplace_select") do
|
||||
args.each do |a|
|
||||
a.is_a?(String) || raise("Invalid arguments - must be strings")
|
||||
@sel.push(a)
|
||||
end
|
||||
self
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -238,9 +249,11 @@ module DRC
|
|||
%w(select cell clip touching overlapping).each do |f|
|
||||
eval <<"CODE"
|
||||
def #{f}(*args)
|
||||
s = self.dup
|
||||
s.inplace_#{f}(*args)
|
||||
s
|
||||
@engine._context("#{f}") do
|
||||
s = self.dup
|
||||
s.inplace_#{f}(*args)
|
||||
s
|
||||
end
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
|
@ -256,13 +269,15 @@ CODE
|
|||
# @/code
|
||||
|
||||
def extent
|
||||
layer = input
|
||||
if @box
|
||||
layer.insert(RBA::DBox::from_ibox(@box) * @layout.dbu)
|
||||
else
|
||||
layer.insert(RBA::DBox::from_ibox(@cell.bbox) * @layout.dbu)
|
||||
@engine._context("extent") do
|
||||
layer = input
|
||||
if @box
|
||||
layer.insert(RBA::DBox::from_ibox(@box) * @layout.dbu)
|
||||
else
|
||||
layer.insert(RBA::DBox::from_ibox(@cell.bbox) * @layout.dbu)
|
||||
end
|
||||
layer
|
||||
end
|
||||
layer
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -309,8 +324,10 @@ CODE
|
|||
# Use the global version of "input" without a source object to address the default source.
|
||||
|
||||
def input(*args)
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, RBA::Region))
|
||||
@engine._context("input") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, RBA::Region))
|
||||
end
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -331,8 +348,10 @@ CODE
|
|||
# Use the global version of "labels" without a source object to address the default source.
|
||||
|
||||
def labels(*args)
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, RBA::Texts))
|
||||
@engine._context("labels") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, RBA::Texts))
|
||||
end
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -352,8 +371,10 @@ CODE
|
|||
# Use the global version of "polygons" without a source object to address the default source.
|
||||
|
||||
def polygons(*args)
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, RBA::Region))
|
||||
@engine._context("polygons") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, RBA::Region))
|
||||
end
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -376,8 +397,10 @@ CODE
|
|||
# This method has been introduced in version 0.27.
|
||||
|
||||
def edges(*args)
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, RBA::Edges))
|
||||
@engine._context("edges") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, RBA::Edges))
|
||||
end
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -400,8 +423,10 @@ CODE
|
|||
# This method has been introduced in version 0.27.
|
||||
|
||||
def edge_pairs(*args)
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, RBA::EdgePairs))
|
||||
@engine._context("edge_pairs") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, RBA::EdgePairs))
|
||||
end
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
<file alias="_drc_patch.rb">built-in-macros/_drc_patch.rb</file>
|
||||
<file alias="_drc_source.rb">built-in-macros/_drc_source.rb</file>
|
||||
<file alias="_drc_tags.rb">built-in-macros/_drc_tags.rb</file>
|
||||
<file alias="_drc_complex_ops.rb">built-in-macros/_drc_complex_ops.rb</file>
|
||||
<file alias="_drc_cop_integration.rb">built-in-macros/_drc_cop_integration.rb</file>
|
||||
<file alias="drc_interpreters.lym">built-in-macros/drc_interpreters.lym</file>
|
||||
<file alias="drc_install.lym">built-in-macros/drc_install.lym</file>
|
||||
</qresource>
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ class DRCLayer
|
|||
|
||||
def drc(op)
|
||||
requires_region("drc")
|
||||
return DRCLayer::new(@engine, self.data.complex_op(op.create_node))
|
||||
return DRCLayer::new(@engine, self.data.complex_op(op.create_node({})))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
class DRCOpNode
|
||||
|
||||
|
|
@ -37,7 +37,16 @@ class DRCOpNode
|
|||
self.description = "Basic"
|
||||
end
|
||||
|
||||
def create_node
|
||||
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
|
||||
|
||||
|
|
@ -342,9 +351,9 @@ class DRCOpNodeLogicalBool < DRCOpNode
|
|||
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
|
||||
end
|
||||
|
||||
def create_node
|
||||
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 })
|
||||
RBA::CompoundRegionOperationNode::new_logical_boolean(log_op, false, self.children.collect { |c| c.create_node(cache) })
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -365,14 +374,14 @@ class DRCOpNodeBool < DRCOpNode
|
|||
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
|
||||
end
|
||||
|
||||
def create_node
|
||||
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
|
||||
n = c.create_node(cache)
|
||||
if n.result_type == RBA::CompoundRegionOperationNode::ResultType::EdgePairs
|
||||
n = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(n)
|
||||
end
|
||||
|
|
@ -397,8 +406,8 @@ class DRCOpNodeCase < DRCOpNode
|
|||
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
|
||||
end
|
||||
|
||||
def create_node
|
||||
RBA::CompoundRegionOperationNode::new_case(self.children.collect { |c| c.create_node })
|
||||
def do_create_node(cache)
|
||||
RBA::CompoundRegionOperationNode::new_case(self.children.collect { |c| c.create_node(cache) })
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -556,8 +565,8 @@ class DRCOpNodeAreaFilter < DRCOpNodeWithCompare
|
|||
self.inverted ? "area" : "not_area"
|
||||
end
|
||||
|
||||
def create_node
|
||||
args = [ self.input.create_node, self.inverse ]
|
||||
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
|
||||
|
|
@ -589,8 +598,8 @@ class DRCOpNodeEdgeLengthFilter < DRCOpNodeWithCompare
|
|||
self.inverted ? "length" : "not_length"
|
||||
end
|
||||
|
||||
def create_node
|
||||
args = [ self.input.create_node, self.inverse ]
|
||||
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
|
||||
|
|
@ -622,8 +631,8 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare
|
|||
self.inverted ? "angle" : "not_angle"
|
||||
end
|
||||
|
||||
def create_node
|
||||
args = [ self.input.create_node, self.inverse ]
|
||||
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))
|
||||
|
|
@ -654,8 +663,8 @@ class DRCOpNodePerimeterFilter < DRCOpNodeWithCompare
|
|||
self.inverted ? "perimeter" : "not_perimeter"
|
||||
end
|
||||
|
||||
def create_node
|
||||
args = [ self.input.create_node, self.inverse ]
|
||||
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
|
||||
|
|
@ -686,8 +695,8 @@ class DRCOpNodeInteractingWithCount < DRCOpNodeWithCompare
|
|||
self.description = (self.inverted ? "" : "not_") + self.op.to_s
|
||||
end
|
||||
|
||||
def create_node
|
||||
args = [ self.a.create_node, self.b.create_node, self.inverse ]
|
||||
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
|
||||
|
|
@ -721,10 +730,10 @@ class DRCOpNodeInteracting < DRCOpNode
|
|||
self.description = (self.inverted ? "" : "not_") + self.op.to_s
|
||||
end
|
||||
|
||||
def create_node
|
||||
def do_create_node(cache)
|
||||
factory = { :inside => :new_inside,
|
||||
:outside => :new_outside }[self.op]
|
||||
RBA::CompoundRegionOperationNode::send(factory, self.a.create_node, self.b.create_node, self.inverse)
|
||||
RBA::CompoundRegionOperationNode::send(factory, self.a.create_node(cache), self.b.create_node(cache), self.inverse)
|
||||
end
|
||||
|
||||
def inverted
|
||||
|
|
@ -741,7 +750,7 @@ class DRCOpNodeFilter < DRCOpNode
|
|||
attr_accessor :factory
|
||||
attr_accessor :args
|
||||
|
||||
def initialize(engine, input, factory, description, args = [])
|
||||
def initialize(engine, input, factory, description, *args)
|
||||
super(engine)
|
||||
self.input = input
|
||||
self.factory = factory
|
||||
|
|
@ -757,8 +766,8 @@ class DRCOpNodeFilter < DRCOpNode
|
|||
end
|
||||
end
|
||||
|
||||
def create_node
|
||||
RBA::CompoundRegionOperationNode::send(self.factory, self.input.create_node, *args)
|
||||
def do_create_node(cache)
|
||||
RBA::CompoundRegionOperationNode::send(self.factory, self.input.create_node(cache), *args)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -785,7 +794,7 @@ class DRCOpNodeCheck < DRCOpNodeWithCompare
|
|||
end
|
||||
end
|
||||
|
||||
def create_node
|
||||
def do_create_node(cache)
|
||||
|
||||
if !(self.lt || self.le) && !(self.gt || self.ge)
|
||||
raise("No value given for check #{self.check}")
|
||||
|
|
@ -840,8 +849,8 @@ class DRCOpNodeBBoxParameterFilter < DRCOpNodeWithCompare
|
|||
self.description = description
|
||||
end
|
||||
|
||||
def create_node
|
||||
args = [ self.input.create_node, self.inverse ]
|
||||
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
|
||||
|
|
@ -870,8 +879,8 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
|
|||
self.description = "corners"
|
||||
end
|
||||
|
||||
def create_node
|
||||
args = [ self.input.create_node ]
|
||||
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))
|
||||
|
|
@ -905,7 +914,7 @@ class DRCOpNodeRelativeExtents < DRCOpNode
|
|||
end
|
||||
end
|
||||
|
||||
def create_node
|
||||
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
|
||||
|
|
@ -929,7 +938,7 @@ class DRCEngine
|
|||
end
|
||||
|
||||
def secondary(layer)
|
||||
layer.requires_region
|
||||
layer.requires_region("secondary")
|
||||
res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_secondary(layer.data))
|
||||
res.description = "secondary"
|
||||
return res
|
||||
|
|
|
|||
Loading…
Reference in New Issue