From 5ceeafc0ff8d375821b90195af32b13b891a8bf8 Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sat, 29 May 2021 17:57:38 +0200
Subject: [PATCH] Implemented #808 (Feature suggestion: DRC to report edges
attached to corners as edge pairs). Solution is available for DRC layers and
universal DRC expressions.
---
src/db/db/dbRegionProcessors.cc | 2 +-
src/db/db/dbRegionProcessors.h | 51 ++++++++++++++++--
src/db/db/gsiDeclDbCompoundOperation.cc | 12 +++++
src/db/db/gsiDeclDbRegion.cc | 17 +++++-
.../drc/built-in-macros/_drc_complex_ops.rb | 22 ++++----
.../built-in-macros/_drc_cop_integration.rb | 11 ++--
src/drc/drc/built-in-macros/_drc_engine.rb | 10 ++--
src/drc/drc/built-in-macros/_drc_layer.rb | 29 ++++++----
src/drc/drc/built-in-macros/_drc_tags.rb | 2 +-
src/lay/lay/doc/about/drc_ref_global.xml | 7 +--
src/lay/lay/doc/about/drc_ref_layer.xml | 2 +
testdata/drc/drcGenericTests_6.drc | 1 +
testdata/drc/drcGenericTests_au6.gds | Bin 9974 -> 10422 bytes
testdata/drc/drcGenericTests_au6d.gds | Bin 9578 -> 10026 bytes
testdata/drc/drcSimpleTests_2.drc | 3 ++
testdata/drc/drcSimpleTests_au2.gds | Bin 12638 -> 13926 bytes
16 files changed, 132 insertions(+), 37 deletions(-)
diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc
index 9134dc12f..89c90d84c 100644
--- a/src/db/db/dbRegionProcessors.cc
+++ b/src/db/db/dbRegionProcessors.cc
@@ -52,7 +52,7 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo
db::Point pn = ctr [j];
if (m_checker (pt - pp, pn - pt)) {
- delivery.make_point (pt);
+ delivery.make_point (pt, db::Edge (pp, pt), db::Edge (pt, pn));
}
pp = pt;
diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h
index d91613148..516529144 100644
--- a/src/db/db/dbRegionProcessors.h
+++ b/src/db/db/dbRegionProcessors.h
@@ -41,7 +41,7 @@ namespace db
class DB_PUBLIC CornerPointDelivery
{
public:
- virtual void make_point (const db::Point &pt) const = 0;
+ virtual void make_point (const db::Point &pt, const db::Edge &e1, const db::Edge &e2) const = 0;
};
/**
@@ -55,7 +55,7 @@ public:
: m_d (dim, dim), mp_result (&result)
{ }
- virtual void make_point (const db::Point &pt) const
+ virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const
{
mp_result->push_back (db::Polygon (db::Box (pt - m_d, pt + m_d)));
}
@@ -76,7 +76,7 @@ public:
: mp_result (&result)
{ }
- virtual void make_point (const db::Point &pt) const
+ virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const
{
mp_result->push_back (db::Edge (pt, pt));
}
@@ -86,6 +86,26 @@ private:
std::vector *mp_result;
};
+/**
+ * @brief An interface to accept corners and turns them into edge pairs
+ */
+class DB_PUBLIC CornerEdgePairDelivery
+ : public CornerPointDelivery
+{
+public:
+ CornerEdgePairDelivery (std::vector &result)
+ : mp_result (&result)
+ { }
+
+ virtual void make_point (const db::Point &, const db::Edge &e1, const db::Edge &e2) const
+ {
+ mp_result->push_back (db::EdgePair (e1, e2));
+ }
+
+private:
+ std::vector *mp_result;
+};
+
/**
* @brief A helper class implementing the core corner detection algorithm
*/
@@ -155,6 +175,31 @@ public:
virtual bool wants_variants () const { return false; }
};
+/**
+ * @brief A corner detector delivering edge pairs for the corners
+ */
+class DB_PUBLIC CornersAsEdgePairs
+ : public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore
+{
+public:
+ CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
+ : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
+ {
+ // .. nothing yet ..
+ }
+
+ void process (const db::Polygon &poly, std::vector &result) const
+ {
+ detect_corners (poly, CornerEdgePairDelivery (result));
+ }
+
+ virtual const TransformationReducer *vars () const { return 0; }
+ virtual bool result_is_merged () const { return false; }
+ virtual bool result_must_not_be_merged () const { return true; } // to preserve dots
+ virtual bool requires_raw_input () const { return false; }
+ virtual bool wants_variants () const { return false; }
+};
+
// -----------------------------------------------------------------------------------
// Extents
diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc
index e9699eed7..bea6a2de9 100644
--- a/src/db/db/gsiDeclDbCompoundOperation.cc
+++ b/src/db/db/gsiDeclDbCompoundOperation.cc
@@ -226,6 +226,12 @@ static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionO
return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
}
+static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
+{
+ check_non_null (input, "input");
+ return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
+}
+
static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e)
{
check_non_null (input, "input");
@@ -603,6 +609,12 @@ Class decl_CompoundRegionOperationNode ("db", "
gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"),
"@brief Creates a node turning corners into dots (single-point edges).\n"
) +
+ gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"),
+ "@brief Creates a node turning corners into edge pairs containing the two edges adjacent to the corner.\n"
+ "The first edge will be the incoming edge and the second one the outgoing edge.\n"
+ "\n"
+ "This feature has been introduced in version 0.27.1.\n"
+ ) +
gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0),
"@brief Creates a node returning the extents of the objects.\n"
"The 'e' parameter provides a generic enlargement which is applied to the boxes. This is helpful to cover dot-like edges or edge pairs in the input."
diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc
index 86a6dd4e5..87b7f6e31 100644
--- a/src/db/db/gsiDeclDbRegion.cc
+++ b/src/db/db/gsiDeclDbRegion.cc
@@ -123,6 +123,11 @@ static db::Region corners_to_boxes (const db::Region *r, double angle_start, dou
return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim));
}
+static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end)
+{
+ return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end));
+}
+
static db::Region *new_si (const db::RecursiveShapeIterator &si)
{
return new db::Region (si);
@@ -1373,14 +1378,22 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region",
"\n"
"A similar function that reports corners as point-like edges is \\corners_dots.\n"
"\n"
- "This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
+ "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
) +
method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
"\n"
"This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n"
"\n"
- "This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
+ "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
+ ) +
+ method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
+ "@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
+ "\n"
+ "This method is similar to \\corners, but delivers an \\EdgePairs collection with an edge pairs for each corner.\n"
+ "The first edge is the incoming edge of the corner, the second one the outgoing edge.\n"
+ "\n"
+ "This method has been introduced in version 0.27.1.\n"
) +
method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge,
"@brief Merge the region\n"
diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb
index 6e24ce748..e0d2ea017 100644
--- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb
+++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb
@@ -787,14 +787,14 @@ CODE
# The "corners" method is available as a plain function or as a method on \DRC# expressions.
# The plain function is equivalent to "primary.corners".
- def corners(as_dots = DRCAsDots::new(false))
+ def corners(output_mode = DRCOutputMode::new(:dots))
@engine._context("corners") do
- if as_dots.is_a?(DRCAsDots)
- as_dots = as_dots.value
+ if output_mode.is_a?(DRCOutputMode)
+ output_mode = output_mode.value
else
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
end
- DRCOpNodeCornersFilter::new(@engine, as_dots, self)
+ DRCOpNodeCornersFilter::new(@engine, output_mode, self)
end
end
@@ -859,8 +859,8 @@ CODE
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 a.is_a?(DRCOutputMode)
+ as_edges = (a.value == :edges || a.value == :dots)
elsif @@std_refs[a] && :#{f} != :middle
f = @@std_refs[a]
else
@@ -1996,11 +1996,11 @@ end
class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
attr_accessor :input
- attr_accessor :as_dots
+ attr_accessor :output_mode
- def initialize(engine, as_dots, input)
+ def initialize(engine, output_mode, input)
super(engine)
- self.as_dots = as_dots
+ self.output_mode = output_mode
self.input = input
self.description = "corners"
end
@@ -2011,8 +2011,10 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
args << (self.gt ? false : true)
args << (self.lt ? self.lt : (self.le ? self.le : 180.0))
args << (self.lt ? false : true)
- if self.as_dots
+ if self.output_mode == :dots || self.output_mode == :edges
RBA::CompoundRegionOperationNode::new_corners_as_dots(*args)
+ elsif self.output_mode == :edge_pairs
+ RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args)
else
args << 2 # dimension is 2x2 DBU
RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args)
diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb
index a53504a73..58b5518a4 100644
--- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb
+++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb
@@ -781,7 +781,7 @@ CODE
# %DRC%
# @name corners
- # @brief Selects all polygons which are rectilinear
+ # @brief Selects corners of polygons
# @synopsis corners([ options ]) (in condition)
# @synopsis corners(layer [, options ])
#
@@ -791,15 +791,16 @@ CODE
# \DRC# expressions (see \Layer#drc and \DRC#corners for more details).
#
# Like the layer-based version, the "corners" operator accepts the
- # output type option: "as_dots" for dot-like edges and "as_boxes" for
- # small (2x2 DBU) box markers.
+ # output type option: "as_dots" for dot-like edges, "as_boxes" for
+ # small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs.
+ # The default output type is "as_boxes".
#
# The "corners" operator can be put into a condition which means it's
# applied to corners meeting a particular angle constraint.
- def _cop_corners(as_dots = DRCAsDots::new(false))
+ def _cop_corners(output_mode = DRCOutputMode::new(:boxes))
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
- return primary.corners(as_dots)
+ return primary.corners(output_mode)
end
# %DRC%
diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb
index bc979d8e7..51f44c266 100644
--- a/src/drc/drc/built-in-macros/_drc_engine.rb
+++ b/src/drc/drc/built-in-macros/_drc_engine.rb
@@ -226,15 +226,19 @@ module DRC
end
def as_dots
- DRCAsDots::new(true)
+ DRCOutputMode::new(:dots)
end
def as_edges
- DRCAsDots::new(true)
+ DRCOutputMode::new(:edges)
end
def as_boxes
- DRCAsDots::new(false)
+ DRCOutputMode::new(:boxes)
+ end
+
+ def as_edge_pairs
+ DRCOutputMode::new(:edge_pairs)
end
def area_only(r)
diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb
index 4a354267a..041ad60c5 100644
--- a/src/drc/drc/built-in-macros/_drc_layer.rb
+++ b/src/drc/drc/built-in-macros/_drc_layer.rb
@@ -1118,8 +1118,8 @@ CODE
elsif a.is_a?(DRCPattern)
as_pattern = a.as_pattern
pattern = a.pattern
- elsif a.is_a?(DRCAsDots)
- as_dots = a.value
+ elsif a.is_a?(DRCOutputMode)
+ as_dots = (a.value == :edges || a.value == :dots)
else
raise("Invalid argument type #{a.inspect}")
end
@@ -1167,6 +1167,8 @@ CODE
# @ul
# @li @b as_boxes @/b: with this option, small boxes will be produced as markers @/li
# @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li
+ # @li @b as_edge_pairs @/b: with this option, an edge pair is produced for each corner selected. The first edge
+ # is the incoming edge to the corner, the second edge the outgoing edge. @/li
# @/ul
#
# The following images show the effect of this method:
@@ -1185,7 +1187,7 @@ CODE
requires_region
- as_dots = false
+ output_mode = :boxes
amin = -180.0
amax = 180.0
@@ -1199,14 +1201,23 @@ CODE
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
+ elsif a.is_a?(DRCOutputMode)
+ output_mode = a.value
else
raise("Invalid argument #{a.inspect}")
end
end
- DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax))
+ f = :corners
+ cls = RBA::Region
+ if output_mode == :edges || output_mode == :dots
+ f = :corners_dots
+ cls = RBA::Edges
+ elsif output_mode == :edge_pairs
+ f = :corners_edge_pairs
+ cls = RBA::EdgePairs
+ end
+ DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, amin, amax))
end
@@ -1375,8 +1386,8 @@ CODE
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 a.is_a?(DRCOutputMode)
+ as_edges = (a.value == :edges || a.value == :dots)
elsif @@std_refs[a] && :#{f} != :middle
f = @@std_refs[a]
else
@@ -4736,7 +4747,7 @@ END
end
def requires_edge_pairs
- self.data.is_a?(RBA::EdgePairs) || raise("Requires a edge pair layer")
+ self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge pair layer")
end
def requires_edges
diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb
index 611cb414a..dedd797ef 100644
--- a/src/drc/drc/built-in-macros/_drc_tags.rb
+++ b/src/drc/drc/built-in-macros/_drc_tags.rb
@@ -77,7 +77,7 @@ module DRC
# A wrapper for the "as_dots" or "as_boxes" flag for
# some DRC functions. The purpose of this class
# is to identify the value by the class.
- class DRCAsDots
+ class DRCOutputMode
attr_accessor :value
def initialize(v)
self.value = v
diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml
index cf4e00cce..ef4aa3e2c 100644
--- a/src/lay/lay/doc/about/drc_ref_global.xml
+++ b/src/lay/lay/doc/about/drc_ref_global.xml
@@ -342,7 +342,7 @@ See Netter#connect_global
See Netter#connect_implicit for a description of that function.
-"corners" - Selects all polygons which are rectilinear
+"corners" - Selects corners of polygons
Usage:
@@ -356,8 +356,9 @@ argument, "corners" represents the corner generator/filter in primary shapes for
DRC expressions (see Layer#drc and DRC#corners for more details).
Like the layer-based version, the "corners" operator accepts the
-output type option: "as_dots" for dot-like edges and "as_boxes" for
-small (2x2 DBU) box markers.
+output type option: "as_dots" for dot-like edges, "as_boxes" for
+small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs.
+The default output type is "as_boxes".
The "corners" operator can be put into a condition which means it's
applied to corners meeting a particular angle constraint.
diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml
index 916647af0..24b008f5d 100644
--- a/src/lay/lay/doc/about/drc_ref_layer.xml
+++ b/src/lay/lay/doc/about/drc_ref_layer.xml
@@ -263,6 +263,8 @@ The options available are:
- as_boxes : with this option, small boxes will be produced as markers
- as_dots : with this option, point-like edges will be produced instead of small boxes
+- as_edge_pairs : with this option, an edge pair is produced for each corner selected. The first edge
+is the incoming edge to the corner, the second edge the outgoing edge.
The following images show the effect of this method:
diff --git a/testdata/drc/drcGenericTests_6.drc b/testdata/drc/drcGenericTests_6.drc
index 7e345876f..34eeb7d0c 100644
--- a/testdata/drc/drcGenericTests_6.drc
+++ b/testdata/drc/drcGenericTests_6.drc
@@ -22,6 +22,7 @@ l1.drc(-90 < corners(as_dots) <= 90.0).output(101, 0)
l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners
l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners
l1.drc(corners(as_boxes) <= -90).output(104, 0)
+l1.drc(corners(as_edge_pairs) == 90).output(105, 0)
l1.drc(middle).output(110, 0)
l1.drc(middle(as_dots)).output(111, 0)
diff --git a/testdata/drc/drcGenericTests_au6.gds b/testdata/drc/drcGenericTests_au6.gds
index 1cdcbf50a53b7e7af474884f48e0f2a29cdf4877..6674e75527a71ba5b8f0c73a73440d51b7d23e4c 100644
GIT binary patch
delta 991
zcmaKq&ubHL5XC1y+$2rYCN*i2wn>{GT^0{|XtRW(%MywZ+6ZD1(H=FViU%*H7sK^recq%6Th*Y`~tY)fNcnVC29nR&a!skLwCMH8?0p3Hf?
zf$wrjG7{rsp3YzA3yBLGAMa;1^a3_^Gz(_PhNIoY^|2M06E@6X1s$aaD(D3*v{~;2
zZIs6?IOCk<%(h}8oQrVB7#|h6uNStk&3-#fhVv%%_5}BErW0Z>yowdhbX3h<-P963
z1lHh$u0o40;KgJaZ==iDpDe$L#jzW`i_NHsUz6+DnBIU9BQkAcH)f%pw9!gb&}H6=
zS72PW(O|AKZzQO(ZY5W7Wy*px4>pkgCz*
z65gk4Xr#%eY#5A2%EFHn`|~vzGn}7iZ-#yrcrQyo%v%}ynWG=(I`c-B8tc{q{mjx2
zneHq(=6ydI`k|(iZ&1^@tFAaQU9$Ak+!AfVD7({(cp2ozgWbh4${77
zaPUMQVf6Ie3D-Qc;Qt4erQ`7M;NVTw!O=!VdjH!uFA|3C#e
yH_n5&mHP}m$G<~|J6on@#X0lEu`@WE692+5+3_)c08N|YnLP#OedI)G8f>`Zl1~as8L2JB|MoZa
zM*82y!>A&|l`WV70aH1|RbU^}u)s#OFp+I+6oFolGxu)5!dfO55|~5_2_*1;3!GtGpqWiX5nF-}@o#RcXGR?5aAW5G!a=xG~9Qed1FFj8cgbCw*y
z;Ovq0(&REWJnEeUby+gh8}u^hF^_Y{S10SF4saQ2W0vnj&ymS9v$~7Z`~Y4~V8NRH
z%!L8QCHfSYrN}DNw@UvC^HiuSF;9*D<=;Fd=Hcv-^(y45yh*)Nq^?GWdV^jDJ>~`O
z`08Yx@^797@7G_jYCPk;*};9&M5JY4-sobx^~V1$!dpdL>m7XR7jX67gV}7M!HDRz~$tgd(XLNzM~tR+w&3^!?+{Wh>`dz
zF{z0y4Jn=d|6;zGA3xaI7(aM-@H#Sn(tBn+Fffd*Vj<~a5OuH}cd>ARmGIC_I9P~#
z@YxS0U37^pVk_q1&6ES5J44pPDHm_)(M`JWIrmNOYfX}R(?g5%u1W8hi*3tAhc`Oh
z+p?GNY0|=g`z?!e>W_GDFxA4h)GUsz6?{nf2vW{&sMcdRAs*%pEfXVV_4(=Ph?HOyw|MQxwD
zzUHVsOnV5(4a`OC6>eg`$o-OoO3}q(f!|{;VZC^Z@3M&Xx`lR)H%iP^rdNZx%FI>a
zof&4RGeeoho%Rg53b_hF>lG2{eWW
zm6CIWA`{=0`mv!=qiW
z!w($Z7b!l{{RlNL(dF?^8k*~;*I%h$L>q;@#@_Qr5W(i0kNVt7?_jPQ!K>yK_|4_s
I$L7n(Z>eum1ONa4
delta 595
zcmZvWyK2H<9L1ARjhAS=#cQ;!;v!NkR*@1y2+~2J4qZe#bm%1F)Ccelq&-U!U!W2S
z#W(2GxwBB0b_hN|$DTNKa5(&L=lgwUb?x@bf?trNp`eSB{4Iz=P)G~sLi~UKSgvS?
z52KsIhqw0^@$h9mlco};J_D}Y#WZZ77PPTDGO_a8aC|1*u!V8NM%`~=60kA#8(0Od
z(BPYT#Kf5XNr*n51($CovW0rcg2UXzEXOe|71
z*}TPVEHzfnj?%;+5y2)&pGKd@P%Y>lLn9-_J$HJVHPyvk?2@@!n5X+#WGsxRe3m}m
z9k~fL(}x>uK`oM*?P6PEt~{AJN+&Z%Z-G5^OKoz@Rb;LLZ+Ye_v2xz$Dv+VmSEA2j
wWSPTbl*lM@&z*kET4b)=KG%A1Iv3Habg(Ut*R#s2i2LdV>ecr8x%wpj0i#;B*#H0l
diff --git a/testdata/drc/drcSimpleTests_2.drc b/testdata/drc/drcSimpleTests_2.drc
index d2055c458..89de3aa2e 100644
--- a/testdata/drc/drcSimpleTests_2.drc
+++ b/testdata/drc/drcSimpleTests_2.drc
@@ -66,6 +66,9 @@ a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0)
a1.corners.sized(0.05).output(1060, 0)
a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0)
a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0)
+a1.corners(-90.0, as_edge_pairs).polygons(0).output(1063, 0)
+a1.corners(-90.0, as_edge_pairs).first_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1064, 0)
+a1.corners(-90.0, as_edge_pairs).second_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1065, 0)
a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0)
a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0)
diff --git a/testdata/drc/drcSimpleTests_au2.gds b/testdata/drc/drcSimpleTests_au2.gds
index 76c8b8afe3ea045d93a6b2382300fd039ce43d2e..b916b5cae234201b60d468f09837d694f779f247 100644
GIT binary patch
delta 987
zcma)(KTiTd5XI*XJfsjo5&^@X3t|LgY)FinXm4jHu`q^8jj^<_(B6VT_6u0rs$alZ
zP!bC(3oE1~MhqMf8E3g$*i$Z0{chjA*`0awp0AxHK?7CIK~zyaA20k@
z6dWPnA8hNvWzc*o0HpjbMBG69JStV<>_zu@2fx^@9%kXikWE=g2|u_40Pbl3ZANtu
ziu^7BrO0~8sP1GyY(F73s-;5eS}qj2$<{IA&mH#(-T6M@>+T^MhFK@rsM_k%*5IrC
zMt2Vpo9EOqv9(To9#ZH;c~aX2(4pkkD8~+~$LF`Kql2A-)41JYlXyz!K3UHfQPM1z
zhA~LG$HJwpWO6l#NB98+oU8$v;PVK5(*nB6QL}IlH@YdaaDe
z$zSQ#h7`I{w$*3kbjODy*~f;H84-?DZ|+d+Y{Fei^?$?J{2z|Z-7c6>a(-V@Fkgaf
RXEu_xc_&-j