*** empty log message ***
This commit is contained in:
parent
d4d24efb9c
commit
2524515269
|
|
@ -96,19 +96,26 @@ class DirectAnalogs(AnalogNode, PandaObject):
|
|||
def disable(self):
|
||||
self.nodePath.reparentTo(base.dataUnused)
|
||||
|
||||
def normalize(self, val, min = -1, max = -1):
|
||||
def normalize(self, val, minVal = -1, maxVal = -1):
|
||||
# First record sign
|
||||
if val < 0:
|
||||
sign = -1
|
||||
else:
|
||||
sign = 1
|
||||
# Zero out values in deadband
|
||||
val = sign * max(abs(val) - ANALOG_DEADBAND, 0.0)
|
||||
# Now clamp value between minVal and maxVal
|
||||
val = CLAMP(val, ANALOG_MIN, ANALOG_MAX)
|
||||
if abs(val) < ANALOG_DEADBAND:
|
||||
val = 0.0
|
||||
return ((max - min) * ((val - ANALOG_MIN) / ANALOG_RANGE)) + min
|
||||
return (((maxVal - minVal) * ((val - ANALOG_MIN) / ANALOG_RANGE))
|
||||
+ minVal)
|
||||
|
||||
def normalizeChannel(self, chan, min = -1, max = 1):
|
||||
def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
|
||||
try:
|
||||
if (chan == 2) | (chan == 6):
|
||||
# These channels have reduced range
|
||||
return self.normalize(self[chan] * 3.0, min, max)
|
||||
return self.normalize(self[chan] * 3.0, minVal, maxVal)
|
||||
else:
|
||||
return self.normalize(self[chan], min, max)
|
||||
return self.normalize(self[chan], minVal, maxVal)
|
||||
except IndexError:
|
||||
return 0.0
|
||||
|
||||
|
|
|
|||
|
|
@ -30,22 +30,23 @@ class DirectJoybox(PandaObject):
|
|||
joyboxCount = 0
|
||||
xyzMultiplier = 1.0
|
||||
hprMultiplier = 1.0
|
||||
def __init__(self, nodePath = direct.camera):
|
||||
def __init__(self, device = 'CerealBox', nodePath = direct.camera):
|
||||
# See if device manager has been initialized
|
||||
if direct.deviceManager == None:
|
||||
direct.deviceManager = DirectDeviceManager()
|
||||
# Set name
|
||||
self.name = 'Joybox-' + `DirectJoybox.joyboxCount`
|
||||
# Get buttons and analogs
|
||||
self.device = base.config.GetString('joybox-device', 'CerealBox')
|
||||
self.device = device
|
||||
self.analogs = direct.deviceManager.createAnalogs(self.device)
|
||||
self.buttons = direct.deviceManager.createButtons(self.device)
|
||||
self.aList = [0,0,0,0,0,0,0,0]
|
||||
self.bList = [0,0,0,0,0,0,0,0]
|
||||
# For joybox fly mode
|
||||
self.mapping = [L_LEFT_RIGHT, L_FWD_BACK, L_TWIST,
|
||||
R_TWIST, R_FWD_BACK, R_LEFT_RIGHT]
|
||||
self.modifier = [1,1,1,1,1,1]
|
||||
# Default is joe mode
|
||||
self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
|
||||
R_TWIST, L_TWIST, NULL_AXIS]
|
||||
self.modifier = [1,1,1,-1,-1,0]
|
||||
# Initialize time
|
||||
self.lastTime = globalClock.getTime()
|
||||
# Record node path
|
||||
|
|
@ -58,7 +59,7 @@ class DirectJoybox(PandaObject):
|
|||
# List of functions to cycle through
|
||||
self.modeList = [self.joeMode, self.driveMode]
|
||||
# Pick initial mode
|
||||
self.updateFunc = self.joeFly
|
||||
self.updateFunc = self.joyboxFly
|
||||
self.modeName = 'Joe Mode'
|
||||
# Button registry
|
||||
self.addButtonEvents()
|
||||
|
|
@ -173,35 +174,8 @@ class DirectJoybox(PandaObject):
|
|||
self.showMode(self.modeName)
|
||||
self.enable()
|
||||
|
||||
def joeMode(self):
|
||||
self.setMode(self.joeFly, 'Joe Mode')
|
||||
|
||||
def joeFly(self):
|
||||
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 200) *
|
||||
DirectJoybox.hprMultiplier)
|
||||
posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) *
|
||||
DirectJoybox.xyzMultiplier)
|
||||
# XYZ
|
||||
x = self.aList[R_LEFT_RIGHT]
|
||||
y = self.aList[R_FWD_BACK]
|
||||
if self.bList[L_STICK]:
|
||||
z = 0.0
|
||||
else:
|
||||
z = self.aList[L_FWD_BACK]
|
||||
pos = Vec3(x,y,z) * (posScale * self.deltaTime)
|
||||
# HPR
|
||||
h = -1 * self.aList[R_TWIST]
|
||||
if self.bList[L_STICK]:
|
||||
p = -1 * self.aList[L_FWD_BACK]
|
||||
else:
|
||||
p = 0.0
|
||||
r = 0.0
|
||||
hpr = Vec3(h,p,r) * (hprScale * self.deltaTime)
|
||||
# Move node path
|
||||
self.nodePath.setPosHpr(self.nodePath, pos, hpr)
|
||||
|
||||
def joyboxFly(self):
|
||||
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 200) *
|
||||
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 100) *
|
||||
DirectJoybox.hprMultiplier)
|
||||
posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) *
|
||||
DirectJoybox.xyzMultiplier)
|
||||
|
|
@ -223,18 +197,30 @@ class DirectJoybox(PandaObject):
|
|||
# Move node path
|
||||
self.nodePath.setPosHpr(self.nodePath, pos, hpr)
|
||||
|
||||
def joeMode(self):
|
||||
self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
|
||||
R_TWIST, L_TWIST, NULL_AXIS]
|
||||
self.modifier = [1,1,1,-1,-1,0]
|
||||
self.setMode(self.joyboxFly, 'Joe Mode')
|
||||
|
||||
def driveMode(self):
|
||||
self.mapping = [L_LEFT_RIGHT, R_FWD_BACK, R_TWIST,
|
||||
R_LEFT_RIGHT, L_FWD_BACK, NULL_AXIS]
|
||||
self.modifier = [1,1,-1,-1,-1,0]
|
||||
self.setMode(self.joyboxFly, 'Drive Mode')
|
||||
|
||||
def lookAtMode(self):
|
||||
self.mapping = [R_LEFT_RIGHT, R_TWIST, R_FWD_BACK,
|
||||
L_LEFT_RIGHT, L_FWD_BACK, NULL_AXIS]
|
||||
self.modifier = [1,1,1,-1,1,0]
|
||||
self.setMode(self.joyboxFly, 'Look At Mode')
|
||||
|
||||
def demoMode(self):
|
||||
self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
|
||||
R_TWIST, NULL_AXIS, NULL_AXIS]
|
||||
self.modifier = [1,1,1,-1,0,0]
|
||||
self.setMode(self.joyboxFly, 'Demo Mode')
|
||||
|
||||
def driveMode(self):
|
||||
self.mapping = [L_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
|
||||
R_LEFT_RIGHT, NULL_AXIS, NULL_AXIS]
|
||||
self.modifier = [1,1,1,-1,0,0]
|
||||
self.setMode(self.joyboxFly, 'Drive Mode')
|
||||
|
||||
def hprXyzMode(self):
|
||||
self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, R_TWIST,
|
||||
L_TWIST, L_FWD_BACK, L_LEFT_RIGHT]
|
||||
|
|
@ -257,7 +243,7 @@ class DirectJoybox(PandaObject):
|
|||
self.setMode(self.orbitFly, 'Orbit Mode')
|
||||
|
||||
def orbitFly(self):
|
||||
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 200) *
|
||||
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 100) *
|
||||
DirectJoybox.hprMultiplier)
|
||||
posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) *
|
||||
DirectJoybox.xyzMultiplier)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ class DirectSession(PandaObject):
|
|||
# Create a vrpn client vrpn-server or default
|
||||
if base.config.GetBool('want-vrpn', 0):
|
||||
self.deviceManager = DirectDeviceManager()
|
||||
# Automatically create any devices specified in config file
|
||||
joybox = base.config.GetString('vrpn-joybox-device', '')
|
||||
if joybox:
|
||||
from DirectJoybox import *
|
||||
self.joybox = DirectJoybox(joybox)
|
||||
else:
|
||||
self.deviceManager = None
|
||||
|
||||
|
|
|
|||
|
|
@ -41,12 +41,30 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
|
||||
self.initialiseoptions(MopathRecorder)
|
||||
|
||||
self.selectRecordNodePathNamed('init')
|
||||
self.selectPlaybackNodePathNamed('init')
|
||||
|
||||
def appInit(self):
|
||||
# Dictionary of widgets
|
||||
self.widgetDict = {}
|
||||
self.variableDict = {}
|
||||
# Initialize state
|
||||
self.tempCS = direct.group.attachNewNode('mopathRecorderTempCS')
|
||||
self.recorderNodePath = direct.group.attachNewNode(self['name'])
|
||||
self.tempCS = self.recorderNodePath.attachNewNode(
|
||||
'mopathRecorderTempCS')
|
||||
self.playbackMarker = loader.loadModel('models/directmodels/happy')
|
||||
self.playbackMarker.reparentTo(self.recorderNodePath)
|
||||
# For node path selectors
|
||||
self.recNodePathDict = {}
|
||||
self.recNodePathDict['marker'] = self.playbackMarker
|
||||
self.recNodePathDict['camera'] = direct.camera
|
||||
self.recNodePathDict['widget'] = direct.widget
|
||||
self.recNodePathNames = ['marker', 'camera', 'widget', 'selected']
|
||||
self.pbNodePathDict = {}
|
||||
self.pbNodePathDict['marker'] = self.playbackMarker
|
||||
self.pbNodePathDict['camera'] = direct.camera
|
||||
self.pbNodePathDict['widget'] = direct.widget
|
||||
self.pbNodePathNames = ['marker', 'camera', 'widget', 'selected']
|
||||
# Count of point sets recorded
|
||||
self.pointSet = []
|
||||
self.pointSetDict = {}
|
||||
|
|
@ -54,18 +72,33 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
self.pointSetName = self['name'] + '-ps-' + `self.pointSetCount`
|
||||
# Hook to start/stop recording
|
||||
self.startStopHook = 'f6'
|
||||
self.keyframeHook = 'f12'
|
||||
# Curve fitter object
|
||||
self.fHasPoints = 0
|
||||
self.startPos = Point3(0)
|
||||
self.xyzCurveFitter = CurveFitter()
|
||||
self.hprCurveFitter = CurveFitter()
|
||||
# Curve objects
|
||||
self.nurbsCurve = NurbsCurve()
|
||||
self.nurbsCurveDrawer = NurbsCurveDrawer(self.nurbsCurve)
|
||||
self.curveNodePath = None
|
||||
# Curve variables
|
||||
# Number of ticks per parametric unit
|
||||
self.numTicks = 1
|
||||
# Number of segments to represent each parametric unit
|
||||
# This just affects the visual appearance of the curve
|
||||
self.numSegs = 2
|
||||
# The nurbs curves
|
||||
self.xyzNurbsCurve = None
|
||||
self.hprNurbsCurve = None
|
||||
# Curve drawers
|
||||
self.nurbsCurveDrawer = NurbsCurveDrawer(NurbsCurve())
|
||||
self.nurbsCurveDrawer.setNumSegs(self.numSegs)
|
||||
self.curveNodePath = self.recorderNodePath.attachNewNode(
|
||||
self.nurbsCurveDrawer.getGeomNode())
|
||||
# Playback variables
|
||||
self.playbackTime = 0.0
|
||||
self.loopPlayback = 1
|
||||
# Sample variables
|
||||
self.fEven = 0
|
||||
self.desampleFrequency = 1
|
||||
self.numSamples = 100
|
||||
# Set up event hooks
|
||||
self.undoEvents = [('undo', self.undoHook),
|
||||
('pushUndo', self.pushUndoHook),
|
||||
|
|
@ -158,7 +191,7 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
self.undoButton['state'] = 'normal'
|
||||
else:
|
||||
self.undoButton['state'] = 'disabled'
|
||||
self.undoButton.pack(side = 'left', expand = 0)
|
||||
self.undoButton.pack(side = LEFT, expand = 0)
|
||||
self.bind(self.undoButton, 'Undo last operation')
|
||||
|
||||
self.redoButton = Button(self.menuFrame, text = 'Redo',
|
||||
|
|
@ -167,14 +200,13 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
self.redoButton['state'] = 'normal'
|
||||
else:
|
||||
self.redoButton['state'] = 'disabled'
|
||||
self.redoButton.pack(side = 'left', expand = 0)
|
||||
self.redoButton.pack(side = LEFT, expand = 0)
|
||||
self.bind(self.redoButton, 'Redo last operation')
|
||||
|
||||
# Create notebook pages
|
||||
self.mainNotebook = Pmw.NoteBook(interior)
|
||||
self.mainNotebook.pack(fill = BOTH, expand = 1)
|
||||
self.recordPage = self.mainNotebook.add('Record')
|
||||
self.cvPage = self.mainNotebook.add('CV Controls')
|
||||
self.refinePage = self.mainNotebook.add('Refine')
|
||||
# Put this here so it isn't called right away
|
||||
self.mainNotebook['raisecommand'] = self.updateInfo
|
||||
|
|
@ -184,85 +216,179 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
borderwidth = 2)
|
||||
label = Label(recordFrame, text = 'RECORD PATH',
|
||||
font=('MSSansSerif', 12, 'bold'))
|
||||
label.pack(fill = 'x')
|
||||
label.pack(fill = X)
|
||||
# Recording Buttons
|
||||
frame = Frame(recordFrame)
|
||||
self.createCheckbutton(frame, 'Recording', 'Recording Path',
|
||||
'On: path is being recorded',
|
||||
self.toggleRecord, 0,
|
||||
side = 'left')
|
||||
self.getWidget('Recording', 'Recording Path').configure(
|
||||
foreground = 'Red', relief = RAISED, borderwidth = 2,
|
||||
anchor = CENTER)
|
||||
self.getWidget('Recording', 'Recording Path').pack(
|
||||
fill = 'x', expand = 1)
|
||||
self.createLabeledEntry(frame, 'Recording', 'Start/Stop Hook',
|
||||
'Hook used to start/stop recording',
|
||||
initialValue = self.startStopHook,
|
||||
command = self.setStartStopHook)
|
||||
self.setStartStopHook(None)
|
||||
frame.pack(fill = 'x', expand = 1)
|
||||
recordFrame.pack(fill = 'x')
|
||||
# Record node path
|
||||
self.gridFrame = Frame(recordFrame)
|
||||
self.recNodePathMenu = Pmw.ComboBox(
|
||||
self.gridFrame, labelpos = W, label_text = 'Record Node Path:',
|
||||
entry_width = 20,
|
||||
selectioncommand = self.selectRecordNodePathNamed,
|
||||
scrolledlist_items = self.recNodePathNames)
|
||||
self.recNodePathMenu.selectitem('camera')
|
||||
self.recNodePathMenuEntry = (
|
||||
self.recNodePathMenu.component('entryfield_entry'))
|
||||
self.recNodePathMenuBG = (
|
||||
self.recNodePathMenuEntry.configure('background')[3])
|
||||
self.recNodePathMenu.grid(row = 0, col = 0, sticky = NSEW)
|
||||
self.bind(self.recNodePathMenu,
|
||||
'Select node path to track when recording a new curve')
|
||||
# Record type
|
||||
self.recordType = StringVar()
|
||||
self.recordType.set('Continuous')
|
||||
widget = self.createRadiobutton(
|
||||
self.gridFrame, 'left',
|
||||
'Recording', 'Continuous Recording',
|
||||
('New point added to curve fitter every frame'),
|
||||
self.recordType, 'Continuous', self.setRecordType,
|
||||
expand = 1)
|
||||
widget['anchor'] = 'center'
|
||||
widget.pack_forget()
|
||||
widget.grid(row = 1, col = 0, sticky = NSEW)
|
||||
widget = self.createRadiobutton(
|
||||
self.gridFrame, 'left',
|
||||
'Recording', 'Keyframe Recording',
|
||||
('Add new point to curve fitter by pressing keyframe button'),
|
||||
self.recordType, 'Keyframe', self.setRecordType,
|
||||
expand = 1)
|
||||
widget['anchor'] = 'center'
|
||||
widget.pack_forget()
|
||||
widget.grid(row = 1, col = 1, sticky = NSEW)
|
||||
# Record button
|
||||
widget = self.createCheckbutton(
|
||||
self.gridFrame, 'Recording', 'Recording Path',
|
||||
'On: path is being recorded', self.toggleRecord, 0,
|
||||
side = LEFT, fill = BOTH, expand = 1)
|
||||
widget.pack_forget()
|
||||
widget.grid(row=2, column=0, sticky = NSEW)
|
||||
widget.configure(foreground = 'Red', relief = RAISED, borderwidth = 2,
|
||||
anchor = CENTER, width = 10)
|
||||
widget = self.createButton(self.gridFrame, 'Recording', 'Add Key Frame',
|
||||
'Add Keyframe To Current Path',
|
||||
self.addKeyframe,
|
||||
side = LEFT, expand = 1)
|
||||
widget.configure(state = 'disabled', width = 24)
|
||||
widget.pack_forget()
|
||||
widget.grid(row=2, column=1, sticky = NSEW)
|
||||
# Keyframe button
|
||||
# Hook
|
||||
widget = self.createLabeledEntry(
|
||||
self.gridFrame, 'Recording', 'Record Hook',
|
||||
'Hook used to start/stop recording',
|
||||
initialValue = self.startStopHook,
|
||||
command = self.setStartStopHook)[0]
|
||||
label['width'] = 14
|
||||
self.setStartStopHook()
|
||||
widget.pack_forget()
|
||||
widget.grid(row=3, column=0, sticky = NSEW)
|
||||
widget = self.createLabeledEntry(
|
||||
self.gridFrame, 'Recording', 'Keyframe Hook',
|
||||
'Hook used to add a new keyframe',
|
||||
initialValue = self.keyframeHook,
|
||||
command = self.setKeyframeHook)[0]
|
||||
label['width'] = 14
|
||||
self.setKeyframeHook()
|
||||
widget.pack_forget()
|
||||
widget.grid(row=3, column=1, sticky = NSEW)
|
||||
self.gridFrame.pack(fill = X, expand = 1)
|
||||
# This gets the widgets to spread out
|
||||
self.gridFrame.grid_columnconfigure(1,weight = 1)
|
||||
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')
|
||||
self.createEntryScale(playbackFrame, 'Playback', 'Time',
|
||||
'Set current playback time',
|
||||
resolution = 0.01,
|
||||
command = self.setPlaybackTime)
|
||||
font=('MSSansSerif', 12, 'bold')).pack(fill = X)
|
||||
# Playback modifiers
|
||||
frame = Frame(playbackFrame)
|
||||
self.createButton(frame, 'Playback', 'Stop', 'Stop playback',
|
||||
self.stopPlayback, side = 'left', expand = 1)
|
||||
self.createButton(frame, 'Playback', 'Play', 'Start playback',
|
||||
self.startPlayback, side = 'left', expand = 1)
|
||||
self.createButton(frame, 'Playback', 'Pause', 'Pause playback',
|
||||
self.pausePlayback, side = 'left', expand = 1)
|
||||
frame.pack(fill = 'x', expand = 1)
|
||||
playbackFrame.pack(fill = 'x')
|
||||
|
||||
## CV PAGE ##
|
||||
cvFrame = Frame(self.cvPage, relief = SUNKEN,
|
||||
borderwidth = 2)
|
||||
label = Label(cvFrame, text = 'CV CONTROLS',
|
||||
font=('MSSansSerif', 12, 'bold'))
|
||||
label.pack(fill = 'x')
|
||||
self.createEntryScale(cvFrame, 'CV Controls', 'Delta Pos',
|
||||
'Position threshold between selected points',
|
||||
resolution = 0.01,
|
||||
command = self.setDeltaPos)
|
||||
self.createEntryScale(cvFrame, 'CV Controls', 'Delta Hpr',
|
||||
'Orientation threshold between selected points',
|
||||
resolution = 0.01,
|
||||
command = self.setDeltaHpr)
|
||||
self.createEntryScale(cvFrame, 'CV Controls', 'Delta Time',
|
||||
'Time threshold between selected points',
|
||||
resolution = 0.01,
|
||||
command = self.setDeltaTime)
|
||||
|
||||
# Constant velocity frame
|
||||
frame = Frame(cvFrame)
|
||||
self.createCheckbutton(frame, 'CV Controls', 'Constant Velocity',
|
||||
'On: Resulting path has constant velocity',
|
||||
self.toggleConstantVelocity, 0,
|
||||
side = 'left')
|
||||
self.getWidget('CV Controls', 'Constant Velocity').configure(
|
||||
relief = RAISED, borderwidth = 2, anchor = CENTER)
|
||||
self.getWidget('CV Controls', 'Constant Velocity').pack(
|
||||
fill = 'x', expand = 1)
|
||||
self.createLabeledEntry(frame, 'CV Controls', 'Path Duration',
|
||||
# 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.setTotalTime)
|
||||
frame.pack(fill = 'x', expand = 1)
|
||||
cvFrame.pack(fill = 'x')
|
||||
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
|
||||
desampleFrame = Frame(
|
||||
self.recordPage, relief = SUNKEN, borderwidth = 2)
|
||||
Label(desampleFrame, text = 'DESAMPLE CURVE',
|
||||
font=('MSSansSerif', 12, 'bold')).pack()
|
||||
widget = self.createEntryScale(
|
||||
desampleFrame, 'Resample', 'Points Between Samples',
|
||||
'Recompute curve using every nth point',
|
||||
resolution = 1, max = 100, command = self.setDesampleFrequency)
|
||||
widget.component('hull')['relief'] = RIDGE
|
||||
widget.onRelease = widget.onReturnRelease = self.desampleCurve
|
||||
desampleFrame.pack(fill = X, expand = 0, pady = 2)
|
||||
# Resample
|
||||
resampleFrame = Frame(
|
||||
self.recordPage, relief = SUNKEN, borderwidth = 2)
|
||||
label = Label(resampleFrame, text = 'RESAMPLE CURVE',
|
||||
font=('MSSansSerif', 12, 'bold')).pack()
|
||||
widget = self.createEntryScale(
|
||||
resampleFrame, 'Resample', 'Num. Samples',
|
||||
'Number of samples in resampled curve',
|
||||
resolution = 1, max = 1000, command = self.setNumSamples,
|
||||
side = LEFT)
|
||||
widget.component('hull')['relief'] = RIDGE
|
||||
widget.onRelease = widget.onReturnRelease = self.sampleCurve
|
||||
self.createCheckbutton(resampleFrame, 'Resample', 'Even',
|
||||
'On: Resulting path has constant velocity',
|
||||
self.setEven, self.fEven,
|
||||
side = LEFT, fill = BOTH, expand = 0)
|
||||
resampleFrame.pack(fill = X, expand = 0, pady = 2)
|
||||
|
||||
## REFINE PAGE ##
|
||||
refineFrame = Frame(self.refinePage, relief = SUNKEN,
|
||||
borderwidth = 2)
|
||||
label = Label(refineFrame, text = 'REFINE CURVE',
|
||||
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,
|
||||
|
|
@ -271,12 +397,12 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
'Stop time of refine pass',
|
||||
resolution = 0.01,
|
||||
command = self.setRefineStop)
|
||||
refineFrame.pack(fill = 'x')
|
||||
refineFrame.pack(fill = X)
|
||||
|
||||
offsetFrame = Frame(self.refinePage)
|
||||
self.createButton(offsetFrame, 'Refine Page', 'Offset',
|
||||
'Zero refine curve offset',
|
||||
self.resetOffset, side = 'left')
|
||||
self.resetOffset, side = LEFT)
|
||||
self.createLabeledEntry(offsetFrame, 'Refine Page', 'X',
|
||||
'Refine pass X offset', width = 3, expand = 1)
|
||||
self.createLabeledEntry(offsetFrame, 'Refine Page', 'Y',
|
||||
|
|
@ -289,12 +415,12 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
'Refine pass P offset', width = 3, expand = 1)
|
||||
self.createLabeledEntry(offsetFrame, 'Refine Page', 'R',
|
||||
'Refine pass R offset', width = 3, expand = 1)
|
||||
offsetFrame.pack(fill = 'x')
|
||||
offsetFrame.pack(fill = X)
|
||||
|
||||
frame = Frame(self.refinePage)
|
||||
self.createButton(frame, 'Refine Page', 'Speed',
|
||||
'Reset refine speed',
|
||||
self.resetRefineSpeed, side = 'left',)
|
||||
self.resetRefineSpeed, side = LEFT,)
|
||||
|
||||
self.mainNotebook.setnaturalsize()
|
||||
|
||||
|
|
@ -337,7 +463,8 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
self.ignore(event)
|
||||
# remove start stop hook
|
||||
self.ignore(self.startStopHook)
|
||||
self.tempCS.removeNode()
|
||||
self.ignore(self.keyframeHook)
|
||||
self.recorderNodePath.removeNode()
|
||||
|
||||
def createNewPointSet(self):
|
||||
self.pointSet = []
|
||||
|
|
@ -351,11 +478,24 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
names = list(listbox.get(0,'end'))
|
||||
names.append(self.pointSetName)
|
||||
scrolledList.setlist(names)
|
||||
comboBox.selectitem(self.pointSetName)
|
||||
# Update count
|
||||
self.pointSetCount += 1
|
||||
|
||||
def selectPointSetNamed(self, name):
|
||||
self.pointSet = self.pointSetDict.get(name, None)
|
||||
# Reload points into curve fitter
|
||||
# Reset curve fitters
|
||||
self.xyzCurveFitter.reset()
|
||||
self.hprCurveFitter.reset()
|
||||
for time, pos, hpr in self.pointSet:
|
||||
# Add it to the curve fitters
|
||||
self.xyzCurveFitter.addPoint(time, pos )
|
||||
self.hprCurveFitter.addPoint(time, hpr)
|
||||
self.fHasPoints = 1
|
||||
# Compute curve
|
||||
self.computeCurves()
|
||||
|
||||
|
||||
def setTraceVis(self):
|
||||
print self.traceVis.get()
|
||||
|
|
@ -382,17 +522,41 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
self.nurbsCurveDrawer.setNumTicks(0)
|
||||
|
||||
def setMarkerVis(self):
|
||||
print self.markerVis.get()
|
||||
if self.markerVis.get():
|
||||
self.playbackMarker.reparentTo(self.recorderNodePath)
|
||||
else:
|
||||
self.playbackMarker.reparentTo(hidden)
|
||||
|
||||
def setStartStopHook(self, event):
|
||||
def setStartStopHook(self, event = None):
|
||||
# Clear out old hook
|
||||
self.ignore(self.startStopHook)
|
||||
# Record new one
|
||||
hook = self.getVariable('Recording', 'Start/Stop Hook').get()
|
||||
hook = self.getVariable('Recording', 'Record Hook').get()
|
||||
self.startStopHook = hook
|
||||
# Add new one
|
||||
self.accept(self.startStopHook, self.toggleRecordVar)
|
||||
|
||||
def setKeyframeHook(self, event = None):
|
||||
# Clear out old hook
|
||||
self.ignoreKeyframeHook()
|
||||
# Record new one
|
||||
hook = self.getVariable('Recording', 'Keyframe Hook').get()
|
||||
self.keyframeHook = hook
|
||||
|
||||
def acceptKeyframeHook(self):
|
||||
# Add new one
|
||||
self.accept(self.keyframeHook, self.addKeyframe)
|
||||
|
||||
def ignoreKeyframeHook(self):
|
||||
# Clear out old hook
|
||||
self.ignore(self.keyframeHook)
|
||||
|
||||
def setRecordType(self):
|
||||
if self.recordType.get() == 'Keyframe':
|
||||
self.getWidget('Recording', 'Add Key Frame')['state'] = 'normal'
|
||||
else:
|
||||
self.getWidget('Recording', 'Add Key Frame')['state'] = 'disabled'
|
||||
|
||||
def toggleRecordVar(self):
|
||||
# Get recording variable
|
||||
v = self.getVariable('Recording', 'Recording Path')
|
||||
|
|
@ -410,18 +574,52 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
self.hprCurveFitter.reset()
|
||||
# Create a new point set to hold raw data
|
||||
self.createNewPointSet()
|
||||
# Start new task
|
||||
t = taskMgr.spawnMethodNamed(
|
||||
self.recordTask, self['name'] + '-recordTask')
|
||||
t.startTime = globalClock.getTime()
|
||||
t.uponDeath = self.endRecordTask
|
||||
# Continuous or keyframe?
|
||||
if self.recordType.get() == 'Continuous':
|
||||
# Start new task
|
||||
t = taskMgr.spawnMethodNamed(
|
||||
self.recordTask, self['name'] + '-recordTask')
|
||||
t.startTime = globalClock.getTime()
|
||||
t.uponDeath = self.endRecordTask
|
||||
else:
|
||||
# Add hook
|
||||
self.acceptKeyframeHook()
|
||||
# Record first point
|
||||
self.startPos = self['nodePath'].getPos()
|
||||
self.recordPoint(0.0)
|
||||
|
||||
# Don't want to change record modes
|
||||
self.getWidget('Recording', 'Continuous Recording')['state'] = (
|
||||
'disabled')
|
||||
self.getWidget('Recording', 'Keyframe Recording')['state'] = (
|
||||
'disabled')
|
||||
else:
|
||||
# Kill old task
|
||||
taskMgr.removeTasksNamed(self['name'] + '-recordTask')
|
||||
if self.recordType.get() == 'Continuous':
|
||||
# Kill old task
|
||||
taskMgr.removeTasksNamed(self['name'] + '-recordTask')
|
||||
else:
|
||||
# Add last point
|
||||
self.addKeyframe()
|
||||
# Ignore hook
|
||||
self.ignoreKeyframeHook()
|
||||
# Compute curve
|
||||
self.computeCurves()
|
||||
# Now you can change record modes
|
||||
self.getWidget('Recording', 'Continuous Recording')['state'] = (
|
||||
'normal')
|
||||
self.getWidget('Recording', 'Keyframe Recording')['state'] = (
|
||||
'normal')
|
||||
|
||||
def recordTask(self, state):
|
||||
# Record raw data point
|
||||
time = globalClock.getTime() - state.startTime
|
||||
self.recordPoint(time)
|
||||
return Task.cont
|
||||
|
||||
def endRecordTask(self, state):
|
||||
self.computeCurves()
|
||||
|
||||
def recordPoint(self, time):
|
||||
pos = self['nodePath'].getPos()
|
||||
hpr = self['nodePath'].getHpr()
|
||||
# Add it to the point set
|
||||
|
|
@ -429,45 +627,278 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
# Add it to the curve fitters
|
||||
self.xyzCurveFitter.addPoint(time, pos )
|
||||
self.hprCurveFitter.addPoint(time, hpr)
|
||||
return Task.cont
|
||||
self.fHasPoints = 1
|
||||
|
||||
def endRecordTask(self, state):
|
||||
# Create curve
|
||||
def addKeyframe(self):
|
||||
time = Vec3(self['nodePath'].getPos() - self.startPos).length()
|
||||
self.recordPoint(time)
|
||||
|
||||
def computeCurves(self):
|
||||
# MRM: Would be better if curvefitter had getNumPoints
|
||||
if not self.fHasPoints:
|
||||
print 'MopathRecorder: Must define curve first'
|
||||
return
|
||||
# Create curves
|
||||
# XYZ
|
||||
self.xyzCurveFitter.computeTangents(1)
|
||||
self.hprCurveFitter.computeTangents(1)
|
||||
self.nurbsCurve = self.xyzCurveFitter.makeNurbs()
|
||||
self.nurbsCurveDrawer.setCurve(self.nurbsCurve)
|
||||
self.nurbsCurveDrawer.setNumSegs(self.numSegs)
|
||||
self.xyzNurbsCurve = self.xyzCurveFitter.makeNurbs()
|
||||
self.nurbsCurveDrawer.setCurve(self.xyzNurbsCurve)
|
||||
self.nurbsCurveDrawer.draw()
|
||||
self.curveNodePath = render.attachNewNode(
|
||||
self.nurbsCurveDrawer.getGeomNode())
|
||||
|
||||
def setPlaybackTime(self, time):
|
||||
print time
|
||||
# HPR
|
||||
self.hprCurveFitter.wrapHpr()
|
||||
self.hprCurveFitter.computeTangents(1)
|
||||
self.hprNurbsCurve = self.hprCurveFitter.makeNurbs()
|
||||
# Update widget based on new curve
|
||||
self.updateCurveInfo()
|
||||
|
||||
def stopPlayback(self):
|
||||
print 'stop'
|
||||
def updateCurveInfo(self):
|
||||
if not self.xyzNurbsCurve:
|
||||
return
|
||||
# Widgets depending on max T
|
||||
maxT = '%.2f' % self.xyzNurbsCurve.getMaxT()
|
||||
self.getWidget('Playback', 'Time').configure(max = maxT)
|
||||
self.getVariable('Resample', 'Path Duration').set(maxT)
|
||||
self.getWidget('Refine Page', 'From').configure(max = maxT)
|
||||
self.getWidget('Refine Page', 'To').configure(max = maxT)
|
||||
self.maxT = float(maxT)
|
||||
# Widgets depending on number of knots
|
||||
numKnots = self.xyzNurbsCurve.getNumKnots()
|
||||
self.getWidget('Resample', 'Points Between Samples')['max'] = numKnots
|
||||
self.getWidget('Resample', 'Num. Samples')['max'] = 2 * numKnots
|
||||
|
||||
def selectRecordNodePathNamed(self, name):
|
||||
nodePath = None
|
||||
if name == 'init':
|
||||
nodePath = self['nodePath']
|
||||
# Add Combo box entry for the initial node path
|
||||
self.addRecordNodePath(nodePath)
|
||||
elif name == 'selected':
|
||||
nodePath = direct.selected.last
|
||||
# Add Combo box entry for this selected object
|
||||
self.addRecordNodePath(nodePath)
|
||||
else:
|
||||
nodePath = self.recNodePathDict.get(name, None)
|
||||
if (nodePath == None):
|
||||
# See if this evaluates into a node path
|
||||
try:
|
||||
nodePath = eval(name)
|
||||
if isinstance(nodePath, NodePath):
|
||||
self.addRecordNodePath(nodePath)
|
||||
else:
|
||||
# Good eval but not a node path, give up
|
||||
nodePath = None
|
||||
except:
|
||||
# Bogus eval
|
||||
nodePath = None
|
||||
# Clear bogus entry from listbox
|
||||
listbox = self.recNodePathMenu.component('scrolledlist')
|
||||
listbox.setlist(self.recNodePathNames)
|
||||
else:
|
||||
if name == 'widget':
|
||||
# Record relationship between selected nodes and widget
|
||||
direct.selected.getWrtAll()
|
||||
# Update active node path
|
||||
self.setRecordNodePath(nodePath)
|
||||
|
||||
def setRecordNodePath(self, nodePath):
|
||||
self['nodePath'] = nodePath
|
||||
if self['nodePath']:
|
||||
self.recNodePathMenuEntry.configure(
|
||||
background = self.recNodePathMenuBG)
|
||||
else:
|
||||
# Flash entry
|
||||
self.recNodePathMenuEntry.configure(background = 'Pink')
|
||||
|
||||
def selectPlaybackNodePathNamed(self, name):
|
||||
nodePath = None
|
||||
if name == 'init':
|
||||
nodePath = self['nodePath']
|
||||
# Add Combo box entry for the initial node path
|
||||
self.addPlaybackNodePath(nodePath)
|
||||
elif name == 'selected':
|
||||
nodePath = direct.selected.last
|
||||
# Add Combo box entry for this selected object
|
||||
self.addPlaybackNodePath(nodePath)
|
||||
else:
|
||||
nodePath = self.pbNodePathDict.get(name, None)
|
||||
if (nodePath == None):
|
||||
# See if this evaluates into a node path
|
||||
try:
|
||||
nodePath = eval(name)
|
||||
if isinstance(nodePath, NodePath):
|
||||
self.addPlaybackNodePath(nodePath)
|
||||
else:
|
||||
# Good eval but not a node path, give up
|
||||
nodePath = None
|
||||
except:
|
||||
# Bogus eval
|
||||
nodePath = None
|
||||
# Clear bogus entry from listbox
|
||||
listbox = self.pbNodePathMenu.component('scrolledlist')
|
||||
listbox.setlist(self.pbNodePathNames)
|
||||
else:
|
||||
if name == 'widget':
|
||||
# Record relationship between selected nodes and widget
|
||||
direct.selected.getWrtAll()
|
||||
# Update active node path
|
||||
self.setPlaybackNodePath(nodePath)
|
||||
|
||||
def setPlaybackNodePath(self, nodePath):
|
||||
self.playbackNodePath = nodePath
|
||||
if self.playbackNodePath:
|
||||
self.pbNodePathMenuEntry.configure(
|
||||
background = self.pbNodePathMenuBG)
|
||||
else:
|
||||
# Flash entry
|
||||
self.pbNodePathMenuEntry.configure(background = 'Pink')
|
||||
|
||||
def addRecordNodePath(self, nodePath):
|
||||
self.addNodePathToDict(nodePath, self.recNodePathNames,
|
||||
self.recNodePathMenu, self.recNodePathDict)
|
||||
|
||||
def addPlaybackNodePath(self, nodePath):
|
||||
self.addNodePathToDict(nodePath, self.pbNodePathNames,
|
||||
self.pbNodePathMenu, self.pbNodePathDict)
|
||||
|
||||
def addNodePathToDict(self, nodePath, names, menu, dict):
|
||||
if not nodePath:
|
||||
return
|
||||
# Get node path's name
|
||||
name = nodePath.getName()
|
||||
if name in ['parent', 'render', 'camera', 'marker']:
|
||||
dictName = name
|
||||
else:
|
||||
# Generate a unique name for the dict
|
||||
dictName = name + '-' + `nodePath.id().this`
|
||||
if not dict.has_key(dictName):
|
||||
# Update combo box to include new item
|
||||
names.append(dictName)
|
||||
listbox = menu.component('scrolledlist')
|
||||
listbox.setlist(names)
|
||||
# Add new item to dictionary
|
||||
dict[dictName] = nodePath
|
||||
menu.selectitem(dictName)
|
||||
|
||||
def setLoopPlayback(self):
|
||||
self.loopPlayback = self.getVariable('Playback', 'Loop').get()
|
||||
|
||||
def setPlaybackTime(self, time):
|
||||
self.playbackGoTo(time)
|
||||
|
||||
def playbackGoTo(self, time):
|
||||
if (self.xyzNurbsCurve == None) & (self.hprNurbsCurve == None):
|
||||
return
|
||||
self.playbackTime = CLAMP(time, 0.0, self.maxT)
|
||||
if self.xyzNurbsCurve != None:
|
||||
pos = Vec3(0)
|
||||
self.xyzNurbsCurve.getPoint(self.playbackTime, pos)
|
||||
self.playbackNodePath.setPos(pos)
|
||||
self.playbackNodePath.setPos(pos)
|
||||
if self.hprNurbsCurve != None:
|
||||
hpr = Vec3(0)
|
||||
self.hprNurbsCurve.getPoint(self.playbackTime, hpr)
|
||||
self.playbackNodePath.setHpr(hpr)
|
||||
|
||||
def startPlayback(self):
|
||||
print 'start'
|
||||
if (self.xyzNurbsCurve == None) & (self.hprNurbsCurve == None):
|
||||
return
|
||||
# Kill any existing tasks
|
||||
self.stopPlayback()
|
||||
# Make sure checkbutton is set
|
||||
self.getVariable('Playback', 'Play').set(1)
|
||||
# Start new playback task
|
||||
t = taskMgr.spawnMethodNamed(
|
||||
self.playbackTask, self['name'] + '-playbackTask')
|
||||
t.startOffset = self.playbackTime
|
||||
t.startTime = globalClock.getTime()
|
||||
|
||||
def playbackTask(self, state):
|
||||
dTime = globalClock.getTime() - state.startTime
|
||||
if self.loopPlayback:
|
||||
cTime = (state.startOffset + dTime) % self.maxT
|
||||
else:
|
||||
cTime = CLAMP(state.startOffset + dTime, 0.0, self.maxT)
|
||||
self.getWidget('Playback', 'Time').set(cTime)
|
||||
# Stop task if not looping and at end of curve
|
||||
if ((self.loopPlayback == 0) & ((cTime + 0.01) > self.maxT)):
|
||||
self.stopPlayback()
|
||||
return Task.done
|
||||
return Task.cont
|
||||
|
||||
def pausePlayback(self):
|
||||
print 'pause'
|
||||
def stopPlayback(self):
|
||||
self.getVariable('Playback', 'Play').set(0)
|
||||
taskMgr.removeTasksNamed(self['name'] + '-playbackTask')
|
||||
|
||||
def setDeltaPos(self, dPos):
|
||||
print dPos
|
||||
def jumpToStartOfPlayback(self):
|
||||
self.stopPlayback()
|
||||
self.getWidget('Playback', 'Time').set(0.0)
|
||||
|
||||
def setDeltaHpr(self, dHpr):
|
||||
print dHpr
|
||||
def jumpToEndOfPlayback(self):
|
||||
self.stopPlayback()
|
||||
if self.xyzNurbsCurve != None:
|
||||
self.getWidget('Playback', 'Time').set(self.maxT)
|
||||
|
||||
def setDeltaTime(self, dTime):
|
||||
print dTime
|
||||
def startStopPlayback(self):
|
||||
if self.getVariable('Playback', 'Play').get():
|
||||
self.startPlayback()
|
||||
else:
|
||||
self.stopPlayback()
|
||||
|
||||
def toggleConstantVelocity(self):
|
||||
print self.getWidget('CV Controls', 'Constant Velocity').get()
|
||||
def setDesampleFrequency(self, frequency):
|
||||
self.desampleFrequency = frequency
|
||||
|
||||
def desampleCurve(self):
|
||||
if not self.fHasPoints:
|
||||
print 'MopathRecorder: Must define curve first'
|
||||
return
|
||||
# Record current curve length
|
||||
maxT = self.maxT
|
||||
# NOTE: This is destructive, points will be deleted from curve fitter
|
||||
self.xyzCurveFitter.desample(self.desampleFrequency)
|
||||
self.hprCurveFitter.desample(self.desampleFrequency)
|
||||
self.computeCurves()
|
||||
# Resize curve to original duration
|
||||
self.setPathDurationTo(maxT)
|
||||
|
||||
def setTotalTime(self):
|
||||
print 'path duration'
|
||||
def setNumSamples(self, numSamples):
|
||||
self.numSamples = int(numSamples)
|
||||
|
||||
def sampleCurve(self):
|
||||
if (self.xyzNurbsCurve == None) & (self.hprNurbsCurve == None):
|
||||
print 'MopathRecorder: Must define curve first'
|
||||
return
|
||||
# Record current curve length
|
||||
maxT = self.maxT
|
||||
# Reset curve fitters
|
||||
self.xyzCurveFitter.reset()
|
||||
self.hprCurveFitter.reset()
|
||||
# Get new data points based on given curve
|
||||
self.xyzCurveFitter.sample(
|
||||
self.xyzNurbsCurve, self.numSamples, self.fEven)
|
||||
self.hprCurveFitter.sample(
|
||||
self.hprNurbsCurve, self.numSamples, self.fEven)
|
||||
self.computeCurves()
|
||||
# Resize curve to original duration
|
||||
self.setPathDurationTo(maxT)
|
||||
|
||||
def setEven(self):
|
||||
self.fEven = self.getVariable('Resample', 'Even').get()
|
||||
|
||||
def setPathDuration(self, event):
|
||||
newMaxT = float(self.getWidget('Resample', 'Path Duration').get())
|
||||
self.setPathDurationTo(newMaxT)
|
||||
|
||||
def setPathDurationTo(self, newMaxT):
|
||||
sf = newMaxT/self.maxT
|
||||
# Scale knots
|
||||
for i in range(self.xyzNurbsCurve.getNumKnots()):
|
||||
self.xyzNurbsCurve.setKnot(i, sf * self.xyzNurbsCurve.getKnot(i))
|
||||
self.xyzNurbsCurve.recompute()
|
||||
for i in range(self.hprNurbsCurve.getNumKnots()):
|
||||
self.hprNurbsCurve.setKnot(i, sf * self.hprNurbsCurve.getKnot(i))
|
||||
self.hprNurbsCurve.recompute()
|
||||
# Update info
|
||||
self.updateCurveInfo()
|
||||
|
||||
def setRefineStart(self,value):
|
||||
print 'refine start'
|
||||
|
|
@ -492,21 +923,24 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
return self.variableDict[category + '-' + text]
|
||||
|
||||
def createLabeledEntry(self, parent, category, text, balloonHelp,
|
||||
initialValue = '',
|
||||
command = None, relief = 'sunken',
|
||||
side = 'left', expand = 1, width = 12):
|
||||
initialValue = '', command = None,
|
||||
relief = 'sunken', side = LEFT,
|
||||
expand = 1, width = 12):
|
||||
frame = Frame(parent)
|
||||
variable = StringVar()
|
||||
variable.set(initialValue)
|
||||
frame = Frame(parent)
|
||||
Label(frame, text = text).pack(side = 'left', fill = 'x')
|
||||
label = Label(frame, text = text)
|
||||
label.pack(side = LEFT, fill = X)
|
||||
self.widgetDict[category + '-' + text + '-Label'] = label
|
||||
entry = Entry(frame, width = width, relief = relief,
|
||||
textvariable = variable)
|
||||
entry.pack(side = 'left', fill = 'x', expand = expand)
|
||||
entry.pack(side = LEFT, fill = X, expand = expand)
|
||||
self.widgetDict[category + '-' + text] = entry
|
||||
self.variableDict[category + '-' + text] = variable
|
||||
if command:
|
||||
entry.bind('<Return>', command)
|
||||
frame.pack(side = side, fill = 'x', expand = expand)
|
||||
frame.pack(side = side, fill = X, expand = expand)
|
||||
return (frame, label, entry)
|
||||
|
||||
def createButton(self, parent, category, text, balloonHelp, command,
|
||||
side = 'top', expand = 0):
|
||||
|
|
@ -520,14 +954,14 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
|
||||
def createCheckbutton(self, parent, category, text,
|
||||
balloonHelp, command, initialState,
|
||||
side = 'top'):
|
||||
side = 'top', fill = X, expand = 0):
|
||||
bool = BooleanVar()
|
||||
bool.set(initialState)
|
||||
widget = Checkbutton(parent, text = text, anchor = W,
|
||||
variable = bool)
|
||||
# Do this after the widget so command isn't called on creation
|
||||
widget['command'] = command
|
||||
widget.pack(side = side, fill = X)
|
||||
widget.pack(side = side, fill = fill, expand = expand)
|
||||
self.bind(widget, balloonHelp)
|
||||
self.widgetDict[category + '-' + text] = widget
|
||||
self.variableDict[category + '-' + text] = bool
|
||||
|
|
@ -535,12 +969,12 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
|
||||
def createRadiobutton(self, parent, side, category, text,
|
||||
balloonHelp, variable, value,
|
||||
command):
|
||||
command, fill = X, expand = 0):
|
||||
widget = Radiobutton(parent, text = text, anchor = W,
|
||||
variable = variable, value = value)
|
||||
# Do this after the widget so command isn't called on creation
|
||||
widget['command'] = command
|
||||
widget.pack(side = side, fill = X)
|
||||
widget.pack(side = side, fill = fill, expand = expand)
|
||||
self.bind(widget, balloonHelp)
|
||||
self.widgetDict[category + '-' + text] = widget
|
||||
return widget
|
||||
|
|
@ -573,7 +1007,8 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
|
||||
def createEntryScale(self, parent, category, text, balloonHelp,
|
||||
command = None, min = 0.0, max = 1.0,
|
||||
resolution = None, **kw):
|
||||
resolution = None,
|
||||
side = TOP, fill = X, expand = 1, **kw):
|
||||
kw['text'] = text
|
||||
kw['min'] = min
|
||||
kw['max'] = max
|
||||
|
|
@ -581,7 +1016,7 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
widget = apply(EntryScale.EntryScale, (parent,), kw)
|
||||
# Do this after the widget so command isn't called on creation
|
||||
widget['command'] = command
|
||||
widget.pack(fill = X)
|
||||
widget.pack(side = side, fill = fill, expand = expand)
|
||||
self.bind(widget, balloonHelp)
|
||||
self.widgetDict[category + '-' + text] = widget
|
||||
return widget
|
||||
|
|
@ -656,7 +1091,7 @@ class MopathRecorder(AppShell, PandaObject):
|
|||
widget.selectitem(items[0])
|
||||
# Bind selection command
|
||||
widget['selectioncommand'] = command
|
||||
widget.pack(side = 'left', expand = 0)
|
||||
widget.pack(side = LEFT, expand = 0)
|
||||
# Bind help
|
||||
self.bind(widget, balloonHelp)
|
||||
# Record widget
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class EntryScale(Pmw.MegaWidget):
|
|||
('initialValue', 0.0, Pmw.INITOPT),
|
||||
('resolution', 0.001, None),
|
||||
('command', None, None),
|
||||
('callbackData', [], None),
|
||||
('min', 0.0, self._updateValidate),
|
||||
('max', 100.0, self._updateValidate),
|
||||
('text', 'EntryScale', self._updateLabelText),
|
||||
|
|
@ -99,6 +100,12 @@ class EntryScale(Pmw.MegaWidget):
|
|||
self.scale.pack(side = 'left', expand = 1, fill = 'x')
|
||||
# Set scale to the middle of its range
|
||||
self.scale.set(self['initialValue'])
|
||||
self.scale.bind('<Button-1>',
|
||||
lambda event, s = self:
|
||||
apply(s.onPress, s['callbackData']))
|
||||
self.scale.bind('<ButtonRelease-1>',
|
||||
lambda event, s = self:
|
||||
apply(s.onRelease, s['callbackData']))
|
||||
|
||||
self.maxLabel = self.createcomponent('maxLabel', (), None,
|
||||
Button, self.minMaxFrame,
|
||||
|
|
@ -176,7 +183,9 @@ class EntryScale(Pmw.MegaWidget):
|
|||
def _entryCommand(self, event = None):
|
||||
try:
|
||||
val = string.atof( self.entryValue.get() )
|
||||
apply(self.onReturn,self['callbackData'])
|
||||
self.set( val )
|
||||
apply(self.onReturnRelease,self['callbackData'])
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
|
@ -213,6 +222,21 @@ class EntryScale(Pmw.MegaWidget):
|
|||
if fCommand & (self['command'] is not None):
|
||||
self['command']( newVal )
|
||||
|
||||
def onReturn(self, *args):
|
||||
""" User redefinable callback executed on <Return> in entry """
|
||||
pass
|
||||
|
||||
def onReturnRelease(self, *args):
|
||||
""" User redefinable callback executed on <Return> release in entry """
|
||||
pass
|
||||
|
||||
def onPress(self, *args):
|
||||
""" User redefinable callback executed on button press """
|
||||
pass
|
||||
|
||||
def onRelease(self, *args):
|
||||
""" User redefinable callback executed on button release """
|
||||
pass
|
||||
|
||||
class EntryScaleGroup(Pmw.MegaToplevel):
|
||||
def __init__(self, parent = None, **kw):
|
||||
|
|
|
|||
Loading…
Reference in New Issue