Start Python bindings for Raycast
This is only beginning of the bindings here. It's starting to get more complicated due to the use of callbacks. To be able to define a callback function in Python which is then called by a C++ algorithm requires quite a bit of wrapping boilerplate. The class PyCallback here will wrap a Python callable and call it with the density value of the voxel. It's not very generic and at present it can't pass the sampler itself since it's not available in the Python bindings. Regardless, the new test added here (TestRaycast.py) works as expected and hopefully we will be able to build up from here.
This commit is contained in:
parent
37fbe16939
commit
3ed3ac6998
@ -75,3 +75,4 @@ EXTRACTOR(shortname, LargeVolume)
|
|||||||
%include "SurfaceMesh.i"
|
%include "SurfaceMesh.i"
|
||||||
%include "MarchingCubesSurfaceExtractor.i"
|
%include "MarchingCubesSurfaceExtractor.i"
|
||||||
//%include "CubicSurfaceExtractor.i"
|
//%include "CubicSurfaceExtractor.i"
|
||||||
|
%include "Raycast.i"
|
||||||
|
54
library/bindings/Raycast.i
Normal file
54
library/bindings/Raycast.i
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
%module Raycast
|
||||||
|
%{
|
||||||
|
#include "Raycast.h"
|
||||||
|
|
||||||
|
template<typename VolumeType>
|
||||||
|
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<typename VolumeType, typename Callback>
|
||||||
|
PolyVox::RaycastResult raycastWithEndpointsPython(VolumeType* volData, const PolyVox::Vector3DFloat& v3dStart, const PolyVox::Vector3DFloat& v3dEnd, PyObject *callback)
|
||||||
|
{
|
||||||
|
PyCallback<VolumeType> newCallback(callback);
|
||||||
|
return PolyVox::raycastWithEndpoints(volData, v3dStart, v3dEnd, newCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
%include "Raycast.h"
|
||||||
|
|
||||||
|
template<typename VolumeType, typename Callback>
|
||||||
|
PolyVox::RaycastResult raycastWithEndpointsPython(VolumeType* volData, const PolyVox::Vector3DFloat& v3dStart, const PolyVox::Vector3DFloat& v3dEnd, PyObject *callback);
|
||||||
|
|
||||||
|
%template(raycastWithEndpointsSimpleVolumeDensity8) raycastWithEndpointsPython<PolyVox::SimpleVolume<PolyVox::Density8>, PyCallback<PolyVox::SimpleVolume<PolyVox::Density8> > >;
|
||||||
|
//%template(raycastWithEndpointsSimpleVolumeMaterial8) raycastWithEndpointsPython<PolyVox::SimpleVolume<PolyVox::Material8>, PyCallback<PolyVox::SimpleVolume<PolyVox::Material8> > >;
|
@ -51,6 +51,7 @@ REMOVE_DEFINITIONS(-DQT_GUI_LIB) #Make sure the tests don't link to the QtGui
|
|||||||
# Python tests
|
# Python tests
|
||||||
IF(BUILD_BINDINGS)
|
IF(BUILD_BINDINGS)
|
||||||
ADD_TEST(PythonSurfaceExtractorTest python ${CMAKE_CURRENT_SOURCE_DIR}/TestSurfaceExtractor.py)
|
ADD_TEST(PythonSurfaceExtractorTest python ${CMAKE_CURRENT_SOURCE_DIR}/TestSurfaceExtractor.py)
|
||||||
|
ADD_TEST(PythonRaycastTest python ${CMAKE_CURRENT_SOURCE_DIR}/TestRaycast.py)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# AmbientOcclusionGenerator tests
|
# AmbientOcclusionGenerator tests
|
||||||
|
28
tests/TestRaycast.py
Normal file
28
tests/TestRaycast.py
Normal file
@ -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()
|
Loading…
x
Reference in New Issue
Block a user