diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index e89e0cb13..6070f5ab5 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -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; diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h index fdbe0028c..9cc55f968 100644 --- a/src/db/db/dbCompoundOperation.h +++ b/src/db/db/dbCompoundOperation.h @@ -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 &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 &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb new file mode 100644 index 000000000..fa03882e0 --- /dev/null +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -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 diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb new file mode 100644 index 000000000..a8416a1cb --- /dev/null +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -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 + diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 58f39e071..a81354d49 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -92,15 +92,21 @@ module DRC end def projection_limits(*args) - DRCProjectionLimits::new(*args) + self._context("projection_limits") do + DRCProjectionLimits::new(*args) + end end def angle_limit(a) - DRCAngleLimit::new(a) + self._context("angle_limit") do + DRCAngleLimit::new(a) + end end def whole_edges(f = true) - DRCWholeEdges::new(f) + self._context("whole_edges") do + DRCWholeEdges::new(f) + end end def euclidian @@ -148,11 +154,15 @@ module DRC end def pattern(p) - DRCPattern::new(true, p) + self._context("pattern") do + DRCPattern::new(true, p) + end end def text(p) - DRCPattern::new(false, p) + self._context("text") do + DRCPattern::new(false, p) + end end def as_dots @@ -168,15 +178,21 @@ module DRC end def area_only(r) - DRCAreaAndPerimeter::new(r, 1.0, 0.0) + self._context("area_only") do + DRCAreaAndPerimeter::new(r, 1.0, 0.0) + end end def perimeter_only(r, f) - DRCAreaAndPerimeter::new(r, 0.0, f) + self._context("perimeter_only") do + DRCAreaAndPerimeter::new(r, 0.0, f) + end end def area_and_perimeter(r, f) - DRCAreaAndPerimeter::new(r, 1.0, f) + self._context("area_and_perimeter") do + DRCAreaAndPerimeter::new(r, 1.0, f) + end end # %DRC% @@ -190,14 +206,16 @@ module DRC # information comments such as instance coordinates or pin names. def write_spice(use_net_names = nil, with_comments = nil) - writer = RBA::NetlistSpiceWriter::new - if use_net_names != nil - writer.use_net_names = use_net_names + self._context("write_spice") do + writer = RBA::NetlistSpiceWriter::new + if use_net_names != nil + writer.use_net_names = use_net_names + end + if with_comments != nil + writer.with_comments = with_comments + end + writer end - if with_comments != nil - writer.with_comments = with_comments - end - writer end # %DRC% @@ -211,7 +229,9 @@ module DRC # about this extractor (non-strict mode applies for 'mos3'). def mos3(name) - RBA::DeviceExtractorMOS3Transistor::new(name) + self._context("mos3") do + RBA::DeviceExtractorMOS3Transistor::new(name) + end end # %DRC% @@ -225,7 +245,9 @@ module DRC # about this extractor (non-strict mode applies for 'mos4'). def mos4(name) - RBA::DeviceExtractorMOS4Transistor::new(name) + self._context("mos4") do + RBA::DeviceExtractorMOS4Transistor::new(name) + end end # %DRC% @@ -241,7 +263,9 @@ module DRC # about this extractor (strict mode applies for 'dmos3'). def dmos3(name) - RBA::DeviceExtractorMOS3Transistor::new(name, true) + self._context("dmos3") do + RBA::DeviceExtractorMOS3Transistor::new(name, true) + end end # %DRC% @@ -257,7 +281,9 @@ module DRC # about this extractor (strict mode applies for 'dmos4'). def dmos4(name) - RBA::DeviceExtractorMOS4Transistor::new(name, true) + self._context("dmos4") do + RBA::DeviceExtractorMOS4Transistor::new(name, true) + end end # %DRC% @@ -271,7 +297,9 @@ module DRC # about this extractor. def bjt3(name) - RBA::DeviceExtractorBJT3Transistor::new(name) + self._context("bjt3") do + RBA::DeviceExtractorBJT3Transistor::new(name) + end end # %DRC% @@ -285,7 +313,9 @@ module DRC # about this extractor. def bjt4(name) - RBA::DeviceExtractorBJT4Transistor::new(name) + self._context("bjt4") do + RBA::DeviceExtractorBJT4Transistor::new(name) + end end # %DRC% @@ -299,7 +329,9 @@ module DRC # about this extractor. def diode(name) - RBA::DeviceExtractorDiode::new(name) + self._context("diode") do + RBA::DeviceExtractorDiode::new(name) + end end # %DRC% @@ -315,7 +347,9 @@ module DRC # about this extractor. def resistor(name, sheet_rho) - RBA::DeviceExtractorResistor::new(name, sheet_rho) + self._context("resistor") do + RBA::DeviceExtractorResistor::new(name, sheet_rho) + end end # %DRC% @@ -330,7 +364,9 @@ module DRC # about this extractor. def resistor_with_bulk(name, sheet_rho) - RBA::DeviceExtractorResistorWithBulk::new(name, sheet_rho) + self._context("resistor_with_bulk") do + RBA::DeviceExtractorResistorWithBulk::new(name, sheet_rho) + end end # %DRC% @@ -344,7 +380,9 @@ module DRC # about this extractor. def capacitor(name, area_cap) - RBA::DeviceExtractorCapacitor::new(name, area_cap) + self._context("capacitor") do + RBA::DeviceExtractorCapacitor::new(name, area_cap) + end end # %DRC% @@ -359,7 +397,9 @@ module DRC # about this extractor. def capacitor_with_bulk(name, area_cap) - RBA::DeviceExtractorCapacitorWithBulk::new(name, area_cap) + self._context("capacitor_with_bulk") do + RBA::DeviceExtractorCapacitorWithBulk::new(name, area_cap) + end end # %DRC% @@ -458,7 +498,7 @@ module DRC def use_dbu(d) if @dbu_read - raise "Cannot change the database unit at this point" + raise("Cannot change the database unit at this point") end # Should have a "context", but no such thing for Float or Fixnum 1.0.class._dbu = d @@ -778,63 +818,67 @@ module DRC def source(arg = nil, arg2 = nil) - if arg - - if arg.is_a?(String) + self._context("source") do + + if arg - if arg =~ /^@(\d+)/ - n = $1.to_i - 1 - view = RBA::LayoutView::current - view || raise("No view open") - (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") - cv = view.cellview(n) - cv.is_valid? || raise("Invalid layout @#{n + 1}") - @def_source = make_source(cv.layout, cv.cell, cv.filename) - else - layout = RBA::Layout::new - info("Reading #{arg} ..") - layout.read(arg) - cell = nil - if arg2 - arg2.is_a?(String) || raise("Second argument of 'source' must be a string") - cell = layout.cell(arg2) - cell || raise("Cell name #{arg2} not found in input layout") + if arg.is_a?(String) + + if arg =~ /^@(\d+)/ + n = $1.to_i - 1 + view = RBA::LayoutView::current + view || raise("No view open") + (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") + cv = view.cellview(n) + cv.is_valid? || raise("Invalid layout @#{n + 1}") + @def_source = make_source(cv.layout, cv.cell, cv.filename) + else + layout = RBA::Layout::new + info("Reading #{arg} ..") + layout.read(arg) + cell = nil + if arg2 + arg2.is_a?(String) || raise("Second argument must be a string") + cell = layout.cell(arg2) + cell || raise("Cell name #{arg2} not found in input layout") + end + @def_source = make_source(layout, cell, arg) end - @def_source = make_source(layout, cell, arg) - end - - elsif arg.is_a?(RBA::Layout) + + elsif arg.is_a?(RBA::Layout) - cell = arg2 - if cell.is_a?(String) - cell = arg.cell(cell) - cell || raise("Cell name #{cell} not found in input layout") - elsif !cell.is_a?(RBA::Cell) - raise("Second argument of 'source' must be a string or RBA::Cell object") - end - @def_source = make_source(arg, cell) + cell = arg2 + if cell.is_a?(String) + cell = arg.cell(cell) + cell || raise("Cell name #{cell} not found in input layout") + elsif !cell.is_a?(RBA::Cell) + raise("Second argument must be a string or RBA::Cell object") + end + @def_source = make_source(arg, cell) - elsif arg.is_a?(RBA::Cell) - @def_source = make_source(arg.layout, arg) + elsif arg.is_a?(RBA::Cell) + @def_source = make_source(arg.layout, arg) + else + raise("Invalid argument '" + arg.inspect + "'") + end + else - raise("Invalid argument for 'source' method") + @def_source || @def_layout || raise("No layout loaded - no default layout. Use 'layout' or 'source' to explicitly specify a layout.") + @def_source ||= make_source(@def_layout, @def_cell, @def_path) end - - else - @def_source || @def_layout || raise("No layout loaded - no default layout. Use 'layout' or 'source' to explicitly specify a layout.") - @def_source ||= make_source(@def_layout, @def_cell, @def_path) + + # make default input also default output if none is set yet. + @def_layout ||= @def_source.layout + @def_cell ||= @def_source.cell_obj + @def_path ||= @def_source.path + + # use the DBU of the new input as DBU reference + @dbu_read || use_dbu(@def_source.layout.dbu) + + @def_source + end - # make default input also default output if none is set yet. - @def_layout ||= @def_source.layout - @def_cell ||= @def_source.cell_obj - @def_path ||= @def_source.path - - # use the DBU of the new input as DBU reference - @dbu_read || use_dbu(@def_source.layout.dbu) - - @def_source - end # %DRC% @@ -873,43 +917,47 @@ module DRC def layout(arg = nil, arg2 = nil) - if arg - - if arg.is_a?(String) + self._context("layout") do + + if arg - if arg =~ /^@(\d+)/ - n = $1.to_i - 1 - view = RBA::LayoutView::current - view || raise("No view open") - (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") - cv = view.cellview(n) - cv.is_valid? || raise("Invalid layout @#{n + 1}") - return make_source(cv.layout, cv.cell, cv.filename) - else - layout = RBA::Layout::new - info("Reading #{arg} ..") - layout.read(arg) - cell = nil - if arg2 - arg2.is_a?(String) || raise("Second argument of 'source' must be a string") - cell = layout.cell(arg2) - cell || raise("Cell name #{arg2} not found in input layout") - end - return make_source(layout, cell, arg) - end + if arg.is_a?(String) - elsif arg.is_a?(RBA::Layout) - return make_source(layout) - elsif arg.is_a?(RBA::Cell) - return make_source(arg.layout, arg) + if arg =~ /^@(\d+)/ + n = $1.to_i - 1 + view = RBA::LayoutView::current + view || raise("No view open") + (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") + cv = view.cellview(n) + cv.is_valid? || raise("Invalid layout @#{n + 1}") + return make_source(cv.layout, cv.cell, cv.filename) + else + layout = RBA::Layout::new + info("Reading #{arg} ..") + layout.read(arg) + cell = nil + if arg2 + arg2.is_a?(String) || raise("Second argument of 'source' must be a string") + cell = layout.cell(arg2) + cell || raise("Cell name #{arg2} not found in input layout") + end + return make_source(layout, cell, arg) + end + + elsif arg.is_a?(RBA::Layout) + return make_source(layout) + elsif arg.is_a?(RBA::Cell) + return make_source(arg.layout, arg) + else + raise("Invalid argument for 'layout' method") + end + else - raise("Invalid argument for 'layout' method") + @def_source || @def_layout || raise("No layout loaded - no default layout. Use 'layout' or 'source' to explicitly specify a layout.") + @def_source ||= make_source(@def_layout, @def_cell, @def_path) + @def_source end - - else - @def_source || @def_layout || raise("No layout loaded - no default layout. Use 'layout' or 'source' to explicitly specify a layout.") - @def_source ||= make_source(@def_layout, @def_cell, @def_path) - @def_source + end end @@ -936,42 +984,46 @@ module DRC def report(description, filename = nil, cellname = nil) - @output_rdb_file = filename + self._context("report") do - name = filename && File::basename(filename) - name ||= "DRC" - - @output_rdb_index = nil + @output_rdb_file = filename - view = RBA::LayoutView::current - if view - if self._rdb_index - @output_rdb = RBA::ReportDatabase::new("") # reuse existing name - @output_rdb_index = view.replace_rdb(self._rdb_index, @output_rdb) + name = filename && File::basename(filename) + name ||= "DRC" + + @output_rdb_index = nil + + view = RBA::LayoutView::current + if view + if self._rdb_index + @output_rdb = RBA::ReportDatabase::new("") # reuse existing name + @output_rdb_index = view.replace_rdb(self._rdb_index, @output_rdb) + else + @output_rdb = RBA::ReportDatabase::new(name) + @output_rdb_index = view.add_rdb(@output_rdb) + end else @output_rdb = RBA::ReportDatabase::new(name) - @output_rdb_index = view.add_rdb(@output_rdb) end - else - @output_rdb = RBA::ReportDatabase::new(name) + + @output_layout = nil + @output_cell = nil + @output_layout_file = nil + + cn = nil + cn ||= @def_cell && @def_cell.name + cn ||= source && source.cell_name + cn ||= cellname + + cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter") + + @output_rdb_cell = @output_rdb.create_cell(cn) + @output_rdb.generator = self._generator + @output_rdb.top_cell_name = cn + @output_rdb.description = description + end - @output_layout = nil - @output_cell = nil - @output_layout_file = nil - - cn = nil - cn ||= @def_cell && @def_cell.name - cn ||= source && source.cell_name - cn ||= cellname - - cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter of 'report'") - - @output_rdb_cell = @output_rdb.create_cell(cn) - @output_rdb.generator = self._generator - @output_rdb.top_cell_name = cn - @output_rdb.description = description - end # %DRC% @@ -989,12 +1041,18 @@ module DRC # version of the L2N DB format will be used. def report_netlist(filename = nil, long = nil) - @show_l2ndb = true - if filename - filename.is_a?(String) || raise("Argument must be string in report_netlist") + + self._context("report_netlist") do + + @show_l2ndb = true + if filename + filename.is_a?(String) || raise("Argument must be string") + end + @output_l2ndb_file = filename + @output_l2ndb_long = long + end - @output_l2ndb_file = filename - @output_l2ndb_long = long + end # %DRC% @@ -1011,16 +1069,22 @@ module DRC # See \write_spice for more details. def target_netlist(filename, format = nil, comment = nil) - filename.is_a?(String) || raise("First argument must be string in target_netlist") - @target_netlist_file = filename - if format - format.is_a?(RBA::NetlistWriter) || raise("Second argument must be netlist writer object in target_netlist") + + self._context("target_netlist") do + + filename.is_a?(String) || raise("First argument must be string") + @target_netlist_file = filename + if format + format.is_a?(RBA::NetlistWriter) || raise("Second argument must be netlist writer object") + end + @target_netlist_format = format + if comment + comment.is_a?(String) || raise("Third argument must be string") + end + @target_netlist_comment = comment + end - @target_netlist_format = format - if comment - comment.is_a?(String) || raise("Third argument must be string in target_netlist") - end - @target_netlist_comment = comment + end # %DRC% @@ -1033,28 +1097,32 @@ module DRC def output_cell(cellname) - # finish what we got so far - _flush - - if @output_rdb - - cell = nil - @output_rdb.each_cell do |c| - if c.name == cellname - cell = c + self._context("output_cell") do + + # finish what we got so far + _flush + + if @output_rdb + + cell = nil + @output_rdb.each_cell do |c| + if c.name == cellname + cell = c + end end + + cell ||= @output_rdb.create_cell(cellname) + @output_rdb_cell = cell + + else + + @output_layout ||= @def_layout + if @output_layout + @output_cell = @output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s) + end + end - - cell ||= @output_rdb.create_cell(cellname) - @output_rdb_cell = cell - - else - - @output_layout ||= @def_layout - if @output_layout - @output_cell = @output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s) - end - + end end @@ -1085,50 +1153,54 @@ module DRC # def target(arg, cellname = nil) - - # finish what we got so far - _finish(false) - - if arg.is_a?(String) - - if arg =~ /^@(\d+|\+)/ - view = RBA::LayoutView::current - view || raise("No view open") - if $1 == "+" - n = view.create_layout(true) - cellname ||= (@def_cell ? @def_cell.name : "TOP") - else - n = $1.to_i - 1 - end - (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") - cv = view.cellview(n) - cv.is_valid? || raise("Invalid layout @#{n + 1}") - @output_layout = cv.layout - @output_cell = cellname ? (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) : cv.cell - cv.cell = @output_cell - @output_layout_file = nil - else - @output_layout = RBA::Layout::new - @output_cell = cellname && @output_layout.create_cell(cellname.to_s) - @output_layout_file = arg - end - - elsif arg.is_a?(RBA::Layout) - - @output_layout = arg - @output_cell = cellname && (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) - @output_layout_file = nil - - elsif arg.is_a?(RBA::Cell) - - @output_layout = arg.layout - @output_cell = arg - @output_layout_file = nil - else - raise("Invalid argument for 'target' method") - end + self._context("target") do + # finish what we got so far + _finish(false) + + if arg.is_a?(String) + + if arg =~ /^@(\d+|\+)/ + view = RBA::LayoutView::current + view || raise("No view open") + if $1 == "+" + n = view.create_layout(true) + cellname ||= (@def_cell ? @def_cell.name : "TOP") + else + n = $1.to_i - 1 + end + (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") + cv = view.cellview(n) + cv.is_valid? || raise("Invalid layout @#{n + 1}") + @output_layout = cv.layout + @output_cell = cellname ? (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) : cv.cell + cv.cell = @output_cell + @output_layout_file = nil + else + @output_layout = RBA::Layout::new + @output_cell = cellname && @output_layout.create_cell(cellname.to_s) + @output_layout_file = arg + end + + elsif arg.is_a?(RBA::Layout) + + @output_layout = arg + @output_cell = cellname && (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) + @output_layout_file = nil + + elsif arg.is_a?(RBA::Cell) + + @output_layout = arg.layout + @output_cell = arg + @output_layout_file = nil + + else + raise("Invalid argument '" + arg.inspect + "'") + end + + end + end # %DRC% @@ -1139,7 +1211,9 @@ module DRC # RBA::DBox constructors. def box(*args) - RBA::DBox::new(*args) + self._context("box") do + RBA::DBox::new(*args) + end end # %DRC% @@ -1150,7 +1224,9 @@ module DRC # RBA::DPath constructors. def path(*args) - RBA::DPath::new(*args) + self._context("path") do + RBA::DPath::new(*args) + end end # %DRC% @@ -1161,7 +1237,9 @@ module DRC # RBA::DPolygon constructors. def polygon(*args) - RBA::DPolygon::new(*args) + self._context("polygon") do + RBA::DPolygon::new(*args) + end end # %DRC% @@ -1177,7 +1255,9 @@ module DRC # @/code def p(x, y) - RBA::DPoint::new(x, y) + self._context("p") do + RBA::DPoint::new(x, y) + end end # %DRC% @@ -1188,7 +1268,9 @@ module DRC # RBA::DEdge constructors. def edge(*args) - RBA::DEdge::new(*args) + self._context("edge") do + RBA::DEdge::new(*args) + end end # %DRC% @@ -1198,7 +1280,9 @@ module DRC # See \Source#extent for a description of that function. def extent - layout.extent + self._context("extent") do + layout.extent + end end # %DRC% @@ -1209,50 +1293,46 @@ module DRC # polygons and labels. See \polygons and \labels for more specific versions of # this method. - def input(*args) - layout.input(*args) - end - # %DRC% # @name polygons # @brief Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source # @synopsis polygons(args) # See \Source#polygons for a description of that function. - def polygons(*args) - layout.polygons(*args) - end - # %DRC% # @name labels # @brief Gets the labels (text) from an original layer # @synopsis labels(args) # See \Source#labels for a description of that function. - def labels(*args) - layout.labels(*args) - end - # %DRC% # @name edges # @brief Gets the edges from an original layer # @synopsis edges(args) # See \Source#edges for a description of that function. - def edges(*args) - layout.edges(*args) - end - # %DRC% # @name edge_pairs # @brief Gets the edges from an original layer # @synopsis edge_pairs(args) # See \Source#edge_pairs for a description of that function. - def edge_pairs(*args) - layout.edge_pairs(*args) + %w( + edge_pairs + edges + input + labels + polygons + ).each do |f| + eval <<"CODE" + def #{f}(*args) + self._context("#{f}") do + layout.#{f}(*args) + end + end +CODE end - + # %DRC% # @name output # @brief Outputs a layer to the report database or output layout @@ -1260,7 +1340,9 @@ module DRC # This function is equivalent to "layer.output(args)". See \Layer#output for details about this function. def output(layer, *args) - layer.output(*args) + self._context("output") do + layer.output(*args) + end end # %DRC% @@ -1290,8 +1372,10 @@ module DRC # @/code def cell(*args) - @def_source = layout.cell(*args) - output_cell(*args) + self._context("cell") do + @def_source = layout.cell(*args) + output_cell(*args) + end nil end @@ -1302,7 +1386,9 @@ module DRC # See \Source#select for a description of that function. def select(*args) - @def_source = layout.select(*args) + self._context("select") do + @def_source = layout.select(*args) + end nil end @@ -1322,7 +1408,9 @@ module DRC # @/code def clip(*args) - @def_source = layout.clip(*args) + self._context("clip") do + @def_source = layout.clip(*args) + end nil end @@ -1417,41 +1505,145 @@ module DRC # Cheats have been introduced in version 0.26.1. def cheat(*args, &block) - if _dss - _dss.push_state - args.flatten.each { |a| _dss.add_breakout_cells(a.to_s) } - ret = block.call - _dss.pop_state - else - ret = block.call + self._wrapper_context("cheat") do + if _dss + _dss.push_state + args.flatten.each { |a| _dss.add_breakout_cells(a.to_s) } + ret = block.call + _dss.pop_state + else + ret = block.call + end + ret end - ret end # make some DRCLayer methods available as functions # for the engine - %w(join and or xor not - in touching overlapping inside outside interacting - select_touching select_overlapping select_inside select_outside select_interacting - merge merged rectangles rectilinear non_rectangles non_rectilinear - with_area with_perimeter with_angle with_length with_bbox_width with_bbox_area with_bbox_height with_bbox_min with_bbox_max - without_area without_perimeter without_length without_angle without_bbox_width without_bbox_area without_bbox_height without_bbox_min without_bbox_max - bbox - area length perimeter - is_box? is_empty? is_merged? is_clean? is_raw? polygons? edges? edge_pairs? - strict non_strict is_strict? - centers end_segments start_segments - extended extended_in extended_out - extents hulls holes - scaled scale rotated rotate - move moved transform transformed - width space notch isolated overlap - size sized - rounded_corners odd_polygons).each do |f| + %w( + and + andnot + area + bbox + centers + corners + covering + enc + enclosing + end_segments + extended + extended_in + extended_out + extent_refs + extents + first_edges + flatten + holes + hulls + in + inside + inside_part + interacting + intersections + iso + isolated + join + length + merge + merged + middle + move + moved + non_rectangles + non_rectilinear + non_strict + not + notch + not_covering + not_in + not_inside + not_interacting + not_outside + not_overlapping + odd_polygons + ongrid + or + output + outside + outside_part + overlap + overlapping + perimeter + pull_inside + pull_interacting + pull_overlapping + rectangles + rectilinear + rotate + rotated + rounded_corners + scale + scaled + second_edges + select_covering + select_inside + select_interacting + select_not_covering + select_not_inside + select_not_interacting + select_not_outside + select_not_overlapping + select_outside + select_overlapping + select_touching + sep + separation + size + sized + smoothed + snap + snapped + space + start_segments + strict + texts + texts_not + touching + transform + transformed + width + with_angle + with_area + with_bbox_area + with_bbox_height + with_bbox_max + with_bbox_min + with_bbox_width + with_length + without_angle + without_area + without_bbox + without_bbox_height + without_bbox_max + without_bbox_min + without_length + without_perimeter + with_perimeter + xor + ).each do |f| eval <<"CODE" def #{f}(*args) - obj = args.shift - obj.#{f}(*args) + self._context("#{f}") do + if args[0].is_a?(DRCLayer) + obj = args.shift + return obj.#{f}(*args) + elsif self.respond_to?(:_cop_#{f}) + # forward to _cop_ implementation for complex DRC operations + return self._cop_#{f}(*args) + else + raise("Function requires at a layer expression for the first argument") + end + end end CODE end @@ -1463,7 +1655,9 @@ CODE # See \Netter# for more details def netter - DRC::DRCNetter::new + self._context("netter") do + DRC::DRCNetter::new + end end # %DRC% @@ -1523,10 +1717,22 @@ CODE # yet, this method will trigger the extraction process. # See \Netter#netlist for a description of this function. - %w(connect connect_global clear_connections connect_implicit antenna_check l2n_data device_scaling extract_devices netlist).each do |f| + %w( + antenna_check + clear_connections + connect + connect_global + connect_implicit + device_scaling + extract_devices + l2n_data + netlist + ).each do |f| eval <<"CODE" def #{f}(*args) - _netter.#{f}(*args) + self._context("#{f}") do + _netter.#{f}(*args) + end end CODE end @@ -1541,6 +1747,22 @@ CODE return cc end end + + def _wrapper_context(func, *args, &proc) + begin + return yield(*args) + rescue => ex + raise("'" + func + "': " + ex.to_s) + end + end + + def _context(func, *args, &proc) + begin + return yield(*args) + rescue => ex + raise("'" + func + "': " + ex.to_s) + end + end def run_timed(desc, obj) @@ -2087,7 +2309,7 @@ CODE if @output_rdb if args.size < 1 - raise("Invalid number of arguments for 'output' on report - category name and optional description expected") + raise("Invalid number of arguments - category name and optional description expected") end cat = @output_rdb.create_category(args[0].to_s) @@ -2121,12 +2343,12 @@ CODE elsif args[0].is_a?(String) info = RBA::LayerInfo::from_string(args[0]) else - raise("Invalid parameter type for 'output' - must be string or number") + raise("Invalid parameter type - must be string or number") end elsif args.size == 2 || args.size == 3 info = RBA::LayerInfo::new(*args) else - raise("Invalid number of arguments for 'output' - one, two or three arguments expected") + raise("Invalid number of arguments - one, two or three arguments expected") end li = output.find_layer(info) if !li diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 92eaa6a22..d748961c2 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -39,25 +39,32 @@ module DRC # @/code def insert(*args) - requires_edges_or_region("insert") - args.each do |a| - if a.is_a?(RBA::DBox) - self.data.insert(RBA::Box::from_dbox(a * (1.0 / @engine.dbu))) - elsif a.is_a?(RBA::DPolygon) - self.data.insert(RBA::Polygon::from_dpoly(a * (1.0 / @engine.dbu))) - elsif a.is_a?(RBA::DSimplePolygon) - self.data.insert(RBA::SimplePolygon::from_dpoly(a * (1.0 / @engine.dbu))) - elsif a.is_a?(RBA::DPath) - self.data.insert(RBA::Path::from_dpath(a * (1.0 / @engine.dbu))) - elsif a.is_a?(RBA::DEdge) - self.data.insert(RBA::Edge::from_dedge(a * (1.0 / @engine.dbu))) - elsif a.is_a?(Array) - insert(*a) - else - raise("Invalid argument type #{a.class.to_s} for 'insert' method") + + @engine._context("insert") do + + requires_edges_or_region + args.each do |a| + if a.is_a?(RBA::DBox) + self.data.insert(RBA::Box::from_dbox(a * (1.0 / @engine.dbu))) + elsif a.is_a?(RBA::DPolygon) + self.data.insert(RBA::Polygon::from_dpoly(a * (1.0 / @engine.dbu))) + elsif a.is_a?(RBA::DSimplePolygon) + self.data.insert(RBA::SimplePolygon::from_dpoly(a * (1.0 / @engine.dbu))) + elsif a.is_a?(RBA::DPath) + self.data.insert(RBA::Path::from_dpath(a * (1.0 / @engine.dbu))) + elsif a.is_a?(RBA::DEdge) + self.data.insert(RBA::Edge::from_dedge(a * (1.0 / @engine.dbu))) + elsif a.is_a?(Array) + insert(*a) + else + raise("Invalid argument type for #{a.inspect}") + end end + + self + end - self + end # %DRC% @@ -75,9 +82,15 @@ module DRC # This feature has been introduced in version 0.23.2. def strict - requires_region("strict") - self.data.strict_handling = true - self + + @engine._context("strict") do + + requires_region + self.data.strict_handling = true + self + + end + end # %DRC% @@ -90,9 +103,15 @@ module DRC # This feature has been introduced in version 0.23.2. def non_strict - requires_region("non_strict") - self.data.strict_handling = false - self + + @engine._context("non_strict") do + + requires_region + self.data.strict_handling = false + self + + end + end # %DRC% @@ -105,8 +124,14 @@ module DRC # This feature has been introduced in version 0.23.2. def is_strict? - requires_region("is_strict?") - self.data.strict_handling? + + @engine._context("is_strict") do + + requires_region + self.data.strict_handling? + + end + end # %DRC% @@ -123,9 +148,15 @@ module DRC # propagated. def clean - requires_edges_or_region("clean") - self.data.merged_semantics = true - self + + @engine._context("clean") do + + requires_edges_or_region + self.data.merged_semantics = true + self + + end + end # %DRC% @@ -154,9 +185,15 @@ module DRC # To avoid that, use the \dup method to create a real (deep) copy. def raw - requires_edges_or_region("raw") - self.data.merged_semantics = false - self + + @engine._context("raw") do + + requires_edges_or_region + self.data.merged_semantics = false + self + + end + end # %DRC% @@ -167,8 +204,14 @@ module DRC # See \clean for a discussion of the clean state. def is_clean? - requires_edges_or_region("is_clean?") - self.data.merged_semantics? + + @engine._context("is_clean?") do + + requires_edges_or_region + self.data.merged_semantics? + + end + end # %DRC% @@ -179,8 +222,14 @@ module DRC # See \clean for a discussion of the raw state. def is_raw? - requires_edges_or_region("is_raw?") - !self.data.merged_semantics? + + @engine._context("is_raw?") do + + requires_edges_or_region + !self.data.merged_semantics? + + end + end # %DRC% @@ -214,10 +263,16 @@ module DRC # By setting the layer to nil, it is ensured that it can no longer be accessed. def forget - if @data - @data._destroy - @data = nil + + @engine._context("forget") do + + if @data + @data._destroy + @data = nil + end + end + end # %DRC% @@ -282,19 +337,25 @@ module DRC mn = (inv ? "without" : "with") + "_" + f eval <<"CODE" def #{mn}(*args) - requires_region("#{f}") - if args.size == 1 - a = args[0] - if a.is_a?(Range) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a.first), @engine._prep_value_area(a.last), #{inv.inspect})) + + @engine._context("#{mn}") do + + requires_region + if args.size == 1 + a = args[0] + if a.is_a?(Range) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a.first), @engine._prep_value_area(a.last), #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a), #{inv.inspect})) + end + elsif args.size == 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(args[0]), @engine._prep_value_area(args[1]), #{inv.inspect})) else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a), #{inv.inspect})) + raise("Invalid number of arguments (1 or 2 expected)") end - elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(args[0]), @engine._prep_value_area(args[1]), #{inv.inspect})) - else - raise("Invalid number of arguments for method '#{mn}'") + end + end CODE end @@ -432,19 +493,25 @@ CODE mn = (inv ? "without" : "with") + "_" + f eval <<"CODE" def #{mn}(*args) - requires_region("#{mn}") - if args.size == 1 - a = args[0] - if a.is_a?(Range) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect})) + + @engine._context("#{mn}") do + + requires_region + if args.size == 1 + a = args[0] + if a.is_a?(Range) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a), #{inv.inspect})) + end + elsif args.size == 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect})) else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a), #{inv.inspect})) + raise("Invalid number of arguments (1 or 2 expected)") end - elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect})) - else - raise("Invalid number of arguments for method '#{mn}'") + end + end CODE end @@ -481,19 +548,25 @@ CODE mn = (inv ? "without" : "with") + "_" + f eval <<"CODE" def #{mn}(*args) - requires_edges("#{mn}") - if args.size == 1 - a = args[0] - if a.is_a?(Range) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect})) + + @engine._context("#{mn}") do + + requires_edges + if args.size == 1 + a = args[0] + if a.is_a?(Range) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a), #{inv.inspect})) + end + elsif args.size == 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect})) else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a), #{inv.inspect})) + raise("Invalid number of arguments (1 or 2 expected)") end - elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect})) - else - raise("Invalid number of arguments for method '#{mn}'") + end + end CODE end @@ -532,7 +605,6 @@ CODE # @/tr # @/table - # %DRC% # @name without_angle # @brief Selects edges by the their angle @@ -548,20 +620,26 @@ CODE mn = (inv ? "without" : "with") + "_angle" eval <<"CODE" def #{mn}(*args) - requires_edges_or_region("#{mn}") - result_class = self.data.is_a?(RBA::Region) ? RBA::EdgePairs : RBA::Edges - if args.size == 1 - a = args[0] - if a.is_a?(Range) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a.first, a.last, #{inv.inspect})) + + @engine._context("#{mn}") do + + requires_edges_or_region + result_class = self.data.is_a?(RBA::Region) ? RBA::EdgePairs : RBA::Edges + if args.size == 1 + a = args[0] + if a.is_a?(Range) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a.first, a.last, #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a, #{inv.inspect})) + end + elsif args.size == 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, args[0], args[1], #{inv.inspect})) else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a, #{inv.inspect})) + raise("Invalid number of arguments (1 or 2 expected)") end - elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, args[0], args[1], #{inv.inspect})) - else - raise("Invalid number of arguments for method '#{mn}'") + end + end CODE end @@ -594,8 +672,10 @@ CODE # @/table def rounded_corners(inner, outer, n) - requires_region("rounded_corners") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :rounded_corners, @engine._prep_value(inner), @engine._prep_value(outer), n)) + @engine._context("rounded_corners") do + requires_region + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :rounded_corners, @engine._prep_value(inner), @engine._prep_value(outer), n)) + end end # %DRC% @@ -611,8 +691,10 @@ CODE # method (see \raw and \clean). def smoothed(d) - requires_region("smoothed") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :smoothed, @engine._prep_value(d))) + @engine._context("smoothed") do + requires_region + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :smoothed, @engine._prep_value(d))) + end end # %DRC% @@ -665,8 +747,10 @@ CODE # @/table def texts(*args) - requires_texts_or_region("texts") - self._texts_impl(false, *args) + @engine._context("texts") do + requires_texts_or_region + self._texts_impl(false, *args) + end end # %DRC% @@ -684,8 +768,10 @@ CODE # not matching the filter. def texts_not(*args) - requires_texts("texts_not") - self._texts_impl(true, *args) + @engine._context("texts_not") do + requires_texts + self._texts_impl(true, *args) + end end # Implementation of texts or texts_not @@ -706,7 +792,7 @@ CODE elsif a.is_a?(DRCAsDots) as_dots = a.value else - raise("Invalid argument for 'texts' method") + raise("Invalid argument type #{a.inspect}") end end @@ -766,30 +852,34 @@ CODE def corners(*args) - requires_region("corners") + @engine._context("corners") do - as_dots = false - amin = -180.0 - amax = 180.0 + requires_region - args.each do |a| - if a.is_a?(Range) - if (!a.min.is_a?(1.0.class) && !a.min.is_a?(1.class)) || (!a.max.is_a?(1.0.class) && !a.max.is_a?(1.class)) - raise("An angle limit requires an interval of two angles") + as_dots = false + amin = -180.0 + amax = 180.0 + + args.each do |a| + if a.is_a?(Range) + if (!a.min.is_a?(1.0.class) && !a.min.is_a?(1.class)) || (!a.max.is_a?(1.0.class) && !a.max.is_a?(1.class)) + raise("An angle limit requires an interval of two angles") + end + amin = a.min.to_f + amax = a.max.to_f + elsif a.is_a?(1.0.class) || a.is_a?(1.class) + amin = a.to_f + amax = a.to_f + elsif a.is_a?(DRCAsDots) + as_dots = a.value + else + raise("Invalid argument #{a.inspect}") end - amin = a.min.to_f - amax = a.max.to_f - elsif a.is_a?(1.0.class) || a.is_a?(1.class) - amin = a.to_f - amax = a.to_f - elsif a.is_a?(DRCAsDots) - as_dots = a.value - else - raise("Invalid argument for 'corners' method") end - end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax)) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax)) + + end end @@ -915,67 +1005,71 @@ CODE eval <<"CODE" def #{f}(*args) - requires_region("#{f}") + @engine._context("#{f}") do - f = [] - as_edges = false + requires_region - @@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 ] - } + f = [] + as_edges = false - args.each do |a| - 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 for '#{f}' method") + @@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 do |a| + 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: " + a.inspect) + end + end + + if f.size == 2 + f = f + f + else + f = (f + [0.5] * 4)[0..3] + end + + if as_edges + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :extent_refs_edges, *f)) + else + # add oversize for point- and edge-like regions + zero_area = (f[0] - f[2]).abs < 1e-7 || (f[1] - f[3]).abs < 1e-7 + f += [ zero_area ? 1 : 0 ] * 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :extent_refs, *f)) end - end - if f.size == 2 - f = f + f - else - f = (f + [0.5] * 4)[0..3] - end - - if as_edges - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :extent_refs_edges, *f)) - else - # add oversize for point- and edge-like regions - zero_area = (f[0] - f[2]).abs < 1e-7 || (f[1] - f[3]).abs < 1e-7 - f += [ zero_area ? 1 : 0 ] * 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :extent_refs, *f)) end end @@ -1000,14 +1094,20 @@ CODE # @/code def select(&block) - new_data = self.data.class.new - t = RBA::CplxTrans::new(@engine.dbu) - @engine.run_timed("\"select\" in: #{@engine.src_line}", self.data) do - self.data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object| - block.call(object.transformed(t)) && new_data.insert(object) + + @engine._wrapper_context("select") do + + new_data = self.data.class.new + t = RBA::CplxTrans::new(@engine.dbu) + @engine.run_timed("\"select\" in: #{@engine.src_line}", self.data) do + self.data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object| + block.call(object.transformed(t)) && new_data.insert(object) + end end + DRCLayer::new(@engine, new_data) + end - DRCLayer::new(@engine, new_data) + end # %DRC% @@ -1021,12 +1121,18 @@ CODE # apply to this method. def each(&block) - t = RBA::CplxTrans::new(@engine.dbu) - @engine.run_timed("\"select\" in: #{@engine.src_line}", self.data) do - self.data.send(self.data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object| - block.call(object.transformed(t)) + + @engine._wrapper_context("each") do + + t = RBA::CplxTrans::new(@engine.dbu) + @engine.run_timed("\"select\" in: #{@engine.src_line}", self.data) do + self.data.send(self.data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object| + block.call(object.transformed(t)) + end end + end + end # %DRC% @@ -1087,26 +1193,30 @@ CODE eval <<"CODE" def #{f}(&block) - if :#{f} == :collect - new_data = self.data.class.new - elsif :#{f} == :collect_to_region - new_data = RBA::Region.new - elsif :#{f} == :collect_to_edges - new_data = RBA::Edges.new - elsif :#{f} == :collect_to_edge_pairs - new_data = RBA::EdgePairs.new - end + @engine._wrapper_context("#{f}") do - t = RBA::CplxTrans::new(@engine.dbu) - dbu_trans = RBA::VCplxTrans::new(1.0 / @engine.dbu) - - @engine.run_timed("\\"select\\" in: " + @engine.src_line, self.data) do - self.data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object| - insert_object_into(new_data, block.call(object.transformed(t)), dbu_trans) + if :#{f} == :collect + new_data = self.data.class.new + elsif :#{f} == :collect_to_region + new_data = RBA::Region.new + elsif :#{f} == :collect_to_edges + new_data = RBA::Edges.new + elsif :#{f} == :collect_to_edge_pairs + new_data = RBA::EdgePairs.new end - end - DRCLayer::new(@engine, new_data) + t = RBA::CplxTrans::new(@engine.dbu) + dbu_trans = RBA::VCplxTrans::new(1.0 / @engine.dbu) + + @engine.run_timed("\\"select\\" in: " + @engine.src_line, self.data) do + self.data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object| + insert_object_into(new_data, block.call(object.transformed(t)), dbu_trans) + end + end + + DRCLayer::new(@engine, new_data) + + end end CODE @@ -1120,8 +1230,10 @@ CODE # Merged semantics does not apply for this method. Always the raw polygons are taken (see \raw). def odd_polygons - requires_region("ongrid") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :strange_polygon_check)) + @engine._context("odd_polygons") do + requires_region + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :strange_polygon_check)) + end end # %DRC% @@ -1137,13 +1249,15 @@ CODE # This method requires a polygon layer. Merged semantics applies (see \raw and \clean). def ongrid(*args) - requires_region("ongrid") - if args.size == 1 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[0]))) - elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[1]))) - else - raise("Invalid number of arguments for method 'ongrid'") + @engine._context("ongrid") do + requires_region + if args.size == 1 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[0]))) + elsif args.size == 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[1]))) + else + raise("Invalid number of arguments (1 or 2 expected)") + end end end @@ -1173,27 +1287,33 @@ CODE %w(snap snapped).each do |f| eval <<"CODE" def #{f}(*args) - requires_region("#{f}") - gx = gy = 0 - if args.size == 1 - gx = gy = @engine._prep_value(args[0]) - elsif args.size == 2 - gx = @engine._prep_value(args[0]) - gy = @engine._prep_value(args[1]) - else - raise("Invalid number of arguments for method 'ongrid'") - end - aa = args.collect { |a| @engine._prep_value(a) } - if :#{f} == :snap && @engine.is_tiled? - # in tiled mode, no modifying versions are available - self.data = @engine._tcmd(self.data, 0, self.data.class, :snapped, gx, gy) - self - elsif :#{f} == :snap - @engine._tcmd(self.data, 0, self.data.class, :#{f}, gx, gy) - self - else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, gx, gy)) + + @engine._context("#{f}") do + + requires_region + gx = gy = 0 + if args.size == 1 + gx = gy = @engine._prep_value(args[0]) + elsif args.size == 2 + gx = @engine._prep_value(args[0]) + gy = @engine._prep_value(args[1]) + else + raise("Invalid number of arguments (1 or 2 expected)") + end + aa = args.collect { |a| @engine._prep_value(a) } + if :#{f} == :snap && @engine.is_tiled? + # in tiled mode, no modifying versions are available + self.data = @engine._tcmd(self.data, 0, self.data.class, :snapped, gx, gy) + self + elsif :#{f} == :snap + @engine._tcmd(self.data, 0, self.data.class, :#{f}, gx, gy) + self + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, gx, gy)) + end + end + end CODE end @@ -1232,7 +1352,9 @@ CODE # @/table def and(other) - self & other + @engine._context("and") do + self & other + end end # %DRC% @@ -1269,7 +1391,9 @@ CODE # @/table def not(other) - self - other + @engine._context("not") do + self - other + end end # %DRC% @@ -1292,7 +1416,9 @@ CODE # @/table def xor(other) - self ^ other + @engine._context("xor") do + self ^ other + end end # %DRC% @@ -1315,7 +1441,9 @@ CODE # @/table def or(other) - self | other + @engine._context("or") do + self | other + end end # %DRC% @@ -1338,7 +1466,9 @@ CODE # @/table def join(other) - self + other + @engine._context("join") do + self + other + end end # %DRC% @@ -1361,12 +1491,16 @@ CODE def andnot(other) - requires_region("andnot") - other.requires_region("andnot") + @engine._context("andnot") do - res = @engine._tcmd_a2(self.data, 0, self.data.class, self.data.class, :andnot, other.data) + requires_region + other.requires_region - [ DRCLayer::new(@engine, res[0]), DRCLayer::new(@engine, res[1]) ] + res = @engine._tcmd_a2(self.data, 0, self.data.class, self.data.class, :andnot, other.data) + + [ DRCLayer::new(@engine, res[0]), DRCLayer::new(@engine, res[1]) ] + + end end @@ -1981,20 +2115,27 @@ CODE %w(pull_interacting pull_overlapping pull_inside).each do |f| eval <<"CODE" def #{f}(other) - if :#{f} != :pull_interacting - requires_region("#{f}") - other.requires_region("#{f}") - else - requires_edges_texts_or_region("#{f}") - if self.data.is_a?(RBA::Text) - other.requires_region("#{f}") - elsif self.data.is_a?(RBA::Region) - other.requires_edges_texts_or_region("#{f}") + + @engine._context("#{f}") do + + if :#{f} != :pull_interacting + requires_region + other.requires_region else - other.requires_edges_or_region("#{f}") + requires_edges_texts_or_region + if self.data.is_a?(RBA::Text) + other.requires_region + elsif self.data.is_a?(RBA::Region) + other.requires_edges_texts_or_region + else + other.requires_edges_or_region + end end + + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, other.data.class, :#{f}, other.data)) + end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, other.data.class, :#{f}, other.data)) + end CODE end @@ -2002,9 +2143,11 @@ CODE %w(| ^ inside not_inside outside not_outside in not_in).each do |f| eval <<"CODE" def #{f}(other) - requires_same_type(other, "#{f}") - requires_edges_or_region("#{f}") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + @engine._context("#{f}") do + requires_same_type(other) + requires_edges_or_region + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + end end CODE end @@ -2012,13 +2155,19 @@ CODE %w(& -).each do |f| eval <<"CODE" def #{f}(other) - other.requires_edges_texts_or_region("#{f}") - if self.data.is_a?(RBA::Texts) - other.requires_region("#{f}") - else - other.requires_edges_or_region("#{f}") + + @engine._context("#{f}") do + + other.requires_edges_texts_or_region + if self.data.is_a?(RBA::Texts) + other.requires_region + else + other.requires_edges_or_region + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + end CODE end @@ -2026,8 +2175,10 @@ CODE %w(+).each do |f| eval <<"CODE" def #{f}(other) - requires_same_type(other, "#{f}") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + @engine._context("#{f}") do + requires_same_type(other) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + end end CODE end @@ -2035,15 +2186,22 @@ CODE %w(interacting not_interacting).each do |f| eval <<"CODE" def #{f}(other, *args) - other.requires_edges_texts_or_region("#{f}") - if self.data.is_a?(RBA::Text) - other.requires_region("#{f}") - elsif self.data.is_a?(RBA::Region) - other.requires_edges_texts_or_region("#{f}") - else - other.requires_edges_or_region("#{f}") + + @engine._context("#{f}") do + + other.requires_edges_texts_or_region + if self.data.is_a?(RBA::Text) + other.requires_region + elsif self.data.is_a?(RBA::Region) + other.requires_edges_texts_or_region + else + other.requires_edges_or_region + end + + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data, *minmax_count(*args))) + end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data, *minmax_count(:#{f}, *args))) + end CODE end @@ -2053,20 +2211,26 @@ CODE # In tiled mode, there are no modifying versions. Emulate using the non-modifying one. eval <<"CODE" def #{f}(other, *args) - requires_edges_texts_or_region("#{f}") - if self.data.is_a?(RBA::Text) - other.requires_region("#{f}") - elsif self.data.is_a?(RBA::Region) - other.requires_edges_texts_or_region("#{f}") - else - other.requires_edges_or_region("#{f}") - end - if @engine.is_tiled? - self.data = @engine._tcmd(self.data, 0, self.data.class, :#{fi}, other.data, *minmax_count(:#{fi}, *args)) - DRCLayer::new(@engine, self.data) - else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data, *minmax_count(:#{f}, *args))) + + @engine._context("#{f}") do + + requires_edges_texts_or_region + if self.data.is_a?(RBA::Text) + other.requires_region + elsif self.data.is_a?(RBA::Region) + other.requires_edges_texts_or_region + else + other.requires_edges_or_region + end + if @engine.is_tiled? + self.data = @engine._tcmd(self.data, 0, self.data.class, :#{fi}, other.data, *minmax_count(*args)) + DRCLayer::new(@engine, self.data) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data, *minmax_count(*args))) + end + end + end CODE end @@ -2074,9 +2238,11 @@ CODE %w(overlapping not_overlapping covering not_covering).each do |f| eval <<"CODE" def #{f}(other, *args) - requires_same_type(other, "#{f}") - requires_region("#{f}") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data, *minmax_count(:#{f}, *args))) + @engine._context("#{f}") do + requires_same_type(other) + requires_region + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data, *minmax_count(*args))) + end end CODE end @@ -2086,14 +2252,20 @@ CODE # In tiled mode, there are no modifying versions. Emulate using the non-modifying one. eval <<"CODE" def #{f}(other, *args) - requires_region("#{f}") - requires_same_type(other, "#{f}") - if @engine.is_tiled? - self.data = @engine._tcmd(self.data, 0, self.data.class, :#{fi}, other.data, *minmax_count(:#{fi}, *args)) - DRCLayer::new(@engine, self.data) - else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data, *minmax_count(:#{f}, *args))) + + @engine._context("#{f}") do + + requires_region + requires_same_type(other) + if @engine.is_tiled? + self.data = @engine._tcmd(self.data, 0, self.data.class, :#{fi}, other.data, *minmax_count(*args)) + DRCLayer::new(@engine, self.data) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data, *minmax_count(*args))) + end + end + end CODE end @@ -2103,14 +2275,20 @@ CODE # In tiled mode, there are no modifying versions. Emulate using the non-modifying one. eval <<"CODE" def #{f}(other) - requires_region("#{f}") - requires_same_type(other, "#{f}") - if @engine.is_tiled? - self.data = @engine._tcmd(self.data, 0, self.data.class, :#{fi}, other.data) - DRCLayer::new(@engine, self.data) - else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + + @engine._context("#{f}") do + + requires_region + requires_same_type(other) + if @engine.is_tiled? + self.data = @engine._tcmd(self.data, 0, self.data.class, :#{fi}, other.data) + DRCLayer::new(@engine, self.data) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + end + end + end CODE end @@ -2118,9 +2296,11 @@ CODE %w(inside_part outside_part).each do |f| eval <<"CODE" def #{f}(other) - other.requires_region("#{f}") - requires_edges("#{f}") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + @engine._context("#{f}") do + other.requires_region + requires_edges + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + end end CODE end @@ -2128,9 +2308,11 @@ CODE %w(intersections).each do |f| eval <<"CODE" def #{f}(other) - other.requires_edges("#{f}") - requires_edges("#{f}") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + @engine._context("#{f}") do + other.requires_edges + requires_edges + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :#{f}, other.data)) + end end CODE end @@ -2209,8 +2391,10 @@ CODE holes hulls).each do |f| eval <<"CODE" def #{f} - requires_region("#{f}") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f})) + @engine._context("#{f}") do + requires_region + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f})) + end end CODE end @@ -2290,9 +2474,11 @@ CODE %w(end_segments start_segments centers).each do |f| eval <<"CODE" def #{f}(length, fraction = 0.0) - requires_edges("#{f}") - length = @engine._prep_value(length) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f}, length, fraction)) + @engine._context("#{f}") do + requires_edges + length = @engine._prep_value(length) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f}, length, fraction)) + end end CODE end @@ -2334,33 +2520,37 @@ CODE eval <<"CODE" def #{f}(*args) - requires_edges("#{f}") - - av = [ 0, 0, 0, 0, false ] - args.each_with_index do |a,i| - if a.is_a?(Hash) - a[:begin] && av[0] = @engine._prep_value(a[:begin]) - a[:end] && av[1] = @engine._prep_value(a[:end]) - a[:out] && av[2] = @engine._prep_value(a[:out]) - a[:in] && av[3] = @engine._prep_value(a[:in]) - a[:joined] && av[4] = true - elsif i < 4 - if !a.is_a?(1.class) && !a.is_a?(Float) - raise("Invalid type for argument " + (i+1).to_s + " (method '#{f}')") - end - av[i] = @engine._prep_value(a) - elsif i == 4 - if a.is_a?(DRCJoinFlag) - av[i] = a.value - else - av[i] = (a ? true : false) - end - else - raise("Too many arguments for method '#{f}' (1 to 5 expected)") - end - end + @engine._context("#{f}") do - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f}, *av)) + requires_edges + + av = [ 0, 0, 0, 0, false ] + args.each_with_index do |a,i| + if a.is_a?(Hash) + a[:begin] && av[0] = @engine._prep_value(a[:begin]) + a[:end] && av[1] = @engine._prep_value(a[:end]) + a[:out] && av[2] = @engine._prep_value(a[:out]) + a[:in] && av[3] = @engine._prep_value(a[:in]) + a[:joined] && av[4] = true + elsif i < 4 + if !a.is_a?(1.class) && !a.is_a?(Float) + raise("Invalid type for argument #" + (i+1).to_s) + end + av[i] = @engine._prep_value(a) + elsif i == 4 + if a.is_a?(DRCJoinFlag) + av[i] = a.value + else + av[i] = (a ? true : false) + end + else + raise("Too many arguments (1 to 5 expected)") + end + end + + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f}, *av)) + + end end CODE @@ -2393,8 +2583,10 @@ CODE %w(extended_in extended_out).each do |f| eval <<"CODE" def #{f}(dist) - requires_edges("#{f}") - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f}, @engine._prep_value(dist))) + @engine._context("#{f}") do + requires_edges + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :#{f}, @engine._prep_value(dist))) + end end CODE end @@ -2414,12 +2606,14 @@ CODE %w(edges).each do |f| eval <<"CODE" def #{f} - if self.data.is_a?(RBA::Region) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f})) - elsif self.data.is_a?(RBA::EdgePairs) - DRCLayer::new(@engine, @engine._cmd(self.data, :#{f})) - else - raise "#{f}: Layer must be a polygon or edge pair layer" + @engine._context("#{f}") do + if self.data.is_a?(RBA::Region) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :#{f})) + elsif self.data.is_a?(RBA::EdgePairs) + DRCLayer::new(@engine, @engine._cmd(self.data, :#{f})) + else + raise "Layer must be a polygon or edge pair layer" + end end end CODE @@ -2444,8 +2638,10 @@ CODE %w(first_edges second_edges).each do |f| eval <<"CODE" def #{f} - requires_edge_pairs("#{f}") - DRCLayer::new(@engine, @engine._cmd(self.data, :#{f})) + @engine._context("#{f}") do + requires_edge_pairs + DRCLayer::new(@engine, @engine._cmd(self.data, :#{f})) + end end CODE end @@ -2511,8 +2707,10 @@ CODE # The returned value gives the area in square micrometer units. def area - requires_region("area") - @engine._tdcmd(self.data, 0, :area) * (@engine.dbu.to_f * @engine.dbu.to_f) + @engine._context("area") do + requires_region + @engine._tdcmd(self.data, 0, :area) * (@engine.dbu.to_f * @engine.dbu.to_f) + end end # %DRC% @@ -2528,10 +2726,12 @@ CODE # The returned value gives the perimeter in micrometer units. def perimeter - requires_region("perimeter") - # Note: we have to add 1 DBU border to collect the neighbors. It's important - # to know then since they tell us whether an edge is an outside edge. - @engine._tdcmd(self.data, 1, :perimeter) * @engine.dbu.to_f + @engine._context("perimeter") do + requires_region + # Note: we have to add 1 DBU border to collect the neighbors. It's important + # to know then since they tell us whether an edge is an outside edge. + @engine._tdcmd(self.data, 1, :perimeter) * @engine.dbu.to_f + end end # %DRC% @@ -2544,8 +2744,10 @@ CODE # is composed of multiple pieces, this method will not return true. def is_box? - requires_region("is_box?") - @engine._cmd(self.data, :is_box?) + @engine._context("is_box?") do + requires_region + @engine._cmd(self.data, :is_box?) + end end # %DRC% @@ -2560,8 +2762,10 @@ CODE # edges are not counted twice. def length - requires_edges("length") - @engine._cmd(self.data, :length) * @engine.dbu.to_f + @engine._context("length") do + requires_edges + @engine._cmd(self.data, :length) * @engine.dbu.to_f + end end # %DRC% @@ -2575,7 +2779,9 @@ CODE # to a flat collection of texts, polygons, edges or edge pairs. def flatten - DRC::DRCLayer::new(@engine, @engine._cmd(self.data, :flatten)) + @engine._context("flatten") do + DRC::DRCLayer::new(@engine, @engine._cmd(self.data, :flatten)) + end end # %DRC% @@ -2590,8 +2796,10 @@ CODE # tells, whether calling \merge is necessary. def is_merged? - requires_edges_or_region("is_merged?") - self.data.is_merged? + @engine._context("is_merged?") do + requires_edges_or_region + self.data.is_merged? + end end # %DRC% @@ -2961,90 +3169,94 @@ CODE eval <<"CODE" def #{f}(*args) - if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosing || :#{f} == :separation - requires_edges_or_region("#{f}") - else - requires_region("#{f}") - end - - value = nil - 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 + @engine._context("#{f}") do - 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?(DRCLayer) - other = a - elsif a.is_a?(DRCProjectionLimits) - minp = @engine._prep_value(a.min) - maxp = @engine._prep_value(a.max) - elsif a.is_a?(DRCShielded) - shielded = a.value - elsif a.is_a?(Float) || a.is_a?(1.class) - value && raise("Value already specified") - value = @engine._prep_value(a) + if :#{f} == :width || :#{f} == :space || :#{f} == :overlap || :#{f} == :enclosing || :#{f} == :separation + requires_edges_or_region else - raise("#{f}: Parameter #" + n.to_s + " does not have an expected type") + requires_region end - n += 1 - end + + value = nil + 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 - if !value - raise("#{f}: A check value must be specified") - end + 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?(DRCLayer) + other = a + elsif a.is_a?(DRCProjectionLimits) + minp = @engine._prep_value(a.min) + maxp = @engine._prep_value(a.max) + elsif a.is_a?(DRCShielded) + shielded = a.value + elsif a.is_a?(Float) || a.is_a?(1.class) + value && raise("Value already specified") + value = @engine._prep_value(a) + else + raise("Parameter #" + n.to_s + " does not have an expected type") + end + n += 1 + end - args = [ value, whole_edges, metrics, alim, minp, maxp ] + if !value + raise("A check value must be specified") + end - if self.data.is_a?(RBA::Region) - args << (shielded == nil ? true : shielded) - if :#{f} != :width && :#{f} != :notch - args << opposite_filter - args << rect_filter + args = [ value, whole_edges, metrics, alim, minp, maxp ] + + if self.data.is_a?(RBA::Region) + 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 + elsif shielded != nil + raise("Shielding can only be used for polygon layers") elsif opposite_filter != RBA::Region::NoOppositeFilter - raise("#{f}: an opposite error filter cannot be used with this check") + raise("An opposite error filter can only be used for polygon layers") elsif rect_filter != RBA::Region::NoRectFilter - raise("#{f}: a rectangle error filter cannot be used with this check") + raise("A rectangle error filter can only be used for polygon layers") end - elsif shielded != nil - raise("#{f}: shielding can only be used for polygon layers") - elsif opposite_filter != RBA::Region::NoOppositeFilter - raise("#{f}: an opposite error filter can only be used for polygon layers") - elsif rect_filter != RBA::Region::NoRectFilter - raise("#{f}: a rectangle error filter can only be used for polygon layers") + + border = (metrics == RBA::Region::Square ? value * 1.5 : value) + + if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated + if other + raise("No other layer must be specified for a single-layer check") + end + DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, *args)) + else + if !other + raise("The other layer must be specified for a two-layer check") + end + requires_same_type(other) + DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, other.data, *args)) + end + end - - border = (metrics == RBA::Region::Square ? value * 1.5 : value) - - 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 - DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, *args)) - else - if !other - raise("The other layer must be specified for a two-layer check (here: #{f})") - end - requires_same_type(other, "#{f}") - DRCLayer::new(@engine, @engine._tcmd(self.data, border, RBA::EdgePairs, :#{f}_check, other.data, *args)) - end - + end CODE end @@ -3066,7 +3278,9 @@ CODE # @/table def scaled(f) - transformed(RBA::ICplxTrans::new(f.to_f)) + @engine._context("scaled") do + transformed(RBA::ICplxTrans::new(f.to_f)) + end end # %DRC% @@ -3079,7 +3293,9 @@ CODE # the modified version is returned for further processing. def scale(f) - transform(RBA::ICplxTrans::new(f.to_f)) + @engine._context("scale") do + transform(RBA::ICplxTrans::new(f.to_f)) + end end # %DRC% @@ -3099,7 +3315,9 @@ CODE # @/table def rotated(a) - transformed(RBA::ICplxTrans::new(1.0, a, false, 0, 0)) + @engine._context("rotated") do + transformed(RBA::ICplxTrans::new(1.0, a, false, 0, 0)) + end end # %DRC% @@ -3111,7 +3329,9 @@ CODE # upon is modified and the modified version is returned for further processing. def rotate(a) - transform(RBA::ICplxTrans::new(1.0, a, false, 0, 0)) + @engine._context("rotate") do + transform(RBA::ICplxTrans::new(1.0, a, false, 0, 0)) + end end # %DRC% @@ -3182,45 +3402,49 @@ CODE eval <<"CODE" def #{f}(*args) - requires_region("#{f}") - - dist = 0 - - mode = 2 - values = [] - args.each do |a| - if a.is_a?(1.class) || a.is_a?(Float) - v = @engine._prep_value(a) - v.abs > dist && dist = v.abs - values.push(v) - elsif a.is_a?(DRCSizingMode) - mode = a.value + @engine._context("#{f}") do + + requires_region + + dist = 0 + + mode = 2 + values = [] + args.each do |a| + if a.is_a?(1.class) || a.is_a?(Float) + v = @engine._prep_value(a) + v.abs > dist && dist = v.abs + values.push(v) + elsif a.is_a?(DRCSizingMode) + mode = a.value + end end + + aa = [] + if values.size < 1 + raise "Method requires one or two sizing values" + elsif values.size > 2 + raise "Method must not have more than two values" + else + aa.push(values[0]) + aa.push(values[-1]) + end + + aa.push(mode) + + if :#{f} == :size && @engine.is_tiled? + # in tiled mode, no modifying versions are available + self.data = @engine._tcmd(self.data, dist, RBA::Region, :sized, *aa) + self + elsif :#{f} == :size + @engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa) + self + else + DRCLayer::new(@engine, @engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa)) + end + end - - aa = [] - if values.size < 1 - raise "#{f}: Method requires one or two sizing values" - elsif values.size > 2 - raise "#{f}: Method must not have more than two values" - else - aa.push(values[0]) - aa.push(values[-1]) - end - - aa.push(mode) - - if :#{f} == :size && @engine.is_tiled? - # in tiled mode, no modifying versions are available - self.data = @engine._tcmd(self.data, dist, RBA::Region, :sized, *aa) - self - elsif :#{f} == :size - @engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa) - self - else - DRCLayer::new(@engine, @engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa)) - end - + end CODE end @@ -3238,10 +3462,12 @@ CODE # parameter is 0, special edge pairs with an area of 0 will be dropped. def polygons(*args) - requires_edge_pairs("polygons") - args.size <= 1 || raise("polygons: Method requires 0 or 1 arguments") - aa = args.collect { |a| @engine._prep_value(a) } - DRCLayer::new(@engine, @engine._cmd(self.data, :polygons, *aa)) + @engine._context("polygons") do + requires_edge_pairs + args.size <= 1 || raise("Method requires zero or 1 arguments") + aa = args.collect { |a| @engine._prep_value(a) } + DRCLayer::new(@engine, @engine._cmd(self.data, :polygons, *aa)) + end end # %DRC% @@ -3329,8 +3555,10 @@ CODE %w(extents moved transformed).each do |f| eval <<"CODE" def #{f}(*args) - aa = args.collect { |a| @engine._prep_value(a) } - DRCLayer::new(@engine, @engine._cmd(self.data, :#{f}, *aa)) + @engine._context("#{f}") do + aa = args.collect { |a| @engine._prep_value(a) } + DRCLayer::new(@engine, @engine._cmd(self.data, :#{f}, *aa)) + end end CODE end @@ -3338,9 +3566,11 @@ CODE %w(move transform).each do |f| eval <<"CODE" def #{f}(*args) - aa = args.collect { |a| @engine._prep_value(a) } - @engine._cmd(self.data, :#{f}, *aa) - self + @engine._context("#{f}") do + aa = args.collect { |a| @engine._prep_value(a) } + @engine._cmd(self.data, :#{f}, *aa) + self + end end CODE end @@ -3383,21 +3613,25 @@ CODE # new layer. def merged(*args) - requires_edges_or_region("merged") - aa = args.collect { |a| @engine._prep_value(a) } - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :merged, *aa)) + @engine._context("merged") do + requires_edges_or_region + aa = args.collect { |a| @engine._prep_value(a) } + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :merged, *aa)) + end end def merge(*args) - requires_edges_or_region("merge") - aa = args.collect { |a| @engine._prep_value(a) } - if @engine.is_tiled? - # in tiled mode, no modifying versions are available - self.data = @engine._tcmd(self.data, 0, self.data.class, :merged, *aa) - else - @engine._tcmd(self.data, 0, self.data.class, :merge, *aa) + @engine._context("merge") do + requires_edges_or_region + aa = args.collect { |a| @engine._prep_value(a) } + if @engine.is_tiled? + # in tiled mode, no modifying versions are available + self.data = @engine._tcmd(self.data, 0, self.data.class, :merged, *aa) + else + @engine._tcmd(self.data, 0, self.data.class, :merge, *aa) + end + self end - self end # %DRC% @@ -3419,7 +3653,9 @@ CODE # or report database. def output(*args) - @engine._vcmd(@engine, :_output, self.data, *args) + @engine._context("output") do + @engine._vcmd(@engine, :_output, self.data, *args) + end end # %DRC% @@ -3433,54 +3669,56 @@ CODE # of the layer's data. def data - @data || raise("Trying to access an invalid layer (did you use 'forget' on it?)") - @data + @engine._context("data") do + @data || raise("Trying to access an invalid layer (did you use 'forget' on it?)") + @data + end end def data=(d) @data = d end - def requires_region(f) - self.data.is_a?(RBA::Region) || raise("#{f}: Requires a polygon layer") + def requires_region + self.data.is_a?(RBA::Region) || raise("Requires a polygon layer") end - def requires_texts_or_region(f) - self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise("#{f}: Requires a polygon or text layer") + def requires_texts_or_region + self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise("Requires a polygon or text layer") end - def requires_texts(f) - self.data.is_a?(RBA::Texts) || raise("#{f}: Requires a text layer") + def requires_texts + self.data.is_a?(RBA::Texts) || raise("Requires a text layer") end - def requires_edge_pairs(f) - self.data.is_a?(RBA::EdgePairs) || raise("#{f}: Requires a edge pair layer") + def requires_edge_pairs + self.data.is_a?(RBA::EdgePairs) || raise("Requires a edge pair layer") end - def requires_edges(f) - self.data.is_a?(RBA::Edges) || raise("#{f}: Requires an edge layer") + def requires_edges + self.data.is_a?(RBA::Edges) || raise("Requires an edge layer") end - def requires_edges_or_region(f) - self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise("#{f}: Requires an edge or polygon layer") + def requires_edges_or_region + self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise("Requires an edge or polygon layer") end - def requires_edges_texts_or_region(f) - self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise("#{f}: Requires an edge, text or polygon layer") + def requires_edges_texts_or_region + self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise("Requires an edge, text or polygon layer") end - def requires_same_type(other, f) - self.data.class == other.data.class || raise("#{f}: Requires input of the same kind") + def requires_same_type(other) + self.data.class == other.data.class || raise("Requires input of the same kind") end - def minmax_count(f, *args) + def minmax_count(*args) if args.size == 0 return [] elsif args.size == 1 a = args[0] if a.is_a?(Range) if a.begin && a.begin.to_i <= 0 - raise("#{f}: lower bound of range must be a positive, non-zero number") + raise("Lower bound of range must be a positive, non-zero number") end if a.end return [(a.begin || 1).to_i, a.end.to_i] @@ -3488,9 +3726,9 @@ CODE return [(a.begin || 1).to_i] end elsif !a.is_a?(1.class) - raise("#{f}: count argument must be an integer number") + raise("Count argument must be an integer number") elsif a <= 0 - raise("#{f}: count argument must be a positive, non-zero number") + raise("Count argument must be a positive, non-zero number") else return [a] end @@ -3498,18 +3736,18 @@ CODE amin = args[0] amax = args[1] if !amin.is_a?(1.class) - raise("#{f}: min_count argument must be an integer number") + raise("Min_count argument must be an integer number") elsif !amax.is_a?(1.class) - raise("#{f}: max_count argument must be an integer number") + raise("Max_count argument must be an integer number") elsif amin <= 0 - raise("#{f}: min_count argument must be a positive, non-zero number") + raise("Min_count argument must be a positive, non-zero number") elsif amax < amin - raise("#{f}: max_count argument must be larger or equal to min_count") + raise("Max_count argument must be larger or equal to min_count") else return [amin, amax] end else - raise("#{f}: too many arguments") + raise("Too many arguments") end end diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 657892e95..747179495 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -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 diff --git a/src/drc/drc/built-in-macros/_drc_source.rb b/src/drc/drc/built-in-macros/_drc_source.rb index 8793e93de..4750c0b1f 100644 --- a/src/drc/drc/built-in-macros/_drc_source.rb +++ b/src/drc/drc/built-in-macros/_drc_source.rb @@ -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% diff --git a/src/drc/drc/drcResources.qrc b/src/drc/drc/drcResources.qrc index b5e8b8e69..f18a4a160 100644 --- a/src/drc/drc/drcResources.qrc +++ b/src/drc/drc/drcResources.qrc @@ -6,6 +6,8 @@ built-in-macros/_drc_patch.rb built-in-macros/_drc_source.rb built-in-macros/_drc_tags.rb + built-in-macros/_drc_complex_ops.rb + built-in-macros/_drc_cop_integration.rb built-in-macros/drc_interpreters.lym built-in-macros/drc_install.lym diff --git a/tmp_devel/complex_drc_devel.lym b/tmp_devel/complex_drc_devel.lym index cf6690576..a7802245c 100644 --- a/tmp_devel/complex_drc_devel.lym +++ b/tmp_devel/complex_drc_devel.lym @@ -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