Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8144c9e
First version.
damienmarchal Aug 27, 2025
ba4d4a4
Merge branch 'master' into pr-add-field-to-surface-mesh
damienmarchal Aug 28, 2025
567cca1
Merge branch 'master' into pr-add-field-to-surface-mesh
damienmarchal Sep 3, 2025
3623617
Add a MarchingCube implementation in a separated file.
damienmarchal Sep 3, 2025
c31b1cd
Use MarchingCube in FieldToSurfaceMesh
damienmarchal Sep 3, 2025
6accc38
Small fix-up on the signature for getValue in BInding_ScalarField
damienmarchal Sep 3, 2025
271999b
Update to sync with the small fix
damienmarchal Sep 3, 2025
7eeb3c8
Use std::function in MarchingCube::generateField
damienmarchal Sep 3, 2025
b8035fd
Merge branch 'master' into pr-add-field-to-surface-mesh
damienmarchal Sep 4, 2025
f389343
Refactoring ImplicitSurfaceMapping [WIP]
damienmarchal Sep 3, 2025
7cf858e
First working version of refactoring on ImplicitSurfaceMapping
damienmarchal Sep 3, 2025
f1cbc17
Improved refactoring of ImplicitSurfaceMapping
damienmarchal Sep 3, 2025
e804f4a
Make a "vectorized" version of the marching cube...
damienmarchal Sep 4, 2025
25c2c03
Merge remote-tracking branch 'upstream/master' into pr-add-field-to-s…
damienmarchal Oct 4, 2025
c4058a0
Remove hacks
damienmarchal Oct 4, 2025
ffeb514
Restored version
damienmarchal Oct 7, 2025
20b2355
[SofaImplicitField] FIX Binding_ScalarField invalid "name" set
damienmarchal Oct 7, 2025
13b8b44
[SofaImplicitField] MarchingCube factorization and cleaning.
damienmarchal Oct 7, 2025
34f215c
[SofaImplicitField] Clean FieldToSurfaceMesh
damienmarchal Oct 7, 2025
e80bd2b
[SofaImplicitField] Add few field function and operator in the xshape…
damienmarchal Oct 7, 2025
044a11d
[SofaImplicitField] Add an example of use of the FieldToSurfaceMesh c…
damienmarchal Oct 7, 2025
a870250
[SofaImplicitField] Fix memory allocation bug.
damienmarchal Oct 8, 2025
d664bd9
[SofaImplicitField] FIX the rendering of normal that was inverted.
damienmarchal Oct 8, 2025
66cfe3f
[SofaImplicitField] Factorize the filling code in a lambda.
damienmarchal Oct 8, 2025
e966ddd
Add vectorized version
damienmarchal Oct 8, 2025
55eb49f
Merge branch 'master' into pr-add-vectorized-scalarfield-binding
damienmarchal Oct 13, 2025
55cfd29
Merge branch 'master' into pr-add-vectorized-scalarfield-binding
damienmarchal Oct 15, 2025
89ab0d8
Merge branch 'master' into pr-add-vectorized-scalarfield-binding
damienmarchal May 7, 2026
4f4e66d
FIXs
damienmarchal May 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion applications/plugins/SofaImplicitField/MarchingCube.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ void MarchingCube::generateSurfaceMesh(const double isoval, const double mstep,
if( nz < 2 || ny < 2 || nx < 2 )
return;

double cx,cy,cz;
int z,mk;
const int *tri;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ void FieldToSurfaceMesh::init()
d_componentState = core::objectmodel::ComponentState::Invalid;
}

updateMeshIfNeeded();
d_componentState = core::objectmodel::ComponentState::Valid;
}

Expand Down Expand Up @@ -109,12 +110,8 @@ void FieldToSurfaceMesh::updateMeshIfNeeded()
tmpTriangles.clear();

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include <SofaImplicitField/components/geometry/ScalarField.h>
#include <SofaImplicitField/MarchingCube.h>
#include <sofa/core/topology/BaseMeshTopology.h>
#include <future>

////////////////////////////////////////////////////////////////////////////////////////////////////
namespace sofaimplicitfield::component::engine
Expand All @@ -35,15 +34,15 @@ typedef sofa::core::topology::BaseMeshTopology::SeqTriangles SeqTriangles;
typedef sofa::core::topology::BaseMeshTopology::Triangle Triangle;
typedef sofa::type::vector<sofa::type::Vec3d> VecCoord;

using sofa::core::objectmodel::BaseComponent;
using sofa::component::geometry::ScalarField;
using sofa::core::visual::VisualParams ;
using BaseObject [[deprecated("Use sofa::core::objectmodel::BaseObject instead.")]] = sofa::core::objectmodel::BaseObject;
using sofa::type::Vec3d ;

class FieldToSurfaceMesh : public BaseObject
class FieldToSurfaceMesh : public BaseComponent
{
public:
SOFA_CLASS(FieldToSurfaceMesh, BaseObject);
SOFA_CLASS(FieldToSurfaceMesh, BaseComponent);

virtual void init() override ;
virtual void draw(const VisualParams*params) override ;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ void ScalarField::init()
d_componentState.setValue(core::objectmodel::ComponentState::Valid);
}

void ScalarField::getValues(const std::vector<Vec3d>& positions, std::vector<double>& results)
{
results.clear();
results.reserve(positions.size());
for(auto position : positions)
{
results.emplace_back(getValue(position));
}
}

Vec3d ScalarField::getGradientByFinitDifference(Vec3d& pos, int& i)
{
Vec3d Result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class SOFA_SOFAIMPLICITFIELD_API ScalarField : public BaseObject
virtual double getValue(Vec3d& pos, int& domain) = 0;
inline double getValue(Vec3d& pos) { int domain=-1; return getValue(pos,domain); }

// Compute the field for a range or input values
virtual void getValues(const std::vector<Vec3d>& positions, std::vector<double>& results);

/// By default compute the gradient using a first order finite difference approache
/// If you have analytical derivative don't hesitate to override this function.
virtual Vec3d getGradient(Vec3d& pos, int& domain);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ def createScene(root : Sofa.Core.Node):
The scalar fields are 'spherical', one implemented in python, the other in c++
One of the produced mesh is then connected to a visual model.
"""
root.addObject("RequiredPlugin", pluginName="SofaImplicitField")

root.addObject("RequiredPlugin", name="SofaImplicitField")
root.addObject(DrawController())

########################### Fields ##################
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Sofa
from primitives import Sphere

class FieldController(Sofa.Core.Controller):
def __init__(self, *args, **kwargs):
Sofa.Core.Controller.__init__(self, *args, **kwargs)
self.field = kwargs.get("target")

def onAnimateEndEvent(self, event):
print("Animation end event")
print("Field value at 0,0,0 is: ", self.field.getValue([0.0,0.0,0.0]) )
print("Field value at 1,0,0 is: ", self.field.getValue([1.0,0.0,0.0]) )
print("Field value at 2,0,0 is: ", self.field.getValue([2.0,0.0,0.0]) )

def createScene(root):
root.addObject(Sphere("field"))
root.addObject(FieldController(target=root.field))
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ def getValue(self, pos):
x,y,z = pos
return numpy.linalg.norm(self.center.value - numpy.array([x,y,z])) - self.radius.value

def getValues(self, positions, out_values):
"""This version of the overrides the getValues so that we fetch the data once"""
center = self.center.value
radius = self.radius.value
for i in range(len(positions)):
r = numpy.linalg.norm(center - positions[i]) - radius
out_values[i] = r

class RoundedBox(ScalarField):
def __init__(self, *args, **kwargs):
ScalarField.__init__(self, *args, **kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,58 @@ using sofa::core::objectmodel::BaseObject;
using sofa::type::Vec3;
using sofa::type::Mat3x3;

py::array_t<double> vector_to_numpy(const std::vector<Vec3>& vec) {
// Pybind11 gère la mémoire en créant un capsule pour que numpy sache comment libérer
// On transfère la propriété du vecteur à numpy, donc pas de copie
const double* data_ptr = (const double*)vec.data();
size_t size = vec.size();

// capsule: mémoire gérée par le vector, sera libérée quand python détruit l'objet numpy
py::capsule free_when_done(vec.data(), [](void *) {
// On ne fait rien ici car vector gère sa mémoire.
// Si on voulait transférer la propriété, on ferait delete ici.
});

// Dimensions du tableau numpy
std::vector<size_t> shape = { size, 3 };

// Strides en bytes (ici, contiguous : 3 colonnes, chaque double 8 octets)
std::vector<size_t> strides = { 3 * sizeof(double), sizeof(double) };

return py::array_t<double>(
shape,
strides,
data_ptr, // data pointer
free_when_done // capsule pour gérer la vie mémoire
);
}

py::array_t<double> vector_to_numpy(const std::vector<double>& vec) {
// Pybind11 gère la mémoire en créant un capsule pour que numpy sache comment libérer
// On transfère la propriété du vecteur à numpy, donc pas de copie
const double* data_ptr = (const double*)vec.data();
size_t size = vec.size();

// capsule: mémoire gérée par le vector, sera libérée quand python détruit l'objet numpy
py::capsule free_when_done(vec.data(), [](void *) {
// On ne fait rien ici car vector gère sa mémoire.
// Si on voulait transférer la propriété, on ferait delete ici.
});

// Dimensions du tableau numpy
std::vector<size_t> shape = { size };

// Strides en bytes (ici, contiguous : 3 colonnes, chaque double 8 octets)
std::vector<size_t> strides = { sizeof(double) };

return py::array_t<double>(
shape,
strides,
data_ptr, // data pointer
free_when_done // capsule pour gérer la vie mémoire
);
}

class ScalarField_Trampoline : public ScalarField {
public:
SOFA_CLASS(ScalarField_Trampoline, ScalarField);
Expand All @@ -58,6 +110,24 @@ class ScalarField_Trampoline : public ScalarField {
PYBIND11_OVERLOAD_PURE(double, ScalarField, getValue, pos);
}

void getValues(const std::vector<Vec3>& positions, std::vector<double>& results) override
{
PythonEnvironment::gil acquire;

// Search if there is a python override,
pybind11::function override = pybind11::get_override(static_cast<const ScalarField*>(this),"getValues");
if(!override){
return ScalarField::getValues(positions, results);
}

// Be sure there is enough space to hold the results
results.resize(positions.size());

// as there is one override, we call it, passing the "pos" argument and storing the return of the
// value in the "o" variable.
auto o = override(vector_to_numpy(positions), vector_to_numpy(results));
}

Vec3 getGradient(Vec3& pos, int& domain) override
{
SOFA_UNUSED(domain);
Expand Down
Loading