diff --git a/pocs/PEHD/README.md b/pocs/PEHD/README.md new file mode 100644 index 0000000..9488fb6 --- /dev/null +++ b/pocs/PEHD/README.md @@ -0,0 +1,9 @@ +Synthesize the discussion done during the Lille's workshop on prefab. +===================================================================== + +Contributors: +- paul.baksic@inria.fr +- eulalie.coevoet@compliance-robotics.com +- hugo.talbot@inria.fr +- damien.marchal@univ-lille.fr + diff --git a/pocs/PEHD/src/entity.py b/pocs/PEHD/src/entity.py new file mode 100644 index 0000000..fbf7024 --- /dev/null +++ b/pocs/PEHD/src/entity.py @@ -0,0 +1,188 @@ +from typing import Callable, Optional, overload + +import Sofa +import dataclasses + +def addBidule(self): + return self.addChild("Bidule") + +DEFAULT_VALUE = object() + +def NONE(*args, **kwargs): + pass + +def to_dict(o): + if isinstance(o, dict): + return o + if hasattr(o, "to_dict"): + return o.to_dict() + return {} + +@dataclasses.dataclass +class PrefabParameters(object): + name : str = "Prefab" + kwargs : dict = dataclasses.field(default_factory=dict) + + def __getattr__(self, name: str) : + if name == "__getstate__": + getattr(PrefabParameters, "__getstate__") + if name == "__setstate__": + getattr(PrefabParameters, "__setstate__") + + try: + a = self.__getattribute__(name) + except Exception as e: + return NONE + return a + + def to_dict(self): + return dataclasses.asdict(self) + +@dataclasses.dataclass +class VisualModelParameters(PrefabParameters): + name : str = "VisualModel" + + filename : str = "mesh/sphere_02.obj" + + renderer : dict = dataclasses.field(default_factory=dict) + mapping : dict = dataclasses.field(default_factory=dict) + +class VisualModel(Sofa.Core.Node): + + def __init__(self, parent=None, parameters : VisualModelParameters = VisualModelParameters()): + Sofa.Core.Node.__init__(self, name=parameters.name) + + if parent != None: + parent.addChild(self) + + self.addObject("MeshOBJLoader", name="loader", filename=parameters.filename) + self.addRenderer(**to_dict(parameters.renderer) | {"src" : "@loader"} ) + self.addMapping(**to_dict(parameters.mapping) ) + + def addRenderer(self, **kwargs): + self.addObject("OglModel", name="renderer", **kwargs) + + def addMapping(self, **kwargs): + self.addObject("RigidMapping", name="mapping", **kwargs) + +class CollisionModel(Sofa.Core.BasePrefab): + def __init__(self, params, **kwargs): + Sofa.Core.Node.__init__(self, **kwargs) + + class Parameters(object): + enabled : bool = False + +class MechanicalObject(Sofa.Core.Object): + positions : list[float] + + @dataclasses.dataclass + class Parameters(object): + name : str = "MechanicalObject" + + def to_dict(self): + return dataclasses.asdict(self) + + +@dataclasses.dataclass +class SimulationParameters(PrefabParameters): + name : str = "Simulation" + iterations : Optional[int] = None + template: Optional[str] = None + solver : dict = dataclasses.field(default_factory=dict) + integration : dict = dataclasses.field(default_factory=dict) + + def to_dict(self): + return self.asdict() + +class Simulation(Sofa.Core.Node): + solver : Sofa.Core.Object + integration : Sofa.Core.Object + iterations : int + + def __init__(self, parent : Sofa.Core.Node = None, parameters : SimulationParameters = SimulationParameters()): + Sofa.Core.Node.__init__(self, name=parameters.name) + if parent is not None: + parent.addChild(self) + + if parameters.iterations != NONE and "iterations" in parameters.solver: + raise Exception("Cannot set direct attribute and internal hack... ") + + self.addObject("EulerImplicitSolver", name = "integration", **to_dict(parameters.integration)) + self.addObject("CGLinearSolver", name = "solver", iterations=parameters.iterations, **to_dict(parameters.solver)) + + + +#@dataclasses.dataclass +#class Solver(object): +# integrationscheme : str +# numericalsolver : str + +@dataclasses.dataclass +class EntityParameters(PrefabParameters): + name : str = "Entity" + + addSimulation : Callable = Simulation + addCollisionModel : Callable = CollisionModel + addVisualModel : Callable = VisualModel + + #setConstitutiveLaw # : Callable = addBidule + #setBoundaryCondition #: Callable = addBidule + + mechanical : dict = dataclasses.field(default_factory=dict) + collision : CollisionModel.Parameters = CollisionModel.Parameters() + visual : VisualModelParameters = VisualModelParameters() + simulation : SimulationParameters = SimulationParameters() + +class Entity(Sofa.Core.Node): + # A simulated object + simulation : Simulation + visual : VisualModel + collision : CollisionModel + + parameters : EntityParameters + + def __init__(self, parent=None, parameters=EntityParameters(), **kwargs): + Sofa.Core.Node.__init__(self, name=parameters.name) + + if parent is not None: + parent.addChild(self) + + self.parameters = parameters + + self.addMechanicalModel(**parameters.mechanical) + self.addSimulation(parameters=parameters.simulation) + self.addVisualModel(parameters=parameters.visual) + self.addCollisionModel() + + def addMechanicalModel(self, **kwargs): + self.addObject("MechanicalObject", **kwargs) + + def addSimulation(self, **kwargs): + self.parameters.addSimulation(self, **kwargs) + + def addVisualModel(self, **kwargs): + self.parameters.addVisualModel(self, **kwargs) + + def addCollisionModel(self): + pass + +class Rigid(Entity): + def __init__(self, **kwargs): + Entity.__init__(self, **kwargs) + + +class Deformable(Entity): + def __init__(self, **kwargs): + Entity.__init__(self, **kwargs) + +@dataclasses.dataclass +class DeformableEntityParameters(EntityParameters): + addConstitutiveLaw : Callable = lambda x: x + + mass : Optional[float] = None + + def to_dict(self): + return dataclasses.asdict(self) + + + diff --git a/pocs/PEHD/src/example.py b/pocs/PEHD/src/example.py new file mode 100644 index 0000000..4dc12d1 --- /dev/null +++ b/pocs/PEHD/src/example.py @@ -0,0 +1,65 @@ +import Sofa.Core +import copy +import entity +from entity import PrefabParameters, EntityParameters, Entity, Simulation + + +oldAdd=Sofa.Core.Node.addObject +def myAddObject(self : Sofa.Core.Node, tname, **kwargs): + kwargs = copy.copy(kwargs) + previouslen = len(self.objects) + try: + oldAdd(self, tname, **kwargs) + except Exception as e: + target = self + if len(self.objects) != previouslen: + target = list(self.objects)[-1] + Sofa.msg_error(target, str(e)) + +Sofa.Core.Node.addObject = myAddObject + + +def myAdd(self : Sofa.Core.Node, c, params = PrefabParameters(), **kwargs): + def findName(cname, node): + """Compute a working unique name in the node""" + rname = cname + for i in range(0, len(node.children)): + if rname not in node.children: + return rname + rname = cname + str(i+1) + return rname + + for k,v in kwargs.items(): + if hasattr(params, k): + setattr(params, k, v) + + params = copy.copy(params) + if params.name in self.children: + params.name = findName(params.name, self) + + return c(parent = self, parameters=params) +Sofa.Core.Node.add = myAdd + +def createScene(root): + #@optionalkwargs + + #def eulalieAddOde(self, **kwargs): + # 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 + + params.mechanical["template"] = "Rigid3" + + #params.simulation.integration["rayleightStiffnessXXX"] = 2.0 + + #params.solver.kwargs["numericalintegration"] = { "firstOrder" : True } + + root.add(Entity, params) + root.add(Entity, params) + root.add(Entity, params) + + #root.add(Simulation, name="mySimulation") diff --git a/pocs/PEHD/src/softrobots.py b/pocs/PEHD/src/softrobots.py new file mode 100644 index 0000000..d8e7871 --- /dev/null +++ b/pocs/PEHD/src/softrobots.py @@ -0,0 +1,23 @@ +class SoftRobots: + class Cable(Sofa.Core.BasePrefab): + length : float + + def __init__(self,**kwargs): + pass + + def Parameters(object): + lenght : float + +class Trunk(Sofa.Core.BasePrefab): + body : Entity.Deformable + cables : list [SoftRobots.Cable] + + def __init__(self, params): + body = Entity.Deformable() + + for param in range(params.cables): + cables.append(SoftRobots.Cable(body, param)) + + class Parameters(object): + body : Entity.Deformable.Parameters + cables : list[SoftRobots.Cable.Parameters]