diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index ded44d2c1..4f66883d3 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -514,7 +514,7 @@ struct simple_polygon_defs ) + method_ext ("touches?", &touches_edge, gsi::arg ("edge"), "@brief Returns true, if the polygon touches the given edge.\n" - "The box and the polygon touch if they overlap or the edge shares at least one point with the polygon's contour.\n" + "The edge and the polygon touch if they overlap or the edge shares at least one point with the polygon's contour.\n" "\n" "This method was introduced in version 0.25.1.\n" ) + @@ -1509,7 +1509,7 @@ struct polygon_defs ) + method_ext ("touches?", &touches_edge, gsi::arg ("edge"), "@brief Returns true, if the polygon touches the given edge.\n" - "The box and the polygon touch if they overlap or the edge shares at least one point with the polygon's contour.\n" + "The edge and the polygon touch if they overlap or the edge shares at least one point with the polygon's contour.\n" "\n" "This method was introduced in version 0.25.1.\n" ) + diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc index 54fa391ff..d35053f1c 100644 --- a/src/pya/pya/pya.cc +++ b/src/pya/pya/pya.cc @@ -1021,7 +1021,7 @@ method_adaptor (int mid, PyObject *self, PyObject *args) // In case of an error upon write, pop the arguments to clean them up. // Without this, there is a risk to keep dead objects on the stack. for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, arglist, NULL, heap); + pop_arg (*a, arglist, 0, heap); } throw; @@ -1572,7 +1572,7 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args) // In case of an error upon write, pop the arguments to clean them up. // Without this, there is a risk to keep dead objects on the stack. for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, arglist, NULL, heap); + pop_arg (*a, arglist, 0, heap); } throw; diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index 3836234e9..c14aca3d5 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -359,7 +359,7 @@ tl::Variant python2c (PyObject *rval, tl::Heap *heap) } PyObject * -object_to_python (void *obj, const gsi::ArgType &atype) +object_to_python (void *obj, PYAObjectBase *self, const gsi::ArgType &atype) { const gsi::ClassBase *cls = atype.cls()->subclass_decl (obj); @@ -369,7 +369,7 @@ object_to_python (void *obj, const gsi::ArgType &atype) bool prefer_copy = atype.is_cref (); bool can_destroy = prefer_copy || atype.is_ptr (); - return object_to_python (obj, cls, pass_obj, is_const, prefer_copy, can_destroy); + return object_to_python (obj, self, cls, pass_obj, is_const, prefer_copy, can_destroy); } /** @@ -390,7 +390,7 @@ void correct_constness (PyObject *obj, bool const_required) } PyObject * -object_to_python (void *obj, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy) +object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy) { if (! obj || ! cls) { Py_RETURN_NONE; @@ -402,7 +402,13 @@ object_to_python (void *obj, const gsi::ClassBase *cls, bool pass_obj, bool is_c } PYAObjectBase *pya_object = 0; - if (! clsact->adapted_type_info () && clsact->is_managed ()) { + + if (self && self->obj () == obj) { + + // reuse self if the object to be converted is self + pya_object = self; + + } else if (! clsact->adapted_type_info () && clsact->is_managed ()) { StatusChangedListener *client = clsact->gsi_object (obj)->find_client (); if (client) { @@ -504,7 +510,7 @@ PyObject *c2python (const tl::Variant &c) const gsi::ClassBase *cls = c.gsi_cls (); if (cls) { void *obj = const_cast (c.to_user ()); - return object_to_python (obj, c.user_cls ()->gsi_cls (), false, false, true, false); + return object_to_python (obj, 0, c.user_cls ()->gsi_cls (), false, false, true, false); } else { // not a known type -> return nil Py_RETURN_NONE; diff --git a/src/pya/pya/pyaConvert.h b/src/pya/pya/pyaConvert.h index 48c55d023..3ccabbd7d 100644 --- a/src/pya/pya/pyaConvert.h +++ b/src/pya/pya/pyaConvert.h @@ -43,12 +43,15 @@ namespace gsi namespace pya { +class PYAObjectBase; + // ------------------------------------------------------------------- // Conversion of a generic object to a Python object /** * @brief Translates an object to a Python object (PyObject) * @param obj The generic object pointer + * @param self If there is an object where the returned object may be a member of or 0 if there isn't * @param cls The class of the object * @param pass_obj If true, the Python object will own the original object which gets destroyed when the Ruby object is finalized * @param is_const If true, the Python object will be a const one unless the original object is already bound in a non-const way @@ -57,14 +60,14 @@ namespace pya * @return The Python object */ PyObject * -object_to_python (void *obj, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy); +object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy); /** * @brief Translates an object to a Python object (PyObject) * This version takes it's flags from the atype given. */ PyObject * -object_to_python (void *obj, const gsi::ArgType &atype); +object_to_python (void *obj, PYAObjectBase *self, const gsi::ArgType &atype); // ------------------------------------------------------------------- // Type checks diff --git a/src/pya/pya/pyaHelpers.cc b/src/pya/pya/pyaHelpers.cc index b81d19605..a2f441ade 100644 --- a/src/pya/pya/pyaHelpers.cc +++ b/src/pya/pya/pyaHelpers.cc @@ -366,7 +366,7 @@ pya_plain_iterator_next (PyObject *self) gsi::SerialArgs args (iter->iter->serial_size ()); iter->iter->get (args); - PythonRef obj = pop_arg (*iter->value_type, args, NULL, heap); + PythonRef obj = pop_arg (*iter->value_type, args, 0, heap); iter->iter->inc (); diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index d327e82fa..be79efbf9 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -522,7 +522,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &, tl::Heap *heap) { std::auto_ptr a ((gsi::StringAdaptor *) rr->read(*heap)); if (!a.get ()) { @@ -534,7 +534,7 @@ struct reader }; static -PyObject *object_from_variant (const tl::Variant &var, const gsi::ArgType &atype) +PyObject *object_from_variant (const tl::Variant &var, PYAObjectBase *self, const gsi::ArgType &atype) { if (var.is_user()) { @@ -579,7 +579,7 @@ PyObject *object_from_variant (const tl::Variant &var, const gsi::ArgType &atype } - return object_to_python ((void *) var.to_user (), var.user_cls ()->gsi_cls (), pass_obj, is_const, prefer_copy, can_destroy); + return object_to_python ((void *) var.to_user (), self, var.user_cls ()->gsi_cls (), pass_obj, is_const, prefer_copy, can_destroy); } else { return c2python (var); @@ -592,7 +592,7 @@ PyObject *object_from_variant (const tl::Variant &var, const gsi::ArgType &atype template <> struct reader { - void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &atype, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase *self, const gsi::ArgType &atype, tl::Heap *heap) { std::auto_ptr a ((gsi::VariantAdaptor *) rr->read(*heap)); if (!a.get ()) { @@ -601,9 +601,9 @@ struct reader gsi::VariantAdaptorImpl *aa = dynamic_cast *> (a.get ()); if (aa) { // A small optimization that saves one variant copy - *ret = object_from_variant (aa->var_ref (), atype); + *ret = object_from_variant (aa->var_ref (), self, atype); } else { - *ret = object_from_variant (a->var (), atype); + *ret = object_from_variant (a->var (), self, atype); } } } @@ -615,7 +615,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &atype, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &atype, tl::Heap *heap) { std::auto_ptr a ((gsi::VectorAdaptor *) rr->read(*heap)); if (!a.get ()) { @@ -635,7 +635,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &atype, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &atype, tl::Heap *heap) { std::auto_ptr a ((gsi::MapAdaptor *) rr->read(*heap)); if (!a.get ()) { @@ -656,13 +656,13 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &atype, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase *self, const gsi::ArgType &atype, tl::Heap *heap) { void *obj = rr->read (*heap); if (! obj) { *ret = PythonRef (Py_None, false /*borrowed*/); } else { - *ret = object_to_python (obj, atype); + *ret = object_to_python (obj, self, atype); } } }; @@ -673,14 +673,14 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *, PythonRef *, PyObject *, const gsi::ArgType &, tl::Heap *) + void operator() (gsi::SerialArgs *, PythonRef *, PYAObjectBase *, const gsi::ArgType &, tl::Heap *) { // .. nothing: void is not serialized } }; PythonRef -pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PyObject *self, tl::Heap &heap) +pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObjectBase *self, tl::Heap &heap) { PythonRef ret; gsi::do_on_type () (atype.type (), &aserial, &ret, self, atype, &heap); @@ -754,7 +754,7 @@ void PythonBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap) { if (PyList_Check (m_array.get ())) { PythonRef member; - gsi::do_on_type () (mp_ainner->type (), &r, &member, (PyObject *) NULL, *mp_ainner, &heap); + gsi::do_on_type () (mp_ainner->type (), &r, &member, (PYAObjectBase *) 0, *mp_ainner, &heap); PyList_Append (m_array.get (), member.get ()); } else if (PyTuple_Check (m_array.get ())) { throw tl::Exception (tl::to_string (QObject::tr ("Tuples cannot be modified and cannot be used as out parameters"))); @@ -825,8 +825,8 @@ gsi::MapAdaptorIterator *PythonBasedMapAdaptor::create_iterator () const void PythonBasedMapAdaptor::insert (gsi::SerialArgs &r, tl::Heap &heap) { PythonRef k, v; - gsi::do_on_type () (mp_ainner_k->type (), &r, &k, (PyObject *) NULL, *mp_ainner_k, &heap); - gsi::do_on_type () (mp_ainner->type (), &r, &v, (PyObject *) NULL, *mp_ainner, &heap); + gsi::do_on_type () (mp_ainner_k->type (), &r, &k, (PYAObjectBase *) 0, *mp_ainner_k, &heap); + gsi::do_on_type () (mp_ainner->type (), &r, &v, (PYAObjectBase *) 0, *mp_ainner, &heap); PyDict_SetItem (m_hash.get (), k.get (), v.get ()); } diff --git a/src/pya/pya/pyaMarshal.h b/src/pya/pya/pyaMarshal.h index 0fcadbff1..8c47a7f69 100644 --- a/src/pya/pya/pyaMarshal.h +++ b/src/pya/pya/pyaMarshal.h @@ -33,6 +33,8 @@ namespace pya { +class PYAObjectBase; + /** * @brief Serializes the given argument using the given type. * @@ -56,7 +58,7 @@ push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PyObject *arg, tl * @param self The self object of the method call (for shortcut evaluation to return self if possible) * @return The deserialized object (a new reference) */ -PythonRef pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PyObject *self, tl::Heap &heap); +PythonRef pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObjectBase *self, tl::Heap &heap); /** * @brief Tests whether the given object is compatible with the given type diff --git a/src/pya/pya/pyaObject.cc b/src/pya/pya/pyaObject.cc index 9e3af172e..f1db51c8d 100644 --- a/src/pya/pya/pyaObject.cc +++ b/src/pya/pya/pyaObject.cc @@ -164,7 +164,7 @@ Callee::call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const // TODO: callbacks with default arguments? for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) { - PyTuple_SetItem (argv.get (), arg4self + (a - meth->begin_arguments ()), pop_arg (*a, args, NULL, heap).release ()); + PyTuple_SetItem (argv.get (), arg4self + (a - meth->begin_arguments ()), pop_arg (*a, args, 0, heap).release ()); } PythonRef result (PyObject_CallObject (callable.get (), argv.get ())); @@ -218,7 +218,7 @@ void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gs int args_avail = int (std::distance (meth->begin_arguments (), meth->end_arguments ())); PythonRef argv (PyTuple_New (args_avail)); for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) { - PyTuple_SetItem (argv.get (), int (a - meth->begin_arguments ()), pop_arg (*a, args, NULL, heap).release ()); + PyTuple_SetItem (argv.get (), int (a - meth->begin_arguments ()), pop_arg (*a, args, 0, heap).release ()); } // NOTE: in case one event handler deletes the object, it's safer to first collect the handlers and diff --git a/src/pya/unit_tests/pya.cc b/src/pya/unit_tests/pya.cc index 07a20f01c..d01b69eb4 100644 --- a/src/pya/unit_tests/pya.cc +++ b/src/pya/unit_tests/pya.cc @@ -99,6 +99,7 @@ void run_pythontest (tl::TestBase *_this, const std::string &fn) PYTHONTEST (dbLayoutTest, "dbLayoutTest.py") PYTHONTEST (dbRegionTest, "dbRegionTest.py") PYTHONTEST (dbPCellsTest, "dbPCells.py") +PYTHONTEST (dbPolygonTest, "dbPolygonTest.py") PYTHONTEST (tlTest, "tlTest.py") #if defined(HAVE_QTBINDINGS) PYTHONTEST (qtbinding, "qtbinding.py") diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 079b3770f..67bda1e46 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -977,7 +977,7 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) // In case of an error upon write, pop the arguments to clean them up. // Without this, there is a risk to keep dead objects on the stack. for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, arglist, heap); + pop_arg (*a, 0, arglist, heap); } throw; @@ -1020,7 +1020,7 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) // In case of an error upon write, pop the arguments to clean them up. // Without this, there is a risk to keep dead objects on the stack. for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, arglist, heap); + pop_arg (*a, 0, arglist, heap); } throw; @@ -1046,7 +1046,7 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) rr.reset (); iter->get (rr); - VALUE value = pop_arg (meth->ret_type (), rr, heap); + VALUE value = pop_arg (meth->ret_type (), p, rr, heap); rba_yield_checked (value); iter->inc (); @@ -1060,7 +1060,7 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) } } else { - ret = pop_arg (meth->ret_type (), retlist, heap); + ret = pop_arg (meth->ret_type (), p, retlist, heap); } } @@ -1679,7 +1679,7 @@ rba_init (RubyInterpreterPrivateData *d) gsi::SerialArgs arglist (c->meth->argsize ()); c->meth->call (0, arglist, retlist); tl::Heap heap; - VALUE ret = pop_arg (c->meth->ret_type (), retlist, heap); + VALUE ret = pop_arg (c->meth->ret_type (), 0, retlist, heap); rb_define_const (c->klass, c->name.c_str (), ret); } catch (tl::Exception &ex) { diff --git a/src/rba/rba/rbaConvert.cc b/src/rba/rba/rbaConvert.cc index 2422cc145..312d7b3ca 100644 --- a/src/rba/rba/rbaConvert.cc +++ b/src/rba/rba/rbaConvert.cc @@ -126,7 +126,7 @@ tl::Variant ruby2c (VALUE rval) } } -VALUE object_to_ruby (void *obj, const gsi::ArgType &atype) +VALUE object_to_ruby (void *obj, Proxy *self, const gsi::ArgType &atype) { const gsi::ClassBase *cls = atype.cls()->subclass_decl (obj); @@ -136,7 +136,7 @@ VALUE object_to_ruby (void *obj, const gsi::ArgType &atype) bool prefer_copy = atype.is_cref (); bool can_destroy = prefer_copy || atype.is_ptr (); - return object_to_ruby (obj, cls, pass_obj, is_const, prefer_copy, can_destroy); + return object_to_ruby (obj, self, cls, pass_obj, is_const, prefer_copy, can_destroy); } /** @@ -156,7 +156,7 @@ static void correct_constness (Proxy *p, bool const_required) } VALUE -object_to_ruby (void *obj, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy) +object_to_ruby (void *obj, Proxy *self, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy) { VALUE ret = Qnil; if (! obj || ! cls) { @@ -170,7 +170,12 @@ object_to_ruby (void *obj, const gsi::ClassBase *cls, bool pass_obj, bool is_con // Derive a Proxy reference if the object is already bound Proxy *rba_data = 0; - if (! clsact->adapted_type_info () && clsact->is_managed ()) { + if (self && self->obj () == obj) { + + // reuse "self" if the object to convert is self. + rba_data = self; + + } else if (! clsact->adapted_type_info () && clsact->is_managed ()) { rba_data = clsact->gsi_object (obj)->find_client (); if (rba_data) { @@ -281,7 +286,7 @@ VALUE c2ruby (const tl::Variant &c) const gsi::ClassBase *cls = c.gsi_cls (); if (cls) { void *obj = const_cast (c.to_user ()); - return object_to_ruby (obj, c.user_cls ()->gsi_cls (), false, false, true, false); + return object_to_ruby (obj, 0, c.user_cls ()->gsi_cls (), false, false, true, false); } else { // not a known type -> return nil return Qnil; diff --git a/src/rba/rba/rbaConvert.h b/src/rba/rba/rbaConvert.h index 1e6e163c4..92dd64f38 100644 --- a/src/rba/rba/rbaConvert.h +++ b/src/rba/rba/rbaConvert.h @@ -34,11 +34,14 @@ namespace rba { +class Proxy; + // ------------------------------------------------------------------- /** * @brief Translates an object to a Ruby object (VALUE) * @param obj The generic object pointer + * @param self The object which the object is derived from (self in a method call) or 0 if there is no such object * @param cls The class of the object * @param pass_obj If true, the Ruby object will own the original object which gets destroyed when the Ruby object is finalized * @param is_const If true, the Ruby object will be a const one unless the original object is already bound in a non-const way @@ -46,13 +49,13 @@ namespace rba * @param can_destroy If true, the Ruby object can be destroyed explicitly * @return The Ruby object */ -VALUE object_to_ruby (void *obj, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy); +VALUE object_to_ruby (void *obj, Proxy *self, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy); /** * @brief Translates an object to a Ruby object (VALUE) * This version takes it's flags from the atype given. */ -VALUE object_to_ruby (void *obj, const gsi::ArgType &atype); +VALUE object_to_ruby (void *obj, Proxy *self, const gsi::ArgType &atype); // ------------------------------------------------------------------- // Type checks diff --git a/src/rba/rba/rbaInspector.cc b/src/rba/rba/rbaInspector.cc index 78fbcf6ac..c19f1e370 100644 --- a/src/rba/rba/rbaInspector.cc +++ b/src/rba/rba/rbaInspector.cc @@ -416,7 +416,7 @@ public: meth->call (obj, arglist, retlist); tl::Heap heap; - return pop_arg (meth->ret_type (), retlist, heap); + return pop_arg (meth->ret_type (), p, retlist, heap); } diff --git a/src/rba/rba/rbaInternal.cc b/src/rba/rba/rbaInternal.cc index dc6be87cb..931fe151d 100644 --- a/src/rba/rba/rbaInternal.cc +++ b/src/rba/rba/rbaInternal.cc @@ -320,7 +320,7 @@ Proxy::call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const // TODO: callbacks with default arguments? for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) { - rb_ary_push (argv, pop_arg (*a, args, heap)); + rb_ary_push (argv, pop_arg (*a, 0, args, heap)); } VALUE rb_ret = rba_funcall2_checked (m_self, mid, RARRAY_LEN (argv), RARRAY_PTR (argv)); @@ -891,7 +891,7 @@ void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gs // TODO: signals with default arguments? for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) { - rb_ary_push (argv, pop_arg (*a, args, heap)); + rb_ary_push (argv, pop_arg (*a, 0, args, heap)); } // call the signal handlers ... the last one will deliver the return value diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index 47acf340e..30f4b97a3 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -481,7 +481,7 @@ push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, VALUE arg, tl::He template struct reader { - void operator() (gsi::SerialArgs *rr, VALUE *ret, const gsi::ArgType &arg, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, VALUE *ret, Proxy * /*self*/, const gsi::ArgType &arg, tl::Heap *heap) { if (arg.is_ref ()) { *ret = c2ruby (rr->template read (*heap)); @@ -516,7 +516,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, VALUE *ret, const gsi::ArgType &arg, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, VALUE *ret, Proxy * /*self*/, const gsi::ArgType &arg, tl::Heap *heap) { tl_assert (! arg.is_cref ()); tl_assert (! arg.is_ref ()); @@ -532,7 +532,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, VALUE *ret, const gsi::ArgType &, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, VALUE *ret, Proxy * /*self*/, const gsi::ArgType &, tl::Heap *heap) { std::auto_ptr a ((gsi::StringAdaptor *) rr->read(*heap)); if (!a.get ()) { @@ -543,7 +543,7 @@ struct reader } }; -static VALUE object_from_variant (const tl::Variant &var, const gsi::ArgType &atype) +static VALUE object_from_variant (const tl::Variant &var, Proxy *self, const gsi::ArgType &atype) { if (var.is_user()) { @@ -588,7 +588,7 @@ static VALUE object_from_variant (const tl::Variant &var, const gsi::ArgType &at } - return object_to_ruby ((void *) var.to_user (), var.user_cls ()->gsi_cls (), pass_obj, is_const, prefer_copy, can_destroy); + return object_to_ruby ((void *) var.to_user (), self, var.user_cls ()->gsi_cls (), pass_obj, is_const, prefer_copy, can_destroy); } else { return c2ruby (var); @@ -601,7 +601,7 @@ static VALUE object_from_variant (const tl::Variant &var, const gsi::ArgType &at template <> struct reader { - void operator() (gsi::SerialArgs *rr, VALUE *ret, const gsi::ArgType &atype, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, VALUE *ret, Proxy *self, const gsi::ArgType &atype, tl::Heap *heap) { std::auto_ptr a ((gsi::VariantAdaptor *) rr->read(*heap)); if (!a.get ()) { @@ -610,9 +610,9 @@ struct reader gsi::VariantAdaptorImpl *aa = dynamic_cast *> (a.get ()); if (aa) { // A small optimization that saves one variant copy - *ret = object_from_variant (aa->var_ref (), atype); + *ret = object_from_variant (aa->var_ref (), self, atype); } else { - *ret = object_from_variant (a->var (), atype); + *ret = object_from_variant (a->var (), self, atype); } } } @@ -624,7 +624,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, VALUE *ret, const gsi::ArgType &atype, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, VALUE *ret, Proxy * /*self*/, const gsi::ArgType &atype, tl::Heap *heap) { std::auto_ptr a ((gsi::VectorAdaptor *) rr->read(*heap)); if (!a.get ()) { @@ -644,7 +644,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, VALUE *ret, const gsi::ArgType &atype, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, VALUE *ret, Proxy * /*self*/, const gsi::ArgType &atype, tl::Heap *heap) { std::auto_ptr a ((gsi::MapAdaptor *) rr->read(*heap)); if (!a.get ()) { @@ -665,13 +665,13 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs *rr, VALUE *ret, const gsi::ArgType &atype, tl::Heap *heap) + void operator() (gsi::SerialArgs *rr, VALUE *ret, Proxy *self, const gsi::ArgType &atype, tl::Heap *heap) { void *obj = rr->read (*heap); if (! obj) { *ret = Qnil; } else { - *ret = object_to_ruby (obj, atype); + *ret = object_to_ruby (obj, self, atype); } } }; @@ -679,7 +679,7 @@ struct reader template <> struct reader { - void operator() (gsi::SerialArgs * /*rr*/, VALUE * /*ret*/, const gsi::ArgType & /*atype*/, tl::Heap * /*heap*/) + void operator() (gsi::SerialArgs * /*rr*/, VALUE * /*ret*/, Proxy * /*self*/, const gsi::ArgType & /*atype*/, tl::Heap * /*heap*/) { // .. nothing: void is not serialized } @@ -755,7 +755,7 @@ gsi::VectorAdaptorIterator *RubyBasedVectorAdaptor::create_iterator () const void RubyBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap) { VALUE member; - gsi::do_on_type () (mp_ainner->type (), &r, &member, *mp_ainner, &heap); + gsi::do_on_type () (mp_ainner->type (), &r, &member, (Proxy *) 0, *mp_ainner, &heap); rb_ary_push (m_array, member); } @@ -831,8 +831,8 @@ gsi::MapAdaptorIterator *RubyBasedMapAdaptor::create_iterator () const void RubyBasedMapAdaptor::insert (gsi::SerialArgs &r, tl::Heap &heap) { VALUE k, v; - gsi::do_on_type () (mp_ainner_k->type (), &r, &k, *mp_ainner_k, &heap); - gsi::do_on_type () (mp_ainner->type (), &r, &v, *mp_ainner, &heap); + gsi::do_on_type () (mp_ainner_k->type (), &r, &k, (Proxy *) 0, *mp_ainner_k, &heap); + gsi::do_on_type () (mp_ainner->type (), &r, &v, (Proxy *) 0, *mp_ainner, &heap); rb_hash_aset (m_hash, k, v); } @@ -855,10 +855,10 @@ size_t RubyBasedMapAdaptor::serial_size () const // Pops an argument from the call or return stack VALUE -pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, tl::Heap &heap) +pop_arg (const gsi::ArgType &atype, Proxy *self, gsi::SerialArgs &aserial, tl::Heap &heap) { VALUE ret = Qnil; - gsi::do_on_type () (atype.type (), &aserial, &ret, atype, &heap); + gsi::do_on_type () (atype.type (), &aserial, &ret, self, atype, &heap); return ret; } diff --git a/src/rba/rba/rbaMarshal.h b/src/rba/rba/rbaMarshal.h index 6df331c6d..bb4955707 100644 --- a/src/rba/rba/rbaMarshal.h +++ b/src/rba/rba/rbaMarshal.h @@ -33,10 +33,14 @@ namespace rba { +class Proxy; + /** * @brief Pops an argument from the call or return stack + * + * "self" is a reference to the object that the method is called on or 0 if there is no such object. */ -VALUE pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, tl::Heap &heap); +VALUE pop_arg (const gsi::ArgType &atype, Proxy *self, gsi::SerialArgs &aserial, tl::Heap &heap); /** * @brief Pushes an argument on the call or return stack diff --git a/testdata/python/dbPolygonTest.py b/testdata/python/dbPolygonTest.py new file mode 100644 index 000000000..c66abb633 --- /dev/null +++ b/testdata/python/dbPolygonTest.py @@ -0,0 +1,731 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2017 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import pya +import unittest +import sys + +class DBPolygonTests(unittest.TestCase): + + # DPolygon basics + def test_1_DPolygon(self): + + a = pya.DPolygon() + self.assertEqual( str(a), "()" ) + self.assertEqual( str(pya.DPolygon.from_s(str(a))), str(a) ) + self.assertEqual( a.is_box(), False ) + + b = a.dup() + a = pya.DPolygon( [ pya.DPoint( 0, 1 ), pya.DPoint( 1, 5 ), pya.DPoint( 5, 5 ) ] ) + self.assertEqual( str(a), "(0,1;1,5;5,5)" ) + self.assertEqual( str(a * 2), "(0,2;2,10;10,10)" ) + self.assertEqual( str(pya.DPolygon.from_s(str(a))), str(a) ) + self.assertEqual( a.is_box(), False ) + self.assertEqual( a.num_points_hull(), 3 ) + c = a.dup() + + self.assertEqual( a == b, False ) + self.assertEqual( a == c, True ) + self.assertEqual( a != b, True ) + self.assertEqual( a != c, False ) + + a = pya.DPolygon( pya.DBox( 5, -10, 20, 15 ) ) + self.assertEqual( a.is_box(), True ) + self.assertEqual( str(a), "(5,-10;5,15;20,15;20,-10)" ) + self.assertEqual( str(pya.Polygon(a)), "(5,-10;5,15;20,15;20,-10)" ) + self.assertEqual( a.num_points_hull(), 4 ) + self.assertEqual( a.area(), 15*25 ) + self.assertEqual( a.perimeter(), 80 ) + self.assertEqual( a.inside( pya.DPoint( 10, 0 ) ), True ) + self.assertEqual( a.inside( pya.DPoint( 5, 0 ) ), True ) + self.assertEqual( a.inside( pya.DPoint( 30, 0 ) ), False ) + + arr = [] + for p in a.each_point_hull(): + arr.append( str(p) ) + self.assertEqual( arr, ["5,-10", "5,15", "20,15", "20,-10"] ) + + b = a.dup() + + self.assertEqual( str(a.moved( pya.DPoint( 0, 1 ) )), "(5,-9;5,16;20,16;20,-9)" ) + self.assertEqual( str(a.moved( 0, 1 )), "(5,-9;5,16;20,16;20,-9)" ) + aa = a.dup() + aa.move( 1, 0 ) + self.assertEqual( str(aa), "(6,-10;6,15;21,15;21,-10)" ) + a.move( pya.DPoint( 1, 0 ) ) + self.assertEqual( str(a), "(6,-10;6,15;21,15;21,-10)" ) + + b = b.transformed( pya.DTrans( pya.DTrans.R0, pya.DPoint( 1, 0 )) ) + self.assertEqual( str(b), "(6,-10;6,15;21,15;21,-10)" ) + + m = pya.DCplxTrans( pya.DTrans(), 1.5 ) + self.assertEqual( type(a.transformed(m)).__name__, "DPolygon" ) + self.assertEqual( str(a.transformed(m)), "(9,-15;9,22.5;31.5,22.5;31.5,-15)" ) + + m = pya.VCplxTrans( 1000.0 ) + self.assertEqual( type(a.transformed(m)).__name__, "Polygon" ) + self.assertEqual( str(a.transformed(m)), "(6000,-10000;6000,15000;21000,15000;21000,-10000)" ) + + a.hull = [ pya.DPoint( 0, 1 ), pya.DPoint( 1, 1 ), pya.DPoint( 1, 5 ) ] + self.assertEqual( str(a.bbox()), "(0,1;1,5)" ) + + self.assertEqual( a.holes(), 0 ) + a.insert_hole( [ pya.DPoint( 1, 2 ), pya.DPoint( 2, 2 ), pya.DPoint( 2, 6 ) ] ) + self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" ) + self.assertEqual( str(pya.DPolygon.from_s(str(a))), str(a) ) + self.assertEqual( a.area(), 0 ) + self.assertEqual( a.num_points_hole(0), 3 ) + self.assertEqual( a.holes(), 1 ) + self.assertEqual( str(a.point_hull(1)), "1,5" ) + self.assertEqual( str(a.point_hull(0)), "0,1" ) + self.assertEqual( str(a.point_hull(100)), "0,0" ) + self.assertEqual( str(a.point_hole(0, 100)), "0,0" ) + self.assertEqual( str(a.point_hole(0, 1)), "2,2" ) + self.assertEqual( str(a.point_hole(1, 1)), "0,0" ) + a.compress(False); + self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" ) + a.compress(True); + self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" ) + + b = a.dup() + b.assign_hole(0, pya.DBox( 10, 20, 20, 60 )) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60;10,60)" ) + b.insert_hole(pya.DBox( 10, 20, 20, 60 )) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60;10,60/10,20;20,20;20,60;10,60)" ) + self.assertEqual( b.is_box(), False ) + + b = a.dup() + b.assign_hole(0, [ pya.DPoint( 10, 20 ), pya.DPoint( 20, 20 ), pya.DPoint( 20, 60 ) ]) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60)" ) + b.assign_hole(1, [ pya.DPoint( 15, 25 ), pya.DPoint( 25, 25 ), pya.DPoint( 25, 65 ) ]) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60)" ) + b.insert_hole( [ pya.DPoint( 1, 2 ), pya.DPoint( 2, 2 ), pya.DPoint( 2, 6 ) ] ) + self.assertEqual( str(b), "(0,1;1,5;1,1/1,2;2,2;2,6/10,20;20,20;20,60)" ) + b.assign_hole(0, [ pya.DPoint( 15, 25 ), pya.DPoint( 25, 25 ), pya.DPoint( 25, 65 ) ]) + self.assertEqual( str(b), "(0,1;1,5;1,1/15,25;25,25;25,65/10,20;20,20;20,60)" ) + + arr = [] + for p in a.each_point_hole(0): + arr.append( str(p) ) + + self.assertEqual( arr, ["1,2", "2,2", "2,6"] ) + + arr = [] + for p in a.each_edge(): + arr.append( str(p) ) + self.assertEqual( arr, ["(0,1;1,5)", "(1,5;1,1)", "(1,1;0,1)", "(1,2;2,2)", "(2,2;2,6)", "(2,6;1,2)"] ) + + # Ellipse constructor + p = pya.DPolygon.ellipse( pya.DBox(-10000, -20000, 30000, 40000), 200 ) + self.assertEqual(p.num_points(), 200) + self.assertEqual(str(p.bbox()), "(-10000,-20000;30000,40000)") + self.assertEqual(int(p.area()), 1884645544) # roughly box.area*PI/4 + + p = pya.DPolygon.ellipse( pya.DBox(-10000, -20000, 30000, 40000), 4 ) + self.assertEqual(str(p), "(10000,-20000;-10000,10000;10000,40000;30000,10000)") + + # Polygon basics + def test_1_Polygon(self): + + a = pya.Polygon() + self.assertEqual( str(a), "()" ) + self.assertEqual( str(pya.Polygon.from_s(str(a))), str(a) ) + self.assertEqual( a.is_box(), False ) + + b = a.dup() + a = pya.Polygon( [ pya.Point( 0, 1 ), pya.Point( 1, 5 ), pya.Point( 5, 5 ) ] ) + self.assertEqual( str(a), "(0,1;1,5;5,5)" ) + self.assertEqual( str(a * 2), "(0,2;2,10;10,10)" ) + self.assertEqual( str(pya.Polygon.from_s(str(a))), str(a) ) + self.assertEqual( a.num_points_hull(), 3 ) + c = a.dup() + + self.assertEqual( a == b, False ) + self.assertEqual( a == c, True ) + self.assertEqual( a != b, True ) + self.assertEqual( a != c, False ) + + a = pya.Polygon( pya.Box( 5, -10, 20, 15 ) ) + self.assertEqual( a.is_box(), True ) + self.assertEqual( str(a), "(5,-10;5,15;20,15;20,-10)" ) + self.assertEqual( str(pya.DPolygon(a)), "(5,-10;5,15;20,15;20,-10)" ) + self.assertEqual( a.num_points_hull(), 4 ) + self.assertEqual( a.area(), 15*25 ) + self.assertEqual( a.perimeter(), 80 ) + self.assertEqual( a.inside( pya.Point( 10, 0 ) ), True ) + self.assertEqual( a.inside( pya.Point( 5, 0 ) ), True ) + self.assertEqual( a.inside( pya.Point( 30, 0 ) ), False ) + + arr = [] + for p in a.each_point_hull(): + arr.append(str(p)) + self.assertEqual( arr, ["5,-10", "5,15", "20,15", "20,-10"] ) + + b = a.dup() + + self.assertEqual( str(a.moved( pya.Point( 0, 1 ) )), "(5,-9;5,16;20,16;20,-9)" ) + self.assertEqual( str(a.moved( 0, 1 )), "(5,-9;5,16;20,16;20,-9)" ) + aa = a.dup() + aa.move( 1, 0 ) + self.assertEqual( str(aa), "(6,-10;6,15;21,15;21,-10)" ) + a.move( pya.Point( 1, 0 ) ) + self.assertEqual( str(a), "(6,-10;6,15;21,15;21,-10)" ) + + b = b.transformed( pya.Trans( pya.Trans.R0, pya.Point( 1, 0 )) ) + self.assertEqual( str(b), "(6,-10;6,15;21,15;21,-10)" ) + + m = pya.CplxTrans( pya.Trans(), 1.5 ) + self.assertEqual( str(a.transformed(m)), "(9,-15;9,22.5;31.5,22.5;31.5,-15)" ) + self.assertEqual( str(a.transformed(pya.ICplxTrans(m))), "(9,-15;9,23;32,23;32,-15)" ) + + a.hull = [ pya.Point( 0, 1 ), pya.Point( 1, 1 ), pya.Point( 1, 5 ) ] + self.assertEqual( str(a.bbox()), "(0,1;1,5)" ) + + self.assertEqual( a.holes(), 0 ) + a.insert_hole( [ pya.Point( 1, 2 ), pya.Point( 2, 2 ), pya.Point( 2, 6 ) ] ) + self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" ) + self.assertEqual( str(pya.Polygon.from_s(str(a))), str(a) ) + self.assertEqual( a.area(), 0 ) + self.assertEqual( a.num_points_hole(0), 3 ) + self.assertEqual( a.holes(), 1 ) + self.assertEqual( str(a.point_hull(1)), "1,5" ) + self.assertEqual( str(a.point_hull(0)), "0,1" ) + self.assertEqual( str(a.point_hull(100)), "0,0" ) + self.assertEqual( str(a.point_hole(0, 100)), "0,0" ) + self.assertEqual( str(a.point_hole(0, 1)), "2,2" ) + self.assertEqual( str(a.point_hole(1, 1)), "0,0" ) + a.compress(False); + self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" ) + a.compress(True); + self.assertEqual( str(a), "(0,1;1,5;1,1/1,2;2,2;2,6)" ) + + b = a.dup() + b.assign_hole(0, pya.Box( 10, 20, 20, 60 )) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60;10,60)" ) + self.assertEqual( b.is_box(), False ) + b.insert_hole(pya.Box( 10, 20, 20, 60 )) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60;10,60/10,20;20,20;20,60;10,60)" ) + + b = a.dup() + b.assign_hole(0, [ pya.Point( 10, 20 ), pya.Point( 20, 20 ), pya.Point( 20, 60 ) ]) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60)" ) + b.assign_hole(1, [ pya.Point( 15, 25 ), pya.Point( 25, 25 ), pya.Point( 25, 65 ) ]) + self.assertEqual( str(b), "(0,1;1,5;1,1/10,20;20,20;20,60)" ) + b.insert_hole( [ pya.Point( 1, 2 ), pya.Point( 2, 2 ), pya.Point( 2, 6 ) ] ) + self.assertEqual( str(b), "(0,1;1,5;1,1/1,2;2,2;2,6/10,20;20,20;20,60)" ) + b.assign_hole(0, [ pya.Point( 15, 25 ), pya.Point( 25, 25 ), pya.Point( 25, 65 ) ]) + self.assertEqual( str(b), "(0,1;1,5;1,1/15,25;25,25;25,65/10,20;20,20;20,60)" ) + + arr = [] + for p in a.each_point_hole(0): + arr.append(str(p)) + self.assertEqual( arr, ["1,2", "2,2", "2,6"] ) + + arr = [] + for p in a.each_edge(): + arr.append(str(p)) + self.assertEqual( arr, ["(0,1;1,5)", "(1,5;1,1)", "(1,1;0,1)", "(1,2;2,2)", "(2,2;2,6)", "(2,6;1,2)"] ) + + a = pya.Polygon( [ pya.Point( 0, 1 ), pya.Point( 1, 5 ), pya.Point( 5, 5 ) ] ) + self.assertEqual( str(a), "(0,1;1,5;5,5)" ) + self.assertEqual( str(a.sized(2)), "(0,-2;-2,0;-1,7;7,7;8,5)" ) + self.assertEqual( str(a.sized(2, 2)), "(0,-2;-2,0;-1,7;7,7;8,5)" ) + aa = a.dup() + a.size(2, 2) + self.assertEqual( str(a), "(0,-2;-2,0;-1,7;7,7;8,5)" ) + a = aa.dup() + a.size(2) + self.assertEqual( str(a), "(0,-2;-2,0;-1,7;7,7;8,5)" ) + + a = pya.Polygon( [ pya.Point( 0, 1 ), pya.Point( 1, 5 ), pya.Point( 5, 5 ) ] ) + self.assertEqual( str(a), "(0,1;1,5;5,5)" ) + self.assertEqual( str(a.sized(2, 0, 2)), "(-2,1;-1,5;7,5;2,1)" ) + a.size(2, 0, 2); + self.assertEqual( str(a), "(-2,1;-1,5;7,5;2,1)" ) + + a = pya.Polygon() + self.assertEqual( str(a), "()" ) + + # corner rounding + a = pya.Polygon( [ pya.Point(0, 0), pya.Point(0, 2000), pya.Point(4000, 2000), + pya.Point(4000, 1000), pya.Point(2000, 1000), pya.Point(2000, 0) ] ) + ar = a.round_corners(100, 200, 8) + self.assertEqual( str(ar), "(117,0;0,117;0,1883;117,2000;3883,2000;4000,1883;4000,1117;3883,1000;2059,1000;2000,941;2000,117;1883,0)" ) + ar = a.round_corners(200, 100, 32) + self.assertEqual( str(ar), "(90,0;71,4;53,11;36,22;22,36;11,53;4,71;0,90;0,1910;4,1929;11,1947;22,1964;36,1978;53,1989;71,1996;90,2000;3910,2000;3929,1996;3947,1989;3964,1978;3978,1964;3989,1947;3996,1929;4000,1910;4000,1090;3996,1071;3989,1053;3978,1036;3964,1022;3947,1011;3929,1004;3910,1000;2180,1000;2142,992;2105,977;2073,955;2045,927;2023,895;2008,858;2000,820;2000,90;1996,71;1989,53;1978,36;1964,22;1947,11;1929,4;1910,0)" ) + + # Minkowsky sums + p = pya.Polygon( [ pya.Point.new(0, -100), pya.Point.new(0, -50), pya.Point.new(-100, -75), pya.Point.new(0, 100), pya.Point.new(50, 50), pya.Point.new(100, 75), pya.Point.new(100, 0), pya.Point.new(100, -50) ] ) + self.assertEqual(str(p.minkowsky_sum(pya.Edge.new(pya.Point.new(10, 10), pya.Point.new(210, 110)), True)), "(10,-90;10,-40;-90,-65;10,110;210,210;260,160;310,185;310,60)") + self.assertEqual(str(p.minkowsky_sum([pya.Point.new(10, 10), pya.Point.new(10, 310), pya.Point.new(510, 310), pya.Point.new(510, 10), pya.Point.new(10, 10) ], False)), "(10,-90;10,-65;-90,-65;-90,235;10,410;510,410;535,385;610,385;610,-40;510,-90/110,110;410,110;410,210;110,210)") + self.assertEqual(str(p.minkowsky_sum([pya.Point.new(10, 10), pya.Point.new(10, 310), pya.Point.new(510, 310), pya.Point.new(510, 10), pya.Point.new(10, 10) ], True)), "(10,-90;10,-65;-90,-65;-90,210;110,210;110,110;410,110;410,210;-90,210;-90,235;10,410;510,410;535,385;610,385;610,-40;510,-90)") + self.assertEqual(str(p.minkowsky_sum(pya.Box.new(pya.Point.new(10, 10), pya.Point.new(210, 110)), True)), "(10,-90;10,-65;-90,-65;-90,35;10,210;210,210;235,185;310,185;310,-40;210,-90)") + self.assertEqual(str(p.minkowsky_sum(pya.Box.new(pya.Point.new(10, 10), pya.Point.new(210, 10)), True)), "(10,-90;10,-65;-90,-65;10,110;210,110;235,85;310,85;310,-40;210,-90)") + self.assertEqual(str(p.minkowsky_sum(pya.Polygon.new(pya.Box.new(pya.Point.new(10, 10), pya.Point.new(210, 110))), True)), "(10,-90;10,-65;-90,-65;-90,35;10,210;210,210;235,185;310,185;310,-40;210,-90)") + + # Smoothing + p = pya.Polygon( [ pya.Point.new(0, 0), pya.Point.new(10, 50), pya.Point.new(0, 100), pya.Point.new(200, 100), pya.Point.new(200, 0) ]) + self.assertEqual(str(p.smooth(5)), "(0,0;10,50;0,100;200,100;200,0)") + self.assertEqual(str(p.smooth(15)), "(0,0;0,100;200,100;200,0)") + + # Ellipse constructor + p = pya.Polygon.ellipse( pya.Box(-10000, -20000, 30000, 40000), 200 ) + self.assertEqual(p.num_points(), 200) + self.assertEqual(str(p.bbox()), "(-10000,-20000;30000,40000)") + self.assertEqual(p.area(), 1884651158) # roughly box.area*PI/4 + + p = pya.Polygon.ellipse( pya.Box(-10000, -20000, 30000, 40000), 4 ) + self.assertEqual(str(p), "(10000,-20000;-10000,10000;10000,40000;30000,10000)") + + # Polygon parametrized edge iterator + def test_2(self): + + hull = [ pya.Point(0, 0), pya.Point(6000, 0), + pya.Point(6000, 3000), pya.Point(0, 3000) ] + hole1 = [ pya.Point(1000, 1000), pya.Point(2000, 1000), + pya.Point(2000, 2000), pya.Point(1000, 2000) ] + hole2 = [ pya.Point(3000, 1000), pya.Point(4000, 1000), + pya.Point(4000, 2000), pya.Point(3000, 2000) ] + poly = pya.Polygon(hull) + poly.insert_hole(hole1) + poly.insert_hole(hole2) + + es = [] + for e in poly.each_edge(): + es.append(str(e)) + self.assertEqual( "/".join(es), "(0,0;0,3000)/(0,3000;6000,3000)/(6000,3000;6000,0)/(6000,0;0,0)/(1000,1000;2000,1000)/(2000,1000;2000,2000)/(2000,2000;1000,2000)/(1000,2000;1000,1000)/(3000,1000;4000,1000)/(4000,1000;4000,2000)/(4000,2000;3000,2000)/(3000,2000;3000,1000)" ) + es = [] + for e in poly.each_edge(0): + es.append(str(e)) + self.assertEqual( "/".join(es), "(0,0;0,3000)/(0,3000;6000,3000)/(6000,3000;6000,0)/(6000,0;0,0)" ) + es = [] + for e in poly.each_edge(1): + es.append(str(e)) + self.assertEqual( "/".join(es), "(1000,1000;2000,1000)/(2000,1000;2000,2000)/(2000,2000;1000,2000)/(1000,2000;1000,1000)" ) + es = [] + for e in poly.each_edge(2): + es.append(str(e)) + self.assertEqual( "/".join(es), "(3000,1000;4000,1000)/(4000,1000;4000,2000)/(4000,2000;3000,2000)/(3000,2000;3000,1000)" ) + es = [] + for e in poly.each_edge(3): + es.append(str(e)) + self.assertEqual( "/".join(es), "" ) + + hull = [ pya.DPoint(0, 0), pya.DPoint(6000, 0), + pya.DPoint(6000, 3000), pya.DPoint(0, 3000) ] + hole1 = [ pya.DPoint(1000, 1000), pya.DPoint(2000, 1000), + pya.DPoint(2000, 2000), pya.DPoint(1000, 2000) ] + hole2 = [ pya.DPoint(3000, 1000), pya.DPoint(4000, 1000), + pya.DPoint(4000, 2000), pya.DPoint(3000, 2000) ] + poly = pya.DPolygon(hull) + poly.insert_hole(hole1) + poly.insert_hole(hole2) + + es = [] + for e in poly.each_edge(): + es.append(str(e)) + self.assertEqual( "/".join(es), "(0,0;0,3000)/(0,3000;6000,3000)/(6000,3000;6000,0)/(6000,0;0,0)/(1000,1000;2000,1000)/(2000,1000;2000,2000)/(2000,2000;1000,2000)/(1000,2000;1000,1000)/(3000,1000;4000,1000)/(4000,1000;4000,2000)/(4000,2000;3000,2000)/(3000,2000;3000,1000)" ) + es = [] + for e in poly.each_edge(0): + es.append(str(e)) + self.assertEqual( "/".join(es), "(0,0;0,3000)/(0,3000;6000,3000)/(6000,3000;6000,0)/(6000,0;0,0)" ) + es = [] + for e in poly.each_edge(1): + es.append(str(e)) + self.assertEqual( "/".join(es), "(1000,1000;2000,1000)/(2000,1000;2000,2000)/(2000,2000;1000,2000)/(1000,2000;1000,1000)" ) + es = [] + for e in poly.each_edge(2): + es.append(str(e)) + self.assertEqual( "/".join(es), "(3000,1000;4000,1000)/(4000,1000;4000,2000)/(4000,2000;3000,2000)/(3000,2000;3000,1000)" ) + es = [] + for e in poly.each_edge(3): + es.append(str(e)) + self.assertEqual( "/".join(es), "" ) + + # raw mode polygons + def test_2_Polygon(self): + + pts = [ pya.Point(0, 0) ] + p = pya.Polygon(pts, False) + self.assertEqual(str(p), "()") + + pts = [ pya.Point(0, 0) ] + p = pya.Polygon(pts) + self.assertEqual(str(p), "()") + + pts = [ pya.Point(0, 0) ] + p = pya.Polygon(pts, True) + self.assertEqual(str(p), "(0,0)") + + arr = [] + for e in p.each_edge(): + arr.append(str(e)) + self.assertEqual( arr, ["(0,0;0,0)"] ) + + p = pya.Polygon(pya.Box(0, 0, 100, 100)) + self.assertEqual(str(p), "(0,0;0,100;100,100;100,0)") + p.insert_hole( [ pya.Point(0, 0), pya.Point(10, 0) ] ) + # TODO: this isn't nice (empty hole): + self.assertEqual(str(p), "(0,0;0,100;100,100;100,0/)") + + p = pya.Polygon(pya.Box(0, 0, 100, 100)) + p.insert_hole( [ pya.Point(0, 0), pya.Point(10, 0) ], True ) + self.assertEqual(str(p), "(0,0;0,100;100,100;100,0/0,0;10,0)") + p.assign_hole(0, [ pya.Point(0, 0), pya.Point(10, 0) ] ) + self.assertEqual(str(p), "(0,0;0,100;100,100;100,0/)") + p.assign_hole(0, [ pya.Point(0, 0), pya.Point(10, 0) ], True ) + self.assertEqual(str(p), "(0,0;0,100;100,100;100,0/0,0;10,0)") + + pts = [ pya.Point(0, 0), pya.Point(10, 0) ] + p = pya.Polygon(pts, True) + self.assertEqual(str(p), "(0,0;10,0)") + # conversion of degenerated polygon to simple polygon is not supported currently: + self.assertEqual(str(p.to_simple_polygon()), "()") + self.assertEqual(str(pya.DPolygon(p)), "(0,0;10,0)") + + p.hull = [] + self.assertEqual(str(p), "()") + + p.hull = [ pya.Point(0, 0), pya.Point(10, 0) ] + self.assertEqual(str(p), "(0,0;10,0)") + + p.assign_hull([ pya.Point(0, 0), pya.Point(10, 0) ], False) + self.assertEqual(str(p), "()") + + p.assign_hull([ pya.Point(0, 0), pya.Point(10, 0) ], True) + self.assertEqual(str(p), "(0,0;10,0)") + + arr = [] + for e in p.each_edge(): + arr.append(str(e)) + self.assertEqual( arr, ["(0,0;10,0)", "(10,0;0,0)"] ) + + self.assertEqual(str(p.moved(1, 2)), "(1,2;11,2)") + self.assertEqual(str(p.sized(2)), "(0,-2;0,2;10,2;10,-2)") + self.assertEqual(str(p * 2), "(0,0;20,0)") + self.assertEqual(str(p.transformed(pya.Trans(pya.Trans.R90))), "(0,0;0,10)") + + pp = p.dup() + pp.transform(pya.Trans(pya.Trans.R90)) + self.assertEqual(str(pp), "(0,0;0,10)") + + p = pya.Polygon([ pya.Point(0, 0), pya.Point(0, 10) ], True) + q = pya.Polygon([ pya.Point(1, 1), pya.Point(-9, 1) ], True) + self.assertEqual(str(p.minkowsky_sum(q, False)), "(-9,1;-9,11;1,11;1,1)") + + # raw mode polygons + def test_2_DPolygon(self): + + pts = [ pya.DPoint(0, 0) ] + p = pya.DPolygon(pts, True) + self.assertEqual(str(p), "(0,0)") + + arr = [] + for e in p.each_edge(): + arr.append(str(e)) + self.assertEqual( arr, ["(0,0;0,0)"] ) + + p = pya.DPolygon(pya.DBox(0, 0, 100, 100)) + p.insert_hole( [ pya.DPoint(0, 0), pya.DPoint(10, 0) ], True ) + self.assertEqual(str(p), "(0,0;0,100;100,100;100,0/0,0;10,0)") + p.assign_hole(0, [ pya.DPoint(0, 0), pya.DPoint(10, 0) ] ) + self.assertEqual(str(p), "(0,0;0,100;100,100;100,0/0,0;10,0)") + p.assign_hole(0, [ pya.DPoint(0, 0), pya.DPoint(10, 0) ], True ) + self.assertEqual(str(p), "(0,0;0,100;100,100;100,0/0,0;10,0)") + + pts = [ pya.DPoint(0, 0), pya.DPoint(10, 0) ] + p = pya.DPolygon(pts, True) + self.assertEqual(str(p), "(0,0;10,0)") + self.assertEqual(str(pya.Polygon(p)), "(0,0;10,0)") + + p.hull = [] + self.assertEqual(str(p), "()") + + p.hull = [ pya.DPoint(0, 0), pya.DPoint(10, 0) ] + self.assertEqual(str(p), "(0,0;10,0)") + + p.assign_hull([ pya.DPoint(0, 0), pya.DPoint(10, 0) ], True) + self.assertEqual(str(p), "(0,0;10,0)") + + arr = [] + for e in p.each_edge(): + arr.append(str(e)) + self.assertEqual( arr, ["(0,0;10,0)", "(10,0;0,0)"] ) + + self.assertEqual(str(p.moved(1, 2)), "(1,2;11,2)") + self.assertEqual(str(p.sized(2)), "(0,-2;0,2;10,2;10,-2)") + self.assertEqual(str(p * 2), "(0,0;20,0)") + self.assertEqual(str(p.transformed(pya.DTrans(pya.DTrans.R90))), "(0,0;0,10)") + + pp = p.dup() + pp.transform(pya.DTrans(pya.DTrans.R90)) + self.assertEqual(str(pp), "(0,0;0,10)") + + # is_convex + def test_IsConvex(self): + + self.assertEqual(pya.Polygon(pya.Box(0, 0, 10, 10)).is_convex(), True) + + p = pya.Polygon.from_s("(0,0;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + self.assertEqual(p.is_convex(), False) + + # polygon decomposition + def test_PolygonDecompose(self): + + p = pya.Polygon.from_s("(0,0;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + + self.assertEqual(str(p.decompose_convex()), "[(0,10;0,30;10,30;10,10), (0,30;0,40;30,40;30,30), (30,10;30,40;40,40;40,10), (0,0;0,10;40,10;40,0)]") + self.assertEqual(str(p.decompose_convex(pya.Polygon.PO_any)), "[(0,10;0,30;10,30;10,10), (0,30;0,40;30,40;30,30), (30,10;30,40;40,40;40,10), (0,0;0,10;40,10;40,0)]") + self.assertEqual(str(p.decompose_convex(pya.Polygon.PO_horizontal)), "[(0,10;0,30;10,30;10,10), (0,30;0,40;40,40;40,30), (30,10;30,30;40,30;40,10), (0,0;0,10;40,10;40,0)]") + self.assertEqual(str(p.decompose_convex(pya.Polygon.PO_vertical)), "[(10,0;10,10;30,10;30,0), (0,0;0,40;10,40;10,0), (10,30;10,40;30,40;30,30), (30,0;30,40;40,40;40,0)]") + self.assertEqual(str(p.decompose_convex(pya.Polygon.PO_htrapezoids)), "[(0,10;0,30;10,30;10,10), (0,30;0,40;30,40;30,30), (30,10;30,40;40,40;40,10), (0,0;0,10;40,10;40,0)]") + self.assertEqual(str(p.decompose_convex(pya.Polygon.PO_vtrapezoids)), "[(10,0;10,10;30,10;30,0), (0,0;0,30;10,30;10,0), (0,30;0,40;30,40;30,30), (30,0;30,40;40,40;40,0)]") + + self.assertEqual(str(p.decompose_trapezoids()), "[(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)]") + self.assertEqual(str(p.decompose_trapezoids(pya.Polygon.TD_simple)), "[(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)]") + self.assertEqual(str(p.decompose_trapezoids(pya.Polygon.TD_htrapezoids)), "[(0,10;0,30;10,30;10,10), (0,30;0,40;30,40;30,30), (30,10;30,40;40,40;40,10), (0,0;0,10;40,10;40,0)]") + self.assertEqual(str(p.decompose_trapezoids(pya.Polygon.TD_vtrapezoids)), "[(10,0;10,10;30,10;30,0), (0,0;0,30;10,30;10,0), (0,30;0,40;30,40;30,30), (30,0;30,40;40,40;40,0)]") + + # polygon decomposition + def test_extractRad(self): + + ex = pya.SimplePolygon().extract_rad() + self.assertEqual(repr(ex), "[]") + + sp = pya.SimplePolygon.from_s("(0,0;0,200000;300000,200000;300000,100000;100000,100000;100000,0)") + + sp = sp.round_corners(10000, 5000, 200) + ex = sp.extract_rad() + + self.assertEqual(ex, [pya.SimplePolygon.from_s("(0,0;0,200000;300000,200000;300000,100000;100000,100000;100000,0)"), 10000.0, 5000.0, 200]) + + ex = pya.Polygon().extract_rad() + self.assertEqual(ex, []) + + sp = pya.Polygon.from_s("(0,0;0,300000;300000,300000;300000,0/100000,100000;200000,100000;200000,200000;100000,200000)") + + sp = sp.round_corners(10000, 5000, 200) + ex = sp.extract_rad() + + self.assertEqual(ex, [pya.Polygon.from_s("(0,0;0,300000;300000,300000;300000,0/100000,100000;200000,100000;200000,200000;100000,200000)"), 10000.0, 5000.0, 200]) + + # double coords too ... + + ex = pya.DSimplePolygon().extract_rad() + self.assertEqual(ex, []) + + sp = pya.DSimplePolygon.from_s("(0,0;0,200000;300000,200000;300000,100000;100000,100000;100000,0)") + + sp = sp.round_corners(10000, 5000, 200) + ex = sp.extract_rad() + + # round to integers for better comparison + + ex[0] = pya.SimplePolygon(ex[0]) + self.assertEqual(ex, [pya.SimplePolygon.from_s("(0,0;0,200000;300000,200000;300000,100000;100000,100000;100000,0)"), 10000.0, 5000.0, 200]) + + ex = pya.DPolygon().extract_rad() + self.assertEqual(ex, []) + + sp = pya.DPolygon.from_s("(0,0;0,300000;300000,300000;300000,0/100000,100000;200000,100000;200000,200000;100000,200000)") + + sp = sp.round_corners(10000, 5000, 200) + ex = sp.extract_rad() + + # round to integers for better comparison + ex[0] = pya.Polygon(ex[0]) + + self.assertEqual(ex, [pya.Polygon.from_s("(0,0;0,300000;300000,300000;300000,0/100000,100000;200000,100000;200000,200000;100000,200000)"), 10000.0, 5000.0, 200]) + + # fuzzy compare + def test_FuzzyCompare(self): + + p1 = pya.DPolygon.from_s("(0,0;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p2a = pya.DPolygon.from_s("(0.0000001,0;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p2b = pya.DPolygon.from_s("(0,0;0,40;40,40;40,0/10.0000001,10;30,10;30,30;10,30)") + p3a = pya.DPolygon.from_s("(0.0001,0;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p3b = pya.DPolygon.from_s("(0,0;0,40;40,40;40,0/10.0001,10;30,10;30,30;10,30)") + p4a = pya.DPolygon.from_s("(0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p4b = pya.DPolygon.from_s("(0,0;1,1;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p4c = pya.DPolygon.from_s("(0,0;0,40;40,40;40,0)") + p4d = pya.DPolygon.from_s("(0,0;1,1;0,40;40,40;40,0/10,10;30,10;30,30;10,30/15,15;16,15;16,16;15,16)") + + self.assertEqual(p1 == p2a, True) + self.assertEqual(p1 == p2b, True) + self.assertEqual(p1 == p3a, False) + self.assertEqual(p1 == p3b, False) + self.assertEqual(p1 == p4a, False) + self.assertEqual(p1 == p4b, False) + self.assertEqual(p1 == p4c, False) + self.assertEqual(p1 == p4d, False) + self.assertEqual(p1 < p2a, False) + self.assertEqual(p1 < p2b, False) + self.assertEqual(p1 < p3a, True) + self.assertEqual(p1 < p3b, True) + self.assertEqual(p1 < p4a, False) + self.assertEqual(p1 < p4b, True) + self.assertEqual(p1 < p4c, False) + self.assertEqual(p1 < p4d, True) + self.assertEqual(p4a < p4b, True) + self.assertEqual(p4a < p4c, False) + self.assertEqual(p4a < p4d, True) + self.assertEqual(p4b < p4c, False) + self.assertEqual(p4b < p4d, True) + self.assertEqual(p4c < p4d, True) + self.assertEqual(p2a < p1, False) + self.assertEqual(p2b < p1, False) + self.assertEqual(p3a < p1, False) + self.assertEqual(p3b < p1, False) + self.assertEqual(p4a < p1, True) + self.assertEqual(p4b < p1, False) + self.assertEqual(p4c < p1, True) + self.assertEqual(p4d < p1, False) + self.assertEqual(p4b < p4a, False) + self.assertEqual(p4c < p4a, True) + self.assertEqual(p4d < p4a, False) + self.assertEqual(p4c < p4b, True) + self.assertEqual(p4d < p4b, False) + self.assertEqual(p4d < p4c, False) + + # hash values + def test_HashValues(self): + + p1 = pya.DPolygon.from_s("(0,0;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p2a = pya.DPolygon.from_s("(0.0000001,0;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p2b = pya.DPolygon.from_s("(0,0;0,40;40,40;40,0/10.0000001,10;30,10;30,30;10,30)") + p3a = pya.DPolygon.from_s("(0.0001,0;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p3b = pya.DPolygon.from_s("(0,0;0,40;40,40;40,0/10.0001,10;30,10;30,30;10,30)") + p4a = pya.DPolygon.from_s("(0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p4b = pya.DPolygon.from_s("(0,0;1,1;0,40;40,40;40,0/10,10;30,10;30,30;10,30)") + p4c = pya.DPolygon.from_s("(0,0;0,40;40,40;40,0)") + p4d = pya.DPolygon.from_s("(0,0;1,1;0,40;40,40;40,0/10,10;30,10;30,30;10,30/15,15;16,15;16,16;15,16)") + + self.assertEqual(p1.hash() == p2a.hash(), True) + self.assertEqual(p1.hash() == p2b.hash(), True) + self.assertEqual(p1.hash() == p3a.hash(), False) + self.assertEqual(p1.hash() == p3b.hash(), False) + self.assertEqual(p1.hash() == p4a.hash(), False) + self.assertEqual(p1.hash() == p4b.hash(), False) + self.assertEqual(p1.hash() == p4c.hash(), False) + self.assertEqual(p1.hash() == p4d.hash(), False) + self.assertEqual(p4a.hash() == p4b.hash(), False) + self.assertEqual(p4a.hash() == p4c.hash(), False) + self.assertEqual(p4a.hash() == p4d.hash(), False) + self.assertEqual(p4b.hash() == p4c.hash(), False) + self.assertEqual(p4b.hash() == p4d.hash(), False) + self.assertEqual(p4c.hash() == p4d.hash(), False) + + if False: + + # TODO: Currently, complex types are not allowed as hash keys: + + h = { p1: "p1", p3a: "p3a", p3b: "p3b", p4a: "p4a", p4b: "p4b", p4c: "p4c", p4d: "p4d" } + + self.assertEqual(h[p1], "p1") + self.assertEqual(h[p2a], "p1") + self.assertEqual(h[p2b], "p1") + self.assertEqual(h[p3a], "p3a") + self.assertEqual(h[p3b], "p3b") + self.assertEqual(h[p4a], "p4a") + self.assertEqual(h[p4b], "p4b") + self.assertEqual(h[p4c], "p4c") + self.assertEqual(h[p4d], "p4d") + + # touches predicate + def test_touches(self): + + p1 = pya.Polygon(pya.Box(10, 20, 30, 40)) + self.assertEqual(p1.touches(pya.Polygon(pya.Box(30, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.Polygon(pya.Box(31, 20, 40, 50))), False) + self.assertEqual(p1.touches(pya.Polygon(pya.Box(29, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(30, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(31, 20, 40, 50))), False) + self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(29, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.Box(30, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.Box(31, 20, 40, 50)), False) + self.assertEqual(p1.touches(pya.Box(29, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.Edge(30, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.Edge(31, 20, 40, 50)), False) + self.assertEqual(p1.touches(pya.Edge(29, 20, 40, 50)), True) + + p1 = pya.SimplePolygon(pya.Box(10, 20, 30, 40)) + self.assertEqual(p1.touches(pya.Polygon(pya.Box(30, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.Polygon(pya.Box(31, 20, 40, 50))), False) + self.assertEqual(p1.touches(pya.Polygon(pya.Box(29, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(30, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(31, 20, 40, 50))), False) + self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(29, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.Box(30, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.Box(31, 20, 40, 50)), False) + self.assertEqual(p1.touches(pya.Box(29, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.Edge(30, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.Edge(31, 20, 40, 50)), False) + self.assertEqual(p1.touches(pya.Edge(29, 20, 40, 50)), True) + + p1 = pya.DPolygon(pya.DBox(10, 20, 30, 40)) + self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(30, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(31, 20, 40, 50))), False) + self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(29, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(30, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(31, 20, 40, 50))), False) + self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(29, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.DBox(30, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.DBox(31, 20, 40, 50)), False) + self.assertEqual(p1.touches(pya.DBox(29, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.DEdge(30, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.DEdge(31, 20, 40, 50)), False) + self.assertEqual(p1.touches(pya.DEdge(29, 20, 40, 50)), True) + + p1 = pya.DSimplePolygon(pya.DBox(10, 20, 30, 40)) + self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(30, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(31, 20, 40, 50))), False) + self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(29, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(30, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(31, 20, 40, 50))), False) + self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(29, 20, 40, 50))), True) + self.assertEqual(p1.touches(pya.DBox(30, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.DBox(31, 20, 40, 50)), False) + self.assertEqual(p1.touches(pya.DBox(29, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.DEdge(30, 20, 40, 50)), True) + self.assertEqual(p1.touches(pya.DEdge(31, 20, 40, 50)), False) + self.assertEqual(p1.touches(pya.DEdge(29, 20, 40, 50)), True) + + def test_selfRef(self): + + # p1 is a reference to the new'd object: + p1 = pya.Polygon(pya.Box(10, 20, 30, 40)).move(10, 20) + self.assertEqual(str(p1), "(20,40;20,60;40,60;40,40)") + + pp = pya.Polygon(pya.Box(10, 20, 30, 40)) + p1 = pp.move(10, 20) + self.assertEqual(str(p1), "(20,40;20,60;40,60;40,40)") + self.assertEqual(str(pp), "(20,40;20,60;40,60;40,40)") + pp.move(1, 2) + + # p1 and pp are the same object + self.assertEqual(str(p1), "(21,42;21,62;41,62;41,42)") + self.assertEqual(str(pp), "(21,42;21,62;41,62;41,42)") + +# run unit tests +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(DBPolygonTests) + + if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful(): + sys.exit(1) + diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index e36eb7347..b15b1b121 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -720,6 +720,17 @@ class DBPolygon_TestClass < TestBase end + def test_selfRef + + # p1 is a reference to the new'd object: + p1 = RBA::Polygon::new(RBA::Box::new(10, 20, 30, 40)).move(10, 20) + assert_equal(p1.to_s, "(20,40;20,60;40,60;40,40)") + GC.start + # after the GC cleaned up the new'd object, the reference still needs to be valid + assert_equal(p1.to_s, "(20,40;20,60;40,60;40,40)") + + end + end load("test_epilogue.rb")