diff --git a/splib/Testing.py b/splib/Testing.py index ee0f8309..146b4096 100644 --- a/splib/Testing.py +++ b/splib/Testing.py @@ -57,10 +57,10 @@ def createScene(rootNode): addImplicitODE(SimulatedLiver1) addLinearSolver(SimulatedLiver1,iterative=False, template="CompressedRowSparseMatrixMat3x3") loadMesh(SimulatedLiver1,filename="mesh/liver.msh") - addDynamicTopology(SimulatedLiver1,type=ElementType.TETRAHEDRONS,source="@meshLoader") + addDynamicTopology(SimulatedLiver1,type=ElementType.TETRAHEDRA,source="@meshLoader") SimulatedLiver1.addObject("MechanicalObject",name="mstate", template='Vec3d') SimulatedLiver1.addObject("LinearSolverConstraintCorrection",name="constraintCorrection") - addLinearElasticity(SimulatedLiver1,ElementType.TETRAHEDRONS, poissonRatio="0.3", youngModulus="3000", method='large') + 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]) diff --git a/splib/core/enum_types.py b/splib/core/enum_types.py index 8968e2f5..5d450431 100644 --- a/splib/core/enum_types.py +++ b/splib/core/enum_types.py @@ -33,8 +33,8 @@ class ElementType(Enum): EDGES = 2 TRIANGLES = 3 QUADS = 4 - TETRAHEDRONS = 5 - HEXAHEDRONS = 6 + TETRAHEDRA = 5 + HEXAHEDRA = 6 class StateType(Enum): VEC3 = 3 diff --git a/splib/mechanics/hyperelasticity.py b/splib/mechanics/hyperelasticity.py index 27c30635..fb255df4 100644 --- a/splib/mechanics/hyperelasticity.py +++ b/splib/mechanics/hyperelasticity.py @@ -6,7 +6,7 @@ @ReusableMethod def addHyperelasticity(node,elem:ElementType,materialName=DEFAULT_VALUE, parameterSet=DEFAULT_VALUE, matrixRegularization=DEFAULT_VALUE,**kwargs): match elem: - case ElementType.TETRAHEDRONS: + case ElementType.TETRAHEDRA: node.addObject("TetrahedronHyperelasticityFEMForceField",name="constitutiveLaw", materialName=materialName, parameterSet=parameterSet, matrixRegularization=matrixRegularization, **kwargs) return case _: diff --git a/splib/mechanics/linear_elasticity.py b/splib/mechanics/linear_elasticity.py index 33de6189..1e9b3029 100644 --- a/splib/mechanics/linear_elasticity.py +++ b/splib/mechanics/linear_elasticity.py @@ -4,22 +4,22 @@ @ReusableMethod -def addLinearElasticity(node,elem:ElementType,youngModulus=DEFAULT_VALUE, poissonRatio=DEFAULT_VALUE, method=DEFAULT_VALUE,**kwargs): - match elem: +def addLinearElasticity(node, elementType:ElementType, youngModulus=DEFAULT_VALUE, poissonRatio=DEFAULT_VALUE, method=DEFAULT_VALUE, **kwargs): + match elementType: case ElementType.EDGES: - node.addObject("BeamFEMForceField",name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method, **kwargs) + node.addObject("BeamFEMForceField", name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method, **kwargs) return case ElementType.TRIANGLES: - node.addObject("TriangleFEMForceField",name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method,**kwargs) + node.addObject("TriangleFEMForceField", name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method, **kwargs) return case ElementType.QUADS: - node.addObject("QuadBendingFEMForceField",name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method,**kwargs) + node.addObject("QuadBendingFEMForceField", name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method, **kwargs) return - case ElementType.TETRAHEDRONS: - node.addObject("TetrahedronFEMForceField",name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method,**kwargs) + case ElementType.TETRAHEDRA: + node.addObject("TetrahedronFEMForceField", name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method, **kwargs) return - case ElementType.HEXAHEDRONS: - node.addObject("HexahedronFEMForceField",name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method,**kwargs) + case ElementType.HEXAHEDRA: + node.addObject("HexahedronFEMForceField", name="constitutiveLaw", youngModulus=youngModulus, poissonRatio=poissonRatio, method=method, **kwargs) return case _: print('Linear elasticity is only available for topology of type EDGES, TRIANGLES, QUADS, TETRAHEDRON, HEXAHEDRON') diff --git a/splib/mechanics/mass.py b/splib/mechanics/mass.py index 2dc8dbdf..917fc936 100644 --- a/splib/mechanics/mass.py +++ b/splib/mechanics/mass.py @@ -5,15 +5,15 @@ # TODO : use the massDensity only and deduce totalMass if necessary from it + volume @ReusableMethod -def addMass(node,template,totalMass=DEFAULT_VALUE,massDensity=DEFAULT_VALUE,lumping=DEFAULT_VALUE,**kwargs): +def addMass(node, template, totalMass=DEFAULT_VALUE, massDensity=DEFAULT_VALUE, lumping=DEFAULT_VALUE, **kwargs): if (not isDefault(totalMass)) and (not isDefault(massDensity)) : print("[warning] You defined the totalMass and the massDensity in the same time, only taking massDensity into account") kwargs.pop('massDensity') - if(template=="Rigid3"): - node.addObject("UniformMass",name="mass", totalMass=totalMass, massDensity=massDensity, lumping=lumping, **kwargs) - else: - node.addObject("MeshMatrixMass",name="mass", totalMass=totalMass, massDensity=massDensity, lumping=lumping, **kwargs) + # if(template=="Rigid3"): + node.addObject("UniformMass",name="mass", totalMass=totalMass, massDensity=massDensity, lumping=lumping, **kwargs) + # else: + # node.addObject("MeshMatrixMass",name="mass", totalMass=totalMass, massDensity=massDensity, lumping=lumping, **kwargs) diff --git a/splib/topology/dynamic.py b/splib/topology/dynamic.py index c84bd185..1f18be29 100644 --- a/splib/topology/dynamic.py +++ b/splib/topology/dynamic.py @@ -41,9 +41,8 @@ def addHexahedronTopology(node,position=DEFAULT_VALUE,edges=DEFAULT_VALUE,quads= node.addObject("HexahedronSetTopologyContainer", name="container", src=source, position=position, edges=edges, quads=quads, hexahedra=hexahedra, **kwargs) # node.addObject("HexahedronSetGeometryAlgorithms", name="algorithms",**kwargs) -def addDynamicTopology(node,type:ElementType,**kwargs): - - match type: +def addDynamicTopology(node, elementType:ElementType, **kwargs): + match elementType: case ElementType.POINTS: addPointTopology(node,**kwargs) return @@ -56,12 +55,13 @@ def addDynamicTopology(node,type:ElementType,**kwargs): case ElementType.QUADS: addQuadTopology(node,**kwargs) return - case ElementType.TETRAHEDRONS: + case ElementType.TETRAHEDRA: addTetrahedronTopology(node,**kwargs) return - case ElementType.HEXAHEDRONS: + case ElementType.HEXAHEDRA: addHexahedronTopology(node,**kwargs) return case _: - print('Topology type should be one of the following : "ElementType.POINTS, ElementType.EDGES, ElementType.TRIANGLES, ElementType.QUADS, ElementType.TETRAHEDRONS, ElementType.HEXAHEDRONS" ') + print('Topology type should be one of the following : "ElementType.POINTS, ElementType.EDGES, ElementType.TRIANGLES, ElementType.QUADS, ElementType.TETRAHEDRA, ElementType.HEXAHEDRA" ') return + \ No newline at end of file diff --git a/splib/topology/loader.py b/splib/topology/loader.py index b040491a..c4f54b01 100644 --- a/splib/topology/loader.py +++ b/splib/topology/loader.py @@ -13,7 +13,7 @@ def loadMesh(node,filename,**kwargs): elif splitedName[-1] == "sph": return node.addObject("SphereLoader", name="loader",filename=filename, **kwargs) else: - return node.addObject("Mesh"+splitedName[-1].upper()+"Loader", name="loader",filename=filename, **kwargs) + return node.addObject("Mesh"+splitedName[-1].upper()+"Loader", name="loader", filename=filename, **kwargs) else: print('[Error] : File extension ' + splitedName[-1] + ' not recognised.') diff --git a/splib/topology/static.py b/splib/topology/static.py index 2e49fcf9..59146042 100644 --- a/splib/topology/static.py +++ b/splib/topology/static.py @@ -2,6 +2,6 @@ from splib.core.utils import DEFAULT_VALUE @ReusableMethod -def addStaticTopology(node,source=DEFAULT_VALUE,**kwargs): +def addStaticTopology(node, source=DEFAULT_VALUE, **kwargs): node.addObject("MeshTopology", name="container", src=source, **kwargs) diff --git a/stlib/README.md b/stlib/README.md index fd71c6d1..c4e7dd90 100644 --- a/stlib/README.md +++ b/stlib/README.md @@ -6,12 +6,12 @@ | -------------- | -------------------------------------------------------------- | | Component* | Element of the scene hierarchy implementing a given behavior | | ~~Object~~ | A deprecated synonym of a Component | -| Node* | Element of the scene hierarchy holding other Node (often refered as childs) or Objects | +| Node* | Element of the scene hierarchy holding other Nodes (often refered as childs) or Components | | Data* | Attribute of a Component or a Node | -| Prefab | A Sofa.Node assembling of Objects and Nodes (a "fragment" of a scene) | -| Geometry | A prefab that describe shapes with their topologies (i.e a shape with its space descritization and its associated connectivity) | -| Entity | A physical prefab that represents real-world properties and behaviors used in a simulation. An entity should always have a geometry but includes neither a linear solver nor an integration scheme.| -| Parameters | Every prefab has a set of parameters. These parameters can contain data, links, callable or being composed of other parameters. Some of them can be optional. ~~Must inherit from `stlib.core.baseParameter.BaseParameter` and have a `@dataclasses.dataclass` decorator~~. Must have a `@stlib.parameters` decorator. | +| Prefab | A Node assembling of Components and Nodes (a "fragment" of a scene) | +| Geometry | A Prefab that describes shapes with their topologies (i.e a shapes with their space descritization and their associated connectivity) | +| Entity | A physical Prefab that represents real-world properties and behaviors used in a simulation. An entity should always have a geometry but includes neither a linear solver nor an integration scheme.| +| Parameters | Every Prefab has a set of parameters. These parameters can contain data, links, callable or being composed of other parameters. Some of them can be optional. ~~Must inherit from `stlib.core.baseParameter.BaseParameter` and have a `@dataclasses.dataclass` decorator~~. Must have a `@stlib.parameters` decorator. | \*Defined in SOFA documentation [here](https://www.sofa-framework.org/doc/using-sofa/terminology). diff --git a/stlib/core/baseParameters.py b/stlib/core/baseParameters.py index 883b17b5..be7a633b 100644 --- a/stlib/core/baseParameters.py +++ b/stlib/core/baseParameters.py @@ -6,7 +6,7 @@ @dataclasses.dataclass class BaseParameters(object): - name : str = "object" + name : str = "Object" kwargs : dict = dataclasses.field(default_factory=dict) def toDict(self): diff --git a/stlib/entities/__entity__.py b/stlib/entities/__entity__.py index f1737532..f39d8b4e 100644 --- a/stlib/entities/__entity__.py +++ b/stlib/entities/__entity__.py @@ -1,4 +1,5 @@ from stlib.core.baseParameters import BaseParameters +from stlib.core.basePrefab import BasePrefab from stlib.prefabs.collision import CollisionParameters, Collision from stlib.prefabs.visual import VisualParameters, Visual from stlib.prefabs.material import Material, MaterialParameters @@ -14,9 +15,9 @@ @dataclasses.dataclass class EntityParameters(BaseParameters): - name = "Entity" + name : str = "Entity" - template : StateType = None + stateType : StateType = StateType.VEC3 ### QUID addCollision : Optional[Callable] = lambda x : Collision(CollisionParameters()) @@ -41,38 +42,40 @@ class Entity(BasePrefab): def __init__(self, parameters=EntityParameters(), **kwargs): - Sofa.Core.Node.__init__(self, name=parameters.name) - - + BasePrefab.__init__(self, parameters) self.parameters = parameters self.geometry = self.add(Geometry, self.parameters.geometry) ### Check compatilibility of Material - if self.parameters.material.stateType != self.parameters.template: + if self.parameters.material.stateType != self.parameters.stateType: print("WARNING: imcompatibility between templates of both the entity and the material") - self.parameters.material.stateType = self.parameters.template + self.parameters.material.stateType = self.parameters.stateType - self.material = self.add(Material,self.parameters.material) + self.material = self.add(Material, self.parameters.material) + self.material.States.position.parent = self.geometry.container.position.linkpath if self.parameters.collision is not None: - self.collision = self.add(Collision,self.parameters.collision) - self.addMapping(self.parameters.collision, self.collision) - + self.collision = self.add(Collision, self.parameters.collision) + self.addMapping(self.collision) if self.parameters.visual is not None: - self.visual = self.add(Visual,self.parameters.visual) - self.addMapping(self.parameters.visual, self.visual) + self.visual = self.add(Visual, self.parameters.visual) + self.addMapping(self.visual) - def addMapping(self, destParameter, destPrefab): + def addMapping(self, destinationPrefab): - templateString = f'{self.parameters.template},{destParameter.template}' + template = f'{self.parameters.stateType},Vec3' # TODO: check that it is always true - if( self.parameters.template == StateType.VEC3): - if isinstance(destParameter.geometry,ExtractParameters): - destPrefab.addObject("IdentityMapping", input="@../material/", output="@.", template=templateString) - else : - destPrefab.addObject("BarycentricMapping", input="@../material/", output="@.", template=templateString) + if( self.parameters.stateType == StateType.VEC3): + destinationPrefab.addObject("BarycentricMapping", + input="@../Material/", + input_topology=destinationPrefab.geometry.container.linkpath, + output="@.", + template=template) else: - destPrefab.addObject("RigidMapping", input="@../material", output="@.", template=templateString) + destinationPrefab.addObject("RigidMapping", + input="@../Material", + output="@.", + template=template) diff --git a/stlib/entities/deformable/__deformable__.py b/stlib/entities/deformable/__deformable__.py index 7213d6a7..1c73b2d9 100644 --- a/stlib/entities/deformable/__deformable__.py +++ b/stlib/entities/deformable/__deformable__.py @@ -1,71 +1,46 @@ -from stlib.entities import Entity, EntityParameters -from stlib.prefabs.material import Material, MaterialParameters -from stlib.prefabs.visual import Visual -from splib.core.enum_types import ConstitutiveLaw, ElementType, StateType +from stlib.prefabs.material import MaterialParameters +from splib.core.enum_types import ConstitutiveLaw, ElementType +from stlib.core.baseParameters import Callable, Optional, dataclasses from splib.mechanics.linear_elasticity import * from splib.mechanics.hyperelasticity import * from splib.mechanics.mass import addMass +@dataclasses.dataclass class DeformableBehaviorParameters(MaterialParameters): - constitutiveLawType : ConstitutiveLaw = None - elementType : ElementType = None - parameters : list[float] = None - - def addMaterial(self, node): - - addMass(node, str(node.stateType), massDensity=node.massDensity, lumping=node.massLumping) + constitutiveLawType : ConstitutiveLaw = ConstitutiveLaw.ELASTIC + elementType : ElementType = ElementType.TETRAHEDRA + parameters : list[float] = dataclasses.field(default_factory=lambda: [1000, 0.45]) # young modulus, poisson ratio + def addDeformableMaterial(node): + addMass(node, node.parameters.stateType, massDensity=node.parameters.massDensity, lumping=node.parameters.massLumping) # TODO : change this with inheritance - if(self.constitutiveLawType == ConstitutiveLaw.HYPERELASTIC): - addHyperelasticity(node,self.elementType, self.parameters) + if(node.parameters.constitutiveLawType == ConstitutiveLaw.HYPERELASTIC): + addHyperelasticity(node, node.parameters.elementType, node.parameters.parameters, topology="@../Geometry/container") else: - addLinearElasticity(node,self.elementType, self.parameters[0], self.parameters[1]) - - -# class Deformable(Entity): - -# params : DeformableParameters - -# @staticmethod -# def getParameters(**kwargs) -> DeformableParameters: -# return DeformableParameters(**kwargs) - - -# def __init__(self, params : DeformableParameters, **kwargs): -# Entity.__init__(self, **kwargs) - -# self.__addConstitutiveLaw__() -# self.addCollision(params.collision) - - -# #@customizable -# # Need generic way of defining paramaters (linear/hyper...) -# def __addConstitutiveLaw__(self): -# self.params.addConstitutiveLaw() - - -# #@customizable -# def __addVisual__(self): -# #Extract surface and add identity mapping -# self.add(Visual, self.params.visual) - - + addLinearElasticity(node, node.parameters.elementType, node.parameters.parameters[0], node.parameters.parameters[1], topology="@../Geometry/container") + addMaterial : Optional[Callable] = addDeformableMaterial def createScene(root) : - + from stlib.entities import Entity, EntityParameters + from stlib.prefabs.visual import VisualParameters + from stlib.geometry.extract import ExtractParameters from stlib.geometry.file import FileParameters - # from stlib.geometry.extract import ExtractParameters - liverParameters = EntityParameters() - liverParameters.template = StateType.VEC3 - liverParameters.material = DeformableBehaviorParameters() - liverParameters.material.stateType = StateType.VEC3 - liverParameters.material.constitutiveLawType = ConstitutiveLaw.ELASTIC - liverParameters.material.parameters = [1000, 0.45] - liverParameters.geometry = FileParameters("liver.vtk") - # liverParameters.visual = ExtractParameters() - myDeformableObject = root.add(Entity, liverParameters) \ No newline at end of file + root.addObject("VisualStyle", displayFlags=["showBehavior"]) + + bunnyParameters = EntityParameters() + bunnyParameters.geometry = FileParameters(filename="mesh/Bunny.vtk") + bunnyParameters.geometry.elementType = ElementType.TETRAHEDRA # TODO: this is required by extract.py. Should it be done automatically in geometry.py ? + bunnyParameters.material = DeformableBehaviorParameters() + bunnyParameters.material.constitutiveLawType = ConstitutiveLaw.ELASTIC + bunnyParameters.material.parameters = [1000, 0.45] + bunnyParameters.visual = VisualParameters() + # bunnyParameters.visual.geometry = ExtractParameters(sourceParameters=bunnyParameters.geometry, + # destinationType=ElementType.TRIANGLES) + bunnyParameters.visual.geometry = FileParameters(filename="mesh/Bunny.stl") + bunnyParameters.visual.color = [1, 1, 1, 0.5] + bunny = root.add(Entity, bunnyParameters) \ No newline at end of file diff --git a/stlib/geometry/__geometry__.py b/stlib/geometry/__geometry__.py index 8b63a20c..085215da 100644 --- a/stlib/geometry/__geometry__.py +++ b/stlib/geometry/__geometry__.py @@ -13,11 +13,11 @@ class Geometry(BasePrefab):... class InternalDataProvider(object): position : Any = None # Topology information - edges : Any = DEFAULT_VALUE - triangles : Any = DEFAULT_VALUE - quads : Any = DEFAULT_VALUE - tetrahedra : Any = DEFAULT_VALUE - hexahedra : Any = DEFAULT_VALUE + edges : Any = DEFAULT_VALUE + triangles : Any = DEFAULT_VALUE + quads : Any = DEFAULT_VALUE + tetrahedra : Any = DEFAULT_VALUE + hexahedra : Any = DEFAULT_VALUE def generateAttribute(self, parent : Geometry): pass @@ -25,6 +25,7 @@ def generateAttribute(self, parent : Geometry): @dataclasses.dataclass class GeometryParameters(BaseParameters): + name : str = "Geometry" # Type of the highest degree element elementType : Optional[ElementType] = None @@ -34,20 +35,33 @@ class GeometryParameters(BaseParameters): class Geometry(BasePrefab): - container : Object # This should be more specialized into the right SOFA type - modifier : Optional[Object] - - params : GeometryParameters - - def __init__(self, params: GeometryParameters): - BasePrefab.__init__(self, params) - self.params = params - if params.data is not None : - params.data.generateAttribute(self) - if(params.dynamicTopology): - if(params.elementType is not None): - addDynamicTopology(self, container = dataclasses.asdict(params.data)) + # container : Object # This should be more specialized into the right SOFA type + # modifier : Optional[Object] + + parameters : GeometryParameters + + def __init__(self, parameters: GeometryParameters): + + BasePrefab.__init__(self, parameters) + + self.parameters = parameters + + # Generate attribute (positions, edges, triangles, quads, tetrahedra, hexahedra) from the internal data provider + if parameters.data is not None : + parameters.data.generateAttribute(self) + if parameters.dynamicTopology : + if parameters.elementType is not None : + addDynamicTopology(self, container = dataclasses.asdict(parameters.data)) else: raise ValueError else: - addStaticTopology(self, container = dataclasses.asdict(params.data)) + addStaticTopology(self, + container = + { + "position": parameters.data.position, + "edges": parameters.data.edges, + "triangles": parameters.data.triangles, + "quads": parameters.data.quads, + "tetrahedra": parameters.data.tetrahedra, + "hexahedra": parameters.data.hexahedra + }) diff --git a/stlib/geometry/extract.py b/stlib/geometry/extract.py index b7b17cbc..c2024966 100644 --- a/stlib/geometry/extract.py +++ b/stlib/geometry/extract.py @@ -4,45 +4,56 @@ from splib.topology.loader import loadMesh from splib.core.enum_types import ElementType +import Sofa from Sofa.Core import Node class ExtractInternalDataProvider(InternalDataProvider): - destElementType : ElementType - fromElemenType : ElementType - fromNodeName : str + destinationType : ElementType + sourceType : ElementType + sourceName : str - def __init__(self, destElementType : ElementType, fromElementType : ElementType, fromNodeName : str): - self.destElementType = destElementType, - self.fromElementType = fromElementType, - self.fromNodeName = fromNodeName + def __init__(self, destinationType : ElementType, sourceType : ElementType, sourceName : str): + self.destinationType = destinationType + self.sourceType = sourceType + self.sourceName = sourceName def __post_init__(self): - if(not (self.fromElementType == ElementType.TETRAHEDRONS and self.destElementType == ElementType.TRIANGLES) - and not (self.fromElementType == ElementType.HEXAHEDRONS and self.destElementType == ElementType.QUADS) ): - raise ValueError("Only configuration possible are 'Tetrahedrons to Triangles' and 'Hexahedrons to Quads'") + if(not (self.sourceType == ElementType.TETRAHEDRA and self.destinationType == ElementType.TRIANGLES) + and not (self.sourceType == ElementType.HEXAHEDRA and self.destinationType == ElementType.QUADS) ): + raise ValueError("Only configuration possible are 'Tetrahedra to Triangles' and 'Hexahedra to Quads'") InternalDataProvider.__init__(self) - def generateAttribute(self, parent : Geometry): - tmn = parent.addChild("TopologicalMappingNode") + node = parent.addChild("ExtractedGeometry") #TODO: Specify somewhere in the doc that this should only be used for mapped topologies that extract parent topology surface - - fromLink = parent.parents[0].parents[0].getChild(self.fromNodeName).container.linkpath - addDynamicTopology(tmn, type=self.destElementType) - if self.fromElementType == ElementType.TETRAHEDRONS: - tmn.addObject("Tetra2TriangleTopologicalMapping", input=fromLink, output=tmn.container.linkpath) - elif self.fromElementType == ElementType.HEXAHEDRONS: - tmn.addObject("Hexa2QuadTopologicalMapping", input=fromLink, output=tmn.container.linkpath) + # fromLink = parent.parents[0].parents[0].getChild(self.SourceName).container.linkpath + # TODO: the line above cannot work if the nodes and objects are not added to the graph prior the end of __init__() call + # !!! also, on a fail, nothing is added to the graph, which makes things harder to debug + # !!! also, does not work because of the function canCreate(), which checks the input (not yet created?) + # this is all related + fromLink = "@../../Geometry.container" # TODO: can we do better than this? + addDynamicTopology(node, elementType=self.sourceType) + if self.sourceType == ElementType.TETRAHEDRA: + node.addObject("Tetra2TriangleTopologicalMapping", input=fromLink, output=node.container.linkpath) + elif self.sourceType == ElementType.HEXAHEDRA: + node.addObject("Hexa2QuadTopologicalMapping", input=fromLink, output=node.container.linkpath) + else: + Sofa.msg_error("[stlib/geometry/exctrat.py]", "Element type: " + str(self.sourceType) + " not supported.") - self.position = tmn.container.position.linkpath - self.edges = tmn.container.edges.linkpath - self.triangles = tmn.container.triangles.linkpath - self.quads = tmn.container.quads.linkpath - self.hexahedra = tmn.container.hexahedra.linkpath - self.tetrahedra = tmn.container.tetras.linkpath + self.position = node.container.position.linkpath + if node.container.findData("edges") is not None: + self.edges = node.container.edges.linkpath + if node.container.findData("triangles") is not None: + self.triangles = node.container.triangles.linkpath + if node.container.findData("quads") is not None: + self.quads = node.container.quads.linkpath + if node.container.findData("hexahedra") is not None: + self.hexahedra = node.container.hexahedra.linkpath + if node.container.findData("tetras") is not None: + self.tetrahedra = node.container.tetras.linkpath @@ -50,11 +61,11 @@ class ExtractParameters(GeometryParameters): def __init__(self, sourceParameters : GeometryParameters, destinationType : ElementType, - dynamicTopology = False, ): + dynamicTopology : bool = False, ): GeometryParameters.__init__(self, - data = ExtractInternalDataProvider(destElementType = sourceParameters, - fromElementType = destinationType, - fromNodeName = destinationType.name), + data = ExtractInternalDataProvider(destinationType = destinationType, + sourceType = sourceParameters.elementType, + sourceName = sourceParameters.name), dynamicTopology = dynamicTopology, elementType = destinationType) diff --git a/stlib/geometry/file.py b/stlib/geometry/file.py index 70738b02..cc51c077 100644 --- a/stlib/geometry/file.py +++ b/stlib/geometry/file.py @@ -12,7 +12,7 @@ class FileInternalDataProvider(InternalDataProvider): def __post_init__(self, **kwargs): InternalDataProvider.__init__(self,**kwargs) - def generateAttribute(self, parent : Geometry): + def generateAttribute(self, parent : Geometry): loadMesh(parent, self.filename) self.position = str(parent.loader.position.linkpath) @@ -25,9 +25,10 @@ def generateAttribute(self, parent : Geometry): class FileParameters(GeometryParameters): + def __init__(self, filename, dynamicTopology = False, elementType : ElementType = None ): GeometryParameters.__init__(self, - data = FileInternalDataProvider(filename), + data = FileInternalDataProvider(filename=filename), dynamicTopology = dynamicTopology, elementType = elementType) diff --git a/stlib/misc/entity.py b/stlib/misc/entity.py index fbf70243..a3549b08 100644 --- a/stlib/misc/entity.py +++ b/stlib/misc/entity.py @@ -66,7 +66,7 @@ def addMapping(self, **kwargs): self.addObject("RigidMapping", name="mapping", **kwargs) class CollisionModel(Sofa.Core.BasePrefab): - def __init__(self, params, **kwargs): + def __init__(self, parameters, **kwargs): Sofa.Core.Node.__init__(self, **kwargs) class Parameters(object): diff --git a/stlib/misc/softrobots.py b/stlib/misc/softrobots.py index d8e7871a..aca8ab50 100644 --- a/stlib/misc/softrobots.py +++ b/stlib/misc/softrobots.py @@ -12,10 +12,10 @@ class Trunk(Sofa.Core.BasePrefab): body : Entity.Deformable cables : list [SoftRobots.Cable] - def __init__(self, params): + def __init__(self, parameters): body = Entity.Deformable() - for param in range(params.cables): + for param in range(parameters.cables): cables.append(SoftRobots.Cable(body, param)) class Parameters(object): diff --git a/stlib/misc/test-1.py b/stlib/misc/test-1.py index 289f1ff0..2b898703 100644 --- a/stlib/misc/test-1.py +++ b/stlib/misc/test-1.py @@ -15,39 +15,39 @@ def createScene(root): #root.add(entity.Deformable) #root.addChild(entity2.Deformable(root)) - params = entity.Deformable.Parameters() - params.name = "Deformable2" - root.add(entity.Deformable, params=auto_load) + parameters = entity.Deformable.Parameters() + parameters.name = "Deformable2" + root.add(entity.Deformable, parameters=auto_load) #def addCustomVisual(self, **kwargs): # Rigid.addVisualModel( mapping={"toto":"in"} ) - #params = Entity.Parameters() - #params.addVisualModel = addCustomVisual - #root.add(Entity, params) + #parameters = Entity.Parameters() + #parameters.addVisualModel = addCustomVisual + #root.add(Entity, parameters) #  - #params = Rigid.new_parameters() - #params.mass = 4.5 - #root.add(Entity, params) + #parameters = Rigid.new_parameters() + #parameters.mass = 4.5 + #root.add(Entity, parameters) #root.add(Entity) - #params.addVisualModelOverride = addCustomVisual + #parameters.addVisualModelOverride = addCustomVisual ###  #Entity._addVisualModel = addCustomVisual - #root.add(Entity, params) + #root.add(Entity, parameters) #root.add(Entity.Rigid) #root.add(Entity.Deformable) #root.add(Entity) - #root.add(VisualModel, params) + #root.add(VisualModel, parameters) #root.add(VisualModel) - #params = Entity.Deformable.Parameters() - #params.visual = None - #a = root.add(Entity.Deformable, params) + #parameters = Entity.Deformable.Parameters() + #parameters.visual = None + #a = root.add(Entity.Deformable, parameters) return root \ No newline at end of file diff --git a/stlib/misc/test2.py b/stlib/misc/test2.py index 4dc12d14..cdebfad1 100644 --- a/stlib/misc/test2.py +++ b/stlib/misc/test2.py @@ -19,7 +19,7 @@ def myAddObject(self : Sofa.Core.Node, tname, **kwargs): Sofa.Core.Node.addObject = myAddObject -def myAdd(self : Sofa.Core.Node, c, params = PrefabParameters(), **kwargs): +def myAdd(self : Sofa.Core.Node, c, parameters = PrefabParameters(), **kwargs): def findName(cname, node): """Compute a working unique name in the node""" rname = cname @@ -30,14 +30,14 @@ def findName(cname, node): return rname for k,v in kwargs.items(): - if hasattr(params, k): - setattr(params, k, v) + if hasattr(parameters, k): + setattr(parameters, k, v) - params = copy.copy(params) - if params.name in self.children: - params.name = findName(params.name, self) + parameters = copy.copy(parameters) + if parameters.name in self.children: + parameters.name = findName(parameters.name, self) - return c(parent = self, parameters=params) + return c(parent = self, parameters=parameters) Sofa.Core.Node.add = myAdd def createScene(root): @@ -47,19 +47,19 @@ def createScene(root): # self.addObject("EulerExplicitSolver", name="numericalintegration") # self.addObject("LinearSolver", name="numericalsolver", firstOrder=True) - params = EntityParameters() - params.simulation.iterations = 10 - params.simulation.integration["rayleighStiffness"] = 2.0 - params.addSimulation = entity.NONE + parameters = EntityParameters() + parameters.simulation.iterations = 10 + parameters.simulation.integration["rayleighStiffness"] = 2.0 + parameters.addSimulation = entity.NONE - params.mechanical["template"] = "Rigid3" + parameters.mechanical["template"] = "Rigid3" - #params.simulation.integration["rayleightStiffnessXXX"] = 2.0 + #parameters.simulation.integration["rayleightStiffnessXXX"] = 2.0 - #params.solver.kwargs["numericalintegration"] = { "firstOrder" : True } + #parameters.solver.kwargs["numericalintegration"] = { "firstOrder" : True } - root.add(Entity, params) - root.add(Entity, params) - root.add(Entity, params) + root.add(Entity, parameters) + root.add(Entity, parameters) + root.add(Entity, parameters) #root.add(Simulation, name="mySimulation") diff --git a/stlib/prefabs/collision.py b/stlib/prefabs/collision.py index 218c0c46..da603146 100644 --- a/stlib/prefabs/collision.py +++ b/stlib/prefabs/collision.py @@ -9,6 +9,8 @@ @dataclasses.dataclass class CollisionParameters(BaseParameters): + name : str = "Collision" + primitives : list[CollisionPrimitive] = dataclasses.field(default_factory = lambda :[CollisionPrimitive.TRIANGLES]) selfCollision : Optional[bool] = DEFAULT_VALUE @@ -20,18 +22,18 @@ class CollisionParameters(BaseParameters): class Collision(BasePrefab): - def __init__(self, params: CollisionParameters): - BasePrefab.__init__(self, params) + def __init__(self, parameters: CollisionParameters): + BasePrefab.__init__(self, parameters) - geom = self.add(Geometry, params.geometry) + geom = self.add(Geometry, parameters.geometry) - self.addObject("MechanicalObject", template="Vec3", position=f"@{params.geometry.name}/container.position") - for primitive in params.primitives: + self.addObject("MechanicalObject", template="Vec3", position=f"@{parameters.geometry.name}/container.position") + for primitive in parameters.primitives: addCollisionModels(self, primitive, - topology=f"@{params.geometry.name}/container", - selfCollision=params.selfCollision, - group=params.group, - **params.kwargs) + topology=f"@{parameters.geometry.name}/container", + selfCollision=parameters.selfCollision, + group=parameters.group, + **parameters.kwargs) @staticmethod @@ -44,14 +46,14 @@ def createScene(root): root.addObject("VisualStyle", displayFlags="showCollisionModels") # Create a visual from a mesh file - params = Collision.getParameters() - params.group = 1 - params.geometry = FileParameters(filename="mesh/cube.obj") + parameters = Collision.getParameters() + parameters.group = 1 + parameters.geometry = FileParameters(filename="mesh/cube.obj") # Expert parameters - # params.kwargs = { + # parameters.kwargs = { # "TriangleCollisionModel":{"contactStiffness": 100.0, "contactFriction": 0.5} # } - collision = root.add(Collision, params) + collision = root.add(Collision, parameters) # OR set the parameters post creation # collision.TriangleCollisionModel.contactStiffness = 100.0 diff --git a/stlib/prefabs/material.py b/stlib/prefabs/material.py index 3102f0e3..07009f42 100644 --- a/stlib/prefabs/material.py +++ b/stlib/prefabs/material.py @@ -1,28 +1,30 @@ from stlib.core.baseParameters import BaseParameters, Callable, Optional, dataclasses, Any -from splib.core.enum_types import * +from splib.core.utils import defaultValueType, DEFAULT_VALUE, isDefault +from splib.core.enum_types import StateType + from stlib.core.basePrefab import BasePrefab from splib.mechanics.mass import addMass @dataclasses.dataclass class MaterialParameters(BaseParameters): - name: str = "Material" + name : str = "Material" - massDensity: float = 0 - massLumping: bool = False + massDensity : float = DEFAULT_VALUE + massLumping : bool = DEFAULT_VALUE - stateType: StateType = StateType.VEC3 + stateType : StateType = StateType.VEC3 - addMaterial : Optional[Callable] = lambda node : addMass(node, node.stateType, massDensity=node.massDensity, lumping=node.massLumping) + addMaterial : Optional[Callable] = lambda node : addMass(node, node.parameters.stateType, massDensity=node.parameters.massDensity, lumping=node.parameters.massLumping) # TODO : previously called Behavior class Material(BasePrefab): - def __init__(self, params: MaterialParameters): - BasePrefab.__init__(self, params) - self.params = params + parameters : MaterialParameters - self.addObject("MechanicalObject", name="States", template=str(self.params.stateType)) + def __init__(self, parameters: MaterialParameters): + BasePrefab.__init__(self, parameters) + self.parameters = parameters - if(params.addMaterial is not None): - params.addMaterial(self) + self.addObject("MechanicalObject", name="States", template=str(parameters.stateType)) + parameters.addMaterial(self) diff --git a/stlib/prefabs/visual.py b/stlib/prefabs/visual.py index 2326b5d1..bbc34bee 100644 --- a/stlib/prefabs/visual.py +++ b/stlib/prefabs/visual.py @@ -1,29 +1,27 @@ from stlib.core.basePrefab import BasePrefab -from stlib.core.baseParameters import BaseParameters, Callable, Optional, dataclasses, Any +from stlib.core.baseParameters import BaseParameters, Optional, dataclasses, Any from stlib.geometry import Geometry, GeometryParameters -from stlib.geometry.extract import ExtractParameters from stlib.geometry.file import FileParameters -from splib.core.enum_types import ElementType +from splib.core.utils import DEFAULT_VALUE from Sofa.Core import Object @dataclasses.dataclass class VisualParameters(BaseParameters): - color : Optional[list[float]] = None - texture : Optional[str] = None + name : str = "Visual" + + color : Optional[list[float]] = DEFAULT_VALUE + texture : Optional[str] = DEFAULT_VALUE geometry : GeometryParameters = dataclasses.field(default_factory = lambda : GeometryParameters()) class Visual(BasePrefab): - def __init__(self, params: VisualParameters): - BasePrefab.__init__(self, params) - geom = self.add(Geometry, params.geometry) - # TODO : handle optional color, texture using DEFAULT_VALUE mechanism (as implemented by Paul) - self.addObject("OglModel", color=params.color, src=geom.container.linkpath) + def __init__(self, parameters: VisualParameters): + BasePrefab.__init__(self, parameters) - if params.addMapping is not None: - params.addMapping(self) + self.geometry = self.add(Geometry, parameters.geometry) + self.addObject("OglModel", color=parameters.color, src=self.geometry.container.linkpath) @staticmethod def getParameters(**kwargs) -> VisualParameters: @@ -33,14 +31,7 @@ def getParameters(**kwargs) -> VisualParameters: def createScene(root): # Create a visual from a mesh file - params = Visual.getParameters() - params.name = "VisualFromFile" - params.geometry = FileParameters(filename="mesh/cube.obj") - root.add(Visual, params) - - # # Create a visual from a node - # params = Visual.getParameters() - # params.name = "ExtractedVisual" - # params.geometry = ExtractParameters(sourceParameters=FileParameters(filename="mesh/cube.vtk"), - # destinationType=ElementType.TRIANGLES) - # root.add(Visual, params) + parameters = Visual.getParameters() + parameters.name = "LiverVisual" + parameters.geometry = FileParameters(filename="mesh/liver.obj") + root.add(Visual, parameters) \ No newline at end of file