Skip to content

Commit 9ee6042

Browse files
list of changes:
- uses dynapydantic - adds fromYaml and fromDict methods for parameters - adds prefabs: cube, sphere and bunny - and some cleaning
1 parent b2eff82 commit 9ee6042

19 files changed

Lines changed: 208 additions & 80 deletions

File tree

examples/stlib/SofaScene.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from stlib.geometries.plane import PlaneParameters
44
from stlib.geometries.file import FileParameters
55
from stlib.geometries.extract import ExtractParameters
6-
from stlib.materials.deformable import DeformableBehaviorParameters
6+
from stlib.materials.deformable import DeformableMaterialParameters
77
from stlib.collision import Collision, CollisionParameters
88
from stlib.entities import Entity, EntityParameters
99
from stlib.visual import Visual, VisualParameters
@@ -66,7 +66,7 @@ def createScene(root):
6666

6767
LogoParams = EntityParameters(name = "Logo",
6868
geometry = FileParameters(filename="mesh/SofaScene/Logo.vtk"),
69-
material = DeformableBehaviorParameters(),
69+
material = DeformableMaterialParameters(),
7070
collision = CollisionParameters(geometry = FileParameters(filename="mesh/SofaScene/LogoColli.sph")),
7171
visual = VisualParameters(geometry = FileParameters(filename="mesh/SofaScene/LogoVisu.obj")))
7272

@@ -94,12 +94,12 @@ def createScene(root):
9494
SParams.name = "S"
9595
SParams.geometry = FileParameters(filename="mesh/SofaScene/S.vtk")
9696
SParams.geometry.elementType = ElementType.TETRAHEDRA
97-
SParams.material = DeformableBehaviorParameters()
97+
SParams.material = DeformableMaterialParameters()
9898
SParams.material.constitutiveLawType = ConstitutiveLaw.ELASTIC
9999
SParams.material.parameters = [200, 0.45]
100100

101101
def SAddMaterial(node):
102-
DeformableBehaviorParameters.addDeformableMaterial(node)
102+
DeformableMaterialParameters.addDeformableMaterial(node)
103103
#TODO deal with that is a more smooth way in the material directly
104104
node.addObject("LinearSolverConstraintCorrection", name="ConstraintCorrection", linearSolver=SNode.LinearSolver.linkpath, ODESolver=SNode.ODESolver.linkpath)
105105

splib/core/utils.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import List, Callable, Tuple, Dict
22
from functools import wraps
33

4+
45
class defaultValueType():
56
def __init__(self):
67
pass
@@ -32,5 +33,24 @@ def wrapper(*args, **kwargs):
3233
return MapArg
3334

3435

36+
REQUIRES_COLLISIONPIPELINE = "requiresCollisionPipeline"
37+
38+
def setRequiresCollisionPipeline(rootnode):
39+
if rootnode is not None:
40+
if rootnode.findData(REQUIRES_COLLISIONPIPELINE) is None:
41+
rootnode.addData(name=REQUIRES_COLLISIONPIPELINE, type="bool", value=True, help="Some prefabs in the scene requires a collision pipeline.")
42+
else:
43+
rootnode.requiresCollisionPipeline.value = True
44+
45+
46+
REQUIRES_LAGRANGIANCONSTRAINTSOLVER = "requiresLagrangianConstraintSolver"
47+
48+
def setRequiresLagrangianConstraintSolver(rootnode):
49+
if rootnode is not None:
50+
if rootnode.findData(REQUIRES_LAGRANGIANCONSTRAINTSOLVER) is None:
51+
rootnode.addData(name=REQUIRES_LAGRANGIANCONSTRAINTSOLVER, type="bool", value=True, help="Some prefabs in the scene requires a Lagrangian constraint solver.")
52+
else:
53+
rootnode.requiresLagrangianConstraintSolver.value = True
54+
3555

3656

splib/mechanics/mass.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@
33
from splib.core.enum_types import ElementType
44

55

6-
# TODO : use the massDensity ONLY and deduce totalMass if necessary from it + volume
7-
86
@ReusableMethod
97
def addMass(node, elementType:ElementType, totalMass=DEFAULT_VALUE, massDensity=DEFAULT_VALUE, lumping=DEFAULT_VALUE, topology=DEFAULT_VALUE, **kwargs):
108
if (not isDefault(totalMass)) and (not isDefault(massDensity)) :
119
print("[warning] You defined the totalMass and the massDensity in the same time, only taking massDensity into account")
1210
del kwargs["massDensity"]
1311

1412
if(elementType is not None and elementType !=ElementType.POINTS and elementType !=ElementType.EDGES):
15-
node.addObject("MeshMatrixMass",name="mass", totalMass=totalMass, massDensity=massDensity, lumping=lumping, topology=topology, **kwargs)
13+
node.addObject("MeshMatrixMass",
14+
name="mass",
15+
totalMass=totalMass,
16+
massDensity=massDensity,
17+
lumping=lumping,
18+
topology=topology, **kwargs)
1619
else:
1720
if (not isDefault(massDensity)) :
1821
print("[warning] mass density can only be used on a surface or volumetric topology. Please use totalMass instead")
1922
if (not isDefault(lumping)) :
2023
print("[warning] lumping can only be set for surface or volumetric topology")
2124

22-
node.addObject("UniformMass",name="mass", totalMass=totalMass, topology=topology,**kwargs)
25+
node.addObject("UniformMass", name="mass", totalMass=totalMass, topology=topology,**kwargs)
2326

stlib/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__all__ = ["core","entities","geometries","materials","collision","visual"]
1+
__all__ = ["core","entities","geometries","materials","collision","visual","prefabs"]
22

33
import Sofa.Core
44
from stlib.core.basePrefab import BasePrefab
@@ -48,6 +48,7 @@ def checkName(context : Sofa.Core.Node, name):
4848
else:
4949
params["name"] = checkName(self, params["name"])
5050

51+
5152
# Dispatch the creation to either addObject or addChild
5253
if isinstance(typeName, type) and issubclass(typeName, BasePrefab):
5354
pref = self.addChild(typeName(**params))

stlib/collision.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from splib.core.enum_types import CollisionPrimitive
66
from splib.core.utils import DEFAULT_VALUE
77
from splib.mechanics.collision_model import addCollisionModels
8-
from Sofa.Core import Object
8+
from splib.core.utils import setRequiresCollisionPipeline
9+
910

1011
class CollisionParameters(BaseParameters):
1112
name : str = "Collision"
@@ -29,6 +30,8 @@ def init(self):
2930

3031
geom = self.add(Geometry, parameters = self.parameters.geometry)
3132

33+
setRequiresCollisionPipeline(rootnode=self.getRoot())
34+
3235
self.addObject("MechanicalObject", template="Vec3", position=f"@{self.parameters.geometry.name}/container.position")
3336
for primitive in self.parameters.primitives:
3437
addCollisionModels(self, primitive,

stlib/core/baseParameters.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
from splib.core.utils import DEFAULT_VALUE
22

33
from pydantic import BaseModel, ValidationError
4+
from dynapydantic import SubclassTrackingModel
45
from typing import Callable, Optional, Any
56
import Sofa
67

7-
class BaseParameters(BaseModel):
8+
class BaseParameters(SubclassTrackingModel,
9+
discriminator_field="type",
10+
discriminator_value_generator=lambda t: t.__name__,):
811

912
name : str = "Object"
1013
kwargs : dict = {}
1114

12-
# @classmethod
13-
# def fromYaml(self, data: str):
14-
# import yaml
15-
# dataDict = yaml.safe_load(data)
16-
# return self.fromDict(dataDict)
15+
@classmethod
16+
def fromYaml(self, data: str):
17+
import yaml
18+
dataDict = yaml.safe_load(data)
19+
return self.fromDict(dataDict)
20+
21+
@classmethod
22+
def fromDict(self, data: dict):
23+
try:
24+
return self.model_validate(data, strict=True)
25+
except ValidationError as exc:
26+
for error in exc.errors():
27+
loc = error.get("loc")
28+
message = ""
29+
for locPart in loc:
30+
message += locPart.__str__() + ": "
31+
message += error.get("msg")
32+
Sofa.msg_error(self.__name__, message)
1733

18-
# @classmethod
19-
# def fromDict(self, data: dict):
20-
# try:
21-
# return self.model_validate(data, strict=True)
22-
# except ValidationError as exc:
23-
# for error in exc.errors():
24-
# message = error.get("loc")[0].__str__() + ": "
25-
# message += error.get("msg")
26-
# Sofa.msg_error(self.__name__, message)
2734

stlib/entities/__entity__.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,22 @@
99
from stlib.core.basePrefab import BasePrefab
1010

1111
from stlib.geometries.file import FileParameters
12-
from stlib.materials.rigid import RigidParameters
12+
from stlib.materials.rigid import RigidMaterialParameters
1313
from splib.core.enum_types import ElementType
1414

15+
from dynapydantic import Polymorphic
16+
import Sofa
17+
1518

1619
class EntityParameters(BaseParameters):
1720
name : str = "Entity"
1821

19-
stateType : StateType = StateType.RIGID
20-
2122
### QUID
2223
addCollision : Optional[Callable] = Collision(CollisionParameters())
2324
addVisual : Optional[Callable] = Visual(VisualParameters())
2425

25-
geometry : GeometryParameters = GeometryParameters(elementType = ElementType.POINTS, data = InternalDataProvider(position = [[0., 0., 0.]]))
26-
material : MaterialParameters = RigidParameters()
26+
geometry : Polymorphic[GeometryParameters] = GeometryParameters(elementType = ElementType.POINTS, data = InternalDataProvider(position = [[0., 0., 0.]]))
27+
material : Polymorphic[MaterialParameters] = RigidMaterialParameters()
2728
collision : Optional[CollisionParameters] = None
2829
visual : Optional[VisualParameters] = VisualParameters(geometry = FileParameters(filename="mesh/cube.obj"))
2930

@@ -46,11 +47,10 @@ def __init__(self, parameters: EntityParameters):
4647

4748
def init(self):
4849
self.geometry = self.add(Geometry, parameters=self.parameters.geometry)
49-
self.checkMaterialCompatibility()
5050

5151
self.material = self.add(Material, parameters=self.parameters.material)
52-
self.material.getMechanicalState().position.parent = self.geometry.container.position.linkpath
53-
52+
self.material.getMechanicalState().topology = self.geometry.container.linkpath
53+
5454
if self.parameters.collision is not None:
5555
self.collision = self.add(Collision, parameters=self.parameters.collision)
5656
self.addMapping(self.collision)
@@ -59,18 +59,13 @@ def init(self):
5959
self.visual = self.add(Visual, parameters=self.parameters.visual)
6060
self.addMapping(self.visual)
6161

62-
def checkMaterialCompatibility(self):
63-
if self.parameters.material.stateType != self.parameters.stateType:
64-
print("WARNING: imcompatibility between templates of both the entity and the material")
65-
self.parameters.material.stateType = self.parameters.stateType
66-
6762

6863
def addMapping(self, destinationPrefab):
6964

70-
template = f'{self.parameters.stateType},Vec3' # TODO: check that it is always true
65+
template = f'{self.parameters.material.stateType},Vec3' # TODO: check that it is always true
7166

7267
#TODO: all paths are expecting Geometry to be called Geomtry and so on. We need to robustify this by using the name parameter somehow
73-
if( self.parameters.stateType == StateType.VEC3):
68+
if( self.parameters.material.stateType == StateType.VEC3):
7469
destinationPrefab.addObject("BarycentricMapping",
7570
output=destinationPrefab.linkpath,
7671
output_topology=destinationPrefab.geometry.container.linkpath,

stlib/geometries/__geometry__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,15 @@ def generateAttribute(self, parent : Geometry):
2626

2727

2828
class GeometryParameters(BaseParameters):
29-
mytype : type = BaseParameters
3029
name : str = "Geometry"
3130

32-
# Type of the highest degree element
33-
elementType : Optional[ElementType] = None
31+
elementType : Optional[ElementType] = None # Type of the highest degree element
3432
data : Optional[InternalDataProvider] = None
3533

3634
dynamicTopology : bool = False
3735

3836

3937
class Geometry(BasePrefab):
40-
# container : Object # This should be more specialized into the right SOFA type
41-
# modifier : Optional[Object]
4238

4339
parameters : GeometryParameters
4440

stlib/geometries/file.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from Sofa.Core import Node
66

77
class FileInternalDataProvider(InternalDataProvider):
8-
filename : str = "mesh/cube.obj"
8+
filename : str = "mesh/cube.obj" # This should be linked to FileParameters.filename
99

1010
def generateAttribute(self, parent : Geometry):
1111
loadMesh(parent, self.filename)
@@ -33,6 +33,5 @@ class FileParameters(GeometryParameters):
3333

3434
def model_post_init(self, __context):
3535
self.data = FileInternalDataProvider(filename=self.filename)
36-
37-
36+
3837

stlib/materials/__material__.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
11
from stlib.core.baseParameters import BaseParameters, Callable, Optional, Any
2-
from splib.core.utils import defaultValueType, DEFAULT_VALUE, isDefault
2+
from splib.core.utils import DEFAULT_VALUE
33
from splib.core.enum_types import StateType
44

55
from stlib.core.basePrefab import BasePrefab
66
from splib.mechanics.mass import addMass
77

8+
89
class MaterialParameters(BaseParameters):
910
name : str = "Material"
1011

12+
position : Optional[list[float]] = None
13+
1114
massDensity : float = DEFAULT_VALUE
1215
massLumping : bool = DEFAULT_VALUE
1316

1417
stateType : StateType = StateType.VEC3
1518

16-
addMaterial : Optional[Callable] = lambda node : addMass(node, elementType=None, massDensity=node.parameters.massDensity, lumping=node.parameters.massLumping)
19+
addMaterial : Optional[Callable] = lambda node : addMass(node,
20+
elementType=None,
21+
massDensity=node.parameters.massDensity,
22+
lumping=node.parameters.massLumping)
1723

1824

19-
# TODO : previously called Behavior
2025
class Material(BasePrefab):
2126

2227
parameters : MaterialParameters
2328

2429
def __init__(self, parameters: MaterialParameters):
2530
BasePrefab.__init__(self, parameters)
2631

27-
2832
def init(self):
29-
self.addObject("MechanicalObject", name="States", template=str(self.parameters.stateType))
33+
self.addObject("MechanicalObject", name="States", template=str(self.parameters.stateType),
34+
position = self.parameters.position if self.parameters.position is not None else "")
3035
self.parameters.addMaterial(self)

0 commit comments

Comments
 (0)