From 8f8d754180211035f0b17a21b195bdb6424a0565 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 Jan 2021 00:32:36 +0100 Subject: [PATCH] Create a temp stash for DRC devel. --- tmp_devel/complex_drc_devel.lym | 1104 +++++++++++++++++++++++++++++++ 1 file changed, 1104 insertions(+) create mode 100644 tmp_devel/complex_drc_devel.lym diff --git a/tmp_devel/complex_drc_devel.lym b/tmp_devel/complex_drc_devel.lym new file mode 100644 index 000000000..2d1a1d1d0 --- /dev/null +++ b/tmp_devel/complex_drc_devel.lym @@ -0,0 +1,1104 @@ + + + + + + + + + false + false + + false + + + ruby + + +module DRC + +class DRCLayer + + def drc(op) + requires_region("drc") + return DRCLayer::new(@engine, self.data.complex_op(op.create_node)) + end + +end + +class DRCOpNode + + attr_accessor :description + attr_accessor :engine + + def initialize(engine, node = nil) + @node = nil + self.engine = engine + self.description = "Basic" + end + + def create_node + @node + end + + def dump(indent) + return indent + self.description + end + + def _build_log_bool_node(other, op) + if ! other.is_a?(DRCOpNode) + raise("Second argument to #{op.to_s} must be a DRC expression") + end + if self.is_a?(DRCOpNodeLogicalBool) && self.op == op + self.children << other + return self + elsif other.is_a?(DRCOpNodeLogicalBool) && other.op == op + other.children.unshift(self) + return other + else + res = DRCOpNodeLogicalBool::new(op) + res.children = [ self, other ] + res + end + end + + %w(and or).each do |f| + eval <<"CODE" + def # + #{f}(other) + self._build_log_bool_node(other, :#{f}) + end +CODE + 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(op, self, other) + end + + %w(& - ^ | +).each do |f| + eval <<"CODE" + def # + #{f}(other) + self._build_geo_bool_node(other, :#{f}) + end +CODE + end + + def !() + if self.respond_to?(:inverted) + return self.inverted + else + res = DRCOpNodeCase::new + res.children << self + res.children << nil + res.children << @engine.primary + end + end + + def _check_numeric(v, symbol) + if ! v.is_a?(Float) && ! v.is_a?(1.class) + if symbol + raise Exception("Argument '#{symbol}' (#{v.inspect}) isn't numeric in operation '#{self.description}'") + else + raise Exception("Argument (#{v.inspect}) isn't numeric in operation '#{self.description}'") + end + end + end + + def _make_value(v, symbol) + self._check_numeric(v, symbol) + @engine._prep_value(v) + end + + def _make_area_value(v, symbol) + self._check_numeric(v, symbol) + @engine._prep_area_value(v) + end + + def area + DRCOpNodeAreaFilter::new(@engine, self) + end + + def perimeter + DRCOpNodePerimeterFilter::new(@engine, self) + end + + def bbox_min + DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxMinDim, self) + end + + def bbox_max + DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxMaxDim, self) + end + + def bbox_width + DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxWidth, self) + end + + def bbox_height + DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxHeight, self) + end + + def length + DRCOpNodeEdgeLengthFilter::new(@engine, self) + end + + def angle + DRCOpNodeEdgeOrientationFilter::new(@engine, self) + end + + def rounded_corners(inner, outer, n) + self._check_numeric(n, :n) + DRCOpNodeFilter::new(@engine, self, :new_rounded_corners, "rounded_corners", self.make_value(inner, :inner), self.make_value(outer, :outer), n) + end + + def smoothed(d) + DRCOpNodeFilter::new(@engine, self, :new_smoothed, "smoothed", self.make_value(d, :d)) + end + + def corners(as_dots = DRCAsDots::new(false)) + if as_dots.is_a?(DRCAsDots) + as_dots = as_dots.value + else + raise("Invalid argument (#{as_dots.inspect}) for 'corners' method") + end + DRCOpNodeCornersFilter::new(@engine, self, as_dots) + end + + %w(middle extent_refs).each do |f| + eval <<"CODE" + def #{f}(*args) + + f = [] + as_edges = false + + @@std_refs ||= { + :center => [0.5] * 4, + :c => [0.5] * 4, + :bottom_center => [ 0.5, 0.0, 0.5, 0.0 ], + :bc => [ 0.5, 0.0, 0.5, 0.0 ], + :bottom_left => [ 0.0, 0.0, 0.0, 0.0 ], + :bl => [ 0.0, 0.0, 0.0, 0.0 ], + :bottom_right => [ 1.0, 0.0, 1.0, 0.0 ], + :br => [ 1.0, 0.0, 1.0, 0.0 ], + :top_center => [ 0.5, 1.0, 0.5, 1.0 ], + :tc => [ 0.5, 1.0, 0.5, 1.0 ], + :top_left => [ 0.0, 1.0, 0.0, 1.0 ], + :tl => [ 0.0, 1.0, 0.0, 1.0 ], + :top_right => [ 1.0, 1.0, 1.0, 1.0 ], + :tr => [ 1.0, 1.0, 1.0, 1.0 ], + :left_center => [ 0.0, 0.5, 0.0, 0.5 ], + :lc => [ 0.0, 0.5, 0.0, 0.5 ], + :right_center => [ 1.0, 0.5, 1.0, 0.5 ], + :rc => [ 1.0, 0.5, 1.0, 0.5 ], + :south => [ 0.5, 0.0, 0.5, 0.0 ], + :s => [ 0.5, 0.0, 0.5, 0.0 ], + :left => [ 0.0, 0.0, 0.0, 1.0 ], + :l => [ 0.0, 0.0, 0.0, 1.0 ], + :bottom => [ 1.0, 0.0, 0.0, 0.0 ], + :b => [ 1.0, 0.0, 0.0, 0.0 ], + :right => [ 1.0, 1.0, 1.0, 0.0 ], + :r => [ 1.0, 1.0, 1.0, 0.0 ], + :top => [ 0.0, 1.0, 1.0, 1.0 ], + :t => [ 0.0, 1.0, 1.0, 1.0 ] + } + + args.each_with_index do |a,ia| + if a.is_a?(1.0.class) && :#{f} != :middle + f << a + elsif a.is_a?(DRCAsDots) + as_edges = a.value + elsif @@std_refs[a] && :#{f} != :middle + f = @@std_refs[a] + else + raise("Invalid argument #" + (ia + 1).to_s + " (" + a.inspect + ") for '#{f}' method on operation '" + self.description + "' - needs to be numeric or 'as_dots/as_edges'") + end + end + + if f.size == 2 + f = f + f + else + f = (f + [0.5] * 4)[0..3] + end + + if as_edges + return DRCOpNodeRelativeExtents::new(self, true, *f) + else + # add oversize for point- and edge-like regions + zero_area = (f[0] - f[2]).abs < 1e-7 || (f[1] - f[3]).abs < 1e-7 + f += [ zero_area ? 1 : 0 ] * 2 + return DRCOpNodeRelativeExtents::new(self, false, *f) + end + + end +CODE + end + + def odd_polygons + return DRCOpNodeFilter(@engine, self, :new_strange_polygons_filter, "odd_polygons") + end + + def rectangles + return DRCOpNodeFilter(@engine, self, :new_rectangle_filter, "rectangles") + end + + def rectilinear + return DRCOpNodeFilter(@engine, self, :new_rectilinear_filter, "rectilinear") + end + + def holes + return DRCOpNodeFilter(@engine, self, :new_holes, "holes") + end + + def hulls + return DRCOpNodeFilter(@engine, self, :new_hulls, "hulls") + end + + def edges + return DRCOpNodeFilter(@engine, self, :new_edges, "edges") + end + + def sized(*args) + + dist = 0 + + mode = 2 + values = [] + args.each_with_index do |a,ia| + if a.is_a?(1.class) || a.is_a?(Float) + v = self._make_value(a, "argument ##{ia + 1}") + v.abs > dist && dist = v.abs + values.push(v) + elsif a.is_a?(DRCSizingMode) + mode = a.value + end + end + + args = [] + if values.size < 1 + raise("sized: Method requires one or two sizing values") + elsif values.size > 2 + raise("sized: Method must not have more than two values") + else + args << values[0] + args << values[-1] + end + args << mode + + return DRCOpNodeFilter(@engine, self, :new_sized, "sized", *args) + + end + + def extents(e = 0) + return DRCOpNodeFilter(@engine, self, :new_extents, "extents", self._make_value(e, :e)) + end + + def first_edges + return DRCOpNodeFilter(@engine, self, :new_edge_pair_to_first_edges, "first_edges") + end + + def second_edges + return DRCOpNodeFilter(@engine, self, :new_edge_pair_to_second_edges, "second_edges") + end + + def end_segments(length, fraction = 0.0) + self._check_numeric(fraction, :fraction) + return DRCOpNodeFilter(@engine, self, :new_end_segments, "end_segments", self._make_value(length, :length), fraction) + end + + def start_segments(length, fraction = 0.0) + self._check_numeric(fraction, :fraction) + return DRCOpNodeFilter(@engine, self, :new_start_segments, "start_segments", self._make_value(length, :length), fraction) + end + + def centers(length, fraction = 0.0) + self._check_numeric(fraction, :fraction) + return DRCOpNodeFilter(@engine, self, :new_centers, "centers", self._make_value(length, :length), fraction) + end + + def extended(*args) + + av = [ 0, 0, 0, 0 ] + args.each_with_index do |a,i| + if a.is_a?(Hash) + a[:begin] && av[0] = self._make_value(a[:begin], :begin) + a[:end] && av[1] = self._make_value(a[:end], :end) + a[:out] && av[2] = self._make_value(a[:out], :out) + a[:in] && av[3] = self._make_value(a[:in], :in) + a[:joined] && av[4] = true + elsif i < 4 + av[i] = self._make_value(a, "argument " + (i+1).to_s) + else + raise("Too many arguments for method '#{f}' (1 to 5 expected)") + end + end + + return DRCOpNodeFilter(@engine, self, :new_extended, "extended", *args) + + end + + def extended_in(e) + return DRCOpNodeFilter(@engine, self, :new_extended_in, "extended_in", self._make_value(e)) + end + + def extended_out(e) + return DRCOpNodeFilter(@engine, self, :new_extended_out, "extended_out", self._make_value(e)) + end + + def polygons + return DRCOpNodeFilter(@engine, self, :new_polygons, "polygons") + end + +end + +class DRCOpNodeLogicalBool < DRCOpNode + + attr_accessor :children + attr_accessor :op + + def initialize(engine, op) + super(engine) + self.children = [] + self.op = op + self.description = "Logical #{op.to_s}" + end + + def dump(indent) + return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n") + end + + def create_node + log_op = { :and => RBA::CompoundOperationNode::LogAnd, :or => RBA::CompoundOperationNode::LogOr }[self.op] + RBA::CompoundOperationNode::new_logical_boolean(log_op, false, self.children.collect { |c| c.create_node }) + 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 create_node + bool_op = { :& => RBA::CompoundOperationNode::And, + :+ => RBA::CompoundOperationNode::Or, + :| => RBA::CompoundOperationNode::Or, + :- => RBA::CompoundOperationNode::Not, + :^ => RBA::CompoundOperationNode::Xor }[self.op] + RBA::CompoundOperationNode::new_logical_boolean(bool_op, false, self.children.collect { |c| c.create_node }) + end + +end + +class DRCOpCase < 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 create_node + RBA::CompoundOperationNode::new_case(self.children.collect { |c| c.create_node }) + 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) + self.super(engine) + self.input = input + self.inverted = false + self.description = "area" + end + + def _description_for_dump + self.inverted ? "area" : "not_area" + end + + def create_node + args = [ self.input.create_node, 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) + self.super(engine) + self.input = input + self.inverted = false + self.description = "length" + end + + def _description_for_dump + self.inverted ? "length" : "not_length" + end + + def create_node + args = [ self.input.create_node, 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) + self.super(engine) + self.input = input + self.inverted = false + self.description = "angle" + end + + def _description_for_dump + self.inverted ? "angle" : "not_angle" + end + + def create_node + args = [ self.input.create_node, 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) + self.super(engine) + self.input = input + self.inverted = false + self.description = "perimeter" + end + + def _description_for_dump + self.inverted ? "perimeter" : "not_perimeter" + end + + def create_node + args = [ self.input.create_node, 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) + self.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 create_node + args = [ self.a.create_node, self.b.create_node, 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) + self.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 create_node + factory = { :inside => :new_inside, + :outside => :new_outside }[self.op] + RBA::CompoundRegionOperationNode::send(factory, self.a.create_node, self.b.create_node, 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 = []) + self.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 create_node + RBA::CompoundRegionOperationNode::send(self.factory, self.input.create_node, *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 create_node + + 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] + + res = nil + + 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) + end + + if self.gt || self.ge + dmax = self.ge ? self._make_value(self.ge, :ge) : self._make_value(self.gt, :gt) + 1 + inv = RBA::CompoundRegionOperationNode::send(factory, dmax, *self.args) + # TODO: instead of doing the NOT we could make use of inverse output of the checks + # (needs GSI enabling first) + if self.check == :width || self.check == :notch + # Same polygon check - we need to take both edges of the result + to_subtract = RBA::CompoundRegionOperationNode::new_edges(inv) + else + to_subtract = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(inv) + end + if res + if self.check == :width || self.check == :notch + # Same polygon check - we need to take both edges of the result + subtract_from = RBA::CompoundRegionOperationNode::new_edges(res) + else + subtract_from = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(res) + end + else + subtract_from = RBA::CompoundRegionOperationNode::new_edges(RBA::CompoundRegionOperationNode::new_primary) + end + res = RBA::CompoundRegionOperationNode::new_geometrical_boolean(RBA::CompoundRegionOperationNode::GeometricalOp::Not, subtract_from, to_subtract) + end + + return res + + end + +end + +class DRCOpNodeBBoxParameterFilter < DRCOpNodeWithCompare + + attr_accessor :input + attr_accessor :parameter + attr_accessor :inverted + + def initialize(engine, parameter, input, description) + self.super(engine) + self.parameter = parameter + self.input = input + self.inverted = false + self.description = description + end + + def create_node + args = [ self.input.create_node, 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) + self.super(engine) + self.as_dots = as_dots + self.input = input + self.description = "corners" + end + + def create_node + args = [ self.input.create_node ] + 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) + self.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 create_node + if !self.as_edges + RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy) + else + RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2) + end + end + +end + +class DRCEngine + + def switch(*args) + anum = 1 + args.each do |a| + if !a.is_a?(DRCOpNode) + raise("All inputs to 'switch' need to be valid compound operation expressions (#{anum} isn't)") + end + anum += 1 + end + res = DRCOpNodeCase::new(self) + res.children = args + return res + end + + def secondary(layer) + layer.requires_region + res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_secondary(layer.data)) + res.description = "secondary" + return res + end + + def primary + res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_primary) + res.description = "primary" + return res + end + + %w(area perimeter bbox_min bbox_max bbox_width bbox_height rectangles rectilinear edges odd_polygons hulls holes).each do |f| + eval <<"CODE" + def #{f} + primary.#{f} + end +CODE + end + + def corners(as_dots = DRCAsDots::new(false)) + return primary.corners(as_dots) + end + + %w(middle extent_refs rounded_corners smoothed sized extents).each do |f| + eval <<"CODE" + def #{f}(*args) + primary.#{f}(*args) + end +CODE + end + + %w(covering overlapping inside outside interacting).each do |f| + eval <<"CODE" + def #{f}(other) + primary.#{f}(other) + end +CODE + end + + def covering(input) + return DRCOpNodeInteractingWithCount::new(self, primary, input, :enclosing) + end + + def overlapping + return DRCOpNodeInteractingWithCount::new(self, primary, input, :overlapping) + end + + def inside + return DRCOpNodeInteracting::new(self, primary, input, :inside) + end + + def outside + return DRCOpNodeInteracting::new(self, primary, input, :outside) + end + + def interacting + return DRCOpNodeInteractingWithCount::new(self, primary, input, :interacting) + end + + %w(width space overlap enclosing separation isolated notch).each do |f| + eval <<"CODE" + def #{f}(*args) + + metrics = RBA::Region::Euclidian + minp = nil + maxp = nil + alim = nil + whole_edges = false + other = nil + shielded = nil + opposite_filter = RBA::Region::NoOppositeFilter + rect_filter = RBA::Region::NoRectFilter + + n = 1 + args.each do |a| + if a.is_a?(DRCMetrics) + metrics = a.value + elsif a.is_a?(DRCWholeEdges) + whole_edges = a.value + elsif a.is_a?(DRCOppositeErrorFilter) + opposite_filter = a.value + elsif a.is_a?(DRCRectangleErrorFilter) + rect_filter = RBA::Region::RectFilter::new(a.value.to_i | rect_filter.to_i) + elsif a.is_a?(DRCAngleLimit) + alim = a.value + elsif a.is_a?(DRCOpNode) + other = a + elsif a.is_a?(DRCProjectionLimits) + minp = self._make_value(a.min, :pmin) + maxp = self._make_value(a.max, :pmax) + elsif a.is_a?(DRCShielded) + shielded = a.value + else + raise("#{f}: Parameter #" + n.to_s + " does not have an expected type") + end + n += 1 + end + + args = [ whole_edges, metrics, alim, minp, maxp ] + + args << (shielded == nil ? true : shielded) + if :#{f} != :width && :#{f} != :notch + args << opposite_filter + args << rect_filter + elsif opposite_filter != RBA::Region::NoOppositeFilter + raise("#{f}: an opposite error filter cannot be used with this check") + elsif rect_filter != RBA::Region::NoRectFilter + raise("#{f}: a rectangle error filter cannot be used with this check") + end + + if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated + if other + raise("No other layer must be specified for a single-layer check (here: #{f})") + end + else + if !other + raise("The other layer must be specified for a two-layer check (here: #{f})") + end + end + + DRCOpNodeCheck::new(self, :#{f}, other, *args) + + end +CODE + end + + def iso(*args) + isolated(*args) + end + + def sep(*args) + separation(*args) + end + + def enc(*args) + separation(*args) + end + +end + +end +