Exemple #1
0
class SNMApp(wx.App):
    """A simple class that should handle almost everything a simbicon application typically needs."""
    def __init__(self,
                 appTitle="Simbicon Application",
                 fps=30.0,
                 dt=1 / 2000.0,
                 glCanvasSize=wx.DefaultSize,
                 size=wx.DefaultSize,
                 redirect=False,
                 filename=None,
                 useBestVisual=False,
                 clearSigInt=True,
                 showConsole=True):
        """
        appTitle is the window title
        fps is the desired number of frames per seconds
        dt is the desired simulation timestep
        :see: wx.BasicApp.__init__`
        """

        wx.App.__init__(self, redirect, filename, useBestVisual, clearSigInt)

        # No annoying error logging window
        wx.Log.SetActiveTarget(wx.LogStderr())

        import UI

        # Setup the main window style
        style = wx.DEFAULT_FRAME_STYLE
        if size == wx.DefaultSize:
            size = wx.GetDisplaySize()
            size.height *= 0.75
            size.width *= 0.75
            if glCanvasSize == wx.DefaultSize:
                style |= wx.MAXIMIZE

        # Setup the environment for the python interactive console
        consoleEnvironment = {"wx": wx, "Physics": Physics, "Utils": Utils}
        exec "from MathLib import *\n" + \
             "app = wx.GetApp()\n" + \
             "from PyUtils import load" in consoleEnvironment, consoleEnvironment

        # Create the main window
        self._frame = UI.MainWindow(None,
                                    -1,
                                    appTitle,
                                    size=size,
                                    style=style,
                                    fps=fps,
                                    glCanvasSize=glCanvasSize,
                                    showConsole=showConsole,
                                    consoleEnvironment=consoleEnvironment)

        # Define GL callbacks
        self._glCanvas = self._frame.getGLCanvas()
        self._glCanvas.addDrawCallback(self.draw)
        self._glCanvas.addPostDrawCallback(self.postDraw)
        self._glCanvas.addOncePerFrameCallback(self.advanceAnimation)
        self._glCanvas.setDrawAxes(False)
        self._glCanvas.setPrintLoad(True)
        self._glCanvas.setCameraTargetFunction(self.cameraTargetFunction)

        self._glCanvas.setDrawGround(False)

        # Get the tool panel
        self._toolPanel = self._frame.getToolPanel()

        # Show the application
        self._frame.Show()

        # Set-up starting state
        self._dt = dt
        self._drawShadows = True
        self._simulationSecondsPerSecond = 1  # 1 = real time, 2 = twice real time, 1/2 = half real time
        self._animationRunning = False
        self._cameraFollowCharacter = False
        self._drawCollisionVolumes = False
        self._followedCharacter = None  # Pointer to focused character
        self._captureScreenShots = False
        self._printStepReport = True
        self._screenShotNumber = 0
        self._worldOracle = Core.WorldOracle()
        self._worldOracle.initializeWorld(Physics.world())
        self._kinematicMotion = False

        # Set-up starting list of characters and controllers
        self._characters = []

        # Define the observables
        self._controllerList = ObservableList()
        self._characterObservable = PyUtils.Observable()
        self._animationObservable = PyUtils.Observable()
        self._cameraObservable = PyUtils.Observable()
        self._optionsObservable = PyUtils.Observable()

        self._COMObservable = PyUtils.Observable()

        self._curveList = ObservableList()
        self._snapshotTree = SnapshotBranch()

        self._showAbstractView = False
        self._showAbstractViewSkeleton = False
        self._showBodyFrame = False
        self._showCDPrimitives = False
        self._showColors = False
        self._showFrictionParticles = False
        self._showJoints = False
        self._showMesh = True
        self._showMinBDGSphere = False
        self._showCenterOfMass = True

        self._COMErrorScale = 0.01

        params = [
            3.75162180e-04, 1.70361201e+00, -7.30441228e-01, -6.22795336e-01,
            3.05330848e-01
        ]
        fps = 100

        # params = [0, 0, 0, 0, 0]

        # fps = 100.0
        # model_order = (50, 50)
        # params = [0.00019815056771797725, 1.9687785869242351, -0.9709165752219967, -0.565841931234043, 0.3226849680645409]

        self._armaX = ArmaProcess(params[0], params[1:3], params[3:5], fps)
        self._armaY = ArmaProcess(params[0], params[1:3], params[3:5], fps)
        self._armaZ = ArmaProcess(params[0], params[1:3], params[3:5], fps)

    #
    # Private methods

    def draw(self):
        """Draw the content of the world"""
        world = Physics.world()

        flags = 0
        if self._showAbstractView:
            flags = flags | Physics.SHOW_ABSTRACT_VIEW
        if self._showAbstractViewSkeleton:
            flags = flags | Physics.SHOW_ABSTRACT_VIEW_SKELETON
        if self._showBodyFrame:
            flags = flags | Physics.SHOW_BODY_FRAME
        if self._showCDPrimitives:
            flags = flags | Physics.SHOW_CD_PRIMITIVES
        if self._showColors:
            flags = flags | Physics.SHOW_COLOURS
        if self._showFrictionParticles:
            flags = flags | Physics.SHOW_FRICTION_PARTICLES
        if self._showJoints:
            flags = flags | Physics.SHOW_JOINTS
        if self._showMesh:
            flags = flags | Physics.SHOW_MESH
        if self._showMinBDGSphere:
            flags = flags | Physics.SHOW_MIN_BDG_SPHERE
        if self._showCenterOfMass:
            flags = flags | Physics.SHOW_CENTER_OF_MASS

        glEnable(GL_LIGHTING)
        world.drawRBs(flags)

        glDisable(GL_LIGHTING)

        if self._drawShadows:
            self._glCanvas.beginShadows()
            world.drawRBs(Physics.SHOW_MESH)
            self._glCanvas.endShadows()

        if len(self._characters) > 0:

            self.updateCOMError()

            self.COMPanel.update()

            self._characters[0].drawRealCOM(flags)
            self._characters[0].drawPercievedCOM(flags)

    def postDraw(self):
        """Perform some operation once the entire OpenGL window has been drawn"""
        if self._captureScreenShots:
            self._glCanvas.saveScreenshot("../screenShots/%04d.bmp" %
                                          self._screenShotNumber)
            self._screenShotNumber += 1

    def advanceAnimation(self):
        """Called once per frame"""
        if self._animationRunning:
            self.simulationFrame()

    def simulationFrame(self):
        """Performs enough simulation steps to fill one frame"""

        # Enough time elapsed perform simulation loop and render
        simulationSeconds = 1.0 / self._glCanvas.getFps(
        ) * self._simulationSecondsPerSecond
        nbSteps = int(math.ceil(simulationSeconds / self._dt))

        for i in range(0, nbSteps):
            self.simulationStep()

    def advanceAnimationUntilControllerEnds(self, controller):
        """Advances the animation until the specified controller reaches the end.
        Specify either a name, an index, or an instance of a controller object."""
        controller = self.getController(controller)
        initialPhi = controller.getPhase()
        currPhi = initialPhi + 1
        while currPhi > initialPhi:
            self.simulationStep()
            currPhi = controller.getPhase()

    def updateCOMError(self):

        # Apply ARMA process

        self.setCOMX(self._armaX.generate_frame() * 0.004 * 0)
        self.setCOMY(self._armaY.generate_frame() * 0.001 * 0)
        self.setCOMZ(self._armaZ.generate_frame() * 0.004 * 0)

#         sins = [0.3, 1, 2, 3, 0.5, 1.3, 1.8, 3.4, 0.4, 0.8, 1.5, 3.04]
#         t = time.time()
#
#         # Map each sin weight to the reciprocal of the frequency
#         sins = map(lambda x: (x, 1/x), sins)
#
#         x = self.discreteSinusoids(sins[:4], t)
#         y = self.discreteSinusoids(sins[4:8], t + 1021.34353)
#         z = self.discreteSinusoids(sins[8:], t + 543.4346)
#
#         (x, y, z) = map(lambda x: x * self._COMErrorScale, (x, y, z))
#
#         self.setCOMX(x)
#         self.setCOMY(y)
#         self.setCOMZ(z)
#

    def discreteSinusoids(self, sins, t):
        accumulator = 0
        for sin, weight in sins:
            # Here, sin is interpreted as `t-times` per second
            # And t is the number of seconds
            accumulator += math.sin((t * sin) * (2 * math.pi)) * weight

        return accumulator

    def simulationStep(self):
        """Performs a single simulation step"""

        # TODO Quite hacky
        if self._kinematicMotion:
            import KeyframeEditor
            from MathLib import Trajectory3dv
            try:
                pc = self._posableCharacter
                traj = self._stanceFootToSwingFootTrajectory
            except AttributeError:
                pc = self._posableCharacter = KeyframeEditor.PosableCharacter.PosableCharacter(
                    self.getCharacter(0), self.getController(0))
                traj = self._stanceFootToSwingFootTrajectory = Trajectory3dv()
                traj.addKnot(0, Vector3d(-0.13, 0, -0.4))
                traj.addKnot(0.5, Vector3d(-0.13, 0.125, 0))
                traj.addKnot(1, Vector3d(-0.13, 0, 0.4))
                self._phase = 0
                self._stance = Core.LEFT_STANCE

            stanceToSwing = traj.evaluate_catmull_rom(self._phase)
            if self._stance == Core.RIGHT_STANCE:
                stanceToSwing.x = stanceToSwing.x * -1
            pc.updatePose(self._phase, stanceToSwing, self._stance, True)

            self._phase += 0.00069
            if self._phase >= 1.0:
                self._phase = 0
                if self._stance == Core.LEFT_STANCE:
                    self._stance = Core.RIGHT_STANCE
                else:
                    self._stance = Core.LEFT_STANCE
            return

        world = Physics.world()
        controllers = self._controllerList._objects
        contactForces = world.getContactForces()
        for controller in controllers:
            controller.performPreTasks(self._dt, contactForces)
        world.advanceInTime(self._dt)

        contactForces = world.getContactForces()
        for controller in controllers:
            if controller.performPostTasks(self._dt, contactForces):
                step = Vector3d(controller.getStanceFootPos(),
                                controller.getSwingFootPos())
                step = controller.getCharacterFrame().inverseRotate(step)
                v = controller.getV()
                phi = controller.getPhase()
                if self._printStepReport:
                    print "step: %3.5f %3.5f %3.5f. Vel: %3.5f %3.5f %3.5f  phi = %f" % (
                        step.x, step.y, step.z, v.x, v.y, v.z, phi)

    def cameraTargetFunction(self, currentTarget):
        """Private! Return the point to target, or None if nothing to target."""
        if not self._cameraFollowCharacter or self._followedCharacter == None:
            return None

        pos = self._followedCharacter.getRoot().getCMPosition()
        pos.y = currentTarget.y
        return pos

    #
    # Accessors

    def getWorldOracle(self):
        """Return the world oracle for the application."""
        return self._worldOracle

    def getFrame(self):
        """Returns the application frame."""
        return self._frame

    def setDrawShadows(self, drawShadows):
        """Indicates whether the app should draw shadows or not"""
        self._drawShadows = drawShadows

    def getDrawShadows(self):
        """Checks if the app is drawing shadows"""
        return self._drawShadows

    def getGLCanvas(self):
        """Returns the GL canvas"""
        return self._glCanvas

    def getToolPanel(self):
        """Returns the tool panel"""
        return self._toolPanel

    def setAnimationRunning(self, animationRunning):
        """Indicates whether the animation should run or not"""
        self._animationRunning = animationRunning
        self._animationObservable.notifyObservers()

    def isAnimationRunning(self):
        """Return true if the animation is currently running"""
        return self._animationRunning

    def setSimulationSecondsPerSecond(self, simulationSecondsPerSecond):
        """Sets the speed of the playback. 1 is realtime, 0.5 is slower, 2 is faster"""
        self._simulationSecondsPerSecond = simulationSecondsPerSecond
        self._animationObservable.notifyObservers()

    def getSimulationSecondsPerSecond(self):
        """Return the speed of the playback"""
        return self._simulationSecondsPerSecond

    def setCameraFollowCharacter(self, follow):
        """Indicates whether the camera should follow a character or not"""
        if follow != self._cameraFollowCharacter:
            # Need to toggle
            self._cameraFollowCharacter = follow
            if self._followedCharacter == None:
                try:
                    self._followedCharacter = self._characters[0]
                except IndexError:
                    pass
            self._cameraObservable.notifyObservers()

    def doesCameraFollowCharacter(self):
        """Checks if the camera is currently following a character."""
        return self._cameraFollowCharacter and self._followedCharacter != None

    def setFollowedCharacter(self, character):
        """Indicates which character the camera should be following. Pass an index of a string."""
        character = self.getCharacter(character)

        self._cameraFollowCharacter = True
        self._followedCharacter = character
        self._cameraObservable.notifyObservers()

    def setCameraAutoOrbit(self, autoOrbit):
        """Indicates whether the camera should automatically orbit or not"""
        self._glCanvas.setCameraAutoOrbit(autoOrbit)

    def doesCameraAutoOrbit(self):
        """Checks if the camera is currently automatically orbiting."""
        return self._glCanvas.doesCameraAutoOrbit()

    def drawCollisionVolumes(self, draw):
        """Indicates whether the application should draw collision volumes"""
        if draw != self._drawCollisionVolumes:
            self._drawCollisionVolumes = draw
            self._optionsObservable.notifyObservers()

    def getDrawCollisionVolumes(self):
        """Does the application draw collision volumes?"""
        return self._drawCollisionVolumes

    def setKinematicMotion(self, kinematicMotion):
        """Indicates whether the application should animate only kinematic motion"""
        if kinematicMotion != self._kinematicMotion:
            self._kinematicMotion = kinematicMotion
            self._optionsObservable.notifyObservers()

    def getKinematicMotion(self):
        """Does the application animate only kinematic motion?"""
        return self._kinematicMotion

    def setOption(self, option):
        """ Generates a function for setting an attribute of this class instance. """
        def callable_(value):

            if value != getattr(self, option):
                setattr(self, option, value)
                self._optionsObservable.notifyObservers()

        return callable_

    def getOption(self, option):
        """ Generates a function for getting an attribute of this class instance. """
        def callable_():
            return getattr(self, option)

        return callable_

    def captureScreenShots(self, capture):
        """Indicates whether the application should capture a screenshot at every frame."""
        if capture != self._captureScreenShots:
            self._captureScreenShots = capture
            self._optionsObservable.notifyObservers()

    def getCaptureScreenShots(self):
        """Does the application capture a screenshot at every frame?"""
        return self._captureScreenShots

    #
    # Public methods

    def deleteAllObjects(self):
        """Delete all objects: characters, rigid bodies, snapshots, etc."""
        if self._followedCharacter is not None:
            self._followedCharacter = None
            self._cameraFollowCharacter = False
            self._cameraObservable.notifyObservers()
        self._characters = []
        self._controllerList.clear()
        import Physics
        Physics.world().destroyAllObjects()
        self.deleteAllSnapshots()

    def addCharacter(self, character):
        """Adds a character to the application and the world"""
        import Physics
        if PyUtils.sameObjectInList(character, self._characters):
            raise KeyError(
                'Cannot add the same character twice to application.')
        Physics.world().addArticulatedFigure(character)
        self._characters.append(character)
        if self._followedCharacter is None:
            self._followedCharacter = character
            self._cameraFollowCharacter = True
            self._cameraObservable.notifyObservers()
        self._characterObservable.notifyObservers()

    def deleteCharacter(self, character):
        """Removes a character from the application. Specify either a name, an index, or an instance of a character object."""
        character = self.getCharacter(character)
        if self._followedCharacter is character:
            self._followedCharacter = None
            self._cameraFollowCharacter = False
            self._cameraObservable.notifyObservers()
        self._characters.remove(character)
        self._characterObservable.notifyObservers()

    def getCharacter(self, description):
        """Returns a character. Specify either a name, an index. Anything else will be returned unmodified."""
        if isinstance(description, basestring):
            try:
                description = [char.getName()
                               for char in self._characters].index(description)
            except ValueError:
                raise ValueError("No character found with the specified name.")
        if isinstance(description, int):
            return self._characters[description]
        return description

    def getCharacterCount(self):
        """Returns the number of characters."""
        return len(self._character)

    def recenterCharacter(self, character):
        """Reposition the character at the center of the world in X,Z. Specify either a name, an index, or an instance of a character object."""
        character = self.getCharacter(character)
        character.recenter()

    def addController(self, controller):
        """Adds a controller to the application"""
        return self._controllerList.add(controller)

    def deleteController(self, controller):
        """Removes a controller from the application. Specify either a name, an index, or an instance of a controller object."""
        return self._controllerList.delete(controller)

    def getController(self, description):
        """Returns a controller. Specify either a name, an index. Anything else will be returned unmodified."""
        return self._controllerList.get(description)

    def getControllerCount(self):
        """Returns the number of controllers."""
        return self._controllerList.getCount()

    def getControllerList(self):
        """Returns the controller list object. Useful for observation."""
        return self._controllerList

    def addCurve(self, name, trajectory1d, phiPtr=None):
        """Adds a curve to the application"""
        return self._curveList.add(Curve(name, trajectory1d, phiPtr))

    def deleteCurve(self, curve):
        """Removes a curve from the application. Specify either a name, an index, or an instance of a controller object."""
        return self._curveList.delete(curve)

    def clearCurves(self):
        """Remove all the curves from the application."""
        self._curveList.clear()

    def getCurve(self, description):
        """Returns a curve. Specify either a name, an index. Anything else will be returned unmodified."""
        return self._curveList.get(description)

    def getCurveCount(self):
        """Returns the number of curves."""
        return self._curveList.getCount()

    def getCurveList(self):
        """Returns the curve list object. Useful for observation."""
        return self._curveList

    def getSnapshotTree(self):
        """Returns the top-level SnapshotBranch that can be observed."""
        return self._snapshotTree

    def takeSnapshot(self):
        """Take a snapshot of the world.
        The snapshot will be returned and added to the snapshot tree."""
        return self._snapshotTree.takeSnapshot()

    def restoreActiveSnapshot(self, restoreControllerParams=True):
        """Restores the current snapshot. Return it."""
        return self._snapshotTree.restoreActive(restoreControllerParams)

    def previousSnapshot(self, restoreControllerParams=True):
        """Navigate to the previous snapshot. Return it, or None if failed."""
        return self._snapshotTree.previousSnapshot(restoreControllerParams)

    def nextSnapshot(self, restoreControllerParams=True):
        """Navigate to the next snapshot. Return it, or None if failed."""
        return self._snapshotTree.nextSnapshot(restoreControllerParams)

    def deleteAllSnapshots(self):
        """Delete all the snapshots of the world."""
        self._snapshotTree = SnapshotBranch()

    def setCOMX(self, val):
        if len(self._characters) > 0:
            self._characters[0].COM_offset.x = val

    def getCOMX(self):
        if len(self._characters) > 0:
            return self._characters[0].COM_offset.x
        else:
            return 0

    def setCOMY(self, val):
        if len(self._characters) > 0:
            self._characters[0].COM_offset.y = val

    def getCOMY(self):
        if len(self._characters) > 0:
            return self._characters[0].COM_offset.y
        else:
            return 0

    def setCOMZ(self, val):
        if len(self._characters) > 0:
            self._characters[0].COM_offset.z = val

    def getCOMZ(self):
        if len(self._characters) > 0:
            return self._characters[0].COM_offset.z
        else:
            return 0

    #
    # For observers
    #
    def addCharacterObserver(self, observer):
        self._characterObservable.addObserver(observer)

    def deleteCharacterObserver(self, observer):
        self._characterObservable.deleteObserver(observer)

    def addControllerObserver(self, observer):
        self._controllerList.addObserver(observer)

    def deleteControllerObserver(self, observer):
        self._controllerList.deleteObserver(observer)

    def addAnimationObserver(self, observer):
        self._animationObservable.addObserver(observer)

    def deleteAnimationObserver(self, observer):
        self._animationObservable.deleteObserver(observer)

    def addCameraObserver(self, observer):
        self._cameraObservable.addObserver(observer)

    def deleteCameraObserver(self, observer):
        self._cameraObservable.deleteObserver(observer)

    def addOptionsObserver(self, observer):
        self._optionsObservable.addObserver(observer)

    def deleteOptionsObserver(self, observer):
        self._optionsObservable.deleteObserver(observer)

    def addCOMObserver(self, observer):
        self._COMObservable.addObserver(observer)

    def deleteCOMObserver(self, observer):
        self._COMObservable.deleteObserver(observer)
class SNMApp(wx.App):
    """A simple class that should handle almost everything a simbicon application typically needs."""

    def __init__(self, appTitle="Simbicon Application", 
                 fps = 30.0,
                 dt = 1/2000.0,
                 glCanvasSize=wx.DefaultSize,
                 size=wx.DefaultSize, redirect=False, filename=None,
                 useBestVisual=False, clearSigInt=True, showConsole=True):
        """
        appTitle is the window title
        fps is the desired number of frames per seconds
        dt is the desired simulation timestep
        :see: wx.BasicApp.__init__`
        """
        
        wx.App.__init__(self, redirect, filename, useBestVisual, clearSigInt)

        # No annoying error logging window
        wx.Log.SetActiveTarget(wx.LogStderr())

        import UI

        # Setup the main window style
        style = wx.DEFAULT_FRAME_STYLE
        if size == wx.DefaultSize :
            size = wx.GetDisplaySize()
            size.height *= 0.75        
            size.width *= 0.75
            if glCanvasSize == wx.DefaultSize :
                style |= wx.MAXIMIZE
       
        # Setup the environment for the python interactive console
        consoleEnvironment = {
            "wx" : wx,
            "Physics" : Physics,
            "Utils" : Utils }
        exec "from MathLib import *\n" + \
             "app = wx.GetApp()\n" + \
             "from PyUtils import load" in consoleEnvironment, consoleEnvironment
       
        # Create the main window
        self._frame = UI.MainWindow(None, -1, appTitle, size = size, style = style, 
                                    fps = fps, glCanvasSize = glCanvasSize,
                                    showConsole = showConsole,
                                    consoleEnvironment = consoleEnvironment)
        
        # Define GL callbacks
        self._glCanvas = self._frame.getGLCanvas()
        self._glCanvas.addDrawCallback( self.draw )
        self._glCanvas.addPostDrawCallback( self.postDraw )
        self._glCanvas.addOncePerFrameCallback( self.advanceAnimation )
        self._glCanvas.setDrawAxes(False)
        self._glCanvas.setPrintLoad(True)
        self._glCanvas.setCameraTargetFunction( self.cameraTargetFunction )
        
        # Get the tool panel
        self._toolPanel = self._frame.getToolPanel()
        
        # Show the application
        self._frame.Show()
        
        # Set-up starting state
        self._dt = dt      
        self._drawShadows = True
        self._simulationSecondsPerSecond = 1   # 1 = real time, 2 = twice real time, 1/2 = half real time
        self._animationRunning = False
        self._cameraFollowCharacter = False
        self._drawCollisionVolumes = False
        self._followedCharacter = None # Pointer to focused character
        self._captureScreenShots = False
        self._printStepReport = True
        self._screenShotNumber = 0
        self._worldOracle = Core.WorldOracle()
        self._worldOracle.initializeWorld( Physics.world() )
        self._kinematicMotion = False
        
        # Set-up starting list of characters and controllers
        self._characters = []
    
        # Define the observables
        self._controllerList = ObservableList() 
        self._characterObservable = PyUtils.Observable()
        self._animationObservable = PyUtils.Observable()
        self._cameraObservable = PyUtils.Observable()
        self._optionsObservable = PyUtils.Observable()
        self._curveList = ObservableList()
        self._snapshotTree = SnapshotBranch()
            
    #
    # Private methods
        
    def draw(self):
        """Draw the content of the world"""
        world = Physics.world()
                    
        glEnable(GL_LIGHTING)
        if self._drawCollisionVolumes:
            world.drawRBs(Physics.SHOW_MESH|Physics.SHOW_CD_PRIMITIVES)
        else:
            world.drawRBs(Physics.SHOW_MESH|Physics.SHOW_COLOURS)            
#        world.drawRBs(Physics.SHOW_MESH|Physics.SHOW_CD_PRIMITIVES)
        glDisable(GL_LIGHTING);
    
        if self._drawShadows:
            self._glCanvas.beginShadows()
            world.drawRBs(Physics.SHOW_MESH)
            self._glCanvas.endShadows()    

    def postDraw(self):
        """Perform some operation once the entire OpenGL window has been drawn"""
        if self._captureScreenShots:
            self._glCanvas.saveScreenshot("../screenShots/%04d.bmp" % self._screenShotNumber )
            self._screenShotNumber += 1

    def advanceAnimation(self):
        """Called once per frame"""
        if self._animationRunning :
            self.simulationFrame()

    def simulationFrame(self):
        """Performs enough simulation steps to fill one frame"""
        
        # Enough time elapsed perform simulation loop and render
        simulationSeconds = 1.0/self._glCanvas.getFps() * self._simulationSecondsPerSecond
        nbSteps = int( math.ceil( simulationSeconds / self._dt ) )
 
        for i in range(0,nbSteps):
            self.simulationStep()

    def advanceAnimationUntilControllerEnds(self, controller):
        """Advances the animation until the specified controller reaches the end.
        Specify either a name, an index, or an instance of a controller object."""
        controller = self.getController(controller)
        initialPhi = controller.getPhase()
        currPhi = initialPhi + 1
        while currPhi > initialPhi :
            self.simulationStep()
            currPhi = controller.getPhase()
        
    def simulationStep(self):
        """Performs a single simulation step"""

        # TODO Quite hacky
        if self._kinematicMotion:
            import KeyframeEditor 
            from MathLib import Trajectory3dv
            try:
                pc = self._posableCharacter
                traj = self._stanceFootToSwingFootTrajectory
            except AttributeError:
                pc = self._posableCharacter = KeyframeEditor.PosableCharacter.PosableCharacter(self.getCharacter(0),self.getController(0))        
                traj = self._stanceFootToSwingFootTrajectory = Trajectory3dv()
                traj.addKnot(0,Vector3d(-0.13,0,-0.4))
                traj.addKnot(0.5,Vector3d(-0.13,0.125,0))
                traj.addKnot(1,Vector3d(-0.13,0,0.4))
                self._phase = 0                        
                self._stance = Core.LEFT_STANCE
                
            stanceToSwing = traj.evaluate_catmull_rom(self._phase)
            if self._stance == Core.RIGHT_STANCE:
                stanceToSwing.x = stanceToSwing.x * -1
            pc.updatePose( self._phase, stanceToSwing, self._stance, True )
            
            self._phase += 0.00069
            if self._phase >= 1.0:
                self._phase = 0
                if self._stance == Core.LEFT_STANCE:
                    self._stance = Core.RIGHT_STANCE
                else:
                    self._stance = Core.LEFT_STANCE
            return


        
        world = Physics.world()
        controllers = self._controllerList._objects
        contactForces = world.getContactForces()
        for controller in controllers :
            controller.performPreTasks(self._dt, contactForces)
        world.advanceInTime(self._dt)
        
        contactForces = world.getContactForces()
        for controller in controllers :
            if controller.performPostTasks(self._dt, contactForces) :
                step = Vector3d (controller.getStanceFootPos(), controller.getSwingFootPos())
                step = controller.getCharacterFrame().inverseRotate(step);
                v = controller.getV()
                phi = controller.getPhase()
                if self._printStepReport:
                    print "step: %3.5f %3.5f %3.5f. Vel: %3.5f %3.5f %3.5f  phi = %f" % ( step.x, step.y, step.z, v.x, v.y, v.z, phi)

        
    
    def cameraTargetFunction(self, currentTarget):
        """Private! Return the point to target, or None if nothing to target."""
        if not self._cameraFollowCharacter or self._followedCharacter == None:
            return None
        
        pos = self._followedCharacter.getRoot().getCMPosition()
        pos.y = currentTarget.y
        return pos
    

    #    
    # Accessors
    
    def getWorldOracle(self):
        """Return the world oracle for the application."""
        return self._worldOracle
    
    def getFrame(self):
        """Returns the application frame."""
        return self._frame
    
    def setDrawShadows(self, drawShadows):
        """Indicates whether the app should draw shadows or not"""
        self._drawShadows = drawShadows
        
    def getDrawShadows(self):
        """Checks if the app is drawing shadows"""
        return self._drawShadows
    
    def getGLCanvas(self):
        """Returns the GL canvas"""
        return self._glCanvas
    
    def getToolPanel(self):
        """Returns the tool panel"""
        return self._toolPanel
    
    def setAnimationRunning(self, animationRunning):
        """Indicates whether the animation should run or not"""
        self._animationRunning = animationRunning
        self._animationObservable.notifyObservers()

    def isAnimationRunning(self):
        """Return true if the animation is currently running"""
        return self._animationRunning

    def setSimulationSecondsPerSecond(self, simulationSecondsPerSecond):
        """Sets the speed of the playback. 1 is realtime, 0.5 is slower, 2 is faster"""
        self._simulationSecondsPerSecond = simulationSecondsPerSecond
        self._animationObservable.notifyObservers()

    def getSimulationSecondsPerSecond(self):
        """Return the speed of the playback"""
        return self._simulationSecondsPerSecond
    
    def setCameraFollowCharacter(self, follow):
        """Indicates whether the camera should follow a character or not"""
        if follow != self._cameraFollowCharacter:
            # Need to toggle
            self._cameraFollowCharacter = follow
            if self._followedCharacter == None :
                try: self._followedCharacter = self._characters[0]
                except IndexError: pass
            self._cameraObservable.notifyObservers()
    
    def doesCameraFollowCharacter(self):
        """Checks if the camera is currently following a character."""
        return self._cameraFollowCharacter  and  self._followedCharacter != None
    
    def setFollowedCharacter(self, character):
        """Indicates which character the camera should be following. Pass an index of a string."""
        character = self.getCharacter(character)

        self._cameraFollowCharacter = True
        self._followedCharacter = character
        self._cameraObservable.notifyObservers()

    def setCameraAutoOrbit(self, autoOrbit):
        """Indicates whether the camera should automatically orbit or not"""
        self._glCanvas.setCameraAutoOrbit(autoOrbit)
            
    def doesCameraAutoOrbit(self):
        """Checks if the camera is currently automatically orbiting."""
        return self._glCanvas.doesCameraAutoOrbit()    
        
    def drawCollisionVolumes(self, draw):
        """Indicates whether the application should draw collision volumes"""
        if draw != self._drawCollisionVolumes:
            self._drawCollisionVolumes = draw
            self._optionsObservable.notifyObservers()
    
    def getDrawCollisionVolumes(self):
        """Does the application draw collision volumes?"""
        return self._drawCollisionVolumes
        
    def setKinematicMotion(self, kinematicMotion):
        """Indicates whether the application should animate only kinematic motion"""
        if kinematicMotion != self._kinematicMotion:
            self._kinematicMotion = kinematicMotion
            self._optionsObservable.notifyObservers()
    
    def getKinematicMotion(self):
        """Does the application animate only kinematic motion?"""
        return self._kinematicMotion
    
    def captureScreenShots(self, capture):
        """Indicates whether the application should capture a screenshot at every frame."""
        if capture != self._captureScreenShots:
            self._captureScreenShots = capture
            self._optionsObservable.notifyObservers()
    
    def getCaptureScreenShots(self):
        """Does the application capture a screenshot at every frame?"""
        return self._captureScreenShots
    
    #
    # Public methods
       
    def deleteAllObjects(self):
        """Delete all objects: characters, rigid bodies, snapshots, etc."""
        if self._followedCharacter is not None :           
            self._followedCharacter = None
            self._cameraFollowCharacter = False
            self._cameraObservable.notifyObservers()        
        self._characters = []
        self._controllerList.clear()
        import Physics
        Physics.world().destroyAllObjects()
        self.deleteAllSnapshots()        
       
    def addCharacter(self, character):
        """Adds a character to the application and the world"""
        import Physics
        if PyUtils.sameObjectInList(character, self._characters) :
            raise KeyError ('Cannot add the same character twice to application.')
        Physics.world().addArticulatedFigure( character )
        self._characters.append( character )
        if self._followedCharacter is None :
            self._followedCharacter = character
            self._cameraFollowCharacter = True
            self._cameraObservable.notifyObservers()
        self._characterObservable.notifyObservers()

    def deleteCharacter(self, character):
        """Removes a character from the application. Specify either a name, an index, or an instance of a character object."""        
        character = self.getCharacter(character)
        if self._followedCharacter is character :           
            self._followedCharacter = None
            self._cameraFollowCharacter = False
            self._cameraObservable.notifyObservers()
        self._characters.remove(character)
        self._characterObservable.notifyObservers()
        
    def getCharacter(self, description ):
        """Returns a character. Specify either a name, an index. Anything else will be returned unmodified."""
        if isinstance(description,basestring) :
            try: 
                description = [char.getName() for char in self._characters].index(description)
            except ValueError: raise ValueError( "No character found with the specified name." )
        if isinstance(description,int) :
            return self._characters[description]
        return description
    
    def getCharacterCount(self):
        """Returns the number of characters."""
        return len( self._character )
    
    def recenterCharacter(self, character):
        """Reposition the character at the center of the world in X,Z. Specify either a name, an index, or an instance of a character object."""
        character = self.getCharacter(character)
        character.recenter()
        
    
    def addController(self, controller):
        """Adds a controller to the application"""
        return self._controllerList.add(controller)

    def deleteController(self, controller):
        """Removes a controller from the application. Specify either a name, an index, or an instance of a controller object."""        
        return self._controllerList.delete(controller)

    def getController(self, description):        
        """Returns a controller. Specify either a name, an index. Anything else will be returned unmodified."""
        return self._controllerList.get(description)

    def getControllerCount(self):
        """Returns the number of controllers."""
        return self._controllerList.getCount()
        
    def getControllerList(self):
        """Returns the controller list object. Useful for observation."""
        return self._controllerList
    
    
    def addCurve(self, name, trajectory1d, phiPtr = None):
        """Adds a curve to the application"""
        return self._curveList.add( Curve(name, trajectory1d, phiPtr) )

    def deleteCurve(self, curve):
        """Removes a curve from the application. Specify either a name, an index, or an instance of a controller object."""        
        return self._curveList.delete(curve)

    def clearCurves(self):
        """Remove all the curves from the application."""
        self._curveList.clear();
    
    def getCurve(self, description):
        """Returns a curve. Specify either a name, an index. Anything else will be returned unmodified."""
        return self._curveList.get(description)

    def getCurveCount(self):
        """Returns the number of curves."""
        return self._curveList.getCount()
        
    def getCurveList(self):
        """Returns the curve list object. Useful for observation."""
        return self._curveList
    
    def getSnapshotTree(self):
        """Returns the top-level SnapshotBranch that can be observed.""" 
        return self._snapshotTree
    
    def takeSnapshot(self):
        """Take a snapshot of the world.
        The snapshot will be returned and added to the snapshot tree."""
        return self._snapshotTree.takeSnapshot()
    
    def restoreActiveSnapshot(self, restoreControllerParams = True):
        """Restores the current snapshot. Return it."""
        return self._snapshotTree.restoreActive(restoreControllerParams)

    def previousSnapshot(self, restoreControllerParams = True):
        """Navigate to the previous snapshot. Return it, or None if failed."""
        return self._snapshotTree.previousSnapshot(restoreControllerParams)
    
    def nextSnapshot(self, restoreControllerParams = True):
        """Navigate to the next snapshot. Return it, or None if failed."""
        return self._snapshotTree.nextSnapshot(restoreControllerParams)

    def deleteAllSnapshots(self):
        """Delete all the snapshots of the world."""
        self._snapshotTree = SnapshotBranch()    
    #
    # For observers
    #    
    def addCharacterObserver(self, observer):
        self._characterObservable.addObserver(observer)
    
    def deleteCharacterObserver(self, observer):
        self._characterObservable.deleteObserver(observer)
    
    def addControllerObserver(self, observer):
        self._controllerList.addObserver(observer)
    
    def deleteControllerObserver(self, observer):
        self._controllerList.deleteObserver(observer)
    
    def addAnimationObserver(self, observer):
        self._animationObservable.addObserver(observer)
    
    def deleteAnimationObserver(self, observer):
        self._animationObservable.deleteObserver(observer)        
    
    def addCameraObserver(self, observer):
        self._cameraObservable.addObserver(observer)
    
    def deleteCameraObserver(self, observer):
        self._cameraObservable.deleteObserver(observer)
        
    def addOptionsObserver(self, observer):
        self._optionsObservable.addObserver(observer)
    
    def deleteOptionsObserver(self, observer):
        self._optionsObservable.deleteObserver(observer)
Exemple #3
0
class SNMApp(wx.App):
    """A simple class that should handle almost everything a simbicon application typically needs."""
    def __init__(self,
                 appTitle="Simbicon Application",
                 fps=30.0,
                 dt=1 / 2000.0,
                 glCanvasSize=wx.DefaultSize,
                 size=wx.DefaultSize,
                 redirect=False,
                 filename=None,
                 useBestVisual=False,
                 clearSigInt=True,
                 showConsole=True):
        """
        appTitle is the window title
        fps is the desired number of frames per seconds
        dt is the desired simulation timestep
        :see: wx.BasicApp.__init__`
        """

        wx.App.__init__(self, redirect, filename, useBestVisual, clearSigInt)

        # No annoying error logging window
        wx.Log.SetActiveTarget(wx.LogStderr())

        import UI

        # Setup the main window style
        style = wx.DEFAULT_FRAME_STYLE
        if size == wx.DefaultSize:
            size = wx.GetDisplaySize()
            size.height *= 0.75
            size.width *= 0.75
            if glCanvasSize == wx.DefaultSize:
                style |= wx.MAXIMIZE

        # Setup the environment for the python interactive console
        consoleEnvironment = {"wx": wx, "Physics": Physics, "Utils": Utils}
        exec "from MathLib import *\n" + \
             "app = wx.GetApp()\n" + \
             "from PyUtils import load" in consoleEnvironment, consoleEnvironment

        # Create the main window
        self._frame = UI.MainWindow(None,
                                    -1,
                                    appTitle,
                                    size=size,
                                    style=style,
                                    fps=fps,
                                    glCanvasSize=glCanvasSize,
                                    showConsole=showConsole,
                                    consoleEnvironment=consoleEnvironment)

        # Define GL callbacks
        self._glCanvas = self._frame.getGLCanvas()
        self._glCanvas.addDrawCallback(self.draw)
        self._glCanvas.addPostDrawCallback(self.postDraw)
        self._glCanvas.addOncePerFrameCallback(self.advanceAnimation)
        self._glCanvas.setDrawAxes(False)
        self._glCanvas.setPrintLoad(True)
        self._glCanvas.setCameraTargetFunction(self.cameraTargetFunction)

        # Get the tool panel
        self._toolPanel = self._frame.getToolPanel()

        # Show the application
        self._frame.Show()

        # Set-up starting state
        self._dt = dt
        self._drawShadows = True
        self._simulationSecondsPerSecond = 1  # 1 = real time, 2 = twice real time, 1/2 = half real time
        self._animationRunning = False
        self._cameraFollowCharacter = False
        self._drawCollisionVolumes = False
        self._followedCharacter = None  # Pointer to focused character
        self._captureScreenShots = False
        self._printStepReport = True
        self._screenShotNumber = 0
        self._worldOracle = Core.WorldOracle()
        self._worldOracle.initializeWorld(Physics.world())
        self._kinematicMotion = False

        # Set-up starting list of characters and controllers
        self._characters = []

        # Define the observables
        self._controllerList = ObservableList()
        self._characterObservable = PyUtils.Observable()
        self._animationObservable = PyUtils.Observable()
        self._cameraObservable = PyUtils.Observable()
        self._optionsObservable = PyUtils.Observable()
        self._curveList = ObservableList()
        self._snapshotTree = SnapshotBranch()

    #
    # Private methods

    def draw(self):
        """Draw the content of the world"""
        world = Physics.world()

        glEnable(GL_LIGHTING)
        if self._drawCollisionVolumes:
            world.drawRBs(Physics.SHOW_MESH | Physics.SHOW_CD_PRIMITIVES)
        else:
            world.drawRBs(Physics.SHOW_MESH | Physics.SHOW_COLOURS)
#        world.drawRBs(Physics.SHOW_MESH|Physics.SHOW_CD_PRIMITIVES)
        glDisable(GL_LIGHTING)

        if self._drawShadows:
            self._glCanvas.beginShadows()
            world.drawRBs(Physics.SHOW_MESH)
            self._glCanvas.endShadows()

    def postDraw(self):
        """Perform some operation once the entire OpenGL window has been drawn"""
        if self._captureScreenShots:
            self._glCanvas.saveScreenshot("../screenShots/%04d.bmp" %
                                          self._screenShotNumber)
            self._screenShotNumber += 1

    def advanceAnimation(self):
        """Called once per frame"""
        if self._animationRunning:
            self.simulationFrame()

    def simulationFrame(self):
        """Performs enough simulation steps to fill one frame"""

        # Enough time elapsed perform simulation loop and render
        simulationSeconds = 1.0 / self._glCanvas.getFps(
        ) * self._simulationSecondsPerSecond
        nbSteps = int(math.ceil(simulationSeconds / self._dt))

        for i in range(0, nbSteps):
            self.simulationStep()

    def advanceAnimationUntilControllerEnds(self, controller):
        """Advances the animation until the specified controller reaches the end.
        Specify either a name, an index, or an instance of a controller object."""
        controller = self.getController(controller)
        initialPhi = controller.getPhase()
        currPhi = initialPhi + 1
        while currPhi > initialPhi:
            self.simulationStep()
            currPhi = controller.getPhase()

    def simulationStep(self):
        """Performs a single simulation step"""

        # TODO Quite hacky
        if self._kinematicMotion:
            import KeyframeEditor
            from MathLib import Trajectory3dv
            try:
                pc = self._posableCharacter
                traj = self._stanceFootToSwingFootTrajectory
            except AttributeError:
                pc = self._posableCharacter = KeyframeEditor.PosableCharacter.PosableCharacter(
                    self.getCharacter(0), self.getController(0))
                traj = self._stanceFootToSwingFootTrajectory = Trajectory3dv()
                traj.addKnot(0, Vector3d(-0.13, 0, -0.4))
                traj.addKnot(0.5, Vector3d(-0.13, 0.125, 0))
                traj.addKnot(1, Vector3d(-0.13, 0, 0.4))
                self._phase = 0
                self._stance = Core.LEFT_STANCE

            stanceToSwing = traj.evaluate_catmull_rom(self._phase)
            if self._stance == Core.RIGHT_STANCE:
                stanceToSwing.x = stanceToSwing.x * -1
            pc.updatePose(self._phase, stanceToSwing, self._stance, True)

            self._phase += 0.00069
            if self._phase >= 1.0:
                self._phase = 0
                if self._stance == Core.LEFT_STANCE:
                    self._stance = Core.RIGHT_STANCE
                else:
                    self._stance = Core.LEFT_STANCE
            return

        world = Physics.world()
        controllers = self._controllerList._objects
        contactForces = world.getContactForces()
        for controller in controllers:
            controller.performPreTasks(self._dt, contactForces)
        world.advanceInTime(self._dt)

        contactForces = world.getContactForces()
        for controller in controllers:
            if controller.performPostTasks(self._dt, contactForces):
                step = Vector3d(controller.getStanceFootPos(),
                                controller.getSwingFootPos())
                step = controller.getCharacterFrame().inverseRotate(step)
                v = controller.getV()
                phi = controller.getPhase()
                if self._printStepReport:
                    print "step: %3.5f %3.5f %3.5f. Vel: %3.5f %3.5f %3.5f  phi = %f" % (
                        step.x, step.y, step.z, v.x, v.y, v.z, phi)

    def cameraTargetFunction(self, currentTarget):
        """Private! Return the point to target, or None if nothing to target."""
        if not self._cameraFollowCharacter or self._followedCharacter == None:
            return None

        pos = self._followedCharacter.getRoot().getCMPosition()
        pos.y = currentTarget.y
        return pos

    #
    # Accessors

    def getWorldOracle(self):
        """Return the world oracle for the application."""
        return self._worldOracle

    def getFrame(self):
        """Returns the application frame."""
        return self._frame

    def setDrawShadows(self, drawShadows):
        """Indicates whether the app should draw shadows or not"""
        self._drawShadows = drawShadows

    def getDrawShadows(self):
        """Checks if the app is drawing shadows"""
        return self._drawShadows

    def getGLCanvas(self):
        """Returns the GL canvas"""
        return self._glCanvas

    def getToolPanel(self):
        """Returns the tool panel"""
        return self._toolPanel

    def setAnimationRunning(self, animationRunning):
        """Indicates whether the animation should run or not"""
        self._animationRunning = animationRunning
        self._animationObservable.notifyObservers()

    def isAnimationRunning(self):
        """Return true if the animation is currently running"""
        return self._animationRunning

    def setSimulationSecondsPerSecond(self, simulationSecondsPerSecond):
        """Sets the speed of the playback. 1 is realtime, 0.5 is slower, 2 is faster"""
        self._simulationSecondsPerSecond = simulationSecondsPerSecond
        self._animationObservable.notifyObservers()

    def getSimulationSecondsPerSecond(self):
        """Return the speed of the playback"""
        return self._simulationSecondsPerSecond

    def setCameraFollowCharacter(self, follow):
        """Indicates whether the camera should follow a character or not"""
        if follow != self._cameraFollowCharacter:
            # Need to toggle
            self._cameraFollowCharacter = follow
            if self._followedCharacter == None:
                try:
                    self._followedCharacter = self._characters[0]
                except IndexError:
                    pass
            self._cameraObservable.notifyObservers()

    def doesCameraFollowCharacter(self):
        """Checks if the camera is currently following a character."""
        return self._cameraFollowCharacter and self._followedCharacter != None

    def setFollowedCharacter(self, character):
        """Indicates which character the camera should be following. Pass an index of a string."""
        character = self.getCharacter(character)

        self._cameraFollowCharacter = True
        self._followedCharacter = character
        self._cameraObservable.notifyObservers()

    def setCameraAutoOrbit(self, autoOrbit):
        """Indicates whether the camera should automatically orbit or not"""
        self._glCanvas.setCameraAutoOrbit(autoOrbit)

    def doesCameraAutoOrbit(self):
        """Checks if the camera is currently automatically orbiting."""
        return self._glCanvas.doesCameraAutoOrbit()

    def drawCollisionVolumes(self, draw):
        """Indicates whether the application should draw collision volumes"""
        if draw != self._drawCollisionVolumes:
            self._drawCollisionVolumes = draw
            self._optionsObservable.notifyObservers()

    def getDrawCollisionVolumes(self):
        """Does the application draw collision volumes?"""
        return self._drawCollisionVolumes

    def setKinematicMotion(self, kinematicMotion):
        """Indicates whether the application should animate only kinematic motion"""
        if kinematicMotion != self._kinematicMotion:
            self._kinematicMotion = kinematicMotion
            self._optionsObservable.notifyObservers()

    def getKinematicMotion(self):
        """Does the application animate only kinematic motion?"""
        return self._kinematicMotion

    def captureScreenShots(self, capture):
        """Indicates whether the application should capture a screenshot at every frame."""
        if capture != self._captureScreenShots:
            self._captureScreenShots = capture
            self._optionsObservable.notifyObservers()

    def getCaptureScreenShots(self):
        """Does the application capture a screenshot at every frame?"""
        return self._captureScreenShots

    #
    # Public methods

    def deleteAllObjects(self):
        """Delete all objects: characters, rigid bodies, snapshots, etc."""
        if self._followedCharacter is not None:
            self._followedCharacter = None
            self._cameraFollowCharacter = False
            self._cameraObservable.notifyObservers()
        self._characters = []
        self._controllerList.clear()
        import Physics
        Physics.world().destroyAllObjects()
        self.deleteAllSnapshots()

    def addCharacter(self, character):
        """Adds a character to the application and the world"""
        import Physics
        if PyUtils.sameObjectInList(character, self._characters):
            raise KeyError(
                'Cannot add the same character twice to application.')
        Physics.world().addArticulatedFigure(character)
        self._characters.append(character)
        if self._followedCharacter is None:
            self._followedCharacter = character
            self._cameraFollowCharacter = True
            self._cameraObservable.notifyObservers()
        self._characterObservable.notifyObservers()

    def deleteCharacter(self, character):
        """Removes a character from the application. Specify either a name, an index, or an instance of a character object."""
        character = self.getCharacter(character)
        if self._followedCharacter is character:
            self._followedCharacter = None
            self._cameraFollowCharacter = False
            self._cameraObservable.notifyObservers()
        self._characters.remove(character)
        self._characterObservable.notifyObservers()

    def getCharacter(self, description):
        """Returns a character. Specify either a name, an index. Anything else will be returned unmodified."""
        if isinstance(description, basestring):
            try:
                description = [char.getName()
                               for char in self._characters].index(description)
            except ValueError:
                raise ValueError("No character found with the specified name.")
        if isinstance(description, int):
            return self._characters[description]
        return description

    def getCharacterCount(self):
        """Returns the number of characters."""
        return len(self._character)

    def recenterCharacter(self, character):
        """Reposition the character at the center of the world in X,Z. Specify either a name, an index, or an instance of a character object."""
        character = self.getCharacter(character)
        character.recenter()

    def addController(self, controller):
        """Adds a controller to the application"""
        return self._controllerList.add(controller)

    def deleteController(self, controller):
        """Removes a controller from the application. Specify either a name, an index, or an instance of a controller object."""
        return self._controllerList.delete(controller)

    def getController(self, description):
        """Returns a controller. Specify either a name, an index. Anything else will be returned unmodified."""
        return self._controllerList.get(description)

    def getControllerCount(self):
        """Returns the number of controllers."""
        return self._controllerList.getCount()

    def getControllerList(self):
        """Returns the controller list object. Useful for observation."""
        return self._controllerList

    def addCurve(self, name, trajectory1d, phiPtr=None):
        """Adds a curve to the application"""
        return self._curveList.add(Curve(name, trajectory1d, phiPtr))

    def deleteCurve(self, curve):
        """Removes a curve from the application. Specify either a name, an index, or an instance of a controller object."""
        return self._curveList.delete(curve)

    def clearCurves(self):
        """Remove all the curves from the application."""
        self._curveList.clear()

    def getCurve(self, description):
        """Returns a curve. Specify either a name, an index. Anything else will be returned unmodified."""
        return self._curveList.get(description)

    def getCurveCount(self):
        """Returns the number of curves."""
        return self._curveList.getCount()

    def getCurveList(self):
        """Returns the curve list object. Useful for observation."""
        return self._curveList

    def getSnapshotTree(self):
        """Returns the top-level SnapshotBranch that can be observed."""
        return self._snapshotTree

    def takeSnapshot(self):
        """Take a snapshot of the world.
        The snapshot will be returned and added to the snapshot tree."""
        return self._snapshotTree.takeSnapshot()

    def restoreActiveSnapshot(self, restoreControllerParams=True):
        """Restores the current snapshot. Return it."""
        return self._snapshotTree.restoreActive(restoreControllerParams)

    def previousSnapshot(self, restoreControllerParams=True):
        """Navigate to the previous snapshot. Return it, or None if failed."""
        return self._snapshotTree.previousSnapshot(restoreControllerParams)

    def nextSnapshot(self, restoreControllerParams=True):
        """Navigate to the next snapshot. Return it, or None if failed."""
        return self._snapshotTree.nextSnapshot(restoreControllerParams)

    def deleteAllSnapshots(self):
        """Delete all the snapshots of the world."""
        self._snapshotTree = SnapshotBranch()

    #
    # For observers
    #
    def addCharacterObserver(self, observer):
        self._characterObservable.addObserver(observer)

    def deleteCharacterObserver(self, observer):
        self._characterObservable.deleteObserver(observer)

    def addControllerObserver(self, observer):
        self._controllerList.addObserver(observer)

    def deleteControllerObserver(self, observer):
        self._controllerList.deleteObserver(observer)

    def addAnimationObserver(self, observer):
        self._animationObservable.addObserver(observer)

    def deleteAnimationObserver(self, observer):
        self._animationObservable.deleteObserver(observer)

    def addCameraObserver(self, observer):
        self._cameraObservable.addObserver(observer)

    def deleteCameraObserver(self, observer):
        self._cameraObservable.deleteObserver(observer)

    def addOptionsObserver(self, observer):
        self._optionsObservable.addObserver(observer)

    def deleteOptionsObserver(self, observer):
        self._optionsObservable.deleteObserver(observer)