Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
a6ff885
add folder for STLIB
bakpaul Apr 14, 2025
60352a4
Add reusable methods
bakpaul Apr 14, 2025
d024105
Add working files
bakpaul Apr 14, 2025
10082bf
Created example scenes
bakpaul Apr 14, 2025
d167479
Fix example
bakpaul Apr 14, 2025
dd9cc34
orga folders
hugtalbot Apr 14, 2025
7589706
Fix package
bakpaul Apr 14, 2025
2f844d7
bootstrap examples
bakpaul Apr 14, 2025
2c55e7d
start beginner scene
hugtalbot Apr 14, 2025
9b9e057
Move around files
bakpaul Apr 14, 2025
b989159
Move examples into SofaPython3 main example folder
bakpaul Apr 14, 2025
b42833b
Add unsaved modifications
bakpaul Apr 14, 2025
fa0bd74
reoganisation after discussion
EulalieCoevoet Apr 14, 2025
4380356
Add gemetries
bakpaul Apr 14, 2025
ba978ce
WIP FileParameters
bakpaul Apr 14, 2025
fc88100
Add beginner example
bakpaul Apr 14, 2025
7c314c6
Add unsaved changes
bakpaul Apr 14, 2025
b70a52b
Start work on visual and collision
bakpaul Apr 14, 2025
7cedc42
work on Tuesday : visual, geometry
hugtalbot Apr 15, 2025
5f183d5
add basePrefabParameters.py
hugtalbot Apr 15, 2025
97a2381
Add extracted geometry
bakpaul Apr 15, 2025
00fd16b
Fix visual example
bakpaul Apr 15, 2025
423a484
Finished collisions
bakpaul Apr 15, 2025
c97bfa3
[stlib-splib] work on prefabs: cleaning (#501)
EulalieCoevoet Jun 2, 2025
995d1b3
[stlib] adds README
EulalieCoevoet Jun 2, 2025
01531ca
[stlib] minor changes from discussion
EulalieCoevoet Jun 2, 2025
fc1404a
[splib] updates entity
EulalieCoevoet Jun 3, 2025
dd230b9
update README
hugtalbot Jun 3, 2025
57efa1d
add init in prefab
hugtalbot Jun 3, 2025
938f05b
Add first implem of entity
bakpaul Jun 3, 2025
592c13d
before the train 3/3
hugtalbot Jun 3, 2025
ca864d8
very last commit befor train
EulalieCoevoet Jun 3, 2025
1d8cdd3
WIP
bakpaul Jun 3, 2025
bacb258
Retore original. Still crash due to wrong string conversion
bakpaul Jun 3, 2025
09f45c5
[stlib] Deformable Entity (#502)
EulalieCoevoet Jun 13, 2025
d4b6042
Fix linkpath by introducing init method + make addDeformableMaterial …
bakpaul Jun 13, 2025
aad2a5e
[STLIB] Add the unified "add" in Sofa.Core.Node (#503)
damienmarchal Jul 17, 2025
1b15154
Add new tests for prefabs
bakpaul Jul 17, 2025
e2fff56
Revert modification of __genericAdd
bakpaul Jul 17, 2025
9de87d4
update addMass using elementType
hugtalbot Jul 17, 2025
75fa28f
[Prefabs] Reorder files to match concepts (#547)
bakpaul Oct 28, 2025
ae1d496
[Prefabs] cleaning to fix deformable example (#549)
EulalieCoevoet Oct 28, 2025
ac3ed74
Fix by passing state link when using other topology than edges (#550)
bakpaul Oct 28, 2025
c419123
Fix the usage of DEFAULT_VALUE in dict passed as kwargs
bakpaul Oct 29, 2025
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
30 changes: 30 additions & 0 deletions examples/stlib/PrefabScene_beginner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from stlib.materials.rigid import Rigid
from stlib.materials.deformable import Deformable
from stlib.geometries.cube import CubeParameters
from stlib.geometries.file import FileParameters
from splib.simulation.headers import setupLagrangianCollision
from splib.simulation.linear_solvers import addLinearSolver
from splib.simulation.ode_solvers import addImplicitODE

#To be added in splib
def addSolvers(root):
addLinearSolver(root)
addImplicitODE(root)
root.addObject("LinearSolverConstraintCorrection", linearsolver="@LinearSolver")

def createScene(root):
root.gravity = [0, 0, -9.81]

setupLagrangianCollision(root)
addSolvers(root)

rigidParams = Rigid.getParameters()
rigidParams.geometry = CubeParameters([0, 0, 0], 1, 3)
root.add(Rigid,rigidParams)

deformableParams = Deformable.getParameters()
#Add transformation somewhere here
deformableParams.geometry = FileParameters("SofaScene/Logo.vtk")
root.add(Deformable,deformableParams)

return root
14 changes: 14 additions & 0 deletions examples/stlib/PrefabScene_expert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from splib.helper import exportScene
from stlib.misc.entity import Entity


def createScene(root):
Entity.Deformable(Mesh,MechaProperties)

return root


if __name__=="__main__":
root = exportScene()
createScene(root)
pass
8 changes: 8 additions & 0 deletions examples/stlib/PrefabScene_intermediate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from splib.helper import exportScene
from stlib.misc.entity import Entity


def createScene(root):


return root
86 changes: 86 additions & 0 deletions splib/Testing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from splib.topology.dynamic import *
from splib.simulation.headers import *
from splib.simulation.ode_solvers import *
from splib.simulation.linear_solvers import *
from splib.mechanics.linear_elasticity import *
from splib.mechanics.mass import *
from splib.mechanics.fix_points import *
from splib.topology.loader import *
from splib.core.node_wrapper import *

from splib.core.node_wrapper import ChildWrapper, ObjectWrapper
from splib.helper import exportScene


def createScene(rootNode):
rootNode.dt = 0.03
rootNode.gravity = [0,-9.81,0]

setupLagrangianCollision(rootNode,requiredPlugins={"pluginName":['Sofa.Component.Constraint.Projective',
'Sofa.Component.Engine.Select',
'Sofa.Component.LinearSolver.Direct',
'Sofa.Component.Mass',
'Sofa.Component.ODESolver.Backward',
'Sofa.Component.SolidMechanics.FEM.Elastic',
'Sofa.Component.StateContainer',
'Sofa.Component.Topology.Container.Grid',
'Sofa.Component.IO.Mesh',
'Sofa.Component.LinearSolver.Direct',
'Sofa.Component.Topology.Container.Dynamic',
'Sofa.Component.Visual']})
#
#
# ## TODO : Being able to call "childNode.addAnything" by using the __getattr__ method
Liver0=rootNode.addChild("Liver0")
Liver0.addObject("EulerImplicitSolver",name="ODESolver",rayleighStiffness="0",rayleighMass="0")
Liver0.addObject("SparseLDLSolver",name="LinearSolver",template="CompressedRowSparseMatrixMat3x3",parallelInverseProduct="False")
Liver0.addObject("MeshGmshLoader",name="meshLoader",filename="mesh/liver.msh")
Liver0.addObject("TetrahedronSetTopologyModifier",name="modifier")
Liver0.addObject("TetrahedronSetTopologyContainer",name="container",src="@meshLoader")
Liver0.addObject("TetrahedronSetGeometryAlgorithms",name="algorithms")
Liver0.addObject("MechanicalObject",name="mstate",template="Vec3d")
Liver0.addObject("LinearSolverConstraintCorrection",name="constraintCorrection")
Liver0.addObject("TetrahedronFEMForceField",name="constitutiveLaw",youngModulus="3000",poissonRatio="0.3",method="large")
Liver0.addObject("MeshMatrixMass",name="mass",massDensity="1")
Liver0.addObject("BoxROI",name="fixedBoxROI",box="0 3 0 2 5 2")
Liver0.addObject("FixedProjectiveConstraint",name="fixedConstraints",indices="@fixedBoxROI.indices")
Visu=Liver0.addChild("Visu")
Visu.addObject("TriangleSetTopologyModifier",name="modifier")
Visu.addObject("TriangleSetTopologyContainer",name="container")
Visu.addObject("TriangleSetGeometryAlgorithms",name="algorithms")
Visu.addObject("Tetra2TriangleTopologicalMapping",name="TopologicalMapping",input="@../container",output="@container")
Visu.addObject("OglModel",name="OglModel",topology="@container",color="[1.0, 0.2, 0.8]")
Visu.addObject("IdentityMapping",name="Mapping",isMechanical="False")


SimulatedLiver1 = rootNode.addChild("Liver1")
addImplicitODE(SimulatedLiver1)
addLinearSolver(SimulatedLiver1,iterative=False, template="CompressedRowSparseMatrixMat3x3")
loadMesh(SimulatedLiver1,filename="mesh/liver.msh")
addDynamicTopology(SimulatedLiver1,type=ElementType.TETRAHEDRA,source="@meshLoader")
SimulatedLiver1.addObject("MechanicalObject",name="mstate", template='Vec3d')
SimulatedLiver1.addObject("LinearSolverConstraintCorrection",name="constraintCorrection")
addLinearElasticity(SimulatedLiver1,ElementType.TETRAHEDRA, poissonRatio="0.3", youngModulus="3000", method='large')
addMass(SimulatedLiver1,template='Vec3d',massDensity="2")
addFixation(SimulatedLiver1,ConstraintType.PROJECTIVE,boxROIs=[0, 3, 0, 2, 5, 2])

SimulatedLiverVisu = SimulatedLiver1.addChild("Visu")
addDynamicTopology(SimulatedLiverVisu,ElementType.TRIANGLES)
SimulatedLiverVisu.addObject("Tetra2TriangleTopologicalMapping", name="TopologicalMapping", input="@../container", output="@container")
SimulatedLiverVisu.addObject("OglModel", name="OglModel", topology="@container",color=[1.0,0.2,0.8])
SimulatedLiverVisu.addObject("IdentityMapping",name="Mapping",isMechanical=False)


return rootNode



if __name__ == "__main__":
Node = exportScene()
createScene(Node)






4 changes: 1 addition & 3 deletions splib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
"""SPLIB is now relocated at: https://github.com/SofaDefrost/STLIB, please clone and install the plugin to use it"""
raise Exception("SPLIB is now relocated at: https://github.com/SofaDefrost/STLIB, please clone and install the plugin to use it")

__all__ = ["core","topology", "simulation","modeler","mechanics","Testing","helper"]

1 change: 1 addition & 0 deletions splib/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__all__ = ["node_wrapper", "utils","enum_types"]
51 changes: 51 additions & 0 deletions splib/core/enum_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from enum import Enum

class ConstitutiveLaw(Enum):
ELASTIC = 1
HYPERELASTIC = 2

class ODEType(Enum):
EXPLICIT = 1
IMPLICIT = 2

class SolverType(Enum):
DIRECT = 1
ITERATIVE = 2

class MappingType(Enum):
BARYCENTRIC = 1
IDENTITY = 2
RIGID = 3

class ConstraintType(Enum):
PROJECTIVE = 1
WEAK = 2
LAGRANGIAN = 3

class CollisionPrimitive(Enum):
POINTS = 1
LINES = 2
TRIANGLES = 3
SPHERES = 4

class ElementType(Enum):
POINTS = 1
EDGES = 2
TRIANGLES = 3
QUADS = 4
TETRAHEDRA = 5
HEXAHEDRA = 6

class StateType(Enum):
VEC3 = 3
VEC1 = 1
RIGID = 7

def __str__(self):
if self == StateType.VEC3:
return "Vec3"
if self == StateType.VEC1:
return "Vec1"
if self == StateType.RIGID:
return "Rigid3"
return 'Unknown'
75 changes: 75 additions & 0 deletions splib/core/node_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from functools import wraps
from splib.core.utils import defaultValueType
# The two classes are not merged because one could want to use a ReusableMethod
# (enabling to pass dictionary fixing params) without wanting to use a full PrefabSimulation


class BaseWrapper(object):

def __init__(self,node):
self.node = node

def __getattr__(self, item):
return getattr(self.node,item)


class ObjectWrapper(BaseWrapper):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)

def addObject(self,*args, **kwargs):
parameters = {}
# Expand any parameter that has been set by the user in a custom dictionary
# and having the same key as the component name
if "name" in kwargs:
parameters["name"] = kwargs["name"]
if kwargs["name"] in kwargs:
if isinstance(kwargs[kwargs["name"]], dict):
for param in kwargs[kwargs["name"]]:
if not(isinstance(kwargs[kwargs["name"]][param],defaultValueType)):
parameters = {**parameters, param : kwargs[kwargs["name"]][param]}
else:
print("[Warning] You are passing a keyword arg with the same name as one obj without it being a Dict, it will not be used. ")

# Add all the parameters from kwargs that are not dictionary
for param in kwargs:
if param in parameters:
if not(param == "name"):
print("[Warning] You are redefining the parameter '"+ param + "' of object " + str(args[0]))
elif not(isinstance(kwargs[param], dict)) and not(isinstance(kwargs[param],defaultValueType)):
parameters = {**parameters,param:kwargs[param]}

return self.node.addObject(*args,**parameters)




class ChildWrapper(ObjectWrapper):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)

# This method enforces that the object that is created by the addChild method keeps the prefab type
def addChild(self,*args, **kwargs):
child = self.node.addChild(*args,**kwargs)
returnObject = self.__new__(type(self))
returnObject.__init__(child)
return returnObject




def ReusableMethod(method):
@wraps(method)
def wrapper(*args, **kwargs):

node = args[0]
# We don't want to wrap an object that is already wrapped
# It wouldn't break anything but nodes might get wrapped and wrapped again multiplying redirections...
if( not isinstance(node,ObjectWrapper) ):
node = ObjectWrapper(node)

if len(args)>1:
return method(node,*args[1:],**kwargs)
else:
return method(node,**kwargs)
return wrapper
36 changes: 36 additions & 0 deletions splib/core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import List, Callable, Tuple, Dict
from functools import wraps

class defaultValueType():
def __init__(self):
pass

DEFAULT_VALUE = defaultValueType()

def isDefault(obj):
return isinstance(obj,defaultValueType)


def getParameterSet(name : str,parameterSet : Dict) -> Dict:
if name in parameterSet:
if isinstance(parameterSet[name], dict):
return parameterSet[name]
return {}


def MapKeywordArg(objectName,*argumentMaps):
def MapArg(method):
@wraps(method)
def wrapper(*args, **kwargs):
containerParams = getParameterSet(objectName,kwargs)
for argMap in argumentMaps:
if (argMap[0] in kwargs) and not(kwargs[argMap[0]] is None):
containerParams[argMap[1]] = kwargs[argMap[0]]
kwargs[objectName] = containerParams
return method(*args,**kwargs)
return wrapper
return MapArg




35 changes: 35 additions & 0 deletions splib/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class displayNode():
def __init__(self,_level=0):
self.prefix = ""
for i in range(_level):
self.prefix += "| "

def addObject(self,type:str,**kwargs):
print(self.prefix + type + " with " + str(kwargs))

def addChild(self,name:str):
print(self.prefix + "-> Node : " + name)
return displayNode(len(self.prefix) + 1)


class exportScene():
def __init__(self,name="rootNode"):
self.name = name

def addObject(self,type:str,**kwargs):
suffix = ""
for i in kwargs:
suffix += "," + str(i) + "=\"" + str(kwargs[i]) + "\""
print(self.name+".addObject(\"" + type + "\"" + suffix + ")")

def addChild(self,name:str):
print(name + '=' + self.name+".addChild(\"" + name + "\")")
setattr(self,name,exportScene(name))
return getattr(self,name)

def __setattr__(self, key, value):
if(not(key == "name")):
print(self.__dict__["name"] + "." + key + " = " + str(value))
self.__dict__[key] = value
else:
self.__dict__[key] = value
1 change: 1 addition & 0 deletions splib/mechanics/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__all__ = ["linear_elasticity","hyperelasticity","fix_points","collision_model","mass"]
34 changes: 34 additions & 0 deletions splib/mechanics/collision_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from splib.core.node_wrapper import ReusableMethod
from splib.core.utils import DEFAULT_VALUE
from splib.core.enum_types import CollisionPrimitive


@ReusableMethod
def addCollisionModels(node, primitive : CollisionPrimitive,
topology=DEFAULT_VALUE,
selfCollision=DEFAULT_VALUE,
proximity=DEFAULT_VALUE,
group=DEFAULT_VALUE,
contactStiffness=DEFAULT_VALUE,
contactFriction=DEFAULT_VALUE,
spheresRadius=DEFAULT_VALUE,
**kwargs):
match primitive:
case CollisionPrimitive.POINTS:
node.addObject("PointCollisionModel", name="PointCollision", topology=topology, selfCollision=selfCollision, proximity=proximity, contactStiffness=contactStiffness, contactFriction=contactFriction, group=group, **kwargs)
return
case CollisionPrimitive.LINES:
node.addObject("LineCollisionModel", name="EdgeCollision", topology=topology, selfCollision=selfCollision, proximity=proximity, contactStiffness=contactStiffness, contactFriction=contactFriction, group=group, **kwargs)
return
case CollisionPrimitive.TRIANGLES:
node.addObject("TriangleCollisionModel", name="TriangleCollision", topology=topology, selfCollision=selfCollision, proximity=proximity, contactStiffness=contactStiffness, contactFriction=contactFriction, group=group,**kwargs)
return
case CollisionPrimitive.SPHERES:
node.addObject("SphereCollisionModel", name="SphereCollision", topology=topology, selfCollision=selfCollision, proximity=proximity, contactStiffness=contactStiffness, contactFriction=contactFriction, group=group, radius=spheresRadius, **kwargs)
return
case _:
return

#Cube and tetra are missing.


Loading
Loading