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
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,18 @@ void bindCompressedRowSparseMatrixConstraint(pybind11::module& m)

crsmc.def_property_readonly("value", [](sofa::Data<MatrixDeriv>& self)
{
sofa::helper::ReadAccessor accessor(self);
sofa::helper::WriteAccessor accessor(self);
accessor.wref().compress();
return toEigen(accessor.ref());
});

crsmc.def("add", [](sofa::Data<MatrixDeriv>& self, sofa::Index row, sofa::Index col, const typename MatrixDeriv::Block value)
{
sofa::helper::WriteAccessor accessor(self);
auto line = accessor->writeLine(row);
line.addCol(col, value);
});

PythonFactory::registerType(MatrixDeriv::Name(), [](sofa::core::BaseData* data) -> py::object {
auto matrix = reinterpret_cast<sofa::Data<MatrixDeriv>*>(data);
return py::cast(matrix);
Expand Down
8 changes: 4 additions & 4 deletions examples/stlib/SofaScene.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from stlib.geometries.plane import PlaneParameters
from stlib.geometries.file import FileParameters
from stlib.geometries.extract import ExtractParameters
from stlib.materials.deformable import DeformableBehaviorParameters
from stlib.materials.deformable import DeformableMaterialParameters
from stlib.collision import Collision, CollisionParameters
from stlib.entities import Entity, EntityParameters
from stlib.visual import Visual, VisualParameters
Expand Down Expand Up @@ -66,7 +66,7 @@ def createScene(root):

LogoParams = EntityParameters(name = "Logo",
geometry = FileParameters(filename="mesh/SofaScene/Logo.vtk"),
material = DeformableBehaviorParameters(),
material = DeformableMaterialParameters(),
collision = CollisionParameters(geometry = FileParameters(filename="mesh/SofaScene/LogoColli.sph")),
visual = VisualParameters(geometry = FileParameters(filename="mesh/SofaScene/LogoVisu.obj")))

Expand Down Expand Up @@ -94,12 +94,12 @@ def createScene(root):
SParams.name = "S"
SParams.geometry = FileParameters(filename="mesh/SofaScene/S.vtk")
SParams.geometry.elementType = ElementType.TETRAHEDRA
SParams.material = DeformableBehaviorParameters()
SParams.material = DeformableMaterialParameters()
SParams.material.constitutiveLawType = ConstitutiveLaw.ELASTIC
SParams.material.parameters = [200, 0.45]

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

Expand Down
20 changes: 20 additions & 0 deletions splib/core/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import List, Callable, Tuple, Dict
from functools import wraps


class defaultValueType():
def __init__(self):
pass
Expand Down Expand Up @@ -32,5 +33,24 @@ def wrapper(*args, **kwargs):
return MapArg


REQUIRES_COLLISIONPIPELINE = "requiresCollisionPipeline"

def setRequiresCollisionPipeline(rootnode):
if rootnode is not None:
if rootnode.findData(REQUIRES_COLLISIONPIPELINE) is None:
rootnode.addData(name=REQUIRES_COLLISIONPIPELINE, type="bool", value=True, help="Some prefabs in the scene requires a collision pipeline.")
else:
rootnode.requiresCollisionPipeline.value = True


REQUIRES_LAGRANGIANCONSTRAINTSOLVER = "requiresLagrangianConstraintSolver"

def setRequiresLagrangianConstraintSolver(rootnode):
if rootnode is not None:
if rootnode.findData(REQUIRES_LAGRANGIANCONSTRAINTSOLVER) is None:
rootnode.addData(name=REQUIRES_LAGRANGIANCONSTRAINTSOLVER, type="bool", value=True, help="Some prefabs in the scene requires a Lagrangian constraint solver.")
else:
rootnode.requiresLagrangianConstraintSolver.value = True



15 changes: 9 additions & 6 deletions splib/mechanics/mass.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,24 @@
from splib.core.enum_types import ElementType


# TODO : use the massDensity ONLY and deduce totalMass if necessary from it + volume

@ReusableMethod
def addMass(node, elem:ElementType, totalMass=DEFAULT_VALUE, massDensity=DEFAULT_VALUE, lumping=DEFAULT_VALUE, topology=DEFAULT_VALUE, **kwargs):
def addMass(node, elementType:ElementType, totalMass=DEFAULT_VALUE, massDensity=DEFAULT_VALUE, lumping=DEFAULT_VALUE, topology=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")
del kwargs["massDensity"]

if(elem !=ElementType.POINTS and elem !=ElementType.EDGES):
node.addObject("MeshMatrixMass",name="mass", totalMass=totalMass, massDensity=massDensity, lumping=lumping, topology=topology, **kwargs)
if(elementType is not None and elementType !=ElementType.POINTS and elementType !=ElementType.EDGES):
node.addObject("MeshMatrixMass",
name="mass",
totalMass=totalMass,
massDensity=massDensity,
lumping=lumping,
topology=topology, **kwargs)
else:
if (not isDefault(massDensity)) :
print("[warning] mass density can only be used on a surface or volumetric topology. Please use totalMass instead")
if (not isDefault(lumping)) :
print("[warning] lumping can only be set for surface or volumetric topology")

node.addObject("UniformMass",name="mass", totalMass=totalMass, topology=topology,**kwargs)
node.addObject("UniformMass", name="mass", totalMass=totalMass, topology=topology,**kwargs)

3 changes: 2 additions & 1 deletion stlib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__all__ = ["core","entities","geometries","materials","collision","visual"]
__all__ = ["core","entities","geometries","materials","collision","visual","prefabs"]

import Sofa.Core
from stlib.core.basePrefab import BasePrefab
Expand Down Expand Up @@ -48,6 +48,7 @@ def checkName(context : Sofa.Core.Node, name):
else:
params["name"] = checkName(self, params["name"])


# Dispatch the creation to either addObject or addChild
if isinstance(typeName, type) and issubclass(typeName, BasePrefab):
pref = self.addChild(typeName(**params))
Expand Down
13 changes: 8 additions & 5 deletions stlib/collision.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
from stlib.core.basePrefab import BasePrefab
from stlib.core.baseParameters import BaseParameters, Optional, dataclasses
from stlib.core.baseParameters import BaseParameters, Optional
from stlib.geometries import Geometry, GeometryParameters
from stlib.geometries.file import FileParameters
from splib.core.enum_types import CollisionPrimitive
from splib.core.utils import DEFAULT_VALUE
from splib.mechanics.collision_model import addCollisionModels
from Sofa.Core import Object
from splib.core.utils import setRequiresCollisionPipeline


@dataclasses.dataclass
class CollisionParameters(BaseParameters):
name : str = "Collision"

primitives : list[CollisionPrimitive] = dataclasses.field(default_factory = lambda :[CollisionPrimitive.TRIANGLES])
primitives : list[CollisionPrimitive] = [CollisionPrimitive.TRIANGLES]

selfCollision : Optional[bool] = DEFAULT_VALUE
bothSide : Optional[bool] = DEFAULT_VALUE
group : Optional[int] = DEFAULT_VALUE
contactDistance : Optional[float] = DEFAULT_VALUE

geometry : GeometryParameters = dataclasses.field(default_factory = lambda : GeometryParameters())
geometry : GeometryParameters = GeometryParameters()


class Collision(BasePrefab):

def __init__(self, parameters: CollisionParameters):
BasePrefab.__init__(self, parameters)

def init(self):

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

setRequiresCollisionPipeline(rootnode=self.getRoot())

self.addObject("MechanicalObject", template="Vec3", position=f"@{self.parameters.geometry.name}/container.position")
for primitive in self.parameters.primitives:
addCollisionModels(self, primitive,
Expand Down
35 changes: 28 additions & 7 deletions stlib/core/baseParameters.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
import dataclasses
from splib.core.utils import DEFAULT_VALUE

import dataclasses
from pydantic import BaseModel, ValidationError
from dynapydantic import SubclassTrackingModel
from typing import Callable, Optional, Any
import Sofa

class BaseParameters(SubclassTrackingModel,
discriminator_field="type",
discriminator_value_generator=lambda t: t.__name__,):

@dataclasses.dataclass
class BaseParameters(object):
name : str = "Object"
kwargs : dict = dataclasses.field(default_factory=dict)
kwargs : dict = {}

@classmethod
def fromYaml(self, data: str):
import yaml
dataDict = yaml.safe_load(data)
return self.fromDict(dataDict)

@classmethod
def fromDict(self, data: dict):
try:
return self.model_validate(data, strict=True)
except ValidationError as exc:
for error in exc.errors():
loc = error.get("loc")
message = ""
for locPart in loc:
message += locPart.__str__() + ": "
message += error.get("msg")
Sofa.msg_error(self.__name__, message)


def toDict(self):
return dataclasses.asdict(self)
17 changes: 7 additions & 10 deletions stlib/core/basePrefabParameters.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import dataclasses
from stlib.core.baseParameters import BaseParameters

@dataclasses.dataclass
class BasePrefabParameters(object):
class BasePrefabParameters(BaseParameters):
name : str = "object"
kwargs : dict = dataclasses.field(default_factory=dict)
kwargs : dict = {}

# Transformation information
# TODO: these data are going to be added in Node in SOFA (C++ implementation)
translation : list[float] = dataclasses.field(default_factory = lambda : [0., 0., 0.])
rotation : list[float] = dataclasses.field(default_factory = lambda : [0., 0., 0.])
scale : list[float] = dataclasses.field(default_factory = lambda : [1., 1., 1.])

def toDict(self):
return dataclasses.asdict(self)
translation : list[float] = [0., 0., 0.]
rotation : list[float] = [0., 0., 0.]
scale : list[float] = [1., 1., 1.]

46 changes: 22 additions & 24 deletions stlib/entities/__entity__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@
from stlib.visual import VisualParameters, Visual
from stlib.materials import Material, MaterialParameters
from stlib.geometries import Geometry
import dataclasses
from typing import Callable, Optional
from stlib.geometries import GeometryParameters
from stlib.geometries import GeometryParameters, InternalDataProvider
from splib.core.enum_types import StateType
from stlib.core.basePrefab import BasePrefab

from stlib.geometries.file import FileParameters
from stlib.materials.rigid import RigidMaterialParameters
from splib.core.enum_types import ElementType

from dynapydantic import Polymorphic
import Sofa


@dataclasses.dataclass
class EntityParameters(BaseParameters):
name : str = "Entity"

stateType : StateType = StateType.VEC3

### QUID
addCollision : Optional[Callable] = lambda x : Collision(CollisionParameters())
addVisual : Optional[Callable] = lambda x : Visual(VisualParameters())
addCollision : Optional[Callable] = Collision(CollisionParameters())
addVisual : Optional[Callable] = Visual(VisualParameters())

geometry : GeometryParameters = None
material : MaterialParameters = None
geometry : Polymorphic[GeometryParameters] = GeometryParameters(elementType = ElementType.POINTS, data = InternalDataProvider(position = [[0., 0., 0.]]))
material : Polymorphic[MaterialParameters] = RigidMaterialParameters()
collision : Optional[CollisionParameters] = None
visual : Optional[VisualParameters] = None
visual : Optional[VisualParameters] = VisualParameters(geometry = FileParameters(filename="mesh/cube.obj"))



Expand All @@ -38,21 +41,16 @@ class Entity(BasePrefab):
parameters : EntityParameters


def __init__(self, parameters=EntityParameters(), **kwargs):
def __init__(self, parameters: EntityParameters):
BasePrefab.__init__(self, parameters)


def init(self):
self.geometry = self.add(Geometry, parameters=self.parameters.geometry)

### Check compatilibility of Material
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.stateType

self.material = self.add(Material, parameters=self.parameters.material)
self.material.States.position.parent = self.geometry.container.position.linkpath
self.material.getMechanicalState().topology = self.geometry.container.linkpath

if self.parameters.collision is not None:
self.collision = self.add(Collision, parameters=self.parameters.collision)
self.addMapping(self.collision)
Expand All @@ -64,18 +62,18 @@ def init(self):

def addMapping(self, destinationPrefab):

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

#TODO: all paths are expecting Geometry to be called Geomtry and so on. We need to robustify this by using the name parameter somehow
if( self.parameters.stateType == StateType.VEC3):
if( self.parameters.material.stateType == StateType.VEC3):
destinationPrefab.addObject("BarycentricMapping",
output=destinationPrefab.linkpath,
output_topology=destinationPrefab.Geometry.container.linkpath,
input=self.Material.linkpath,
input_topology=self.Geometry.container.linkpath,
output_topology=destinationPrefab.geometry.container.linkpath,
input=self.material.linkpath,
input_topology=self.geometry.container.linkpath,
template=template)
else:
destinationPrefab.addObject("RigidMapping",
output=destinationPrefab.linkpath,
input=self.Material.linkpath,
input=self.material.linkpath,
template=template)
Loading
Loading