Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ void moduleAddBase(py::module &m)
base.def("getDefinitionSourceFilePos", &Base::getDefinitionSourceFilePos, sofapython3::doc::base::getDefinitionSourceFilePos);
base.def("getDefinitionSourceFileName", &Base::getDefinitionSourceFileName, sofapython3::doc::base::getDefinitionSourceFileName);
base.def("getInstanciationSourceFilePos", &Base::getInstanciationSourceFilePos, sofapython3::doc::base::getInstanciationSourceFilePos);
base.def("getInstanciationFileName", &Base::getInstanciationSourceFileName, sofapython3::doc::base::getInstanciationSourceFilePos);
base.def("getInstanciationSourceFileName", &Base::getInstanciationSourceFileName, sofapython3::doc::base::getInstanciationSourceFileName);

base.def("setDefinitionSourceFilePos", &Base::setDefinitionSourceFilePos, sofapython3::doc::base::setDefinitionSourceFilePos);
base.def("setDefinitionSourceFileName", &Base::setDefinitionSourceFileName, sofapython3::doc::base::setDefinitionSourceFileName);
Expand Down
17 changes: 14 additions & 3 deletions bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <sofa/core/CategoryLibrary.h>
#include <pybind11/stl.h>

#include <sofa/simulation/Node.h>
/// Makes an alias for the pybind11 namespace to increase readability.
namespace py { using namespace pybind11; }

Expand Down Expand Up @@ -99,11 +100,20 @@ py::list getSlaves(BaseObject &self)
return slaveList;
}

py::object getContext(const BaseObject &self)
py::object getContext(BaseObject &self)
{
const sofa::core::objectmodel::BaseContext* context = self.getContext();
sofa::core::objectmodel::BaseContext* context = self.getContext();
if (context){
return PythonFactory::toPython(const_cast<sofa::core::objectmodel::BaseContext*>(context));
return PythonFactory::toPython(context);
}
return py::none();
}

py::object getOwner(BaseObject &self)
{
sofa::simulation::Node* node = dynamic_cast<sofa::simulation::Node*>(self.getContext());
if (node){
return PythonFactory::toPython(node);
}
return py::none();
}
Expand Down Expand Up @@ -218,6 +228,7 @@ void moduleAddBaseObject(py::module& m)
p.def("getLinkPath", [](const BaseObject &self){ return std::string("@") + self.getPathName(); }, sofapython3::doc::baseObject::getLink);
p.def("getSlaves", getSlaves, sofapython3::doc::baseObject::getSlaves);
p.def("getContext", getContext, sofapython3::doc::baseObject::getContext);
p.def("getOwner", getOwner);
p.def("getMaster", getMaster, sofapython3::doc::baseObject::getMaster);
p.def("addSlave", &BaseObject::addSlave, sofapython3::doc::baseObject::addSlave);
p.def("storeResetState", &BaseObject::storeResetState, sofapython3::doc::baseObject::storeResetState);
Expand Down
233 changes: 233 additions & 0 deletions examples/modifier-exp/test1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import Sofa

## Pyggy patch ##############################################################
class Parameters(object):
name : str = ""
def __init__(self, **kwargs):
for k,v in kwargs.items():
setattr(self, k, v)

def toDict(self):
return {"name":self.name}

class ModifierParameters(Parameters):
def __init__(self, **kwargs):
Parameters.__init__(self, **kwargs)

def toDict(self):
return Parameters.toDict(self)

def getpythonCallingPoint(depth=0):
import inspect
d = inspect.stack()[depth]
return (d.filename, d.positions.lineno)

class Modifier(Sofa.Core.Controller):
def __init__(self, parameters : ModifierParameters = ModifierParameters()):
Sofa.Core.Controller.__init__(self, **parameters.toDict())

def getModifiedNode(self):
return self.getOwner().parents[0]

Sofa.Core.Modifier = Modifier

def addModifier(self, type, parameters:Parameters, **kwargs) -> Modifier:
if "Modifiers" not in self.children:
self.addChild("Modifiers")
o = self.Modifiers.addObject(type(parameters=parameters, **kwargs))
o.apply()
return o
Sofa.Core.Node.addModifier = addModifier

def add(self, type, parameters:Parameters) -> Sofa.Core.Base:
if issubclass(type, Sofa.Core.Node):
return self.addChild(type(parameters))
Sofa.Core.Node.add = add

#############################################################################
class PrefabParameters(Parameters):
def __init__(self, **kwargs):
Parameters.__init__(self, **kwargs)

def toDict(self):
return Parameters.toDict(self)

class Prefab(Sofa.Core.Node):
def __init__(self, parameters):
Sofa.Core.Node.__init__(self, name=parameters.name)

class MyEntityParameters(PrefabParameters):
def __init__(self, **kwargs):
PrefabParameters.__init__(self, **kwargs)

def toDict(self):
return PrefabParameters.toDict(self)

class MyEntity(Prefab):
def __init__(self, parameters):
Prefab.__init__(self, PrefabParameters(name=parameters.name))

self.addChild("Material")
self.addChild("Geometry")
self.addChild("Collision")
self.addChild("Visual")
self.addChild("Modifiers")

self.Geometry.addObject("MeshOBJLoader", filename="mesh/cube.obj", name="loader")

self.Material.addObject("MechanicalObject", name="state", template="Vec3", position=self.Geometry.loader.position.linkpath)
self.Material.addObject("UniformMass", name="mass", totalMass=1.0)

self.Visual.addObject("OglModel", src=self.Geometry.loader.linkpath)


class FixedPointBoundaryConditionParameters(ModifierParameters):
def __init__(self, **kwargs):
ModifierParameters.__init__(self, **kwargs)

def toDict(self):
return ModifierParameters.toDict(self)

class FixedPointBoundaryCondition(Modifier):
def __init__(self, parameters : FixedPointBoundaryConditionParameters = FixedPointBoundaryConditionParameters()):
Modifier.__init__(self, parameters=parameters)

def apply(self):
self.getModifiedNode().Material.addObject("UnilateralInteractionConstraint")

class BilateralInteractionModifierParameters(ModifierParameters):
object1: Sofa.Core.Object = None
object2: Sofa.Core.Object = None

def __init__(self, **kwargs):
ModifierParameters.__init__(self, **kwargs)

def toDict(self):
return ModifierParameters.toDict(self) | {"object1":self.object1,"object2":self.object2}

class BilateralInteractionModifier(Modifier):
def __init__(self, parameters : BilateralInteractionModifierParameters = BilateralInteractionModifierParameters()):
Modifier.__init__(self, parameters=parameters)

self.object1 = parameters.object1
self.object2 = parameters.object2

if self.object1 is None:
raise Exception("It is mandatory to have 'object1'")

if self.object2 is None:
raise Exception("It is mandatory to have 'object2'")

h = getpythonCallingPoint(3)
self.setInstanciationSourceFileName(h[0])
self.setInstanciationSourceFilePos(h[1])

def apply(self):
a = self.object1.Material.addObject("UnilateralInteractionConstraint")
b = self.object2.Material.addObject("UnilateralInteractionConstraint")

a.setInstanciationSourceFileName(self.getInstanciationSourceFileName())
a.setInstanciationSourceFilePos(self.getInstanciationSourceFilePos())

b.setInstanciationSourceFileName(self.getInstanciationSourceFileName())
b.setInstanciationSourceFilePos(self.getInstanciationSourceFilePos())

class HeaderFromSceneModifierParameters(ModifierParameters):
root : Sofa.Core.Node = None

def __init__(self, **kwargs):
ModifierParameters.__init__(self, **kwargs)

def toDict(self):
return ModifierParameters.toDict(self) | {"root":self.root}

def find_component(cond, node):
c = [component for component in node.objects if cond(component)]
for child in node.children:
c += find_component(cond, child)
return c

class HeaderFromSceneModifier(Modifier):
def __init__(self, parameters : HeaderFromSceneModifierParameters = HeaderFromSceneModifierParameters()):
Modifier.__init__(self, parameters=parameters)
self.root = parameters.root

if self.root is None:
raise Exception("It is mandatory to have 'root' parameter for this modifier")

h = getpythonCallingPoint(3)
self.setInstanciationSourceFileName(h[0])
self.setInstanciationSourceFilePos(h[1])

def apply(self):
# Traverse scene to deduce stuff
constraints = find_component(lambda x: True, self.root)
constraints = [constraint.getClassName() for constraint in constraints]
print(f"Lagrangian {constraints}")
if "UnilateralLagrangianConstraint" in constraints:
self.root.addObject("FreeMotionAnimationLoop")

class HeaderToSceneModifierParameters(ModifierParameters):
root : Sofa.Core.Node = None

def __init__(self, **kwargs):
ModifierParameters.__init__(self, **kwargs)

def toDict(self):
return ModifierParameters.toDict(self) | {"root":self.root}

class HeaderToSceneModifier(Modifier):
def __init__(self, parameters : HeaderFromSceneModifierParameters = HeaderFromSceneModifierParameters()):
Modifier.__init__(self, parameters=parameters)

self.root = parameters.root

if self.root is None:
raise Exception("It is mandatory to have 'root' parameter for this modifier")

def apply(self):
# Traverse scene to deduce stuff

def is_mechanical(object):
return object.getClassName() == "MechanicalObject"

mechanicals = find_component(is_mechanical, self.root)
for mechanical in mechanicals:
mechanical.getOwner().addObject("GenericConstraintCorrection")

def createScene(root):
# Add a modifier in Example1/prefab, it modify the node it is applied to
# The use case is to enrich an object
root.addChild("Example1")
root.Example1.add(MyEntity, parameters=MyEntityParameters(name="prefab"))
root.Example1.prefab.addModifier(FixedPointBoundaryCondition, parameters=FixedPointBoundaryConditionParameters(name="boundary1", stiffness=30.0))

# Add a modifier in Example2, it modify a specific pair of node
# The use case is to explicit an action that involve several possibly disconnected in the graph object
# Personnally I'm not sure I want to express BoundaryCondition as Modifiers.
root.addChild("Example2")
root.Example2.add(MyEntity, parameters=MyEntityParameters(name="prefab1"))
root.Example2.add(MyEntity, parameters=MyEntityParameters(name="prefab2"))
root.Example2.addModifier(BilateralInteractionModifier, parameters=BilateralInteractionModifierParameters(name="constraint",
object1=root.Example2.prefab1,
object2=root.Example2.prefab2))

# The same but with the parameters written before calling the modifier
root.addChild("Example2bis")
root.Example2bis.add(MyEntity, parameters=MyEntityParameters(name="prefab1"))
root.Example2bis.add(MyEntity, parameters=MyEntityParameters(name="prefab2"))
parameters = BilateralInteractionModifierParameters()
parameters.name="constraint"
parameters.object1=root.Example2bis.prefab1
parameters.object2=root.Example2bis.prefab2
root.Example2bis.addModifier(BilateralInteractionModifier, parameters=parameters)


# Add a modifier in the root node, this modifier deduce something from the scene and modify anywhere in the scene.
root.addModifier(HeaderFromSceneModifier, parameters=HeaderFromSceneModifierParameters(name="bottom-up-fix",
root=root))

# Add a modifier in the root node, this modifier deduce something from the scene and modify top down the scene.
root.addModifier(HeaderToSceneModifier, parameters=HeaderToSceneModifierParameters(name="top-down-fix",
root=root))

Loading