From 43359195ea70c5bfaf26ce0155c21010e2ddb1cb Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Sun, 16 Jul 2017 00:03:45 +0200
Subject: [PATCH] DRC: corners function
Plus: a new RBA binding for decompose_convex/trapezoids that delivers a region.
---
scripts/create_drc_samples.rb | 20 +++
src/db/gsiDeclDbRegion.cc | 211 ++++++++++++++++-------
src/gsi/gsiMethods.h | 11 ++
src/gsi/gsiMethodsVar.h | 116 ++++++-------
src/lay/built_in_macros/drc.lym | 60 +++++++
src/lay/doc/about/drc_ref_layer.xml | 32 ++++
src/lay/doc/images/drc_corners1.png | Bin 0 -> 5233 bytes
src/lay/doc/images/drc_corners2.png | Bin 0 -> 5068 bytes
src/lay/doc/images/drc_corners3.png | Bin 0 -> 5245 bytes
src/lay/doc/images/drc_extent_refs30.png | Bin 4434 -> 4623 bytes
src/lay/layHelpResources.qrc | 3 +
testdata/drc/drctest.drc | 4 +
testdata/ruby/dbRegionTest.rb | 18 ++
13 files changed, 351 insertions(+), 124 deletions(-)
create mode 100644 src/lay/doc/images/drc_corners1.png
create mode 100644 src/lay/doc/images/drc_corners2.png
create mode 100644 src/lay/doc/images/drc_corners3.png
diff --git a/scripts/create_drc_samples.rb b/scripts/create_drc_samples.rb
index 824a93c3c..596e45d34 100644
--- a/scripts/create_drc_samples.rb
+++ b/scripts/create_drc_samples.rb
@@ -610,3 +610,23 @@ run_demo gen, "input.extent_refs(:top_right).sized(0.1)", "drc_extent_refs27.png
run_demo gen, "input.extent_refs(0.25, 0.75).sized(0.1)", "drc_extent_refs30.png"
run_demo gen, "input.extent_refs(0.25, 0.75, 0.5, 1.0)", "drc_extent_refs31.png"
+class Gen
+ def produce(s1, s2)
+ pts = [
+ RBA::Point::new(0, 0),
+ RBA::Point::new(0, 8000),
+ RBA::Point::new(4000, 4000),
+ RBA::Point::new(4000, 2000),
+ RBA::Point::new(6000, 2000),
+ RBA::Point::new(6000, 0),
+ ];
+ s1.insert(RBA::Polygon::new(pts))
+ end
+end
+
+gen = Gen::new
+
+run_demo gen, "input.corners.sized(0.1)", "drc_corners1.png"
+run_demo gen, "input.corners(90.0).sized(0.1)", "drc_corners2.png"
+run_demo gen, "input.corners(-90.0 .. -45.0).sized(0.1)", "drc_corners3.png"
+
diff --git a/src/db/gsiDeclDbRegion.cc b/src/db/gsiDeclDbRegion.cc
index a61641d91..f765c6247 100644
--- a/src/db/gsiDeclDbRegion.cc
+++ b/src/db/gsiDeclDbRegion.cc
@@ -72,7 +72,43 @@ static db::Region *new_shapes (const db::Shapes &s)
return r;
}
-static db::Region *new_texts (const db::RecursiveShapeIterator &si_in, const std::string &pat, bool pattern)
+struct DotDelivery
+{
+ typedef db::Edges container_type;
+
+ DotDelivery () : container ()
+ {
+ container.reset (new container_type ());
+ container->set_merged_semantics (false);
+ }
+
+ void insert (const db::Point &pt)
+ {
+ container->insert (db::Edge (pt, pt));
+ }
+
+ std::auto_ptr container;
+};
+
+struct BoxDelivery
+{
+ typedef db::Region container_type;
+
+ BoxDelivery () : container ()
+ {
+ container.reset (new container_type ());
+ }
+
+ void insert (const db::Point &pt)
+ {
+ container->insert (db::Box (pt - db::Vector (1, 1), pt + db::Vector (1, 1)));
+ }
+
+ std::auto_ptr container;
+};
+
+template
+static typename Delivery::container_type *new_texts (const db::RecursiveShapeIterator &si_in, const std::string &pat, bool pattern)
{
db::RecursiveShapeIterator si (si_in);
si.shape_flags (db::ShapeIterator::Texts);
@@ -87,7 +123,7 @@ static db::Region *new_texts (const db::RecursiveShapeIterator &si_in, const std
}
}
- std::auto_ptr r (new db::Region ());
+ Delivery delivery;
while (! si.at_end ()) {
if (si.shape ().is_text () &&
@@ -95,58 +131,81 @@ static db::Region *new_texts (const db::RecursiveShapeIterator &si_in, const std
db::Text t;
si.shape ().text (t);
t.transform (si.trans ());
- r->insert (db::Box (t.box ().enlarged (db::Vector (1, 1))));
+ delivery.insert (t.box ().center ());
}
si.next ();
}
- return r.release ();
+ return delivery.container.release ();
}
-static db::Edges *new_texts_dots (const db::RecursiveShapeIterator &si_in, const std::string &pat, bool pattern)
+template
+static typename Delivery::container_type *texts (const db::Region *r, const std::string &pat, bool pattern)
{
- db::RecursiveShapeIterator si (si_in);
- si.shape_flags (db::ShapeIterator::Texts);
+ return new_texts (r->iter (), pat, pattern);
+}
+
+template
+static typename Delivery::container_type *corners (const db::Region *r, double angle_start, double angle_end)
+{
+ db::CplxTrans t_start (1.0, angle_start, false, db::DVector ());
+ db::CplxTrans t_end (1.0, angle_end, false, db::DVector ());
+
+ bool big_angle = (angle_end - angle_start + db::epsilon) > 180.0;
+ bool all = (angle_end - angle_start - db::epsilon) > 360.0;
+
+ Delivery delivery;
+
+ for (db::Region::const_iterator p = r->begin_merged (); ! p.at_end (); ++p) {
+
+ size_t n = p->holes () + 1;
+ for (size_t i = 0; i < n; ++i) {
+
+ const db::Polygon::contour_type &ctr = p->contour (i);
+ size_t nn = ctr.size ();
+ if (nn > 2) {
+
+ db::Point pp = ctr [nn - 2];
+ db::Point pt = ctr [nn - 1];
+ for (size_t j = 0; j < nn; ++j) {
+
+ db::Point pn = ctr [j];
+
+ if (all) {
+ delivery.insert (pt);
+ } else {
+
+ db::Vector vin (pt - pp);
+ db::DVector vout (pn - pt);
+
+ db::DVector v1 = t_start * vin;
+ db::DVector v2 = t_end * vin;
+
+ bool vp1 = db::vprod_sign (v1, vout) >= 0;
+ bool vp2 = db::vprod_sign (v2, vout) <= 0;
+
+ if (big_angle && (vp1 || vp2)) {
+ delivery.insert (pt);
+ } else if (! big_angle && vp1 && vp2) {
+ if (db::sprod_sign (v1, vout) > 0 && db::sprod_sign (v2, vout) > 0) {
+ delivery.insert (pt);
+ }
+ }
+
+ }
+
+ pp = pt;
+ pt = pn;
+
+ }
+
+ }
- tl::GlobPattern glob_pat;
- bool all = false;
- if (pattern) {
- if (pat == "*") {
- all = true;
- } else {
- glob_pat = tl::GlobPattern (pat);
}
+
}
- std::auto_ptr r (new db::Edges ());
- // Dots will vanish when we try to merge them ... hence disable merged semantics
- r->set_merged_semantics (false);
-
- while (! si.at_end ()) {
- if (si.shape ().is_text () &&
- (all || (pattern && glob_pat.match (si.shape ().text_string ())) || (!pattern && si.shape ().text_string () == pat))) {
- db::Text t;
- si.shape ().text (t);
- t.transform (si.trans ());
- db::Point c = t.box ().center ();
- r->insert (db::Edge (c, c));
- }
- si.next ();
- }
-
- return r.release ();
-}
-
-static db::Region texts (const db::Region *r, const std::string &pat, bool pattern)
-{
- std::auto_ptr o (new_texts (r->iter (), pat, pattern));
- return *o;
-}
-
-static db::Edges texts_dots (const db::Region *r, const std::string &pat, bool pattern)
-{
- std::auto_ptr o (new_texts_dots (r->iter (), pat, pattern));
- return *o;
+ return delivery.container.release ();
}
static db::Region *new_si (const db::RecursiveShapeIterator &si)
@@ -597,32 +656,34 @@ static int projection_metrics ()
return db::Projection;
}
-static db::Shapes decompose_convex (const db::Region *r, int mode)
+template
+static Container *decompose_convex (const db::Region *r, int mode)
{
- db::Shapes shapes;
+ std::auto_ptr shapes (new Container ());
db::SimplePolygonContainer sp;
for (db::Region::const_iterator p = r->begin_merged (); ! p.at_end(); ++p) {
sp.polygons ().clear ();
db::decompose_convex (*p, db::PreferredOrientation (mode), sp);
for (std::vector ::const_iterator i = sp.polygons ().begin (); i != sp.polygons ().end (); ++i) {
- shapes.insert (*i);
+ shapes->insert (*i);
}
}
- return shapes;
+ return shapes.release ();
}
-static db::Shapes decompose_trapezoids (const db::Region *r, int mode)
+template
+static Container *decompose_trapezoids (const db::Region *r, int mode)
{
- db::Shapes shapes;
+ std::auto_ptr shapes (new Container ());
db::SimplePolygonContainer sp;
for (db::Region::const_iterator p = r->begin_merged (); ! p.at_end(); ++p) {
sp.polygons ().clear ();
db::decompose_trapezoids (*p, db::TrapezoidDecompositionMode (mode), sp);
for (std::vector ::const_iterator i = sp.polygons ().begin (); i != sp.polygons ().end (); ++i) {
- shapes.insert (*i);
+ shapes->insert (*i);
}
}
- return shapes;
+ return shapes.release ();
}
// provided by gsiDeclDbPolygon.cc:
@@ -706,7 +767,7 @@ Class decl_Region ("Region",
"r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n"
"@/code\n"
) +
- constructor ("new", &new_texts, gsi::arg("shape_iterator"), gsi::arg ("expr"), gsi::arg ("as_pattern", true),
+ constructor ("new", &new_texts, gsi::arg("shape_iterator"), gsi::arg ("expr"), gsi::arg ("as_pattern", true),
"@brief Constructor from a text set\n"
"\n"
"@param shape_iterator The iterator from which to derive the texts\n"
@@ -726,11 +787,11 @@ Class decl_Region ("Region",
"\n"
"This method has been introduced in version 0.25."
) +
- method_ext ("texts", &texts, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true),
+ factory_ext ("texts", &texts, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true),
"@hide\n"
"This method is provided for DRC implementation only."
) +
- method_ext ("texts_dots", &texts_dots, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true),
+ factory_ext ("texts_dots", &texts, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true),
"@hide\n"
"This method is provided for DRC implementation only."
) +
@@ -1042,6 +1103,26 @@ Class decl_Region ("Region",
"@hide\n"
"This method is provided for DRC implementation.\n"
) +
+ factory_ext ("corners", &corners, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0),
+ "@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
+ "\n"
+ "The angle values specify a range of angles: all corners whose attached edges form an angle "
+ "between angle_start and angle_end will be reported as small (2x2 DBU) boxes. The angle is measured "
+ "between the incoming and the outcoming edge in mathematical sense: a positive value is a turn left "
+ "while a negative value is a turn right. Since polygon contours are oriented clockwise, positive "
+ "angles will report concave corners while negative ones report convex ones.\n"
+ "\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.\n"
+ ) +
+ method_ext ("corners_dots", &corners, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0),
+ "@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.\n"
+ ) +
method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge,
"@brief Merge the region\n"
"\n"
@@ -1518,19 +1599,33 @@ Class decl_Region ("Region",
"\n"
"Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n"
) +
- method_ext ("decompose_convex", &decompose_convex, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"),
+ factory_ext ("decompose_convex", &decompose_convex, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"),
"@brief Decomposes the region into convex pieces.\n"
"\n"
"This method will return a \\Shapes container that holds a decomposition of the region into convex, simple polygons.\n"
- "See \\Polygon#decompose_convex for details.\n"
+ "See \\Polygon#decompose_convex for details. If you want \\Region output, you should use \\decompose_convex_to_region.\n"
"\n"
"This method has been introduced in version 0.25."
) +
- method_ext ("decompose_trapezoids", &decompose_trapezoids, gsi::arg ("mode", td_simple (), "\\Polygon#TD_simple"),
+ factory_ext ("decompose_convex_to_region", &decompose_convex, gsi::arg ("preferred_orientation", po_any (), "\\Polygon#PO_any"),
+ "@brief Decomposes the region into convex pieces into a region.\n"
+ "\n"
+ "This method is identical to \\decompose_convex, but delivers a \\Region object.\n"
+ "\n"
+ "This method has been introduced in version 0.25."
+ ) +
+ factory_ext ("decompose_trapezoids", &decompose_trapezoids, gsi::arg ("mode", td_simple (), "\\Polygon#TD_simple"),
"@brief Decomposes the region into trapezoids.\n"
"\n"
"This method will return a \\Shapes container that holds a decomposition of the region into trapezoids.\n"
- "See \\Polygon#decompose_trapezoids for details.\n"
+ "See \\Polygon#decompose_trapezoids for details. If you want \\Region output, you should use \\decompose_trapezoids_to_region.\n"
+ "\n"
+ "This method has been introduced in version 0.25."
+ ) +
+ factory_ext ("decompose_trapezoids_to_region", &decompose_trapezoids, gsi::arg ("mode", td_simple (), "\\Polygon#TD_simple"),
+ "@brief Decomposes the region into trapezoids.\n"
+ "\n"
+ "This method is identical to \\decompose_trapezoids, but delivers a \\Region object.\n"
"\n"
"This method has been introduced in version 0.25."
) +
diff --git a/src/gsi/gsiMethods.h b/src/gsi/gsiMethods.h
index 3b994ba0b..05d6776bd 100644
--- a/src/gsi/gsiMethods.h
+++ b/src/gsi/gsiMethods.h
@@ -25,6 +25,7 @@
#define _HDR_gsiMethods
#include "tlString.h"
+#include "tlUtils.h"
#include "tlAssert.h"
#include "gsiSerialisation.h"
@@ -810,6 +811,16 @@ constant (const std::string &name, R (*m) (), const std::string &doc = std::stri
return Methods (new ConstantGetter (name, m, doc));
}
+struct return_by_value
+{
+ typedef tl::False is_factory;
+};
+
+struct return_new_object
+{
+ typedef tl::True is_factory;
+};
+
// 0 argument
#define _COUNT 0
diff --git a/src/gsi/gsiMethodsVar.h b/src/gsi/gsiMethodsVar.h
index be6ce90b3..4adedbc88 100644
--- a/src/gsi/gsiMethodsVar.h
+++ b/src/gsi/gsiMethodsVar.h
@@ -220,7 +220,7 @@ private:
_ARGSPECMEM
};
-template
+template
class _NAME(Method)
: public MethodSpecificBase
{
@@ -240,7 +240,11 @@ public:
{
this->clear ();
_ADDARGS
- this->template set_return ();
+ if (tl::value_from_type (typename F::is_factory ())) {
+ this->template set_return_new ();
+ } else {
+ this->template set_return ();
+ }
}
virtual MethodBase *clone () const
@@ -264,7 +268,7 @@ private:
_ARGSPECMEM
};
-template
+template
class _NAME(ConstMethod)
: public MethodSpecificBase
{
@@ -284,7 +288,11 @@ public:
{
this->clear ();
_ADDARGS
- this->template set_return ();
+ if (tl::value_from_type (typename F::is_factory ())) {
+ this->template set_return_new ();
+ } else {
+ this->template set_return ();
+ }
}
virtual MethodBase *clone () const
@@ -308,7 +316,7 @@ private:
_ARGSPECMEM
};
-template
+template
class _NAME(ExtMethod)
: public MethodBase
{
@@ -328,7 +336,11 @@ public:
{
this->clear ();
_ADDARGS
- this->template set_return ();
+ if (tl::value_from_type (typename F::is_factory ())) {
+ this->template set_return_new ();
+ } else {
+ this->template set_return ();
+ }
}
virtual MethodBase *clone () const
@@ -440,50 +452,6 @@ private:
_ARGSPECMEM
};
-template
-class _NAME(Factory)
- : public MethodBase
-{
-public:
- _NAME(Factory) (const std::string &name, X *(*m) (_FUNCARGLIST), const std::string &doc)
- : MethodBase (name, doc), m_m (m)
- {
- }
-
- _NAME(Factory) *add_args (_ARGSPEC)
- {
- _ARGSPECINIT;
- return this;
- }
-
- void initialize ()
- {
- this->clear ();
- _ADDARGS
- this->template set_return_new ();
- }
-
- virtual MethodBase *clone () const
- {
- return new _NAME(Factory) (*this);
- }
-
-#if _COUNT != 0
- virtual void call (void *, SerialArgs &args, SerialArgs &ret) const
-#else
- virtual void call (void *, SerialArgs &, SerialArgs &ret) const
-#endif
- {
- this->mark_called ();
- _GETARGVARS;
- ret.write ((*m_m) (_ARGVARLIST));
- }
-
-private:
- X *(*m_m) (_FUNCARGLIST);
- _ARGSPECMEM
-};
-
// pointer iterator method descriptors
template
@@ -1400,6 +1368,22 @@ method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST) _COMMA _A
}
#endif
+template
+Methods
+factory_ext (const std::string &name, R *(*xm) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ())
+{
+ return Methods (new _NAME(ExtMethod) (name, xm, doc));
+}
+
+#if _COUNT != 0
+template
+Methods
+factory_ext (const std::string &name, R *(*xm) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
+{
+ return Methods ((new _NAME(ExtMethod) (name, xm, doc))->add_args (_ARGSPECARGS));
+}
+#endif
+
template
Methods
constructor (const std::string &name, X *(*m) (_FUNCARGLIST), const std::string &doc = std::string ())
@@ -1416,22 +1400,6 @@ constructor (const std::string &name, X *(*m) (_FUNCARGLIST) _COMMA _ARGSPECS, c
}
#endif
-template
-Methods
-factory (const std::string &name, X *(*m) (_FUNCARGLIST), const std::string &doc = std::string ())
-{
- return Methods (new _NAME(Factory) (name, m, doc));
-}
-
-#if _COUNT != 0
-template
-Methods
-factory (const std::string &name, X *(*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
-{
- return Methods ((new _NAME(Factory) (name, m, doc))->add_args (_ARGSPECARGS));
-}
-#endif
-
template
Methods
method (const std::string &name, R (*m) (_FUNCARGLIST), const std::string &doc = std::string ())
@@ -1480,6 +1448,22 @@ method (const std::string &name, R (X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS
}
#endif
+template
+Methods
+factory (const std::string &name, R *(X::*m) (_FUNCARGLIST) const, const std::string &doc = std::string ())
+{
+ return Methods (new _NAME(ConstMethod) (name, m, doc));
+}
+
+#if _COUNT != 0
+template
+Methods
+factory (const std::string &name, R *(X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
+{
+ return Methods ((new _NAME(ConstMethod) (name, m, doc))->add_args (_ARGSPECARGS));
+}
+#endif
+
template
Methods
callback (const std::string &name, R (X::*m) (_FUNCARGLIST) const, Callback X::*cb, const std::string &doc = std::string ())
diff --git a/src/lay/built_in_macros/drc.lym b/src/lay/built_in_macros/drc.lym
index 5d8ee36ff..7eb0edcca 100644
--- a/src/lay/built_in_macros/drc.lym
+++ b/src/lay/built_in_macros/drc.lym
@@ -887,6 +887,66 @@ CODE
end
+ # %DRC%
+ # @name corners
+ # @brief Selects corners of polygons
+ # @synopsis layer.corners([ options ])
+ # @synopsis layer.corners(angle, [ options ])
+ # @synopsis layer.corners(amin .. amax, [ options ])
+ #
+ # This method produces markers on the corners of the polygons. An angle criterion can be given which
+ # selects corners based on the angle of the connecting edges. Positive angles indicate a left turn
+ # while negative angles indicate a right turn. Since polygons are oriented clockwise, postive angles
+ # indicate concave corners while negative ones indicate convex corners.
+ #
+ # The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
+ #
+ # The options available are:
+ #
+ # @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
+ # @/ul
+ #
+ # The following image shows the effect of this method
+ #
+ # @table
+ # @tr
+ # @td @img(/images/drc_corners1.png) @/td
+ # @td @img(/images/drc_corners2.png) @/td
+ # @td @img(/images/drc_corners3.png) @/td
+ # @/tr
+ # @/table
+
+ def corners(*args)
+
+ requires_region("corners")
+
+ 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 for 'corners' method")
+ end
+ end
+
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax))
+
+ end
+
# %DRC%
# @name middle
# @brief Returns the center points of the bounding boxes of the polygons
diff --git a/src/lay/doc/about/drc_ref_layer.xml b/src/lay/doc/about/drc_ref_layer.xml
index cc2f50d0f..b3a7a9bf8 100644
--- a/src/lay/doc/about/drc_ref_layer.xml
+++ b/src/lay/doc/about/drc_ref_layer.xml
@@ -148,6 +148,38 @@ Clean state is the default.
See raw for some remarks about how this state is
propagated.
+"corners" - Selects corners of polygons
+
+Usage:
+
+- layer.corners([ options ])
+- layer.corners(angle, [ options ])
+- layer.corners(amin .. amax, [ options ])
+
+
+This method produces markers on the corners of the polygons. An angle criterion can be given which
+selects corners based on the angle of the connecting edges. Positive angles indicate a left turn
+while negative angles indicate a right turn. Since polygons are oriented clockwise, postive angles
+indicate concave corners while negative ones indicate convex corners.
+
+The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
+
+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
+
+
+The following image shows the effect of this method
+
+
+
+ |
+ |
+
+
+
"data" - Gets the low-level data object
Usage:
diff --git a/src/lay/doc/images/drc_corners1.png b/src/lay/doc/images/drc_corners1.png
new file mode 100644
index 0000000000000000000000000000000000000000..52a4e8f4d3ca1c1c4017d04ea077f6b81ca0781d
GIT binary patch
literal 5233
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
literal 0
HcmV?d00001
diff --git a/src/lay/doc/images/drc_corners3.png b/src/lay/doc/images/drc_corners3.png
new file mode 100644
index 0000000000000000000000000000000000000000..061cdf486e9fedd936f892cf7eaa4a4033b5f22c
GIT binary patch
literal 5245
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
IxqIw?0f&w(ZvX%Q
literal 0
HcmV?d00001
diff --git a/src/lay/doc/images/drc_extent_refs30.png b/src/lay/doc/images/drc_extent_refs30.png
index 7903231558241859239557092a7a10515c3f580e..2890ce143378f6dc05c3af8d9b059b3035c65c15 100644
GIT binary patch
delta 2078
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
diff --git a/src/lay/layHelpResources.qrc b/src/lay/layHelpResources.qrc
index 902c35f86..abad5f0fc 100644
--- a/src/lay/layHelpResources.qrc
+++ b/src/lay/layHelpResources.qrc
@@ -92,6 +92,9 @@
doc/images/drc_extent_refs27.png
doc/images/drc_extent_refs30.png
doc/images/drc_extent_refs31.png
+ doc/images/drc_corners1.png
+ doc/images/drc_corners2.png
+ doc/images/drc_corners3.png
doc/about/rba_notation.xml
diff --git a/testdata/drc/drctest.drc b/testdata/drc/drctest.drc
index 150ec59a4..b99c7296e 100644
--- a/testdata/drc/drctest.drc
+++ b/testdata/drc/drctest.drc
@@ -542,6 +542,10 @@ a1.extent_refs(:right_center).sized(0.05).output(1052, 0)
a1.extent_refs(:rc).sized(0.05).output(1052, 1)
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)
+
puts "=== Single-cell testsuite ==="
run_testsuite(0, 1)
diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb
index cb9ef787e..062e0dbb5 100644
--- a/testdata/ruby/dbRegionTest.rb
+++ b/testdata/ruby/dbRegionTest.rb
@@ -683,12 +683,30 @@ class DBRegion_TestClass < TestBase
r.insert(p)
assert_equal(RBA::Region::new(r.decompose_convex).to_s, "(0,10;0,30;10,30;10,10);(10,30;10,40;40,40;40,30);(0,30;0,50;10,50;10,30);(30,0;30,30;40,30;40,0);(0,0;0,10;30,10;30,0)")
+ assert_equal(r.decompose_convex_to_region.to_s, "(0,10;0,30;10,30;10,10);(10,30;10,40;40,40;40,30);(0,30;0,50;10,50;10,30);(30,0;30,30;40,30;40,0);(0,0;0,10;30,10;30,0)")
assert_equal(RBA::Region::new(r.decompose_convex(RBA::Polygon::PO_horizontal)).to_s, "(0,10;0,30;10,30;10,10);(0,30;0,40;40,40;40,30);(0,40;0,50;10,50;10,40);(30,10;30,30;40,30;40,10);(0,0;0,10;40,10;40,0)")
assert_equal(RBA::Region::new(r.decompose_trapezoids).to_s, "(0,0;0,10;40,10;40,0);(0,10;0,30;10,30;10,10);(30,10;30,30;40,30;40,10);(0,30;0,40;40,40;40,30);(0,40;0,50;10,50;10,40)")
+ assert_equal(r.decompose_trapezoids_to_region.to_s, "(0,0;0,10;40,10;40,0);(0,10;0,30;10,30;10,10);(30,10;30,30;40,30;40,10);(0,30;0,40;40,40;40,30);(0,40;0,50;10,50;10,40)")
assert_equal(RBA::Region::new(r.decompose_trapezoids(RBA::Polygon::TD_htrapezoids)).to_s, "(0,10;0,30;10,30;10,10);(10,30;10,40;40,40;40,30);(0,30;0,50;10,50;10,30);(30,0;30,30;40,30;40,0);(0,0;0,10;30,10;30,0)")
end
+ # corners functions
+ def test_14
+
+ r = RBA::Region::new
+ p = RBA::Polygon::from_s("(0,0;0,80;40,40;40,0/10,10;30,10;30,30;10,30)")
+ r.insert(p)
+
+ assert_equal(r.corners.to_s, "(39,-1;39,1;41,1;41,-1);(-1,-1;-1,1;1,1;1,-1);(-1,79;-1,81;1,81;1,79);(39,39;39,41;41,41;41,39);(9,29;9,31;11,31;11,29);(9,9;9,11;11,11;11,9);(29,9;29,11;31,11;31,9);(29,29;29,31;31,31;31,29)")
+ assert_equal(r.corners(-90.0, 90.0).to_s, "(39,-1;39,1;41,1;41,-1);(-1,-1;-1,1;1,1;1,-1);(39,39;39,41;41,41;41,39);(9,29;9,31;11,31;11,29);(9,9;9,11;11,11;11,9);(29,9;29,11;31,11;31,9);(29,29;29,31;31,31;31,29)")
+ assert_equal(r.corners(-90.0, -90.0).to_s, "(39,-1;39,1;41,1;41,-1);(-1,-1;-1,1;1,1;1,-1)")
+ assert_equal(r.corners(-45.0, -45.0).to_s, "(39,39;39,41;41,41;41,39)")
+ assert_equal(r.corners(-90.0, -45.0).to_s, "(39,-1;39,1;41,1;41,-1);(-1,-1;-1,1;1,1;1,-1);(39,39;39,41;41,41;41,39)")
+ assert_equal(r.corners(90.0, 90.0).to_s, "(9,29;9,31;11,31;11,29);(9,9;9,11;11,11;11,9);(29,9;29,11;31,11;31,9);(29,29;29,31;31,31;31,29)")
+
+ end
+
end
load("test_epilogue.rb")