Browse Source

collide: Expose CollisionPolygon's setup_points and verify_points to Python

Closes #1035
Daniel 5 years ago
parent
commit
ac6a1a7874

+ 2 - 0
makepanda/makepanda.py

@@ -3829,6 +3829,7 @@ OPTS=['DIR:panda/src/collide']
 IGATEFILES=GetDirectoryContents('panda/src/collide', ["*.h", "*_composite*.cxx"])
 TargetAdd('libp3collide.in', opts=OPTS, input=IGATEFILES)
 TargetAdd('libp3collide.in', opts=['IMOD:panda3d.core', 'ILIB:libp3collide', 'SRCDIR:panda/src/collide'])
+PyTargetAdd('p3collide_ext_composite.obj', opts=OPTS, input='p3collide_ext_composite.cxx')
 
 #
 # DIRECTORY: panda/src/parametrics/
@@ -4073,6 +4074,7 @@ PyTargetAdd('core.pyd', input='p3event_pythonTask.obj')
 PyTargetAdd('core.pyd', input='p3gobj_ext_composite.obj')
 PyTargetAdd('core.pyd', input='p3pgraph_ext_composite.obj')
 PyTargetAdd('core.pyd', input='p3display_ext_composite.obj')
+PyTargetAdd('core.pyd', input='p3collide_ext_composite.obj')
 
 PyTargetAdd('core.pyd', input='core_module.obj')
 if not GetLinkAllStatic() and GetTarget() != 'emscripten':

+ 6 - 1
panda/src/collide/CMakeLists.txt

@@ -65,11 +65,16 @@ set(P3COLLIDE_SOURCES
   config_collide.cxx
 )
 
+set(P3COLLIDE_IGATEEXT
+  collisionPolygon_ext.cxx
+  collisionPolygon_ext.h
+)
+
 composite_sources(p3collide P3COLLIDE_SOURCES)
 add_component_library(p3collide SYMBOL BUILDING_PANDA_COLLIDE
   ${P3COLLIDE_HEADERS} ${P3COLLIDE_SOURCES})
 target_link_libraries(p3collide p3tform)
-target_interrogate(p3collide ALL)
+target_interrogate(p3collide ALL EXTENSIONS ${P3COLLIDE_IGATEEXT})
 
 if(NOT BUILD_METALIBS)
   install(TARGETS p3collide

+ 5 - 1
panda/src/collide/collisionPolygon.h

@@ -59,6 +59,9 @@ PUBLISHED:
   bool is_valid() const;
   bool is_concave() const;
 
+  EXTENSION(static bool verify_points(PyObject *points));
+  EXTENSION(void setup_points(PyObject *points));
+
 PUBLISHED:
   MAKE_SEQ_PROPERTY(points, get_num_points, get_point);
   MAKE_PROPERTY(valid, is_valid);
@@ -71,6 +74,8 @@ public:
                                 const CullTraverserData &data,
                                 bool bounds_only) const;
 
+  void setup_points(const LPoint3 *begin, const LPoint3 *end);
+
   virtual PStatCollector &get_volume_pcollector();
   virtual PStatCollector &get_test_pcollector();
 
@@ -128,7 +133,6 @@ private:
   PN_stdfloat dist_to_polygon(const LPoint2 &p, LPoint2 &edge_p, const Points &points) const;
   void project(const LVector3 &axis, PN_stdfloat &center, PN_stdfloat &extent) const;
 
-  void setup_points(const LPoint3 *begin, const LPoint3 *end);
   INLINE LPoint2 to_2d(const LVecBase3 &point3d) const;
   INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat) const;
   INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat) const;

+ 86 - 0
panda/src/collide/collisionPolygon_ext.cxx

@@ -0,0 +1,86 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file collisionPolygon_ext.cxx
+ * @author Derzsi Daniel
+ * @date 2020-10-13
+ */
+
+#include "collisionPolygon_ext.h"
+
+#ifdef HAVE_PYTHON
+
+#include "collisionPolygon.h"
+
+#ifdef STDFLOAT_DOUBLE
+extern struct Dtool_PyTypedObject Dtool_LPoint3d;
+#else
+extern struct Dtool_PyTypedObject Dtool_LPoint3f;
+#endif
+
+/**
+ * Verifies that the indicated Python list of points will define a
+ * CollisionPolygon.
+ */
+bool Extension<CollisionPolygon>::
+verify_points(PyObject *points) {
+  const pvector<LPoint3> vec = convert_points(points);
+  const LPoint3 *verts_begin = &vec[0];
+  const LPoint3 *verts_end = verts_begin + vec.size();
+
+  return CollisionPolygon::verify_points(verts_begin, verts_end);
+}
+
+/**
+ * Initializes this CollisionPolygon with the given Python list of
+ * points.
+ */
+void Extension<CollisionPolygon>::
+setup_points(PyObject *points) {
+  const pvector<LPoint3> vec = convert_points(points);
+  const LPoint3 *verts_begin = &vec[0];
+  const LPoint3 *verts_end = verts_begin + vec.size();
+
+  _this->setup_points(verts_begin, verts_end);
+}
+
+/**
+ * Converts a Python sequence to a list of LPoint3 objects.
+ */
+pvector<LPoint3> Extension<CollisionPolygon>::
+convert_points(PyObject *points) {
+  pvector<LPoint3> vec;
+  PyObject *seq = PySequence_Fast(points, "function expects a sequence");
+
+  if (!seq) {
+    return vec;
+  }
+
+  PyObject **items = PySequence_Fast_ITEMS(seq);
+  Py_ssize_t len = PySequence_Fast_GET_SIZE(seq);
+  void *ptr;
+
+  vec.reserve(len);
+
+  for (Py_ssize_t i = 0; i < len; ++i) {
+#ifdef STDFLOAT_DOUBLE
+    if (ptr = DtoolInstance_UPCAST(items[i], Dtool_LPoint3d)) {
+#else
+    if (ptr = DtoolInstance_UPCAST(items[i], Dtool_LPoint3f)) {
+#endif
+      vec.push_back(*(LPoint3 *)ptr);
+    } else {
+      collide_cat.warning() << "Argument must be of LPoint3 type.\n";
+    }
+  }
+
+  Py_DECREF(seq);
+  return vec;
+}
+
+#endif

+ 43 - 0
panda/src/collide/collisionPolygon_ext.h

@@ -0,0 +1,43 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file collisionPolygon_ext.h
+ * @author Derzsi Daniel
+ * @date 2020-10-13
+ */
+
+#ifndef COLLISIONPOLYGON_EXT_H
+#define COLLISIONPOLYGON_EXT_H
+
+#include "pandabase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "collisionPolygon.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for CollisionPolygon, which are called
+ * instead of any C++ methods with the same prototype.
+ *
+ * @since 1.11.0
+ */
+template<>
+class Extension<CollisionPolygon> : public ExtensionBase<CollisionPolygon> {
+public:
+  static bool verify_points(PyObject *points);
+  void setup_points(PyObject *points);
+
+private:
+  static pvector<LPoint3> convert_points(PyObject *points);
+};
+
+#endif  // HAVE_PYTHON
+
+#endif

+ 1 - 0
panda/src/collide/p3collide_ext_composite.cxx

@@ -0,0 +1 @@
+#include "collisionPolygon_ext.cxx"

+ 45 - 0
tests/collide/test_collision_polygon.py

@@ -0,0 +1,45 @@
+from panda3d import core
+
+
+def test_collision_polygon_verify_not_enough_points():
+    # Less than 3 points cannot create a polygon
+    assert not core.CollisionPolygon.verify_points([])
+    assert not core.CollisionPolygon.verify_points([core.LPoint3(1, 0, 0)])
+    assert not core.CollisionPolygon.verify_points([core.LPoint3(1, 0, 0), core.LPoint3(0, 0, 1)])
+
+
+def test_collision_polygon_verify_repeating_points():
+    # Repeating points cannot create a polygon
+    assert not core.CollisionPolygon.verify_points([core.LPoint3(1, 0, 0), core.LPoint3(1, 0, 0), core.LPoint3(0, 0, 1)])
+    assert not core.CollisionPolygon.verify_points([core.LPoint3(3, 6, 1), core.LPoint3(1, 3, 5), core.LPoint3(9, 1, 2), core.LPoint3(1, 3, 5)])
+
+
+def test_collision_polygon_verify_colinear_points():
+    # Colinear points cannot create a polygon
+    assert not core.CollisionPolygon.verify_points([core.LPoint3(1, 2, 3), core.LPoint3(2, 3, 4), core.LPoint3(3, 4, 5)])
+    assert not core.CollisionPolygon.verify_points([core.LPoint3(2, 1, 1), core.LPoint3(3, 2, 1), core.LPoint3(4, 3, 1)])
+
+
+def test_collision_polygon_verify_points():
+    # Those should be regular, colinear points
+    assert core.CollisionPolygon.verify_points([core.LPoint3(1, 0, 0), core.LPoint3(0, 1, 0), core.LPoint3(0, 0, 1)])
+    assert core.CollisionPolygon.verify_points([core.LPoint3(10, 2, 8), core.LPoint3(7, 1, 3), core.LPoint3(5, 9, 6)])
+    assert core.CollisionPolygon.verify_points([core.LPoint3(3, -8, -7), core.LPoint3(9, 10, 8), core.LPoint3(7, 0, 10), core.LPoint3(-6, -2, 3)])
+    assert core.CollisionPolygon.verify_points([core.LPoint3(-1, -3, -5), core.LPoint3(10, 3, -10), core.LPoint3(-10, 10, -4), core.LPoint3(0, 1, -4), core.LPoint3(-9, -2, 0)])
+
+
+def test_collision_polygon_setup_points():
+    # Create empty collision polygon
+    polygon = core.CollisionPolygon(core.LVecBase3(0, 0, 0), core.LVecBase3(0, 0, 0), core.LVecBase3(0, 0, 0))
+    assert not polygon.is_valid()
+
+    # Test our setup method against a few test cases
+    for points in [
+        [core.LPoint3(-1, -3, -5), core.LPoint3(10, 3, -10), core.LPoint3(-10, 10, -4), core.LPoint3(0, 1, -4), core.LPoint3(-9, -2, 0)],
+        [core.LPoint3(3, -8, -7), core.LPoint3(9, 10, 8), core.LPoint3(7, 0, 10), core.LPoint3(-6, -2, 3)],
+        [core.LPoint3(1, 0, 0), core.LPoint3(0, 1, 0), core.LPoint3(0, 0, 1)],
+        [core.LPoint3(10, 2, 8), core.LPoint3(7, 1, 3), core.LPoint3(5, 9, 6)]
+    ]:
+        polygon.setup_points(points)
+        assert polygon.is_valid()
+        assert polygon.get_num_points() == len(points)