Skip to content

Commit e966ddd

Browse files
committed
Add vectorized version
1 parent 66cfe3f commit e966ddd

File tree

6 files changed

+96
-7
lines changed

6 files changed

+96
-7
lines changed

applications/plugins/SofaImplicitField/MarchingCube.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ void MarchingCube::generateSurfaceMesh(const double isoval, const double mstep,
4343
if( nz < 2 || ny < 2 || nx < 2 )
4444
return;
4545

46-
double cx,cy,cz;
4746
int z,mk;
4847
const int *tri;
4948

applications/plugins/SofaImplicitField/components/engine/FieldToSurfaceMesh.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ void FieldToSurfaceMesh::init()
6666
d_componentState = core::objectmodel::ComponentState::Invalid;
6767
}
6868

69+
updateMeshIfNeeded();
6970
d_componentState = core::objectmodel::ComponentState::Valid;
7071
}
7172

@@ -109,12 +110,8 @@ void FieldToSurfaceMesh::updateMeshIfNeeded()
109110
tmpTriangles.clear();
110111

111112
marchingCube.generateSurfaceMesh(isoval, mstep, invStep, gridmin, gridmax,
112-
[field](std::vector<Vec3d>& positions, std::vector<double>& res){
113-
int i=0;
114-
for(auto& position : positions)
115-
{
116-
res[i++]=field->getValue(position);
117-
}
113+
[field](const std::vector<Vec3d>& positions, std::vector<double>& res){
114+
field->getValues(positions, res);
118115
},
119116
tmpPoints, tmpTriangles);
120117

applications/plugins/SofaImplicitField/components/geometry/ScalarField.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ void ScalarField::init()
4545
d_componentState.setValue(core::objectmodel::ComponentState::Valid);
4646
}
4747

48+
void ScalarField::getValues(const std::vector<Vec3d>& positions, std::vector<double>& results)
49+
{
50+
results.clear();
51+
results.reserve(positions.size());
52+
for(auto position : positions)
53+
{
54+
results.emplace_back(getValue(position));
55+
}
56+
return;
57+
}
58+
4859
Vec3d ScalarField::getGradientByFinitDifference(Vec3d& pos, int& i)
4960
{
5061
Vec3d Result;

applications/plugins/SofaImplicitField/components/geometry/ScalarField.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class SOFA_SOFAIMPLICITFIELD_API ScalarField : public BaseObject
6666
virtual double getValue(Vec3d& pos, int& domain) = 0;
6767
inline double getValue(Vec3d& pos) { int domain=-1; return getValue(pos,domain); }
6868

69+
// Compute the field for a range or input values
70+
virtual void getValues(const std::vector<Vec3d>& positions, std::vector<double>& results);
71+
6972
/// By default compute the gradient using a first order finite difference approache
7073
/// If you have analytical derivative don't hesitate to override this function.
7174
virtual Vec3d getGradient(Vec3d& pos, int& domain);

applications/plugins/SofaImplicitField/examples/python/xshape/primitives.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66
from SofaImplicitField import ScalarField
77
import numpy
8+
import numba
89

910
class Sphere(ScalarField):
1011
def __init__(self, *args, **kwargs):
@@ -17,6 +18,14 @@ def getValue(self, pos):
1718
x,y,z = pos
1819
return numpy.linalg.norm(self.center.value - numpy.array([x,y,z])) - self.radius.value
1920

21+
def getValues(self, positions, out_values):
22+
"""This version of the overrides the getValues so that we fetch the data once"""
23+
center = self.center.value
24+
radius = self.radius.value
25+
for i in range(len(positions)):
26+
r = numpy.linalg.norm(center - positions[i]) - radius
27+
out_values[i] = r
28+
2029
class RoundedBox(ScalarField):
2130
def __init__(self, *args, **kwargs):
2231
ScalarField.__init__(self, *args, **kwargs)

applications/plugins/SofaImplicitField/python/src/Binding_ScalarField.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,58 @@ using sofa::core::objectmodel::BaseObject;
3636
using sofa::type::Vec3;
3737
using sofa::type::Mat3x3;
3838

39+
py::array_t<double> vector_to_numpy(const std::vector<Vec3>& vec) {
40+
// Pybind11 gère la mémoire en créant un capsule pour que numpy sache comment libérer
41+
// On transfère la propriété du vecteur à numpy, donc pas de copie
42+
const double* data_ptr = (const double*)vec.data();
43+
size_t size = vec.size();
44+
45+
// capsule: mémoire gérée par le vector, sera libérée quand python détruit l'objet numpy
46+
py::capsule free_when_done(vec.data(), [](void *) {
47+
// On ne fait rien ici car vector gère sa mémoire.
48+
// Si on voulait transférer la propriété, on ferait delete ici.
49+
});
50+
51+
// Dimensions du tableau numpy
52+
std::vector<size_t> shape = { size, 3 };
53+
54+
// Strides en bytes (ici, contiguous : 3 colonnes, chaque double 8 octets)
55+
std::vector<size_t> strides = { 3 * sizeof(double), sizeof(double) };
56+
57+
return py::array_t<double>(
58+
shape,
59+
strides,
60+
data_ptr, // data pointer
61+
free_when_done // capsule pour gérer la vie mémoire
62+
);
63+
}
64+
65+
py::array_t<double> vector_to_numpy(const std::vector<double>& vec) {
66+
// Pybind11 gère la mémoire en créant un capsule pour que numpy sache comment libérer
67+
// On transfère la propriété du vecteur à numpy, donc pas de copie
68+
const double* data_ptr = (const double*)vec.data();
69+
size_t size = vec.size();
70+
71+
// capsule: mémoire gérée par le vector, sera libérée quand python détruit l'objet numpy
72+
py::capsule free_when_done(vec.data(), [](void *) {
73+
// On ne fait rien ici car vector gère sa mémoire.
74+
// Si on voulait transférer la propriété, on ferait delete ici.
75+
});
76+
77+
// Dimensions du tableau numpy
78+
std::vector<size_t> shape = { size };
79+
80+
// Strides en bytes (ici, contiguous : 3 colonnes, chaque double 8 octets)
81+
std::vector<size_t> strides = { sizeof(double) };
82+
83+
return py::array_t<double>(
84+
shape,
85+
strides,
86+
data_ptr, // data pointer
87+
free_when_done // capsule pour gérer la vie mémoire
88+
);
89+
}
90+
3991
class ScalarField_Trampoline : public ScalarField {
4092
public:
4193
SOFA_CLASS(ScalarField_Trampoline, ScalarField);
@@ -58,6 +110,24 @@ class ScalarField_Trampoline : public ScalarField {
58110
PYBIND11_OVERLOAD_PURE(double, ScalarField, getValue, pos);
59111
}
60112

113+
void getValues(const std::vector<Vec3>& positions, std::vector<double>& results) override
114+
{
115+
PythonEnvironment::gil acquire;
116+
117+
// Search if there is a python override,
118+
pybind11::function override = pybind11::get_override(static_cast<const ScalarField*>(this),"getValues");
119+
if(!override){
120+
return ScalarField::getValues(positions, results);
121+
}
122+
123+
// Be sure there is enough space to hold the results
124+
results.resize(positions.size());
125+
126+
// as there is one override, we call it, passing the "pos" argument and storing the return of the
127+
// value in the "o" variable.
128+
auto o = override(vector_to_numpy(positions), vector_to_numpy(results));
129+
}
130+
61131
Vec3 getGradient(Vec3& pos, int& domain) override
62132
{
63133
SOFA_UNUSED(domain);

0 commit comments

Comments
 (0)