diff --git a/library/bindings/PolyVoxCore.i b/library/bindings/PolyVoxCore.i index 529d32b8..30ea5c65 100644 --- a/library/bindings/PolyVoxCore.i +++ b/library/bindings/PolyVoxCore.i @@ -75,3 +75,4 @@ EXTRACTOR(shortname, LargeVolume) %include "SurfaceMesh.i" %include "MarchingCubesSurfaceExtractor.i" //%include "CubicSurfaceExtractor.i" +%include "Raycast.i" diff --git a/library/bindings/Raycast.i b/library/bindings/Raycast.i new file mode 100644 index 00000000..a3dc23f0 --- /dev/null +++ b/library/bindings/Raycast.i @@ -0,0 +1,54 @@ +%module Raycast +%{ +#include "Raycast.h" + +template +class PyCallback +{ +private: + PyObject *func; + PyCallback& operator=(const PyCallback&); // Not allowed +public: + PyCallback(const PyCallback& o) : func(o.func) + { + Py_XINCREF(func); + } + PyCallback(PyObject *func) : func(func) + { + Py_XINCREF(this->func); + assert(PyCallable_Check(this->func)); + } + ~PyCallback() + { + Py_XDECREF(func); + } + bool operator()(const typename VolumeType::Sampler& sampler) + { + if (!func || Py_None == func || !PyCallable_Check(func)) + { + return false; //Make this raise a Python exception + } + PyObject *args = Py_BuildValue("(l)", sampler.getVoxel().getDensity()); //TODO pass the sampler object itself in + PyObject *result = PyObject_Call(func,args,0); + Py_DECREF(args); + Py_XDECREF(result); + return (PyInt_AsLong(result) == 0) ? false : true; + } +}; + +template +PolyVox::RaycastResult raycastWithEndpointsPython(VolumeType* volData, const PolyVox::Vector3DFloat& v3dStart, const PolyVox::Vector3DFloat& v3dEnd, PyObject *callback) +{ + PyCallback newCallback(callback); + return PolyVox::raycastWithEndpoints(volData, v3dStart, v3dEnd, newCallback); +} + +%} + +%include "Raycast.h" + +template +PolyVox::RaycastResult raycastWithEndpointsPython(VolumeType* volData, const PolyVox::Vector3DFloat& v3dStart, const PolyVox::Vector3DFloat& v3dEnd, PyObject *callback); + +%template(raycastWithEndpointsSimpleVolumeDensity8) raycastWithEndpointsPython, PyCallback > >; +//%template(raycastWithEndpointsSimpleVolumeMaterial8) raycastWithEndpointsPython, PyCallback > >; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6c6fe163..c2585e41 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -51,6 +51,7 @@ REMOVE_DEFINITIONS(-DQT_GUI_LIB) #Make sure the tests don't link to the QtGui # Python tests IF(BUILD_BINDINGS) ADD_TEST(PythonSurfaceExtractorTest python ${CMAKE_CURRENT_SOURCE_DIR}/TestSurfaceExtractor.py) + ADD_TEST(PythonRaycastTest python ${CMAKE_CURRENT_SOURCE_DIR}/TestRaycast.py) ENDIF() # AmbientOcclusionGenerator tests diff --git a/tests/TestRaycast.py b/tests/TestRaycast.py new file mode 100644 index 00000000..1f45aee3 --- /dev/null +++ b/tests/TestRaycast.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +import sys +sys.path.append("library/bindings/") + +import unittest +import PolyVoxCore + +def test_functor(sampler): + return sampler <= 0 + +class TestSurfaceExtractor(unittest.TestCase): + def setUp(self): + + #Create a small volume + r = PolyVoxCore.Region(PolyVoxCore.Vector3DInt32(0,0,0), PolyVoxCore.Vector3DInt32(31,31,31)) + self.vol = PolyVoxCore.SimpleVolumeDensity8(r) + #Set one single voxel to have a reasonably high density + self.vol.setVoxelAt(PolyVoxCore.Vector3DInt32(5, 5, 5), PolyVoxCore.Density8(200)) + + def test_hit_voxel(self): + self.assertEqual(PolyVoxCore.raycastWithEndpointsSimpleVolumeDensity8(self.vol, PolyVoxCore.Vector3DFloat(0,0,0), PolyVoxCore.Vector3DFloat(31,31,31), test_functor), 1) + + def test_miss_voxel(self): + self.assertEqual(PolyVoxCore.raycastWithEndpointsSimpleVolumeDensity8(self.vol, PolyVoxCore.Vector3DFloat(0,0,0), PolyVoxCore.Vector3DFloat(0,31,31), test_functor), 0) + +if __name__ == '__main__': + unittest.main()