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")