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:
Matt Williams 2012-11-25 18:07:12 +00:00
parent 37fbe16939
commit 3ed3ac6998
4 changed files with 84 additions and 0 deletions

View File

@ -75,3 +75,4 @@ EXTRACTOR(shortname, LargeVolume)
%include "SurfaceMesh.i"
%include "MarchingCubesSurfaceExtractor.i"
//%include "CubicSurfaceExtractor.i"
%include "Raycast.i"

View 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> > >;

View File

@ -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

28
tests/TestRaycast.py Normal file
View 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()