Browse Source

*** empty log message ***

Mark Mine 25 years ago
parent
commit
e3511ec268
1 changed files with 339 additions and 100 deletions
  1. 339 100
      direct/src/tkpanels/MopathRecorder.py

+ 339 - 100
direct/src/tkpanels/MopathRecorder.py

@@ -52,24 +52,34 @@ class MopathRecorder(AppShell, PandaObject):
         self.recorderNodePath = direct.group.attachNewNode(self['name'])
         self.recorderNodePath = direct.group.attachNewNode(self['name'])
         self.tempCS = self.recorderNodePath.attachNewNode(
         self.tempCS = self.recorderNodePath.attachNewNode(
             'mopathRecorderTempCS')
             'mopathRecorderTempCS')
+        self.transitionCS = self.recorderNodePath.attachNewNode(
+            'mopathRecorderTransitionCS')
         self.playbackMarker = loader.loadModel('models/directmodels/happy')
         self.playbackMarker = loader.loadModel('models/directmodels/happy')
         self.playbackMarker.reparentTo(self.recorderNodePath)
         self.playbackMarker.reparentTo(self.recorderNodePath)
+        self.playbackNodePath = None
+        self.lastPlaybackNodePath = None
         # For node path selectors
         # For node path selectors
         self.recNodePathDict = {}
         self.recNodePathDict = {}
         self.recNodePathDict['marker'] = self.playbackMarker
         self.recNodePathDict['marker'] = self.playbackMarker
         self.recNodePathDict['camera'] = direct.camera
         self.recNodePathDict['camera'] = direct.camera
         self.recNodePathDict['widget'] = direct.widget
         self.recNodePathDict['widget'] = direct.widget
+        self.recNodePathDict['mopathRecorderTempCS'] = self.tempCS
         self.recNodePathNames = ['marker', 'camera', 'widget', 'selected']
         self.recNodePathNames = ['marker', 'camera', 'widget', 'selected']
         self.pbNodePathDict = {}
         self.pbNodePathDict = {}
         self.pbNodePathDict['marker'] = self.playbackMarker
         self.pbNodePathDict['marker'] = self.playbackMarker
         self.pbNodePathDict['camera'] = direct.camera
         self.pbNodePathDict['camera'] = direct.camera
         self.pbNodePathDict['widget'] = direct.widget
         self.pbNodePathDict['widget'] = direct.widget
+        self.pbNodePathDict['mopathRecorderTempCS'] = self.tempCS
         self.pbNodePathNames = ['marker', 'camera', 'widget', 'selected']
         self.pbNodePathNames = ['marker', 'camera', 'widget', 'selected']
         # Count of point sets recorded
         # Count of point sets recorded
         self.pointSet = []
         self.pointSet = []
+        self.prePoints = []
+        self.postPoints = []
         self.pointSetDict = {}
         self.pointSetDict = {}
         self.pointSetCount = 0
         self.pointSetCount = 0
         self.pointSetName = self['name'] + '-ps-' + `self.pointSetCount`
         self.pointSetName = self['name'] + '-ps-' + `self.pointSetCount`
+        # User callback to call before recording point
+        self.preRecordFunc = None
         # Hook to start/stop recording
         # Hook to start/stop recording
         self.startStopHook = 'f6'
         self.startStopHook = 'f6'
         self.keyframeHook = 'f12'
         self.keyframeHook = 'f12'
@@ -83,13 +93,14 @@ class MopathRecorder(AppShell, PandaObject):
         self.numTicks = 1
         self.numTicks = 1
         # Number of segments to represent each parametric unit
         # Number of segments to represent each parametric unit
         # This just affects the visual appearance of the curve
         # This just affects the visual appearance of the curve
-        self.numSegs = 2
+        self.numSegs = 5
         # The nurbs curves
         # The nurbs curves
         self.xyzNurbsCurve = None
         self.xyzNurbsCurve = None
         self.hprNurbsCurve = None
         self.hprNurbsCurve = None
         # Curve drawers
         # Curve drawers
         self.nurbsCurveDrawer = NurbsCurveDrawer(NurbsCurve())
         self.nurbsCurveDrawer = NurbsCurveDrawer(NurbsCurve())
         self.nurbsCurveDrawer.setNumSegs(self.numSegs)
         self.nurbsCurveDrawer.setNumSegs(self.numSegs)
+        self.nurbsCurveDrawer.setShowHull(0)
         self.curveNodePath = self.recorderNodePath.attachNewNode(
         self.curveNodePath = self.recorderNodePath.attachNewNode(
             self.nurbsCurveDrawer.getGeomNode())
             self.nurbsCurveDrawer.getGeomNode())
         # Playback variables
         # Playback variables
@@ -99,6 +110,13 @@ class MopathRecorder(AppShell, PandaObject):
         self.fEven = 0
         self.fEven = 0
         self.desampleFrequency = 1
         self.desampleFrequency = 1
         self.numSamples = 100
         self.numSamples = 100
+        # Refining curves
+        self.fRefine = 0
+        self.refineStart = 0.0
+        self.controlStart = 0.0
+        self.controlStop = 0.0
+        self.refineStop = 0.0
+        self.fAdjustingValues = 0
         # Set up event hooks
         # Set up event hooks
         self.undoEvents = [('undo', self.undoHook),
         self.undoEvents = [('undo', self.undoHook),
                            ('pushUndo', self.pushUndoHook),
                            ('pushUndo', self.pushUndoHook),
@@ -136,7 +154,7 @@ class MopathRecorder(AppShell, PandaObject):
                                  variable = self.pathVis,
                                  variable = self.pathVis,
                                  command = self.setPathVis)
                                  command = self.setPathVis)
         self.hullVis = BooleanVar()
         self.hullVis = BooleanVar()
-        self.hullVis.set(1)
+        self.hullVis.set(0)
         self.menuBar.addmenuitem('Show/Hide', 'checkbutton',
         self.menuBar.addmenuitem('Show/Hide', 'checkbutton',
                                  'Toggle Hull Visability',
                                  'Toggle Hull Visability',
                                  label = 'Toggle Hull Vis',
                                  label = 'Toggle Hull Vis',
@@ -203,6 +221,66 @@ class MopathRecorder(AppShell, PandaObject):
         self.redoButton.pack(side = LEFT, expand = 0)
         self.redoButton.pack(side = LEFT, expand = 0)
         self.bind(self.redoButton, 'Redo last operation')
         self.bind(self.redoButton, 'Redo last operation')
 
 
+        # Playback controls
+        playbackFrame = Frame(interior, relief = SUNKEN,
+                              borderwidth = 2)
+        Label(playbackFrame, text = 'PLAYBACK CONTROLS',
+              font=('MSSansSerif', 12, 'bold')).pack(fill = X)
+        # Playback modifiers
+        frame = Frame(playbackFrame)
+        # Playback node path
+        self.pbNodePathMenu = Pmw.ComboBox(
+            frame, labelpos = W, label_text = 'Playback Node Path:',
+            entry_width = 20,
+            selectioncommand = self.selectPlaybackNodePathNamed,
+            scrolledlist_items = self.pbNodePathNames)
+        self.pbNodePathMenu.selectitem('camera')
+        self.pbNodePathMenuEntry = (
+            self.pbNodePathMenu.component('entryfield_entry'))
+        self.pbNodePathMenuBG = (
+            self.pbNodePathMenuEntry.configure('background')[3])
+        self.pbNodePathMenu.pack(side = LEFT, fill = X, expand = 1)
+        self.bind(self.pbNodePathMenu,
+                  'Select node path to fly along path during playback')
+        # Duration entry
+        self.createLabeledEntry(frame, 'Resample', 'Path Duration',
+                                'Set total curve duration',
+                                command = self.setPathDuration)
+        frame.pack(fill = X, expand = 1)
+        frame = Frame(playbackFrame)
+        widget = self.createEntryScale(
+            frame, 'Playback', 'Time', 'Set current playback time',
+            resolution = 0.01, command = self.playbackGoTo, side = LEFT)
+        widget.component('hull')['relief'] = RIDGE
+        # Kill playback task if drag slider
+        widget.component('scale').bind(
+            '<ButtonPress-1>', lambda e = None, s = self: s.stopPlayback())
+        self.createCheckbutton(frame, 'Playback', 'Loop',
+                               'On: loop playback',
+                               self.setLoopPlayback, self.loopPlayback,
+                               side = LEFT, fill = BOTH, expand = 0)
+        frame.pack(fill = X, expand = 1)
+        # Start stop buttons
+        frame = Frame(playbackFrame)
+        widget = self.createButton(frame, 'Playback', '<<',
+                                   'Jump to start of playback',
+                                   self.jumpToStartOfPlayback,
+                                   side = LEFT, expand = 1)
+        widget['font'] = (('MSSansSerif', 12, 'bold'))
+        widget = self.createCheckbutton(frame, 'Playback', 'Play',
+                                        'Start/Stop playback',
+                                        self.startStopPlayback, 0,
+                                        side = LEFT, fill = BOTH, expand = 1)
+        widget.configure(anchor = 'center', justify = 'center',
+                         relief = RAISED, font = ('MSSansSerif', 12, 'bold'))
+        widget = self.createButton(frame, 'Playback', '>>',
+                                   'Jump to end of playback',
+                                   self.jumpToEndOfPlayback,
+                                   side = LEFT, expand = 1)
+        widget['font'] = (('MSSansSerif', 12, 'bold'))
+        frame.pack(fill = X, expand = 1)
+        playbackFrame.pack(fill = X, pady = 2)
+
         # Create notebook pages
         # Create notebook pages
         self.mainNotebook = Pmw.NoteBook(interior)
         self.mainNotebook = Pmw.NoteBook(interior)
         self.mainNotebook.pack(fill = BOTH, expand = 1)
         self.mainNotebook.pack(fill = BOTH, expand = 1)
@@ -277,7 +355,6 @@ class MopathRecorder(AppShell, PandaObject):
             'Hook used to start/stop recording',
             'Hook used to start/stop recording',
             initialValue = self.startStopHook,
             initialValue = self.startStopHook,
             command = self.setStartStopHook)[0]
             command = self.setStartStopHook)[0]
-        label['width'] = 14
         self.setStartStopHook()
         self.setStartStopHook()
         widget.pack_forget()
         widget.pack_forget()
         widget.grid(row=3, column=0, sticky = NSEW)
         widget.grid(row=3, column=0, sticky = NSEW)
@@ -286,73 +363,27 @@ class MopathRecorder(AppShell, PandaObject):
             'Hook used to add a new keyframe',
             'Hook used to add a new keyframe',
             initialValue = self.keyframeHook,
             initialValue = self.keyframeHook,
             command = self.setKeyframeHook)[0]
             command = self.setKeyframeHook)[0]
-        label['width'] = 14
         self.setKeyframeHook()
         self.setKeyframeHook()
         widget.pack_forget()
         widget.pack_forget()
         widget.grid(row=3, column=1, sticky = NSEW)
         widget.grid(row=3, column=1, sticky = NSEW)
+        # PreRecordFunc
+        frame = Frame(self.gridFrame)
+        widget = self.createLabeledEntry(
+            frame, 'Recording', 'Pre Record Func',
+            'Function called before recording each point',
+            command = self.setPreRecordFunc)[0]
+        self.createCheckbutton(frame, 'Recording', 'PRF Active',
+                               'On: Pre Record Func enabled',
+                               None, 0,
+                               side = LEFT, fill = BOTH, expand = 0)
+        frame.grid(row=4, column=0, columnspan = 2, sticky = NSEW)
+        
+        # Pack gridFrame
         self.gridFrame.pack(fill = X, expand = 1)
         self.gridFrame.pack(fill = X, expand = 1)
+        
         # This gets the widgets to spread out
         # This gets the widgets to spread out
-        self.gridFrame.grid_columnconfigure(1,weight = 1)        
+        self.gridFrame.grid_columnconfigure(1,weight = 1)
         recordFrame.pack(fill = X, pady = 2)
         recordFrame.pack(fill = X, pady = 2)
-        # Playback controls
-        playbackFrame = Frame(self.recordPage, relief = SUNKEN,
-                              borderwidth = 2)
-        Label(playbackFrame, text = 'PLAYBACK CONTROLS',
-              font=('MSSansSerif', 12, 'bold')).pack(fill = X)
-        # Playback modifiers
-        frame = Frame(playbackFrame)
-        # Playback node path
-        self.pbNodePathMenu = Pmw.ComboBox(
-            frame, labelpos = W, label_text = 'Playback Node Path:',
-            entry_width = 20,
-            selectioncommand = self.selectPlaybackNodePathNamed,
-            scrolledlist_items = self.pbNodePathNames)
-        self.pbNodePathMenu.selectitem('camera')
-        self.pbNodePathMenuEntry = (
-            self.pbNodePathMenu.component('entryfield_entry'))
-        self.pbNodePathMenuBG = (
-            self.pbNodePathMenuEntry.configure('background')[3])
-        self.pbNodePathMenu.pack(side = LEFT, fill = X, expand = 1)
-        self.bind(self.pbNodePathMenu,
-                  'Select node path to fly along path during playback')
-        # Duration entry
-        self.createLabeledEntry(frame, 'Resample', 'Path Duration',
-                                'Set total curve duration',
-                                command = self.setPathDuration)
-        frame.pack(fill = X, expand = 1)
-        frame = Frame(playbackFrame)
-        widget = self.createEntryScale(
-            frame, 'Playback', 'Time', 'Set current playback time',
-            resolution = 0.01, command = self.setPlaybackTime, side = LEFT)
-        widget.component('hull')['relief'] = RIDGE
-        # Kill playback task if drag slider
-        widget.component('scale').bind(
-            '<ButtonPress-1>', lambda e = None, s = self: s.stopPlayback())
-        self.createCheckbutton(frame, 'Playback', 'Loop',
-                               'On: loop playback',
-                               self.setLoopPlayback, self.loopPlayback,
-                               side = LEFT, fill = BOTH, expand = 0)
-        frame.pack(fill = X, expand = 1)
-        # Start stop buttons
-        frame = Frame(playbackFrame)
-        widget = self.createButton(frame, 'Playback', '<<',
-                                   'Jump to start of playback',
-                                   self.jumpToStartOfPlayback,
-                                   side = LEFT, expand = 1)
-        widget['font'] = (('MSSansSerif', 12, 'bold'))
-        widget = self.createCheckbutton(frame, 'Playback', 'Play',
-                                        'Start/Stop playback',
-                                        self.startStopPlayback, 0,
-                                        side = LEFT, fill = BOTH, expand = 1)
-        widget.configure(anchor = 'center', justify = 'center',
-                         relief = RAISED, font = ('MSSansSerif', 12, 'bold'))
-        widget = self.createButton(frame, 'Playback', '>>',
-                                   'Jump to end of playback',
-                                   self.jumpToEndOfPlayback,
-                                   side = LEFT, expand = 1)
-        widget['font'] = (('MSSansSerif', 12, 'bold'))
-        frame.pack(fill = X, expand = 1)
-        playbackFrame.pack(fill = X, pady = 2)
         # Desample
         # Desample
         desampleFrame = Frame(
         desampleFrame = Frame(
             self.recordPage, relief = SUNKEN, borderwidth = 2)
             self.recordPage, relief = SUNKEN, borderwidth = 2)
@@ -389,14 +420,31 @@ class MopathRecorder(AppShell, PandaObject):
         label = Label(refineFrame, text = 'REFINE CURVE',
         label = Label(refineFrame, text = 'REFINE CURVE',
                       font=('MSSansSerif', 12, 'bold'))
                       font=('MSSansSerif', 12, 'bold'))
         label.pack(fill = X)
         label.pack(fill = X)
-        self.createEntryScale(refineFrame, 'Refine Page', 'From',
-                              'Begin time of refine pass',
-                              resolution = 0.01,
-                              command = self.setRefineStart)
-        self.createEntryScale(refineFrame, 'Refine Page', 'To',
-                              'Stop time of refine pass',
-                              resolution = 0.01,
-                              command = self.setRefineStop)
+        widget = self.createEntryScale(refineFrame,
+                                       'Refine Page', 'Refine From',
+                                       'Begin time of refine pass',
+                                       resolution = 0.01,
+                                       command = self.setRefineStart)
+        widget.onRelease = widget.onReturnRelease = self.getPrePoints
+        widget = self.createEntryScale(
+            refineFrame, 'Refine Page',
+            'Control Start',
+            'Time when full control of node path is given during refine pass',
+            resolution = 0.01,
+            command = self.setControlStart)
+        widget.onRelease = widget.onReturnRelease = self.setRefineFlag
+        widget = self.createEntryScale(
+            refineFrame, 'Refine Page',
+            'Control Stop',
+            'Time when node path begins transition back to original curve',
+            resolution = 0.01,
+            command = self.setControlStop)
+        widget.onRelease = widget.onReturnRelease = self.setRefineFlag
+        widget = self.createEntryScale(refineFrame, 'Refine Page', 'Refine To',
+                                       'Stop time of refine pass',
+                                       resolution = 0.01,
+                                       command = self.setRefineStop)
+        widget.onRelease = widget.onReturnRelease = self.getPostPoints
         refineFrame.pack(fill = X)
         refineFrame.pack(fill = X)
 
 
         offsetFrame = Frame(self.refinePage)
         offsetFrame = Frame(self.refinePage)
@@ -495,7 +543,6 @@ class MopathRecorder(AppShell, PandaObject):
         self.fHasPoints = 1
         self.fHasPoints = 1
         # Compute curve
         # Compute curve
         self.computeCurves()
         self.computeCurves()
-    
 
 
     def setTraceVis(self):
     def setTraceVis(self):
         print self.traceVis.get()
         print self.traceVis.get()
@@ -576,18 +623,35 @@ class MopathRecorder(AppShell, PandaObject):
             self.nurbsCurveDrawer.hide()
             self.nurbsCurveDrawer.hide()
             # Create a new point set to hold raw data
             # Create a new point set to hold raw data
             self.createNewPointSet()
             self.createNewPointSet()
-            # Continuous or keyframe?
-            if self.recordType.get() == 'Continuous':
+            # Record nopath's parent
+            self.nodePathParent = self['nodePath'].getParent()
+            # Refine, Continuous or keyframe?
+            if self.fRefine | (self.recordType.get() == 'Continuous'):
+                if self.fRefine:
+                    # Turn off looping playback
+                    self.loopPlayback = 0
+                    # Update widget to reflect new value
+                    self.getVariable('Playback', 'Loop').set(0)
+                    # Select tempCS as playback nodepath
+                    self.lastPlaybackNodePath = self.playbackNodePath
+                    self.selectPlaybackNodePathNamed('mopathRecorderTempCS')
+                    # Parent record node path to temp
+                    self['nodePath'].reparentTo(self.tempCS)
+                    # Align with temp
+                    self['nodePath'].setPosHpr(0,0,0,0,0,0)
+                    # Set playback start to refineStart
+                    self.playbackGoTo(self.refineStart)
+                    # start flying nodePath along path
+                    self.startPlayback()
                 # Start new task
                 # Start new task
                 t = taskMgr.spawnMethodNamed(
                 t = taskMgr.spawnMethodNamed(
                     self.recordTask, self['name'] + '-recordTask')
                     self.recordTask, self['name'] + '-recordTask')
                 t.startTime = globalClock.getTime()
                 t.startTime = globalClock.getTime()
-                t.uponDeath = self.endRecordTask
             else:
             else:
                 # Add hook
                 # Add hook
                 self.acceptKeyframeHook()
                 self.acceptKeyframeHook()
                 # Record first point
                 # Record first point
-                self.startPos = self['nodePath'].getPos()
+                self.startPos = self['nodePath'].getPos(self.nodePathParent)
                 self.recordPoint(0.0)
                 self.recordPoint(0.0)
 
 
             # Don't want to change record modes
             # Don't want to change record modes
@@ -604,8 +668,20 @@ class MopathRecorder(AppShell, PandaObject):
                 self.addKeyframe()
                 self.addKeyframe()
                 # Ignore hook
                 # Ignore hook
                 self.ignoreKeyframeHook()
                 self.ignoreKeyframeHook()
-                # Compute curve
-                self.computeCurves()
+            if self.fRefine:
+                # Reparent node path back to parent
+                self['nodePath'].wrtReparentTo(self.nodePathParent)
+                # Restore playback Node Path
+                self.playbackNodePath = self.lastPlaybackNodePath
+                # Merge prePoints, pointSet, postPoints
+                self.mergePoints()
+                # Clear out pre and post list
+                self.prePoints = []
+                self.postPoints = []
+                # Reset flag
+                self.fRefine = 0
+            # Compute curve
+            self.computeCurves()
             # Now you can change record modes
             # Now you can change record modes
             self.getWidget('Recording', 'Continuous Recording')['state'] = (
             self.getWidget('Recording', 'Continuous Recording')['state'] = (
                 'normal')
                 'normal')
@@ -614,16 +690,61 @@ class MopathRecorder(AppShell, PandaObject):
             
             
     def recordTask(self, state):
     def recordTask(self, state):
         # Record raw data point
         # Record raw data point
-        time = globalClock.getTime() - state.startTime
+        time = self.refineStart + (globalClock.getTime() - state.startTime)
         self.recordPoint(time)
         self.recordPoint(time)
         return Task.cont
         return Task.cont
 
 
-    def endRecordTask(self, state):
-        self.computeCurves()
+    def addKeyframe(self):
+        time = (self.refineStart +
+                (Vec3(self['nodePath'].getPos(self.nodePathParent) -
+                      self.startPos).length()))
+        self.recordPoint(time)
+
+    def easeInOut(self, t):
+        x = t * t
+        return (3 * x) - (2 * t * x)
+
+    def setPreRecordFunc(self, event):
+        try:
+            self.preRecordFunc = eval(
+                self.getVariable('Recording', 'Pre Record Func').get())
+            # Update widget to reflect new value
+            self.getVariable('Recording', 'PRF Active').set(1)
+        except NameError:
+            # See if you can find func in the globals dictionary
+            # Note: need to set __builtins__.func at command line
+            self.preRecordFunc = eval(
+                self.getVariable('Recording', 'Pre Record Func').get(),
+                globals())
+            # Update widget to reflect new value
+            self.getVariable('Recording', 'PRF Active').set(1)
 
 
     def recordPoint(self, time):
     def recordPoint(self, time):
-        pos = self['nodePath'].getPos()
-        hpr = self['nodePath'].getHpr()
+        # Call user define callback before recording point
+        if (self.getVariable('Recording', 'PRF Active').get() &
+            (self.preRecordFunc != None)):
+            self.preRecordFunc()
+        # Get point
+        pos = self['nodePath'].getPos(self.nodePathParent)
+        hpr = self['nodePath'].getHpr(self.nodePathParent)
+        # Blend between recordNodePath and self['nodePath']
+        if self.fRefine:
+            if (time < self.controlStart):
+                rPos = self.playbackNodePath.getPos(self.nodePathParent)
+                rHpr = self.playbackNodePath.getHpr(self.nodePathParent)
+                t = self.easeInOut(((time - self.refineStart)/
+                                    (self.controlStart - self.refineStart)))
+                # Transition between the recorded node path and the driven one
+                pos = (rPos * (1 - t)) + (pos * t)
+                hpr = (rHpr * (1 - t)) + (hpr * t)
+            elif (time > self.controlStop):
+                rPos = self.playbackNodePath.getPos(self.nodePathParent)
+                rHpr = self.playbackNodePath.getHpr(self.nodePathParent)
+                t = self.easeInOut(((time - self.controlStop)/
+                                    (self.refineStop - self.controlStop)))
+                # Transition between the recorded node path and the driven one
+                pos = (pos * (1 - t)) + (rPos * t)
+                hpr = (hpr * (1 - t)) + (rHpr * t)
         # Add it to the point set
         # Add it to the point set
         self.pointSet.append([time, pos, hpr])
         self.pointSet.append([time, pos, hpr])
         # Add it to the curve fitters
         # Add it to the curve fitters
@@ -631,10 +752,6 @@ class MopathRecorder(AppShell, PandaObject):
         self.hprCurveFitter.addPoint(time, hpr)
         self.hprCurveFitter.addPoint(time, hpr)
         self.fHasPoints = 1
         self.fHasPoints = 1
 
 
-    def addKeyframe(self):
-        time = Vec3(self['nodePath'].getPos() - self.startPos).length()
-        self.recordPoint(time)
-
     def computeCurves(self):
     def computeCurves(self):
         # MRM: Would be better if curvefitter had getNumPoints
         # MRM: Would be better if curvefitter had getNumPoints
         if not self.fHasPoints:
         if not self.fHasPoints:
@@ -642,11 +759,13 @@ class MopathRecorder(AppShell, PandaObject):
             return
             return
         # Create curves
         # Create curves
         # XYZ
         # XYZ
+        self.xyzCurveFitter.sortPoints()
         self.xyzCurveFitter.computeTangents(1)
         self.xyzCurveFitter.computeTangents(1)
         self.xyzNurbsCurve = self.xyzCurveFitter.makeNurbs()
         self.xyzNurbsCurve = self.xyzCurveFitter.makeNurbs()
         self.nurbsCurveDrawer.setCurve(self.xyzNurbsCurve)
         self.nurbsCurveDrawer.setCurve(self.xyzNurbsCurve)
         self.nurbsCurveDrawer.draw()
         self.nurbsCurveDrawer.draw()
         # HPR
         # HPR
+        self.hprCurveFitter.sortPoints()
         self.hprCurveFitter.wrapHpr()
         self.hprCurveFitter.wrapHpr()
         self.hprCurveFitter.computeTangents(1)
         self.hprCurveFitter.computeTangents(1)
         self.hprNurbsCurve = self.hprCurveFitter.makeNurbs()
         self.hprNurbsCurve = self.hprCurveFitter.makeNurbs()
@@ -660,8 +779,18 @@ class MopathRecorder(AppShell, PandaObject):
         maxT = '%.2f' % self.xyzNurbsCurve.getMaxT()
         maxT = '%.2f' % self.xyzNurbsCurve.getMaxT()
         self.getWidget('Playback', 'Time').configure(max = maxT)
         self.getWidget('Playback', 'Time').configure(max = maxT)
         self.getVariable('Resample', 'Path Duration').set(maxT)
         self.getVariable('Resample', 'Path Duration').set(maxT)
-        self.getWidget('Refine Page', 'From').configure(max = maxT)
-        self.getWidget('Refine Page', 'To').configure(max = maxT)
+        widget = self.getWidget('Refine Page', 'Refine From')
+        widget.configure(max = maxT)
+        widget.set(0.0)
+        widget = self.getWidget('Refine Page', 'Control Start')
+        widget.configure(max = maxT)
+        widget.set(0.0)
+        widget = self.getWidget('Refine Page', 'Control Stop')
+        widget.configure(max = maxT)
+        widget.set(float(maxT))
+        widget = self.getWidget('Refine Page', 'Refine To')
+        widget.configure(max = maxT)
+        widget.set(float(maxT))
         self.maxT = float(maxT)
         self.maxT = float(maxT)
         # Widgets depending on number of knots
         # Widgets depending on number of knots
         numKnots = self.xyzNurbsCurve.getNumKnots()
         numKnots = self.xyzNurbsCurve.getNumKnots()
@@ -767,7 +896,7 @@ class MopathRecorder(AppShell, PandaObject):
             return
             return
         # Get node path's name
         # Get node path's name
         name = nodePath.getName()
         name = nodePath.getName()
-        if name in ['parent', 'render', 'camera', 'marker']:
+        if name in ['mopathRecorderTempCS', 'widget', 'camera', 'marker']:
             dictName = name
             dictName = name
         else:
         else:
             # Generate a unique name for the dict
             # Generate a unique name for the dict
@@ -784,9 +913,6 @@ class MopathRecorder(AppShell, PandaObject):
     def setLoopPlayback(self):
     def setLoopPlayback(self):
         self.loopPlayback = self.getVariable('Playback', 'Loop').get()
         self.loopPlayback = self.getVariable('Playback', 'Loop').get()
 
 
-    def setPlaybackTime(self, time):
-        self.playbackGoTo(time)
-
     def playbackGoTo(self, time):
     def playbackGoTo(self, time):
         if (self.xyzNurbsCurve == None) & (self.hprNurbsCurve == None):
         if (self.xyzNurbsCurve == None) & (self.hprNurbsCurve == None):
             return
             return
@@ -795,7 +921,6 @@ class MopathRecorder(AppShell, PandaObject):
             pos = Vec3(0)
             pos = Vec3(0)
             self.xyzNurbsCurve.getPoint(self.playbackTime, pos)
             self.xyzNurbsCurve.getPoint(self.playbackTime, pos)
             self.playbackNodePath.setPos(pos)
             self.playbackNodePath.setPos(pos)
-            self.playbackNodePath.setPos(pos)
         if self.hprNurbsCurve != None:
         if self.hprNurbsCurve != None:
             hpr = Vec3(0)
             hpr = Vec3(0)
             self.hprNurbsCurve.getPoint(self.playbackTime, hpr)
             self.hprNurbsCurve.getPoint(self.playbackTime, hpr)
@@ -819,12 +944,18 @@ class MopathRecorder(AppShell, PandaObject):
         if self.loopPlayback:
         if self.loopPlayback:
             cTime = (state.startOffset + dTime) % self.maxT
             cTime = (state.startOffset + dTime) % self.maxT
         else:
         else:
-            cTime = CLAMP(state.startOffset + dTime, 0.0, self.maxT)
-        self.getWidget('Playback', 'Time').set(cTime)
+            cTime = state.startOffset + dTime
         # Stop task if not looping and at end of curve
         # Stop task if not looping and at end of curve
-        if ((self.loopPlayback == 0) & ((cTime + 0.01) > self.maxT)):
+        # Or if refining curve and past refineStop
+        if (((self.loopPlayback == 0) & (cTime > self.maxT)) |
+            (self.fRefine & (cTime > self.refineStop))):
             self.stopPlayback()
             self.stopPlayback()
+            if self.fRefine:
+                # Kill record task
+                self.toggleRecordVar()
             return Task.done
             return Task.done
+        # Otherwise go to specified time
+        self.getWidget('Playback', 'Time').set(cTime)
         return Task.cont
         return Task.cont
 
 
     def stopPlayback(self):
     def stopPlayback(self):
@@ -902,11 +1033,119 @@ class MopathRecorder(AppShell, PandaObject):
         # Update info
         # Update info
         self.updateCurveInfo()
         self.updateCurveInfo()
 
 
+    def setRefineFlag(self, val = 1):
+        self.fRefine = val
+
     def setRefineStart(self,value):
     def setRefineStart(self,value):
-        print 'refine start'
+        self.refineStart = value
+        # Someone else is adjusting values, let them take care of it
+        if self.fAdjustingValues:
+            return
+        self.fAdjustingValues = 1
+        if self.refineStart > self.controlStart:
+            self.getWidget('Refine Page', 'Control Start').set(
+                self.refineStart)
+        if self.refineStart > self.controlStop:
+            self.getWidget('Refine Page', 'Control Stop').set(
+                self.refineStart)
+        if self.refineStart > self.refineStop:
+            self.getWidget('Refine Page', 'Refine To').set(self.refineStart)
+        self.fAdjustingValues = 0
+
+    def getPrePoints(self):
+        # Set flag so we know to do a refine pass
+        self.setRefineFlag(1)
+        # Reset prePoints
+        self.prePoints = []
+        # See if we need to save any points before refineStart
+        for i in range(len(self.pointSet)):
+            # Have we passed refineStart?
+            if self.refineStart < self.pointSet[i][0]:
+                # Get a copy of the points prior to refineStart
+                self.prePoints = self.pointSet[:i-1]
+                break
+
+    def setControlStart(self, value):
+        self.controlStart = value
+        # Someone else is adjusting values, let them take care of it
+        if self.fAdjustingValues:
+            return
+        self.fAdjustingValues = 1
+        if self.controlStart < self.refineStart:
+            self.getWidget('Refine Page', 'Refine From').set(
+                self.controlStart)
+        if self.controlStart > self.controlStop:
+            self.getWidget('Refine Page', 'Control Stop').set(
+                self.controlStart)
+        if self.controlStart > self.refineStop:
+            self.getWidget('Refine Page', 'Refine To').set(
+                self.controlStart)
+        self.fAdjustingValues = 0
+
+    def setControlStop(self, value):
+        self.controlStop = value
+        # Someone else is adjusting values, let them take care of it
+        if self.fAdjustingValues:
+            return
+        self.fAdjustingValues = 1
+        if self.controlStop < self.refineStart:
+            self.getWidget('Refine Page', 'Refine From').set(
+                self.controlStop)
+        if self.controlStop < self.controlStart:
+            self.getWidget('Refine Page', 'Control Start').set(
+                self.controlStop)
+        if self.controlStop > self.refineStop:
+            self.getWidget('Refine Page', 'Refine To').set(
+                self.controlStop)
+        self.fAdjustingValues = 0
 
 
     def setRefineStop(self, value):
     def setRefineStop(self, value):
-        print 'refine stop'
+        self.refineStop = value
+        # Someone else is adjusting values, let them take care of it
+        if self.fAdjustingValues:
+            return
+        self.fAdjustingValues = 1
+        if self.refineStop < self.refineStart:
+            self.getWidget('Refine Page', 'Refine From').set(
+                self.refineStop)
+        if self.refineStop < self.controlStart:
+            self.getWidget('Refine Page', 'Control Start').set(
+                self.refineStop)
+        if self.refineStop < self.controlStop:
+            self.getWidget('Refine Page', 'Control Stop').set(
+                self.refineStop)
+        self.fAdjustingValues = 0
+
+    def getPostPoints(self):
+        # Set flag so we know to do a refine pass
+        self.setRefineFlag(1)
+        # Reset postPoints
+        self.postPoints = []
+        # See if we need to save any points after refineStop
+        for i in range(len(self.pointSet)):
+            # Have we reached refineStop?
+            if self.refineStop < self.pointSet[i][0]:
+                # Get a copy of the points after refineStop
+                self.postPoints = self.pointSet[i:]
+                break
+
+    def mergePoints(self):
+        # Merge in pre points
+        self.pointSet = self.prePoints + self.pointSet
+        for time, pos, hpr in self.prePoints:
+            # Add it to the curve fitters
+            self.xyzCurveFitter.addPoint(time, pos )
+            self.hprCurveFitter.addPoint(time, hpr)
+        # And post points
+        # What is end time of pointSet?
+        endTime = self.pointSet[-1][0]
+        for time, pos, hpr in self.postPoints:
+            adjustedTime = endTime + (time - self.refineStop)
+            # Add it to point set
+            self.pointSet.append([adjustedTime, pos, hpr])
+            # Add it to the curve fitters
+            self.xyzCurveFitter.addPoint(adjustedTime, pos)
+            self.hprCurveFitter.addPoint(adjustedTime, hpr)
 
 
     def resetOffset(self):
     def resetOffset(self):
         print 'reset offset'
         print 'reset offset'