import os
import shutil
import math
import sys

import SofaPython.Tools
import SofaPython.units
from SofaPython.Tools import listToStr as concat
import BranchingImage.API
import Flexible.API
import Compliant.sml

import Anatomy.Tools
import tools
import check

import Sofa
printLog = True

defaultColor = {"default":[.8,.8,.8,1], "bone":[.8,.8,.8,1],  "flesh":[1.,0.5,0.5,0.5], "skin":[1.,0.75,0.75,0.5], "fat":[1.,0.75,0.75,0.5], "ligament":[.2,.2,1,1], "muscle":[1,.2,.2,1], "capsule":[.8,.8,0.,1] }

class ScenePrepare(Compliant.sml.SceneArticulatedRigid):
    """ Builds a (sub)scene from an anatomy model
    precompute shapeFunction, gauss points, nodes, and mass
    """
    def __init__(self, parentNode, model, dataDir):

        Compliant.sml.SceneArticulatedRigid.__init__(self, parentNode, model)

        check.solidAnatomicalTags(model)

        # load default anatomy material
        self.material.load(os.path.join(os.path.dirname(__file__),"..", "material.json"))
        self.setMaterialByTag("ligament", "ligament")
        self.setMaterialByTag("muscle", "muscle")
        self.setMaterialByTag("bone", "bone")
        self.setMaterialByTag("skin", "fat")
        self.setMaterialByTag("flesh", "flesh")
        self.setMaterialByTag("ligament_1d", "ligament_1d")
        self.setMaterialByTag("muscle_1d", "muscle_1d")

        # create export directory (removing any existing one, to be sure no file are here from a previous export)
        self.dataDir=dataDir
        if os.path.exists(dataDir):
            shutil.rmtree(dataDir)
        os.makedirs(dataDir)

        # variables
        self.entitiesProp = Anatomy.Tools.EntityProperties()

        self.Images=dict()
        self.behaviors=dict()
        self.cells=list()
        self.collisions=dict()
        self.visuals=dict()

        self.labelImage=None
        self.youngImage=None
        self.densityImage=None
        self.shapeFunction=None
        self.AffineDof=None
        self.AffineMass=None

        # parameters
        self.param.voxelSize = 5E-3 # m
        self.param.coarseningFactor = 1
        self.param.autoConnectSkin = True

        # absolute number of nodes by tag
        # for small solids or open mesh, the density is not relevant
        # supersede nodeDensity
        self.param.numberOfNodes = {"default": None}
        # nodes density by tag
        self.param.nodeDensity = {"default": 1000} # nb/m^3
        self.param.GPDensity = 2000  # nb/m^3 # TODO: to be coherent change it to a dict by tag
        self.param.GPType = "331"
        self.param.GPMinNumber = 1  # minimum number of Gauss Points per solid

        self.param.samplingMethod = 'global' # 'local'
        # user defined affines position as {solidId1: [pos1, pos2, pos3], solidId2: [...]}
        # to be used with the 'local' samplingMethod
        self.param.samplingAdditionalPosition = dict()
        # soft tissue solid tags to be sampled on surface
        self.param.sampleSurfaceRatherThanVolume = set()

        self.param.exportShapeFunction = False # should be the same as loadShapeFunction param from simu scene

        self.param.showAffine = True
        self.param.showAffineScale = 50E-3 # m

        self.param.showRigidVertex = True
        self.param.showRigidVertexScale = 5e-3 # m

        self.param.showLabelImage = True
        self.param.showShapeFunctionImage = False

        self.param.showGPScale = 2E-3 # m

        self.param.color = defaultColor

        self.param.softTissueTags={"ligament","muscle","skin","flesh"}
        self.param.boneTags={"bone"}
        self.param.mappedObjectTags=set()

        self.param.springTags={"ligament_1d","muscle_1d"}
        self.param.color["ligament_1d"]=self.param.color["ligament"]
        self.param.color["muscle_1d"]=self.param.color["muscle"]
        self.param.showSpringScale= 1E-3 # m


    def createScene(self):

        # check parameters
        if not type(self.param.nodeDensity) is dict:
            d = self.param.nodeDensity
            self.param.nodeDensity = {"default": d}
            Sofa.msg_deprecated("Anatomy.sml", "param.nodeDensity should be a dict")
        if hasattr(self.param, "sampleSkinSurfaceRatherThanVolume"):
            Sofa.msg_deprecated("Anatomy.sml", "param.sampleSkinSurfaceRatherThanVolume is replaced by param.sampleSurfaceRatherThanVolume")
            if self.param.sampleSkinSurfaceRatherThanVolume:
                self.param.sampleSurfaceRatherThanVolume.add("skin")

        self.param.rigidTags.update(self.param.boneTags) # add bones to compliant tags
        for t in self.param.boneTags:
            self.setMaterialByTag(t, "bone")
        Compliant.sml.SceneArticulatedRigid.createScene(self)

        self.node.createObject("RequiredPlugin", name="Image")
        self.node.createObject("RequiredPlugin", name="BranchingImage")
        self.node.createObject("RequiredPlugin", name="Flexible")

        # self.node.createObject('VisualStyle', displayFlags='showBehaviorModels showVisual')
        # self.node.createObject('BackgroundSetting',color='1 1 1')

        allTags = self.param.boneTags | self.param.softTissueTags | self.param.mappedObjectTags
        tools.computeVolumes(self, self.param.boneTags | self.param.softTissueTags)
        tools.insertImagesFromSolids(self,allTags, self.param.voxelSize)
        tools.connectImages(self)
        self.labelImage = tools.mergeImages(self,allTags,'label', self.param.coarseningFactor)
        self.youngImage = BranchingImage.API.Image(self.labelImage.node, "young", imageType="ImageR") # TODO should not be necessary to create an ImageR
        self.youngImage.fromTransferFunction ( self.labelImage, self.entitiesProp.getYoungMap() )
        self.densityImage = BranchingImage.API.Image(self.labelImage.node, "density", imageType="ImageR") # TODO should not be necessary to create an ImageR
        self.densityImage.fromTransferFunction ( self.labelImage, self.entitiesProp.getDensityMap() )
        if self.param.showLabelImage:
            self.labelImage.addViewer()


        # get a point on each rigid
        # and compute its offset in label image
        rigidVertex=list()
        for solid in self.model.getSolidsByTags(self.param.boneTags):
            p = Anatomy.Tools.transform(Anatomy.Tools.getFirstVertex(solid.mesh[0].source),solid.position) # TODO: do not call again getFirstVertex (it loads the full mesh)
            c = self.Images[solid.id].node.createObject('BranchingCellOffsetsFromPositions', name='cell', template=self.labelImage.template(), image=self.labelImage.getImagePath()+".image", transform=self.labelImage.getImagePath()+".transform", position =concat(p), labels=concat(self.entitiesProp.entities[solid.id].allLabels()))
            rigidVertex.append(p)
            self.cells.append(c)

        # sample affine dofs
        if self.param.samplingMethod=='global':
            if len(self.param.samplingAdditionalPosition) > 0:
                Sofa.msg_warning("Anatomy.sml.rigidAffine", "global sampling does not support samplingAdditionalPosition - ignored")
            tools.sampleFromModel_global(self, rigidVertex, self.youngImage)
        else:
            tools.sampleFromModel_solidPerSolid(self, self.param.samplingAdditionalPosition)

        # merge rigids
        if self.rigids and len(self.param.boneTags):
            self.nodes["dofRigid"] = self.insertMergeRigid("dofRigid",self.param.boneTags)
        else :
            self.nodes["dofRigid"] = None

        # "all" dof
        if not self.nodes["dofAffine"] is None and not self.nodes["dofRigid"] is None:      # there are Affines and rigids in the scene
            self.createChild( [self.nodes["dofRigid"],self.nodes["dofAffine"]], "dof" )
            mergedNodes = self.nodes["dof"].createObject("MergeMeshes", name="mergedNodes", nbMeshes="2", position1=SofaPython.Tools.listListToStr(rigidVertex),  position2=self.AffineDof.src)
            mergedNodesPos = "@"+SofaPython.Tools.getObjectPath(mergedNodes)+".position"

            if printLog:
                Sofa.msg_info('Anatomy.sml',"use Affine and Rigid Dofs")

        elif not self.nodes["dofAffine"] is None:                                           # there are only Affines
            self.nodes["dof"] = self.nodes["dofAffine"]
            mergedNodesPos = self.AffineDof.src

            if printLog:
                Sofa.msg_info('Anatomy.sml',"use Affine Dofs")

        elif not self.nodes["dofRigid"] is None:                                           # there are only Rigids
            self.nodes["dof"] = self.nodes["dofRigid"]
            mergedNodesPos = SofaPython.Tools.listListToStr(rigidVertex)

            if printLog:
                Sofa.msg_info('Anatomy.sml',"use Rigid Dofs")

        else :
            return

        if not self.nodes["dofRigid"] is None and self.param.showRigidVertex:
            self.nodes["rigidVertex"] = self.nodes["dofRigid"].createChild("rigidVertex")
#            self.nodes["rigidVertex"].createObject("VisualModel", name="displayRigidVertex",
#                                                   position=SofaPython.Tools.listListToStr(rigidVertex),
#                                                   pointSize=SofaPython.units.length_from_SI(self.param.showRigidVertexScale),
#                                                   color="1. 0. 0. 1.")
            self.nodes["rigidVertex"].createObject("MechanicalObject", name="displayRigidVertex",
                                                   position=SofaPython.Tools.listListToStr(rigidVertex),
                                                   showObject=True, showObjectScale=SofaPython.units.length_from_SI(self.param.showRigidVertexScale),
                                                   drawMode=1, showColor="1. 0. 0. 1." )

        mergedCells = self.nodes["dof"].createObject('MergeVectors',name='cells',nbInputs=len(self.cells), **dict({ 'input'+str(i+1): '@'+SofaPython.Tools.getObjectPath(c)+'.cell' for i,c in enumerate(self.cells) }) )

        # shape function
        self.shapeFunction = Flexible.API.ShapeFunction(self.nodes["dof"])
        self.youngImage.node.addChild(self.shapeFunction.node)  # this fixes the scene graph but not very satisfactory..
        self.shapeFunction.addVoronoi(self.youngImage, mergedNodesPos, '@'+SofaPython.Tools.getObjectPath(mergedCells)+'.output')
#        self.shapeFunction.shapeFunction.clearData=False
#        self.shapeFunction.node.createObject("BranchingImageToImageConverter", template="BranchingImageD,ImageD", name="converter", inputBranchingImage="@shapeFunction.distances", conversionType="0")
#        self.shapeFunction.node.createObject('ImageViewer',template="ImageD",image="@converter.image",transform="@shapeFunction.transform")
        if self.param.showShapeFunctionImage:
            if not self.nodes["dofAffine"] is None and not self.nodes["dofRigid"] is None:
                self.nodes["dof"].createObject("MechanicalObject", name="dofs", position="@mergedNodes.position", showObject=True, showObjectScale=SofaPython.units.length_from_SI(0.01), showColor="1 1 0 1", drawMode=1, showIndices=True, showIndicesScale=0.1)
            else:
                self.nodes["dof"].getObject("dofs").showIndices=True
                self.nodes["dof"].getObject("dofs").showIndicesScale=0.1


            self.shapeFunction.addViewer()


        #soft tissues
        # @todo: add 'skin' layer
        for solid in self.model.getSolidsByTags(self.param.softTissueTags):
            if printLog:
                Sofa.msg_info('Anatomy.sml',"creating solid "+solid.name)

            self.behaviors[solid.id] = tools.insertBehavior(solid, self.nodes["dof"], SofaPython.units.density_from_SI(self.param.GPDensity), self.param.GPMinNumber, self.param.GPType,
                                                                    self.shapeFunction, self.nodes["dofRigid"], self.nodes["dofAffine"],
                                                                    self.labelImage, self.entitiesProp.entities[solid.id].allLabels(),
                                                                    showSamplesScale=SofaPython.units.length_from_SI(self.param.showGPScale), drawMode=1)
            (self.collisions[solid.id],self.visuals[solid.id]) = tools.insertDeformable(solid, self.nodes["dof"], self.nodes["dofRigid"], self.nodes["dofAffine"],
                labelImage=self.labelImage,labels=self.entitiesProp.entities[solid.id].allLabels(),
                color=solid.getValueByTag(self.param.color))

        # add springs
        for solid in self.model.getSolidsByTags(self.param.springTags):
            if printLog:
                Sofa.msg_info('Anatomy.sml',"creating solid "+solid.name)

            indexLabelPairs=tools.getSpringAttachments(solid, self)
            (self.collisions[solid.id],self.visuals[solid.id]) = tools.insertSpring(solid, self.nodes["dof"], self.nodes["dofRigid"], self.nodes["dofAffine"],
                                                                                       labelImage=self.labelImage,indexLabelPairs=indexLabelPairs,
                                                                                       radius=SofaPython.units.length_from_SI(self.param.showSpringScale), color=solid.getValueByTag(self.param.color))

        # mapped objects                                                                       
        for solid in self.model.getSolidsByTags(self.param.mappedObjectTags):
            if printLog:
                Sofa.msg_info('Anatomy.sml',"creating solid "+solid.name)
            (self.collisions[solid.id],self.visuals[solid.id]) = tools.insertDeformable(solid, self.nodes["dof"], self.nodes["dofRigid"], self.nodes["dofAffine"],
                labelImage=self.labelImage,labels=self.entitiesProp.entities[solid.id].allLabels(),
                color=solid.getValueByTag(self.param.color))

        # Mass
        if not self.nodes["dofAffine"] is None:
            self.AffineMass = Flexible.API.AffineMass( self.nodes["dofAffine"])
            self.AffineMass.massFromDensityImage(self.nodes["dof"],self.nodes["dofRigid"], self.densityImage)

        # export components
        if self.param.exportShapeFunction is True:
            self.labelImage.addExporter(directory=self.dataDir)
            #self.youngImage.addExporter(directory=self.dataDir)
            #self.densityImage.addExporter(directory=self.dataDir)
            self.shapeFunction.addExporter(directory=self.dataDir)


    def export(self):
        self.entitiesProp.write(self.dataDir)

        if not self.nodes["dofAffine"] is None:
            self.AffineDof.write(directory=self.dataDir)
            self.AffineMass.write(directory=self.dataDir)

        for b in self.behaviors.values():
            b.write(directory=self.dataDir)

        for solid in self.model.getSolidsByTags(self.param.softTissueTags|self.param.springTags|self.param.mappedObjectTags):
            for d in self.collisions[solid.id].values() + self.visuals[solid.id].values():
                d.write(directory=self.dataDir)


class SceneSimu(Compliant.sml.SceneArticulatedRigid):
    """ Builds a (sub)scene from an anatomy model
    [entity tag] bone are simulated as RigidBody
    [contact tag] collision + symmetric

    Compliant joints are setup between the rigids
    """

    def addMeshExporters(self, dir, ExportAtEnd=False):
        Compliant.sml.SceneArticulatedRigid.addMeshExporters(self,dir,ExportAtEnd=ExportAtEnd) # export rigids
        tools.addMeshExporters(self,dir,ExportAtEnd=ExportAtEnd) # export deformables


    def addMappedPoint(self, name, position=[0,0,0], entities=[], useGlobalIndices=False, useIndexLabelPairs=False, assemble=True, isMechanical=True):
        """ Add a linear mapped point which belongs to the given entities to the scene.
        \param entities ids of solids as they appear in the sml model
        """
        if self.shapeFunction is None:
            Sofa.msg_warning("Anatomy.sml.rigidAffine", "No shape function, point cannot be mapped: "+name)
            return None
        mp = SceneSimu.MappedPoint(self.nodes["dof"], name, position)
        # gather labels of all entities
        labels = []
        for entity in entities:
            labels += self.entitiesProp.entities[entity].allLabels()
        # use entities' labels to select specific voxels in branching image
        cell = ''
        if not self.labelImage is None and len(labels) :
            if  self.labelImage.prefix=='Branching':
                offsets = mp.node.createObject("BranchingCellOffsetsFromPositions", template=self.labelImage.template(), name="cell", position="@dofs.position", image=self.labelImage.getImagePath()+".image", transform=self.labelImage.getImagePath()+".transform", labels=concat(labels), useGlobalIndices=useGlobalIndices, useIndexLabelPairs=useIndexLabelPairs)
                cell = offsets.getLinkPath()+".cell"

        mp.addMapping(self.nodes["dofRigid"], self.nodes["dofAffine"], cell, isMechanical, assemble)
        return mp

    class MappedPoint:
        """ Mapped point class with similar API as in Compliant.StructuralAPI
        """
        def __init__(self, node, name, position):
            self.node = node.createChild( name )
            self.dofs = self.node.createObject( 'MechanicalObject', name='dofs', template="Vec3", position=concat(position) )

        def addMapping(self, dofRigidNode, dofAffineNode, cell, isMechanical, assemble):
            self.mapping = Flexible.API.insertLinearMapping(self.node, dofRigidNode=dofRigidNode, dofAffineNode=dofAffineNode, cell=cell, assemble=assemble, geometricStiffness=2, isMechanical=isMechanical)

    def __init__(self, parentNode, model, dataDir):

        Compliant.sml.SceneArticulatedRigid.__init__(self, parentNode, model)

        check.solidAnatomicalTags(model)

        # load default anatomy material
        self.material.load(os.path.join(os.path.dirname(__file__), "..", "material.json"))
        self.setMaterialByTag("ligament", "ligament")
        self.setMaterialByTag("muscle", "muscle")
        self.setMaterialByTag("bone", "bone")
        self.setMaterialByTag("skin", "fat")
        self.setMaterialByTag("flesh", "flesh")
        self.setMaterialByTag("ligament_1d", "ligament_1d")
        self.setMaterialByTag("muscle_1d", "muscle_1d")

        # variables
        self.dataDir = dataDir
        self.entitiesProp=None

        self.slidingContacts = dict()
        self.collisionContacts = dict() # where symmetricContact=True, for each contact contains two StrucTuralAPI.Contact index by id and id_inverted
        check.checkContactsModel(self.model)
        self.collisions=dict()
        self.visuals=dict()
        self.behaviors=dict()
        self.behaviors_1d=dict()

        self.labelImage=None
        self.shapeFunction=None
        self.AffineMass=None
        self.AffineDof=None

        # parameters
        self.param.parallel = False # activate parallel computations for component supporting it
        # sliding contacts
        self.param.contactCompliance = 1e-2 # s^2/kg
        self.param.slidingContactRejectBoundary = True
        # collision contacts
        self.param.collisionCompliance = 1e-14 # compliance value for bone collision constraint
        self.param.collisionOffset = 0 # m collisions are detected when the distance is as small as offset
        self.param.collisionNbClosestVertex = None # number of vertices used to detect and react to a collision, None for all vertices

        self.param.loadShapeFunction = False # Load images (label+shapefunction), otherwise load the shape function values. Images are useful to map non-prepared solids (weights are recomputed)

        self.param.behaviorLaw = 'hooke' # 'projective'
        self.param.strainMeasure="Corotational" # 'Green'
        self.param.useStrainOffset = False

        self.param.showAffine = True
        self.param.showAffineScale = 50E-3 # m

        self.param.color = defaultColor

        self.param.softTissueTags={"ligament","muscle","skin","flesh"}
        self.param.boneTags={"bone"}
        self.param.mappedObjectTags=set()

        self.param.springTags={"ligament_1d","muscle_1d"}
        self.param.color["ligament_1d"]=self.param.color["ligament"]
        self.param.color["muscle_1d"]=self.param.color["muscle"]
        self.param.showSpringScale= 1E-3 # m
        self.param.enableSpringLimits = False

        self.param.exportDir=''

    def createScene(self):

        self.param.rigidTags.update(self.param.boneTags) # add bones to compliant tags
        for t in self.param.boneTags:
            self.setMaterialByTag(t, "bone")
        Compliant.sml.SceneArticulatedRigid.createScene(self)

        self.node.createObject("RequiredPlugin", name="Image")
        self.node.createObject("RequiredPlugin", name="BranchingImage")
        self.node.createObject("RequiredPlugin", name="Flexible")

        if len(self.model.getSurfaceLinksByTags({"sliding", "collision"})) > 0:
            self.node.createObject("RequiredPlugin", name="ContactMapping")

        # self.node.createObject('BackgroundSetting',color='1 1 1')

        # entities
        self.entitiesProp = Anatomy.Tools.EntityProperties.read(self.dataDir)

        # "rigid" dof
        if self.rigids and len(self.param.boneTags):
            self.nodes["dofRigid"] = self.insertMergeRigid("dofRigid",self.param.boneTags)
        else:
            self.nodes["dofRigid"] = None

        # "affine" dof
        self.createChild(self.node, "dofAffine")
        self.AffineDof = Flexible.API.AffineDof(self.nodes["dofAffine"])
        self.AffineDof.read(directory=self.dataDir)

        if not self.AffineDof.dof is None: # there are Affines
            self.AffineDof.dof.showObject=self.param.showAffine
            self.AffineDof.dof.showObjectScale=SofaPython.units.length_from_SI(self.param.showAffineScale)
            # Mass
            self.AffineMass = Flexible.API.AffineMass(self.nodes["dofAffine"])
            self.AffineMass.read(directory=self.dataDir)
        else :
            self.node.removeChild(self.nodes["dofAffine"])
            self.nodes["dofAffine"] = None

        # "all" dof
        if not self.nodes["dofAffine"] is None and not self.nodes["dofRigid"] is None:      # there are Affines and rigids in the scene
            self.createChild( [self.nodes["dofRigid"],self.nodes["dofAffine"]], "dof" )
        elif not self.nodes["dofAffine"] is None:                                           # there are only Affines
            self.nodes["dof"] = self.nodes["dofAffine"]
        elif not self.nodes["dofRigid"] is None:                                            # there are only Rigids
            self.nodes["dof"] = self.nodes["dofRigid"]
        else :
            return

        if self.param.loadShapeFunction is True:
            # label, needed to correctly use the shapefunction
            self.labelImage = BranchingImage.API.Image(self.nodes["dof"])
            self.labelImage.addContainer(filename='label.mhd',directory=self.dataDir, name='label')
            # shape function
            self.shapeFunction = Flexible.API.ShapeFunction(self.nodes["dof"])
            self.shapeFunction.addContainer(directory=self.dataDir)

        #soft tissues
        # @todo: add 'skin' layer
        for solid in self.model.getSolidsByTags(self.param.softTissueTags):
            if printLog:
                Sofa.msg_info('Anatomy.sml',"creating solid "+solid.name)

            mat = self.getMaterial(solid.id)
            self.behaviors[solid.id] = tools.loadBehavior(solid, self.nodes["dof"], self.dataDir,
                                                             self.nodes["dofRigid"], self.nodes["dofAffine"],
                                                             self.labelImage, self.entitiesProp.entities[solid.id].allLabels())

            if self.param.behaviorLaw == 'projective':
                self.behaviors[solid.id].addProjective( youngModulus=self.material.youngModulus(mat) )
            else:
                self.behaviors[solid.id].addHooke( strainMeasure=self.param.strainMeasure, youngModulus=self.material.youngModulus(mat), poissonRatio= self.material.poissonRatio(mat),useOffset=self.param.useStrainOffset )
            (self.collisions[solid.id],self.visuals[solid.id]) = tools.insertDeformable(solid, self.nodes["dof"], self.nodes["dofRigid"], self.nodes["dofAffine"],
                                                                                           labelImage=self.labelImage,labels=self.entitiesProp.entities[solid.id].allLabels(),
                                                                                           color=solid.getValueByTag(self.param.color))
            self.behaviors[solid.id].readWeights(directory=self.dataDir)
            for d in self.collisions[solid.id].values() + self.visuals[solid.id].values():
                d.read(directory=self.dataDir)

        # mapped object in the flesh
        for solid in self.model.getSolidsByTags(self.param.mappedObjectTags):
            if printLog:
                Sofa.msg_info('Anatomy.sml',"creating solid "+solid.name)
            (self.collisions[solid.id],self.visuals[solid.id]) = tools.insertDeformable(solid, self.nodes["dof"], self.nodes["dofRigid"], self.nodes["dofAffine"],
                labelImage=self.labelImage,labels= self.entitiesProp.entities[solid.id].allLabels(),
                color=solid.getValueByTag(self.param.color))
            for d in self.visuals[solid.id].values():
                d.read(directory=self.dataDir)

        # add springs
        for solid in self.model.getSolidsByTags(self.param.springTags):
            if printLog:
                Sofa.msg_info('Anatomy.sml',"creating solid "+solid.name)

            indexLabelPairs=tools.getSpringAttachments(solid, self)
            (self.collisions[solid.id],self.visuals[solid.id]) = tools.insertSpring(solid, self.nodes["dof"], self.nodes["dofRigid"], self.nodes["dofAffine"],
                                                                                       labelImage=self.labelImage,indexLabelPairs=indexLabelPairs,
                                                                                       radius=SofaPython.units.length_from_SI(self.param.showSpringScale), color=solid.getValueByTag(self.param.color))
            for d in self.collisions[solid.id].values() + self.visuals[solid.id].values():
                d.read(directory=self.dataDir)
            # add behavior
            mat = self.material.data[self.getMaterial(solid.id)]
            self.behaviors_1d[solid.id]=dict()
            for meshid, c in self.collisions[solid.id].iteritems():
                self.behaviors_1d[solid.id][meshid] = tools.insertSpringBehavior(c, mat, self.param.enableSpringLimits)
        # contacts
        tools.insertSlidingContactsFromModel(self)
        tools.insertCollisionContactsFromModel(self)

        # obj exporters
        if len(self.param.exportDir):
            self.addMeshExporters(self.param.exportDir,ExportAtEnd=True)
