sePlacer.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. """ DIRECT Nine DoF Manipulation Panel """
  2. # Import Tkinter, Pmw, and the dial code from this directory tree.
  3. from direct.showbase.PandaObject import *
  4. from direct.directtools.DirectGlobals import *
  5. from direct.showbase.TkGlobal import *
  6. from direct.tkwidgets.AppShell import *
  7. from direct.tkwidgets import Dial
  8. from direct.tkwidgets import Floater
  9. """
  10. TODO:
  11. Task to monitor pose
  12. """
  13. class Placer(AppShell):
  14. # Override class variables here
  15. appname = 'Placer Panel'
  16. frameWidth = 625
  17. frameHeight = 215
  18. usecommandarea = 0
  19. usestatusarea = 0
  20. def __init__(self, parent = None, **kw):
  21. INITOPT = Pmw.INITOPT
  22. optiondefs = (
  23. ('title', self.appname, None),
  24. ('nodePath', SEditor.camera, None),
  25. )
  26. self.defineoptions(kw, optiondefs)
  27. # Call superclass initialization function
  28. AppShell.__init__(self)
  29. self.initialiseoptions(Placer)
  30. # Accept the message from sceneEditor to update the information about the target nodePath
  31. self.accept('placerUpdate', self.updatePlacer)
  32. def appInit(self):
  33. # Initialize state
  34. self.tempCS = SEditor.group.attachNewNode('placerTempCS')
  35. self.orbitFromCS = SEditor.group.attachNewNode(
  36. 'placerOrbitFromCS')
  37. self.orbitToCS = SEditor.group.attachNewNode('placerOrbitToCS')
  38. self.refCS = self.tempCS
  39. # Dictionary keeping track of all node paths manipulated so far
  40. self.nodePathDict = {}
  41. self.nodePathDict['camera'] = SEditor.camera
  42. self.nodePathDict['widget'] = SEditor.widget
  43. self.nodePathNames = ['camera', 'widget', 'selected']
  44. self.refNodePathDict = {}
  45. self.refNodePathDict['parent'] = self['nodePath'].getParent()
  46. self.refNodePathDict['render'] = render
  47. self.refNodePathDict['camera'] = SEditor.camera
  48. self.refNodePathDict['widget'] = SEditor.widget
  49. self.refNodePathNames = ['parent', 'self', 'render',
  50. 'camera', 'widget', 'selected']
  51. # Initial state
  52. self.initPos = Vec3(0)
  53. self.initHpr = Vec3(0)
  54. self.initScale = Vec3(1)
  55. self.deltaHpr = Vec3(0)
  56. # Offset for orbital mode
  57. self.posOffset = Vec3(0)
  58. # Set up event hooks
  59. self.undoEvents = [('DIRECT_undo', self.undoHook),
  60. ('DIRECT_pushUndo', self.pushUndoHook),
  61. ('DIRECT_undoListEmpty', self.undoListEmptyHook),
  62. ('DIRECT_redo', self.redoHook),
  63. ('DIRECT_pushRedo', self.pushRedoHook),
  64. ('DIRECT_redoListEmpty', self.redoListEmptyHook)]
  65. for event, method in self.undoEvents:
  66. self.accept(event, method)
  67. # Init movement mode
  68. self.movementMode = 'Relative To:'
  69. def createInterface(self):
  70. # The interior of the toplevel panel
  71. interior = self.interior()
  72. interior['relief'] = FLAT
  73. # Add placer commands to menubar
  74. self.menuBar.addmenu('Placer', 'Placer Panel Operations')
  75. self.menuBar.addmenuitem('Placer', 'command',
  76. 'Zero Node Path',
  77. label = 'Zero All',
  78. command = self.zeroAll)
  79. self.menuBar.addmenuitem('Placer', 'command',
  80. 'Reset Node Path',
  81. label = 'Reset All',
  82. command = self.resetAll)
  83. self.menuBar.addmenuitem('Placer', 'command',
  84. 'Print Node Path Info',
  85. label = 'Print Info',
  86. command = self.printNodePathInfo)
  87. self.menuBar.addmenuitem(
  88. 'Placer', 'command',
  89. 'Toggle widget visability',
  90. label = 'Toggle Widget Vis',
  91. command = SEditor.toggleWidgetVis)
  92. self.menuBar.addmenuitem(
  93. 'Placer', 'command',
  94. 'Toggle widget manipulation mode',
  95. label = 'Toggle Widget Mode',
  96. command = SEditor.manipulationControl.toggleObjectHandlesMode)
  97. # Get a handle to the menu frame
  98. menuFrame = self.menuFrame
  99. self.nodePathMenu = Pmw.ComboBox(
  100. menuFrame, labelpos = W, label_text = 'Node Path:',
  101. entry_width = 20,
  102. selectioncommand = self.selectNodePathNamed,
  103. scrolledlist_items = self.nodePathNames)
  104. self.nodePathMenu.selectitem('selected')
  105. self.nodePathMenuEntry = (
  106. self.nodePathMenu.component('entryfield_entry'))
  107. self.nodePathMenuBG = (
  108. self.nodePathMenuEntry.configure('background')[3])
  109. self.nodePathMenu.pack(side = 'left', fill = 'x', expand = 1)
  110. self.bind(self.nodePathMenu, 'Select node path to manipulate')
  111. modeMenu = Pmw.OptionMenu(menuFrame,
  112. items = ('Relative To:',
  113. 'Orbit:'),
  114. initialitem = 'Relative To:',
  115. command = self.setMovementMode,
  116. menubutton_width = 8)
  117. modeMenu.pack(side = 'left', expand = 0)
  118. self.bind(modeMenu, 'Select manipulation mode')
  119. self.refNodePathMenu = Pmw.ComboBox(
  120. menuFrame, entry_width = 16,
  121. selectioncommand = self.selectRefNodePathNamed,
  122. scrolledlist_items = self.refNodePathNames)
  123. self.refNodePathMenu.selectitem('parent')
  124. self.refNodePathMenuEntry = (
  125. self.refNodePathMenu.component('entryfield_entry'))
  126. self.refNodePathMenu.pack(side = 'left', fill = 'x', expand = 1)
  127. self.bind(self.refNodePathMenu, 'Select relative node path')
  128. self.undoButton = Button(menuFrame, text = 'Undo',
  129. command = SEditor.undo)
  130. if SEditor.undoList:
  131. self.undoButton['state'] = 'normal'
  132. else:
  133. self.undoButton['state'] = 'disabled'
  134. self.undoButton.pack(side = 'left', expand = 0)
  135. self.bind(self.undoButton, 'Undo last operation')
  136. self.redoButton = Button(menuFrame, text = 'Redo',
  137. command = SEditor.redo)
  138. if SEditor.redoList:
  139. self.redoButton['state'] = 'normal'
  140. else:
  141. self.redoButton['state'] = 'disabled'
  142. self.redoButton.pack(side = 'left', expand = 0)
  143. self.bind(self.redoButton, 'Redo last operation')
  144. # Create and pack the Pos Controls
  145. posGroup = Pmw.Group(interior,
  146. tag_pyclass = Menubutton,
  147. tag_text = 'Position',
  148. tag_font=('MSSansSerif', 14),
  149. tag_activebackground = '#909090',
  150. ring_relief = RIDGE)
  151. posMenubutton = posGroup.component('tag')
  152. self.bind(posMenubutton, 'Position menu operations')
  153. posMenu = Menu(posMenubutton, tearoff = 0)
  154. posMenu.add_command(label = 'Set to zero', command = self.zeroPos)
  155. posMenu.add_command(label = 'Reset initial',
  156. command = self.resetPos)
  157. posMenubutton['menu'] = posMenu
  158. posGroup.pack(side='left', fill = 'both', expand = 1)
  159. posInterior = posGroup.interior()
  160. # Create the dials
  161. self.posX = self.createcomponent('posX', (), None,
  162. Floater.Floater, (posInterior,),
  163. text = 'X', relief = FLAT,
  164. value = 0.0,
  165. label_foreground = 'Red')
  166. self.posX['commandData'] = ['x']
  167. self.posX['preCallback'] = self.xformStart
  168. self.posX['postCallback'] = self.xformStop
  169. self.posX['callbackData'] = ['x']
  170. self.posX.pack(expand=1,fill='both')
  171. self.posY = self.createcomponent('posY', (), None,
  172. Floater.Floater, (posInterior,),
  173. text = 'Y', relief = FLAT,
  174. value = 0.0,
  175. label_foreground = '#00A000')
  176. self.posY['commandData'] = ['y']
  177. self.posY['preCallback'] = self.xformStart
  178. self.posY['postCallback'] = self.xformStop
  179. self.posY['callbackData'] = ['y']
  180. self.posY.pack(expand=1,fill='both')
  181. self.posZ = self.createcomponent('posZ', (), None,
  182. Floater.Floater, (posInterior,),
  183. text = 'Z', relief = FLAT,
  184. value = 0.0,
  185. label_foreground = 'Blue')
  186. self.posZ['commandData'] = ['z']
  187. self.posZ['preCallback'] = self.xformStart
  188. self.posZ['postCallback'] = self.xformStop
  189. self.posZ['callbackData'] = ['z']
  190. self.posZ.pack(expand=1,fill='both')
  191. # Create and pack the Hpr Controls
  192. hprGroup = Pmw.Group(interior,
  193. tag_pyclass = Menubutton,
  194. tag_text = 'Orientation',
  195. tag_font=('MSSansSerif', 14),
  196. tag_activebackground = '#909090',
  197. ring_relief = RIDGE)
  198. hprMenubutton = hprGroup.component('tag')
  199. self.bind(hprMenubutton, 'Orientation menu operations')
  200. hprMenu = Menu(hprMenubutton, tearoff = 0)
  201. hprMenu.add_command(label = 'Set to zero', command = self.zeroHpr)
  202. hprMenu.add_command(label = 'Reset initial', command = self.resetHpr)
  203. hprMenubutton['menu'] = hprMenu
  204. hprGroup.pack(side='left',fill = 'both', expand = 1)
  205. hprInterior = hprGroup.interior()
  206. # Create the dials
  207. self.hprH = self.createcomponent('hprH', (), None,
  208. Dial.AngleDial, (hprInterior,),
  209. style = 'mini',
  210. text = 'H', value = 0.0,
  211. relief = FLAT,
  212. label_foreground = 'blue')
  213. self.hprH['commandData'] = ['h']
  214. self.hprH['preCallback'] = self.xformStart
  215. self.hprH['postCallback'] = self.xformStop
  216. self.hprH['callbackData'] = ['h']
  217. self.hprH.pack(expand=1,fill='both')
  218. self.hprP = self.createcomponent('hprP', (), None,
  219. Dial.AngleDial, (hprInterior,),
  220. style = 'mini',
  221. text = 'P', value = 0.0,
  222. relief = FLAT,
  223. label_foreground = 'red')
  224. self.hprP['commandData'] = ['p']
  225. self.hprP['preCallback'] = self.xformStart
  226. self.hprP['postCallback'] = self.xformStop
  227. self.hprP['callbackData'] = ['p']
  228. self.hprP.pack(expand=1,fill='both')
  229. self.hprR = self.createcomponent('hprR', (), None,
  230. Dial.AngleDial, (hprInterior,),
  231. style = 'mini',
  232. text = 'R', value = 0.0,
  233. relief = FLAT,
  234. label_foreground = '#00A000')
  235. self.hprR['commandData'] = ['r']
  236. self.hprR['preCallback'] = self.xformStart
  237. self.hprR['postCallback'] = self.xformStop
  238. self.hprR['callbackData'] = ['r']
  239. self.hprR.pack(expand=1,fill='both')
  240. # Create and pack the Scale Controls
  241. # The available scaling modes
  242. self.scalingMode = StringVar()
  243. self.scalingMode.set('Scale Uniform')
  244. # The scaling widgets
  245. scaleGroup = Pmw.Group(interior,
  246. tag_text = 'Scale Uniform',
  247. tag_pyclass = Menubutton,
  248. tag_font=('MSSansSerif', 14),
  249. tag_activebackground = '#909090',
  250. ring_relief = RIDGE)
  251. self.scaleMenubutton = scaleGroup.component('tag')
  252. self.bind(self.scaleMenubutton, 'Scale menu operations')
  253. self.scaleMenubutton['textvariable'] = self.scalingMode
  254. # Scaling menu
  255. scaleMenu = Menu(self.scaleMenubutton, tearoff = 0)
  256. scaleMenu.add_command(label = 'Set to unity',
  257. command = self.unitScale)
  258. scaleMenu.add_command(label = 'Reset initial',
  259. command = self.resetScale)
  260. scaleMenu.add_radiobutton(label = 'Scale Free',
  261. variable = self.scalingMode)
  262. scaleMenu.add_radiobutton(label = 'Scale Uniform',
  263. variable = self.scalingMode)
  264. scaleMenu.add_radiobutton(label = 'Scale Proportional',
  265. variable = self.scalingMode)
  266. self.scaleMenubutton['menu'] = scaleMenu
  267. # Pack group widgets
  268. scaleGroup.pack(side='left',fill = 'both', expand = 1)
  269. scaleInterior = scaleGroup.interior()
  270. # Create the dials
  271. self.scaleX = self.createcomponent('scaleX', (), None,
  272. Floater.Floater, (scaleInterior,),
  273. text = 'X Scale',
  274. relief = FLAT,
  275. min = 0.0001, value = 1.0,
  276. resetValue = 1.0,
  277. label_foreground = 'Red')
  278. self.scaleX['commandData'] = ['sx']
  279. self.scaleX['callbackData'] = ['sx']
  280. self.scaleX['preCallback'] = self.xformStart
  281. self.scaleX['postCallback'] = self.xformStop
  282. self.scaleX.pack(expand=1,fill='both')
  283. self.scaleY = self.createcomponent('scaleY', (), None,
  284. Floater.Floater, (scaleInterior,),
  285. text = 'Y Scale',
  286. relief = FLAT,
  287. min = 0.0001, value = 1.0,
  288. resetValue = 1.0,
  289. label_foreground = '#00A000')
  290. self.scaleY['commandData'] = ['sy']
  291. self.scaleY['callbackData'] = ['sy']
  292. self.scaleY['preCallback'] = self.xformStart
  293. self.scaleY['postCallback'] = self.xformStop
  294. self.scaleY.pack(expand=1,fill='both')
  295. self.scaleZ = self.createcomponent('scaleZ', (), None,
  296. Floater.Floater, (scaleInterior,),
  297. text = 'Z Scale',
  298. relief = FLAT,
  299. min = 0.0001, value = 1.0,
  300. resetValue = 1.0,
  301. label_foreground = 'Blue')
  302. self.scaleZ['commandData'] = ['sz']
  303. self.scaleZ['callbackData'] = ['sz']
  304. self.scaleZ['preCallback'] = self.xformStart
  305. self.scaleZ['postCallback'] = self.xformStop
  306. self.scaleZ.pack(expand=1,fill='both')
  307. # Make sure appropriate labels are showing
  308. self.setMovementMode('Relative To:')
  309. # Set up placer for inital node path
  310. self.selectNodePathNamed('init')
  311. self.selectRefNodePathNamed('parent')
  312. # Update place to reflect initial state
  313. self.updatePlacer()
  314. # Now that you're done setting up, attach commands
  315. self.posX['command'] = self.xform
  316. self.posY['command'] = self.xform
  317. self.posZ['command'] = self.xform
  318. self.hprH['command'] = self.xform
  319. self.hprP['command'] = self.xform
  320. self.hprR['command'] = self.xform
  321. self.scaleX['command'] = self.xform
  322. self.scaleY['command'] = self.xform
  323. self.scaleZ['command'] = self.xform
  324. ### WIDGET OPERATIONS ###
  325. def setMovementMode(self, movementMode):
  326. # Set prefix
  327. namePrefix = ''
  328. self.movementMode = movementMode
  329. if (movementMode == 'Relative To:'):
  330. namePrefix = 'Relative '
  331. elif (movementMode == 'Orbit:'):
  332. namePrefix = 'Orbit '
  333. # Update pos widgets
  334. self.posX['text'] = namePrefix + 'X'
  335. self.posY['text'] = namePrefix + 'Y'
  336. self.posZ['text'] = namePrefix + 'Z'
  337. # Update hpr widgets
  338. if (movementMode == 'Orbit:'):
  339. namePrefix = 'Orbit delta '
  340. self.hprH['text'] = namePrefix + 'H'
  341. self.hprP['text'] = namePrefix + 'P'
  342. self.hprR['text'] = namePrefix + 'R'
  343. # Update temp cs and initialize widgets
  344. self.updatePlacer()
  345. def setScalingMode(self):
  346. if self['nodePath']:
  347. scale = self['nodePath'].getScale()
  348. if ((scale[0] != scale[1]) or
  349. (scale[0] != scale[2]) or
  350. (scale[1] != scale[2])):
  351. self.scalingMode.set('Scale Free')
  352. def selectNodePathNamed(self, name):
  353. nodePath = None
  354. if name == 'init':
  355. nodePath = self['nodePath']
  356. # Add Combo box entry for the initial node path
  357. self.addNodePath(nodePath)
  358. elif name == 'selected':
  359. nodePath = SEditor.selected.last
  360. # Add Combo box entry for this selected object
  361. self.addNodePath(nodePath)
  362. else:
  363. nodePath = self.nodePathDict.get(name, None)
  364. if (nodePath == None):
  365. # See if this evaluates into a node path
  366. try:
  367. nodePath = eval(name)
  368. if isinstance(nodePath, NodePath):
  369. self.addNodePath(nodePath)
  370. else:
  371. # Good eval but not a node path, give up
  372. nodePath = None
  373. except:
  374. # Bogus eval
  375. nodePath = None
  376. # Clear bogus entry from listbox
  377. listbox = self.nodePathMenu.component('scrolledlist')
  378. listbox.setlist(self.nodePathNames)
  379. else:
  380. if name == 'widget':
  381. # Record relationship between selected nodes and widget
  382. SEditor.selected.getWrtAll()
  383. # Update active node path
  384. self.setActiveNodePath(nodePath)
  385. def setActiveNodePath(self, nodePath):
  386. self['nodePath'] = nodePath
  387. if self['nodePath']:
  388. self.nodePathMenuEntry.configure(
  389. background = self.nodePathMenuBG)
  390. # Check to see if node path and ref node path are the same
  391. if ((self.refCS != None) and
  392. (self.refCS.id() == self['nodePath'].id())):
  393. # Yes they are, use temp CS as ref
  394. # This calls updatePlacer
  395. self.setReferenceNodePath(self.tempCS)
  396. # update listbox accordingly
  397. self.refNodePathMenu.selectitem('parent')
  398. else:
  399. # Record initial value and initialize the widgets
  400. self.updatePlacer()
  401. # Record initial position
  402. self.updateResetValues(self['nodePath'])
  403. # Set scaling mode based on node path's current scale
  404. self.setScalingMode()
  405. else:
  406. # Flash entry
  407. self.nodePathMenuEntry.configure(background = 'Pink')
  408. def selectRefNodePathNamed(self, name):
  409. nodePath = None
  410. if name == 'self':
  411. nodePath = self.tempCS
  412. elif name == 'selected':
  413. nodePath = SEditor.selected.last
  414. # Add Combo box entry for this selected object
  415. self.addRefNodePath(nodePath)
  416. elif name == 'parent':
  417. nodePath = self['nodePath'].getParent()
  418. else:
  419. nodePath = self.refNodePathDict.get(name, None)
  420. if (nodePath == None):
  421. # See if this evaluates into a node path
  422. try:
  423. nodePath = eval(name)
  424. if isinstance(nodePath, NodePath):
  425. self.addRefNodePath(nodePath)
  426. else:
  427. # Good eval but not a node path, give up
  428. nodePath = None
  429. except:
  430. # Bogus eval
  431. nodePath = None
  432. # Clear bogus entry from listbox
  433. listbox = self.refNodePathMenu.component('scrolledlist')
  434. listbox.setlist(self.refNodePathNames)
  435. # Check to see if node path and ref node path are the same
  436. if (nodePath != None) and (nodePath.id() == self['nodePath'].id()):
  437. # Yes they are, use temp CS and update listbox accordingly
  438. nodePath = self.tempCS
  439. self.refNodePathMenu.selectitem('parent')
  440. # Update ref node path
  441. self.setReferenceNodePath(nodePath)
  442. def setReferenceNodePath(self, nodePath):
  443. self.refCS = nodePath
  444. if self.refCS:
  445. self.refNodePathMenuEntry.configure(
  446. background = self.nodePathMenuBG)
  447. # Update placer to reflect new state
  448. self.updatePlacer()
  449. else:
  450. # Flash entry
  451. self.refNodePathMenuEntry.configure(background = 'Pink')
  452. def addNodePath(self, nodePath):
  453. self.addNodePathToDict(nodePath, self.nodePathNames,
  454. self.nodePathMenu, self.nodePathDict)
  455. def addRefNodePath(self, nodePath):
  456. self.addNodePathToDict(nodePath, self.refNodePathNames,
  457. self.refNodePathMenu, self.refNodePathDict)
  458. def addNodePathToDict(self, nodePath, names, menu, dict):
  459. if not nodePath:
  460. return
  461. # Get node path's name
  462. name = nodePath.getName()
  463. if name in ['parent', 'render', 'camera']:
  464. dictName = name
  465. else:
  466. # Generate a unique name for the dict
  467. dictName = name + '-' + `nodePath.id()`
  468. if not dict.has_key(dictName):
  469. # Update combo box to include new item
  470. names.append(dictName)
  471. listbox = menu.component('scrolledlist')
  472. listbox.setlist(names)
  473. # Add new item to dictionary
  474. dict[dictName] = nodePath
  475. menu.selectitem(dictName)
  476. def updatePlacer(self):
  477. pos = Vec3(0)
  478. hpr = Vec3(0)
  479. scale = Vec3(1)
  480. np = self['nodePath']
  481. if (np != None) and isinstance(np, NodePath):
  482. # Update temp CS
  483. self.updateAuxiliaryCoordinateSystems()
  484. # Update widgets
  485. if self.movementMode == 'Orbit:':
  486. pos.assign(self.posOffset)
  487. hpr.assign(ZERO_VEC)
  488. scale.assign(np.getScale())
  489. elif self.refCS:
  490. pos.assign(np.getPos(self.refCS))
  491. hpr.assign(np.getHpr(self.refCS))
  492. scale.assign(np.getScale())
  493. self.updatePosWidgets(pos)
  494. self.updateHprWidgets(hpr)
  495. self.updateScaleWidgets(scale)
  496. def updateAuxiliaryCoordinateSystems(self):
  497. # Temp CS
  498. self.tempCS.setPosHpr(self['nodePath'], 0,0,0,0,0,0)
  499. # Orbit CS
  500. # At reference
  501. self.orbitFromCS.setPos(self.refCS, 0,0,0)
  502. # But aligned with target
  503. self.orbitFromCS.setHpr(self['nodePath'], 0,0,0)
  504. # Also update to CS
  505. self.orbitToCS.setPosHpr(self.orbitFromCS, 0,0,0,0,0,0)
  506. # Get offset from origin
  507. self.posOffset.assign(self['nodePath'].getPos(self.orbitFromCS))
  508. ### NODE PATH TRANSFORMATION OPERATIONS ###
  509. def xform(self, value, axis):
  510. if axis in ['sx', 'sy', 'sz']:
  511. self.xformScale(value,axis)
  512. elif self.movementMode == 'Relative To:':
  513. self.xformRelative(value, axis)
  514. elif self.movementMode == 'Orbit:':
  515. self.xformOrbit(value, axis)
  516. if self.nodePathMenu.get() == 'widget':
  517. if SEditor.manipulationControl.fSetCoa:
  518. # Update coa based on current widget position
  519. SEditor.selected.last.mCoa2Dnp.assign(
  520. SEditor.widget.getMat(SEditor.selected.last))
  521. else:
  522. # Move the objects with the widget
  523. SEditor.selected.moveWrtWidgetAll()
  524. def xformStart(self, data):
  525. # Record undo point
  526. self.pushUndo()
  527. # If moving widget kill follow task and update wrts
  528. if self.nodePathMenu.get() == 'widget':
  529. taskMgr.remove('followSelectedNodePath')
  530. # Record relationship between selected nodes and widget
  531. SEditor.selected.getWrtAll()
  532. # Record initial state
  533. self.deltaHpr = self['nodePath'].getHpr(self.refCS)
  534. # Update placer to reflect new state
  535. self.updatePlacer()
  536. def xformStop(self, data):
  537. # Throw event to signal manipulation done
  538. # Send nodepath as a list
  539. messenger.send('DIRECT_manipulateObjectCleanup', [[self['nodePath']]])
  540. # Update placer to reflect new state
  541. self.updatePlacer()
  542. # If moving widget restart follow task
  543. if self.nodePathMenu.get() == 'widget':
  544. # Restart followSelectedNodePath task
  545. SEditor.manipulationControl.spawnFollowSelectedNodePathTask()
  546. def xformRelative(self, value, axis):
  547. nodePath = self['nodePath']
  548. if (nodePath != None) and (self.refCS != None):
  549. if axis == 'x':
  550. nodePath.setX(self.refCS, value)
  551. elif axis == 'y':
  552. nodePath.setY(self.refCS, value)
  553. elif axis == 'z':
  554. nodePath.setZ(self.refCS, value)
  555. else:
  556. if axis == 'h':
  557. self.deltaHpr.setX(value)
  558. elif axis == 'p':
  559. self.deltaHpr.setY(value)
  560. elif axis == 'r':
  561. self.deltaHpr.setZ(value)
  562. # Put node path at new hpr
  563. nodePath.setHpr(self.refCS, self.deltaHpr)
  564. def xformOrbit(self, value, axis):
  565. nodePath = self['nodePath']
  566. if ((nodePath != None) and (self.refCS != None) and
  567. (self.orbitFromCS != None) and (self.orbitToCS != None)):
  568. if axis == 'x':
  569. self.posOffset.setX(value)
  570. elif axis == 'y':
  571. self.posOffset.setY(value)
  572. elif axis == 'z':
  573. self.posOffset.setZ(value)
  574. elif axis == 'h':
  575. self.orbitToCS.setH(self.orbitFromCS, value)
  576. elif axis == 'p':
  577. self.orbitToCS.setP(self.orbitFromCS, value)
  578. elif axis == 'r':
  579. self.orbitToCS.setR(self.orbitFromCS, value)
  580. nodePath.setPosHpr(self.orbitToCS, self.posOffset, ZERO_VEC)
  581. def xformScale(self, value, axis):
  582. if self['nodePath']:
  583. mode = self.scalingMode.get()
  584. scale = self['nodePath'].getScale()
  585. if mode == 'Scale Free':
  586. if axis == 'sx':
  587. scale.setX(value)
  588. elif axis == 'sy':
  589. scale.setY(value)
  590. elif axis == 'sz':
  591. scale.setZ(value)
  592. elif mode == 'Scale Uniform':
  593. scale.set(value,value,value)
  594. elif mode == 'Scale Proportional':
  595. if axis == 'sx':
  596. sf = value/scale[0]
  597. elif axis == 'sy':
  598. sf = value/scale[1]
  599. elif axis == 'sz':
  600. sf = value/scale[2]
  601. scale = scale * sf
  602. self['nodePath'].setScale(scale)
  603. def updatePosWidgets(self, pos):
  604. self.posX.set(pos[0])
  605. self.posY.set(pos[1])
  606. self.posZ.set(pos[2])
  607. def updateHprWidgets(self, hpr):
  608. self.hprH.set(hpr[0])
  609. self.hprP.set(hpr[1])
  610. self.hprR.set(hpr[2])
  611. def updateScaleWidgets(self, scale):
  612. self.scaleX.set(scale[0])
  613. self.scaleY.set(scale[1])
  614. self.scaleZ.set(scale[2])
  615. def zeroAll(self):
  616. self.xformStart(None)
  617. self.updatePosWidgets(ZERO_VEC)
  618. self.updateHprWidgets(ZERO_VEC)
  619. self.updateScaleWidgets(UNIT_VEC)
  620. self.xformStop(None)
  621. def zeroPos(self):
  622. self.xformStart(None)
  623. self.updatePosWidgets(ZERO_VEC)
  624. self.xformStop(None)
  625. def zeroHpr(self):
  626. self.xformStart(None)
  627. self.updateHprWidgets(ZERO_VEC)
  628. self.xformStop(None)
  629. def unitScale(self):
  630. self.xformStart(None)
  631. self.updateScaleWidgets(UNIT_VEC)
  632. self.xformStop(None)
  633. def updateResetValues(self, nodePath):
  634. self.initPos.assign(nodePath.getPos())
  635. self.posX['resetValue'] = self.initPos[0]
  636. self.posY['resetValue'] = self.initPos[1]
  637. self.posZ['resetValue'] = self.initPos[2]
  638. self.initHpr.assign(nodePath.getHpr())
  639. self.hprH['resetValue'] = self.initHpr[0]
  640. self.hprP['resetValue'] = self.initHpr[1]
  641. self.hprR['resetValue'] = self.initHpr[2]
  642. self.initScale.assign(nodePath.getScale())
  643. self.scaleX['resetValue'] = self.initScale[0]
  644. self.scaleY['resetValue'] = self.initScale[1]
  645. self.scaleZ['resetValue'] = self.initScale[2]
  646. def resetAll(self):
  647. if self['nodePath']:
  648. self.xformStart(None)
  649. self['nodePath'].setPosHprScale(
  650. self.initPos, self.initHpr, self.initScale)
  651. self.xformStop(None)
  652. def resetPos(self):
  653. if self['nodePath']:
  654. self.xformStart(None)
  655. self['nodePath'].setPos(self.initPos)
  656. self.xformStop(None)
  657. def resetHpr(self):
  658. if self['nodePath']:
  659. self.xformStart(None)
  660. self['nodePath'].setHpr(self.initHpr)
  661. self.xformStop(None)
  662. def resetScale(self):
  663. if self['nodePath']:
  664. self.xformStart(None)
  665. self['nodePath'].setScale(self.initScale)
  666. self.xformStop(None)
  667. def pushUndo(self, fResetRedo = 1):
  668. SEditor.pushUndo([self['nodePath']])
  669. def undoHook(self, nodePathList = []):
  670. # Reflect new changes
  671. self.updatePlacer()
  672. def pushUndoHook(self):
  673. # Make sure button is reactivated
  674. self.undoButton.configure(state = 'normal')
  675. def undoListEmptyHook(self):
  676. # Make sure button is deactivated
  677. self.undoButton.configure(state = 'disabled')
  678. def pushRedo(self):
  679. SEditor.pushRedo([self['nodePath']])
  680. def redoHook(self, nodePathList = []):
  681. # Reflect new changes
  682. self.updatePlacer()
  683. def pushRedoHook(self):
  684. # Make sure button is reactivated
  685. self.redoButton.configure(state = 'normal')
  686. def redoListEmptyHook(self):
  687. # Make sure button is deactivated
  688. self.redoButton.configure(state = 'disabled')
  689. def printNodePathInfo(self):
  690. np = self['nodePath']
  691. if np:
  692. name = np.getName()
  693. pos = np.getPos()
  694. hpr = np.getHpr()
  695. scale = np.getScale()
  696. posString = '%.2f, %.2f, %.2f' % (pos[0], pos[1], pos[2])
  697. hprString = '%.2f, %.2f, %.2f' % (hpr[0], hpr[1], hpr[2])
  698. scaleString = '%.2f, %.2f, %.2f' % (scale[0], scale[1], scale[2])
  699. print 'NodePath: %s' % name
  700. print 'Pos: %s' % posString
  701. print 'Hpr: %s' % hprString
  702. print 'Scale: %s' % scaleString
  703. print ('%s.setPosHprScale(%s, %s, %s)' %
  704. (name, posString, hprString, scaleString))
  705. def onDestroy(self, event):
  706. # Remove hooks
  707. for event, method in self.undoEvents:
  708. self.ignore(event)
  709. self.tempCS.removeNode()
  710. self.orbitFromCS.removeNode()
  711. self.orbitToCS.removeNode()
  712. # send out a message to let sceneEditor know that placer panel has been closed.
  713. # Also, stop accepting the updata message from sceneEditor
  714. messenger.send('Placer_close')
  715. self.ignore('placerUpdate')
  716. def place(nodePath):
  717. return Placer(nodePath = nodePath)
  718. ######################################################################
  719. # Create demo in root window for testing.
  720. if __name__ == '__main__':
  721. root = Pmw.initialise()
  722. widget = Placer()