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 000000000..52a4e8f4d
Binary files /dev/null and b/src/lay/doc/images/drc_corners1.png differ
diff --git a/src/lay/doc/images/drc_corners2.png b/src/lay/doc/images/drc_corners2.png
new file mode 100644
index 000000000..bad8c9dcb
Binary files /dev/null and b/src/lay/doc/images/drc_corners2.png differ
diff --git a/src/lay/doc/images/drc_corners3.png b/src/lay/doc/images/drc_corners3.png
new file mode 100644
index 000000000..061cdf486
Binary files /dev/null and b/src/lay/doc/images/drc_corners3.png differ
diff --git a/src/lay/doc/images/drc_extent_refs30.png b/src/lay/doc/images/drc_extent_refs30.png
index 790323155..2890ce143 100644
Binary files a/src/lay/doc/images/drc_extent_refs30.png and b/src/lay/doc/images/drc_extent_refs30.png differ
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")