Browse Source

*** empty log message ***

Mark Mine 25 years ago
parent
commit
2135378108

+ 6 - 21
direct/src/directutil/DirectCameraControl.py

@@ -59,8 +59,6 @@ class DirectCameraControl(PandaObject):
             # And then spawn task to determine mouse mode
             numEntries = direct.iRay.pickGeom(
                 render,direct.dr.mouseX,direct.dr.mouseY)
-            # Sort intersection points
-            direct.iRay.cq.sortEntries()
             # Then filter out hidden nodes from entry list
             indexList = []
             for i in range(0,numEntries):
@@ -79,19 +77,6 @@ class DirectCameraControl(PandaObject):
                 hitPt = direct.iRay.camToHitPt(minPt)
                 coa.set(hitPt[0],hitPt[1],hitPt[2])
                 coaDist = Vec3(coa - ZERO_POINT).length()
-                """
-                # Check other intersection points, sorting them
-                # TBD: Use TBS C++ function to do this
-                if len(indexList) > 1:
-                for i in range(1,len(indexList)):
-                entryNum = indexList[i]
-                        hitPt = direct.iRay.camToHitPt(entryNum)
-                        dist = Vec3(hitPt - ZERO_POINT).length()
-                        if (dist < coaDist):
-                            coaDist = dist
-                            coa.set(hitPt[0],hitPt[1],hitPt[2])
-                            minPt = i
-                """
                 # Handle case of bad coa point (too close or too far)
                 if ((coaDist < (1.1 * direct.dr.near)) |
                     (coaDist > direct.dr.far)):
@@ -167,10 +152,10 @@ class DirectCameraControl(PandaObject):
         taskMgr.spawnMethodNamed(self.HPPanTask, 'manipulateCamera')
 
     def XZTranslateOrHPanYZoomTask(self, state):
-        if direct.fShift:
-            return self.XZTranslateTask(state)
-        else:
+        if direct.fShift | direct.fControl:
             return self.HPanYZoomTask(state)
+        else:
+            return self.XZTranslateTask(state)
 
     def XZTranslateOrHPPanTask(self, state):
         if direct.fShift:
@@ -194,14 +179,14 @@ class DirectCameraControl(PandaObject):
         return Task.cont
 
     def HPanYZoomTask(self,state):
-        if direct.fControl:
-            moveDir = Vec3(Y_AXIS)
-        else:
+        if direct.fShift:
             moveDir = Vec3(self.coaMarker.getPos(direct.camera))
             # If marker is behind camera invert vector
             if moveDir[1] < 0.0:
                 moveDir.assign(moveDir * -1)
             moveDir.normalize()
+        else:
+            moveDir = Vec3(Y_AXIS)
         moveDir.assign(moveDir * (-2.0 * direct.dr.mouseDeltaY *
                                         state.zoomSF))
         direct.camera.setPosHpr(direct.camera,

+ 22 - 0
direct/src/directutil/DirectGeometry.py

@@ -126,6 +126,28 @@ def getCrankAngle(center):
     y = direct.dr.mouseY - center[2]
     return (180 + rad2Deg(math.atan2(y,x)))
 
+def relHpr(nodePath, base, h, p, r):
+    # Compute nodePath2newNodePath relative to base coordinate system
+    # nodePath2base
+    mNodePath2Base = nodePath.getMat(base)
+    # delta scale, orientation, and position matrix
+    mBase2NewBase = Mat4()
+    composeMatrix(mBase2NewBase, UNIT_VEC, VBase3(h,p,r), ZERO_VEC,
+                  CSDefault)
+    # base2nodePath
+    mBase2NodePath = base.getMat(nodePath)
+    # nodePath2 Parent
+    mNodePath2Parent = nodePath.getMat()
+    # Compose the result
+    resultMat = mNodePath2Base * mBase2NewBase
+    resultMat = resultMat * mBase2NodePath
+    resultMat = resultMat * mNodePath2Parent
+    # Extract and apply the hpr
+    hpr = Vec3(0)
+    decomposeMatrix(resultMat, VBase3(), hpr, VBase3(),
+                    CSDefault)
+    nodePath.setHpr(hpr)
+
 # Set direct drawing style for an object
 # Never light object or draw in wireframe
 def useDirectRenderStyle(nodePath):

+ 5 - 23
direct/src/directutil/DirectManipulation.py

@@ -269,9 +269,9 @@ class DirectManipulationControl(PandaObject):
                 # Mouse starte in central region, xlate
                 # Mode depends on shift key
                 if direct.fShift:
-                    self.xlateCamXZ(state)
-                else:
                     self.xlateCamXY(state)
+                else:
+                    self.xlateCamXZ(state)
         if self.fSetCoa:
             # Update coa based on current widget position
             direct.selected.last.mCoa2Dnp.assign(
@@ -440,8 +440,8 @@ class DirectManipulationControl(PandaObject):
             deltaX = direct.dr.mouseDeltaX
             deltaY = direct.dr.mouseDeltaY
         # Mouse motion edge to edge of display region results in one full turn
-        self.relHpr(direct.camera, deltaX * tumbleRate,
-                    -deltaY * tumbleRate, 0)
+        relHpr(direct.widget, direct.camera, deltaX * tumbleRate,
+               -deltaY * tumbleRate, 0)
 
     def rotateAboutViewVector(self, state):
         # Reset init flag in case we switch to another mode
@@ -453,25 +453,7 @@ class DirectManipulationControl(PandaObject):
         deltaAngle = angle - state.lastAngle
         state.lastAngle = angle
         # Mouse motion edge to edge of display region results in one full turn
-        self.relHpr(direct.camera, 0, 0, deltaAngle)
-
-    def relHpr(self, base, h, p, r):
-        # Compute widget2newWidget relative to base coordinate system
-        mWidget2Base = direct.widget.getMat(base)
-        mBase2NewBase = Mat4()
-        composeMatrix(mBase2NewBase, UNIT_VEC, VBase3(h,p,r), ZERO_VEC,
-                      CSDefault)
-        mBase2Widget = base.getMat(direct.widget)
-        mWidget2Parent = direct.widget.getMat()
-        # Compose the result
-        resultMat = mWidget2Base * mBase2NewBase
-        resultMat = resultMat * mBase2Widget
-        resultMat = resultMat * mWidget2Parent
-        # Extract and apply the hpr
-        hpr = Vec3(0)
-        decomposeMatrix(resultMat, VBase3(), hpr, VBase3(),
-                        CSDefault)
-        direct.widget.setHpr(hpr)
+        relHpr(direct.widget, direct.camera, 0, 0, deltaAngle)
 
     def scale3D(self, state):
         # Scale the selected node based upon up down mouse motion

+ 158 - 105
direct/src/tkpanels/Placer.py

@@ -1,24 +1,31 @@
-"DIRECT Nine DoF Placer demonstration"
+""" DIRECT Nine DoF Manipulation Panel """
 
 # Import Tkinter, Pmw, and the dial code from this directory tree.
 from PandaObject import *
 from Tkinter import *
+from AppShell import *
+from DirectGeometry import relHpr
 import Pmw
 import Dial
 import Floater
 
 """
 TODO:
-Orbital mode
 Task to monitor pose
 """
 
 ZERO_VEC = Vec3(0)
 UNIT_VEC = Vec3(1)
 
-class Placer(Pmw.MegaToplevel, PandaObject):
-    def __init__(self, parent = None, **kw):
+class Placer(AppShell):
+    # Override class variables here
+    appname = 'Placer Panel'
+    frameWidth      = 625
+    frameHeight     = 290
+    usecommandarea = 1
+    usestatusarea  = 0
 
+    def __init__(self, parent = None, **kw):
         INITOPT = Pmw.INITOPT
         optiondefs = (
             ('title',       'Placer Panel',     None),
@@ -26,29 +33,32 @@ class Placer(Pmw.MegaToplevel, PandaObject):
             )
         self.defineoptions(kw, optiondefs)
 
-        # Initialize the superclass
-        Pmw.MegaToplevel.__init__(self, parent)
+        # Call superclass initialization function
+        AppShell.__init__(self)
+        
+        self.initialiseoptions(Placer)
 
+    def appInit(self):
         # Initialize state
         self.tempCS = direct.group.attachNewNode('placerTempCS')
         self.orbitFromCS = direct.group.attachNewNode(
             'placerOrbitFromCS')
         self.orbitToCS = direct.group.attachNewNode('placerOrbitToCS')
         self.refCS = self.tempCS
-        self.undoList = []
-        self.redoList = []
         
         # Dictionary keeping track of all node paths manipulated so far
         self.nodePathDict = {}
         self.nodePathDict['camera'] = direct.camera
-        self.nodePathNames = ['selected', 'camera']
+        self.nodePathDict['widget'] = direct.widget
+        self.nodePathNames = ['camera', 'widget', 'selected']
 
         self.refNodePathDict = {}
         self.refNodePathDict['parent'] = self['nodePath'].getParent()
         self.refNodePathDict['render'] = render
         self.refNodePathDict['camera'] = direct.camera
+        self.refNodePathDict['widget'] = direct.widget
         self.refNodePathNames = ['self', 'parent', 'render',
-                                 'camera', 'selected', 'coa']
+                                 'camera', 'widget', 'selected']
 
         # Initial state
         self.initPos = Vec3(0)
@@ -71,46 +81,36 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         # Init movement mode
         self.movementMode = 'Relative To:'
 
-        # Create panel
-        # Handle to the toplevels hull
-        hull = self.component('hull')
-
-        menuFrame = Frame(hull, relief = GROOVE, bd = 2)
-        menuFrame.pack(fill = X, expand = 1)
-
-        balloon = self.balloon = Pmw.Balloon()
-        # Start with balloon help disabled
-        self.balloon.configure(state = 'none')
-        
-        menuBar = Pmw.MenuBar(menuFrame, hotkeys = 1, balloon = balloon)
-        menuBar.pack(side = LEFT, expand = 1, fill = X)
-        menuBar.addmenu('Placer', 'Placer Panel Operations')
-        menuBar.addmenuitem('Placer', 'command',
+    def createInterface(self):
+        # The interior of the toplevel panel
+        interior = self.interior()
+        # Add placer commands to menubar
+        self.menuBar.addmenu('Placer', 'Placer Panel Operations')
+        self.menuBar.addmenuitem('Placer', 'command',
                             'Zero Node Path',
                             label = 'Zero All',
                             command = self.zeroAll)
-        menuBar.addmenuitem('Placer', 'command',
+        self.menuBar.addmenuitem('Placer', 'command',
                             'Reset Node Path',
                             label = 'Reset All',
                             command = self.resetAll)
-        menuBar.addmenuitem('Placer', 'command',
+        self.menuBar.addmenuitem('Placer', 'command',
                             'Print Node Path Info',
                             label = 'Print Info',
                             command = self.printNodePathInfo)
-        menuBar.addmenuitem('Placer', 'command',
-                            'Exit Placer Panel',
-                            label = 'Exit',
-                            command = self.destroy)
-
-        menuBar.addmenu('Help', 'Placer Panel Help Operations')
-        self.toggleBalloonVar = IntVar()
-        self.toggleBalloonVar.set(0)
-        menuBar.addmenuitem('Help', 'checkbutton',
-                            'Toggle balloon help',
-                            label = 'Balloon Help',
-                            variable = self.toggleBalloonVar,
-                            command = self.toggleBalloon)
-
+        self.menuBar.addmenuitem(
+            'Placer', 'command',
+            'Toggle widget visability',
+            label = 'Toggle Widget Vis',
+            command = direct.toggleWidgetVis)
+        self.menuBar.addmenuitem(
+            'Placer', 'command',
+            'Toggle widget manipulation mode',
+            label = 'Toggle Widget Mode',
+            command = direct.manipulationControl.toggleObjectHandlesMode)
+        
+        # Get a handle to the menu frame
+        menuFrame = self.menuFrame
         self.nodePathMenu = Pmw.ComboBox(
             menuFrame, labelpos = W, label_text = 'Node Path:',
             entry_width = 20,
@@ -121,7 +121,8 @@ class Placer(Pmw.MegaToplevel, PandaObject):
             self.nodePathMenu.component('entryfield_entry'))
         self.nodePathMenuBG = (
             self.nodePathMenuEntry.configure('background')[3])
-        self.nodePathMenu.pack(side = 'left', expand = 0)
+        self.nodePathMenu.pack(side = 'left', fill = 'x', expand = 1)
+        self.bind(self.nodePathMenu, 'Select node path to manipulate')
 
         modeMenu = Pmw.OptionMenu(menuFrame,
                                   items = ('Relative To:',
@@ -130,6 +131,7 @@ class Placer(Pmw.MegaToplevel, PandaObject):
                                   command = self.setMovementMode,
                                   menubutton_width = 8)
         modeMenu.pack(side = 'left', expand = 0)
+        self.bind(modeMenu, 'Select manipulation mode')
         
         self.refNodePathMenu = Pmw.ComboBox(
             menuFrame, entry_width = 16,
@@ -138,19 +140,29 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.refNodePathMenu.selectitem('self')
         self.refNodePathMenuEntry = (
             self.refNodePathMenu.component('entryfield_entry'))
-        self.refNodePathMenu.pack(side = 'left', expand = 0)
+        self.refNodePathMenu.pack(side = 'left', fill = 'x', expand = 1)
+        self.bind(self.refNodePathMenu, 'Select relative node path')
 
         self.undoButton = Button(menuFrame, text = 'Undo',
-                                 state = 'disabled',
                                  command = direct.undo)
+        if direct.undoList:
+            self.undoButton['state'] = 'normal'
+        else:
+            self.undoButton['state'] = 'disabled'
         self.undoButton.pack(side = 'left', expand = 0)
+        self.bind(self.undoButton, 'Undo last operation')
+
         self.redoButton = Button(menuFrame, text = 'Redo',
-                                 state = 'disabled',
                                  command = direct.redo)
+        if direct.redoList:
+            self.redoButton['state'] = 'normal'
+        else:
+            self.redoButton['state'] = 'disabled'
         self.redoButton.pack(side = 'left', expand = 0)
+        self.bind(self.redoButton, 'Redo last operation')
 
         # The master frame for the dials
-	dialFrame = Frame(hull)
+	dialFrame = Frame(interior)
         dialFrame.pack(fill = 'both', expand = 1)
         
 	# Create and pack the Pos Controls
@@ -159,8 +171,9 @@ class Placer(Pmw.MegaToplevel, PandaObject):
                              tag_text = 'Position',
                              tag_font=('MSSansSerif', 14, 'bold'),
                              tag_activebackground = '#909090',
-                             ring_relief = 'raised')
+                             ring_relief = 'flat')
 	posMenubutton = posGroup.component('tag')
+        self.bind(posMenubutton, 'Position menu operations')
 	posMenu = Menu(posMenubutton)
 	posMenu.add_command(label = 'Set to zero', command = self.zeroPos)
 	posMenu.add_command(label = 'Reset initial',
@@ -178,11 +191,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.posX['command'] = self.xform
         self.posX['commandData'] = ['x']
         self.posX['callbackData'] = ['x']
-        self.posX.onReturn = self.pushUndo
+        self.posX.onReturn = self.xformStart
         self.posX.onReturnRelease = self.xformStop
         self.posX.onPress = self.xformStart
         self.posX.onRelease = self.xformStop
-        self.posX.pack(expand=1,fill='x')
+        self.posX.pack(expand=1,fill='both')
         
 	self.posY = self.createcomponent('posY', (), None,
                                          Floater.Floater, (posInterior,),
@@ -192,11 +205,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.posY['command'] = self.xform
         self.posY['commandData'] = ['y']
         self.posY['callbackData'] = ['y']
-        self.posY.onReturn = self.pushUndo
+        self.posY.onReturn = self.xformStart
         self.posY.onReturnRelease = self.xformStop
         self.posY.onPress = self.xformStart
         self.posY.onRelease = self.xformStop
-        self.posY.pack(expand=1,fill='x')
+        self.posY.pack(expand=1,fill='both')
         
 	self.posZ = self.createcomponent('posZ', (), None,
                                          Floater.Floater, (posInterior,),
@@ -206,11 +219,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.posZ['command'] = self.xform
         self.posZ['commandData'] = ['z']
         self.posZ['callbackData'] = ['z']
-        self.posZ.onReturn = self.pushUndo
+        self.posZ.onReturn = self.xformStart
         self.posZ.onReturnRelease = self.xformStop
         self.posZ.onPress = self.xformStart
         self.posZ.onRelease = self.xformStop
-        self.posZ.pack(expand=1,fill='x')
+        self.posZ.pack(expand=1,fill='both')
 
 	# Create and pack the Hpr Controls
 	hprGroup = Pmw.Group(dialFrame,
@@ -218,8 +231,9 @@ class Placer(Pmw.MegaToplevel, PandaObject):
                              tag_text = 'Orientation',
                              tag_font=('MSSansSerif', 14, 'bold'),
                              tag_activebackground = '#909090',
-                             ring_relief = 'raised')
+                             ring_relief = 'flat')
 	hprMenubutton = hprGroup.component('tag')
+        self.bind(hprMenubutton, 'Orientation menu operations')
 	hprMenu = Menu(hprMenubutton)
 	hprMenu.add_command(label = 'Set to zero', command = self.zeroHpr)
 	hprMenu.add_command(label = 'Reset initial', command = self.resetHpr)
@@ -237,11 +251,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.hprH['command'] = self.xform
         self.hprH['commandData'] = ['h']
         self.hprH['callbackData'] = ['h']
-        self.hprH.onReturn = self.pushUndo
+        self.hprH.onReturn = self.xformStart
         self.hprH.onReturnRelease = self.xformStop
         self.hprH.onPress = self.xformStart
         self.hprH.onRelease = self.xformStop
-        self.hprH.pack(expand=1,fill='x')
+        self.hprH.pack(expand=1,fill='both')
         
 	self.hprP = self.createcomponent('hprP', (), None,
                                          Dial.Dial, (hprInterior,),
@@ -252,11 +266,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.hprP['command'] = self.xform
         self.hprP['commandData'] = ['p']
         self.hprP['callbackData'] = ['p']
-        self.hprP.onReturn = self.pushUndo
+        self.hprP.onReturn = self.xformStart
         self.hprP.onReturnRelease = self.xformStop
         self.hprP.onPress = self.xformStart
         self.hprP.onRelease = self.xformStop
-        self.hprP.pack(expand=1,fill='x')
+        self.hprP.pack(expand=1,fill='both')
         
 	self.hprR = self.createcomponent('hprR', (), None,
                                          Dial.Dial, (hprInterior,),
@@ -267,11 +281,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.hprR['command'] = self.xform
         self.hprR['commandData'] = ['r']
         self.hprR['callbackData'] = ['r']
-        self.hprR.onReturn = self.pushUndo
+        self.hprR.onReturn = self.xformStart
         self.hprR.onReturnRelease = self.xformStop
         self.hprR.onPress = self.xformStart
         self.hprR.onRelease = self.xformStop
-        self.hprR.pack(expand=1,fill='x')
+        self.hprR.pack(expand=1,fill='both')
 
         # Create and pack the Scale Controls
 	# The available scaling modes
@@ -283,8 +297,9 @@ class Placer(Pmw.MegaToplevel, PandaObject):
                                tag_pyclass = Menubutton,
                                tag_font=('MSSansSerif', 14, 'bold'),
                                tag_activebackground = '#909090',
-                               ring_relief = 'raised')
+                               ring_relief = 'flat')
 	self.scaleMenubutton = scaleGroup.component('tag')
+        self.bind(self.scaleMenubutton, 'Scale menu operations')
         self.scaleMenubutton['textvariable'] = self.scalingMode
 	
 	# Scaling menu
@@ -313,11 +328,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.scaleX['command'] = self.xform
         self.scaleX['commandData'] = ['sx']
         self.scaleX['callbackData'] = ['sx']
-        self.scaleX.onReturn = self.pushUndo
+        self.scaleX.onReturn = self.xformStart
         self.scaleX.onReturnRelease = self.xformStop
         self.scaleX.onPress = self.xformStart
         self.scaleX.onRelease = self.xformStop
-        self.scaleX.pack(expand=1,fill='x')
+        self.scaleX.pack(expand=1,fill='both')
         
 	self.scaleY = self.createcomponent('scaleY', (), None,
                                            Dial.Dial, (scaleInterior,),
@@ -327,11 +342,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.scaleY['command'] = self.xform
         self.scaleY['commandData'] = ['sy']
         self.scaleY['callbackData'] = ['sy']
-        self.scaleY.onReturn = self.pushUndo
+        self.scaleY.onReturn = self.xformStart
         self.scaleY.onReturnRelease = self.xformStop
         self.scaleY.onPress = self.xformStart
         self.scaleY.onRelease = self.xformStop
-        self.scaleY.pack(expand=1,fill='x')
+        self.scaleY.pack(expand=1,fill='both')
         
 	self.scaleZ = self.createcomponent('scaleZ', (), None,
                                            Dial.Dial, (scaleInterior,),
@@ -341,11 +356,11 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.scaleZ['command'] = self.xform
         self.scaleZ['commandData'] = ['sz']
         self.scaleZ['callbackData'] = ['sz']
-        self.scaleZ.onReturn = self.pushUndo
+        self.scaleZ.onReturn = self.xformStart
         self.scaleZ.onReturnRelease = self.xformStop
         self.scaleZ.onPress = self.xformStart
         self.scaleZ.onRelease = self.xformStop
-        self.scaleZ.pack(expand=1,fill='x')
+        self.scaleZ.pack(expand=1,fill='both')
 
         # Make sure appropriate labels are showing
         self.setMovementMode('Relative To:')
@@ -353,11 +368,34 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.selectNodePathNamed('init')
 
         # Clean up things when you destroy the panel
-        self.bind('<Destroy>', self.onDestroy)
-
-        # Make sure input variables processed 
-        self.initialiseoptions(Placer)
-
+        interior.bind('<Destroy>', self.onDestroy)
+
+        self.createButtons()
+
+    def createButtons(self):
+        self.buttonAdd('Zero All',
+                       helpMessage='Zero Node Path',
+                       statusMessage='Zero Node Path',
+                       command=self.zeroAll)
+        self.buttonAdd('Reset All',
+                       helpMessage='Reset Node Path',
+                       statusMessage='Reset Node Path',
+                       command=self.resetAll)
+        self.buttonAdd('Print Info',
+                       helpMessage='Print Node Path Info',
+                       statusMessage='Print Node Path Info',
+                       command=self.printNodePathInfo)
+        self.buttonAdd('Toggle Widget Viz',
+                       helpMessage='Toggle Object Handles Visability',
+                       statusMessage='Toggle Object Handles Visability',
+                       command=direct.toggleWidgetVis)
+        self.buttonAdd(
+            'Toggle Widget Mode',
+            helpMessage='Toggle Widget Move/COA Mode',
+            statusMessage='Toggle Widget Move/COA Mode',
+            command=direct.manipulationControl.toggleObjectHandlesMode)
+
+    ### WIDGET OPERATIONS ###
     def setMovementMode(self, movementMode):
         # Set prefix
         namePrefix = ''
@@ -406,6 +444,10 @@ class Placer(Pmw.MegaToplevel, PandaObject):
                     # Clear bogus entry from listbox
                     listbox = self.nodePathMenu.component('scrolledlist')
                     listbox.setlist(self.nodePathNames)
+            else:
+                if name == 'widget':
+                    # Record relationship between selected nodes and widget
+                    direct.selected.getWrtAll()
         # Update active node path
         self.setActiveNodePath(nodePath)
 
@@ -430,11 +472,6 @@ class Placer(Pmw.MegaToplevel, PandaObject):
             nodePath = direct.selected.last
             # Add Combo box entry for this selected object
             self.addRefNodePath(nodePath)
-        elif name == 'coa':
-            if direct.selected.last:
-                nodePath = self.tempCS
-            else:
-                nodePath = None
         elif name == 'parent':
             nodePath = self['nodePath'].getParent()
         else:
@@ -518,15 +555,7 @@ class Placer(Pmw.MegaToplevel, PandaObject):
 
     def updateAuxiliaryCoordinateSystems(self):
         # Temp CS
-        if ((self.refNodePathMenuEntry.get() == 'coa') &
-            (direct.selected.last != None)):
-            pos = Vec3(0)
-            hpr = Vec3(0)
-            decomposeMatrix(direct.selected.last.mCoa2Dnp,
-                            VBase3(0), hpr, pos, CSDefault)
-            self.tempCS.setPosHpr(direct.selected.last, pos, hpr)
-        else:
-            self.tempCS.setPosHpr(self['nodePath'], 0,0,0,0,0,0)
+        self.tempCS.setPosHpr(self['nodePath'], 0,0,0,0,0,0)
         # Orbit CS
         # At reference
         self.orbitFromCS.setPos(self.refCS, 0,0,0)
@@ -537,6 +566,7 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         # Get offset from origin
         self.posOffset.assign(self['nodePath'].getPos(self.orbitFromCS))
 
+    ### NODE PATH TRANSFORMATION OPERATIONS ###
     def xform(self, value, axis):
         if axis in ['sx', 'sy', 'sz']:
             self.xformScale(value,axis)
@@ -544,11 +574,23 @@ class Placer(Pmw.MegaToplevel, PandaObject):
             self.xformRelative(value, axis)
         elif self.movementMode == 'Orbit:':
             self.xformOrbit(value, axis)
+        if self.nodePathMenu.get() == 'widget':
+            if direct.manipulationControl.fSetCoa:
+                # Update coa based on current widget position
+                direct.selected.last.mCoa2Dnp.assign(
+                    direct.widget.getMat(direct.selected.last))
+            else:
+                # Move the objects with the widget
+                direct.selected.moveWrtWidgetAll()
     
     def xformStart(self, data):
         # Record undo point
         self.pushUndo()
-        # Record initial scale
+        # If moving widget kill follow task and update wrts
+        if self.nodePathMenu.get() == 'widget':
+            taskMgr.removeTasksNamed('followSelectedNodePath')
+            # Record relationship between selected nodes and widget
+            direct.selected.getWrtAll()
         # Update placer to reflect new state
         self.updatePlacer()
         
@@ -557,6 +599,10 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         messenger.send('manipulateObjectCleanup')
         # Update placer to reflect new state
         self.updatePlacer()
+        # If moving widget restart follow task
+        if self.nodePathMenu.get() == 'widget':
+            # Restart followSelectedNodePath task
+            direct.manipulationControl.spawnFollowSelectedNodePathTask()
 
     def xformRelative(self, value, axis):
         nodePath = self['nodePath']
@@ -631,18 +677,26 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.scaleZ.set(scale[2])
 
     def zeroAll(self):
-        self.zeroPos()
-        self.zeroHpr()
-        self.unitScale()
+        self.xformStart(None)
+        self.updatePosWidgets(ZERO_VEC)
+        self.updateHprWidgets(ZERO_VEC)
+        self.updateScaleWidgets(UNIT_VEC)
+        self.xformStop(None)
 
     def zeroPos(self):
+        self.xformStart(None)
         self.updatePosWidgets(ZERO_VEC)
+        self.xformStop(None)
 
     def zeroHpr(self):
+        self.xformStart(None)
         self.updateHprWidgets(ZERO_VEC)
+        self.xformStop(None)
 
     def unitScale(self):
+        self.xformStart(None)
         self.updateScaleWidgets(UNIT_VEC)
+        self.xformStop(None)
 
     def updateResetValues(self, nodePath):
         self.initPos.assign(nodePath.getPos())
@@ -651,21 +705,28 @@ class Placer(Pmw.MegaToplevel, PandaObject):
 
     def resetAll(self):
         if self['nodePath']:
+            self.xformStart(None)
             self['nodePath'].setPosHprScale(
                 self.initPos, self.initHpr, self.initScale)
-            self.updatePlacer()
+            self.xformStop(None)
 
     def resetPos(self):
-        self['nodePath'].setPos(self.initPos)
-        self.updatePlacer()
+        if self['nodePath']:
+            self.xformStart(None)
+            self['nodePath'].setPos(self.initPos)
+            self.xformStop(None)
 
     def resetHpr(self):
-        self['nodePath'].setHpr(self.initHpr)
-        self.updatePlacer()
+        if self['nodePath']:
+            self.xformStart(None)
+            self['nodePath'].setHpr(self.initHpr)
+            self.xformStop(None)
 
     def resetScale(self):
-        self['nodePath'].setScale(self.initScale)
-        self.updatePlacer()
+        if self['nodePath']:
+            self.xformStart(None)
+            self['nodePath'].setScale(self.initScale)
+            self.xformStop(None)
 
     def pushUndo(self, fResetRedo = 1):
         direct.pushUndo([self['nodePath']])
@@ -721,15 +782,7 @@ class Placer(Pmw.MegaToplevel, PandaObject):
         self.tempCS.removeNode()
         self.orbitFromCS.removeNode()
         self.orbitToCS.removeNode()
-        # Only do this once
-        self.unbind('<Destroy>')
         
-    def toggleBalloon(self):
-        if self.toggleBalloonVar.get():
-            self.balloon.configure(state = 'balloon')
-        else:
-            self.balloon.configure(state = 'none')
-
 def place(nodePath):
     return Placer(nodePath = nodePath)
 

+ 5 - 6
direct/src/tkwidgets/Floater.py

@@ -2,7 +2,7 @@
 Floater Class: Velocity style controller for floating point values with
                 a label, entry (validated), and scale
 """
-
+from PandaObject import *
 from Tkinter import *
 import Pmw
 import string
@@ -128,16 +128,15 @@ class Floater(Pmw.MegaWidget):
     def _startFloaterTask(self,event):
         self._fFloaterTask = 1
         apply(self.onPress,self['callbackData'])
-        self._floaterTask()
+        taskMgr.spawnMethodNamed(self._floaterTask, 'floaterTask')
 
-    def _floaterTask(self):
+    def _floaterTask(self, state):
         if self.velocity != 0.0:
             self.set( self.value + self.velocity )
-        if self._fFloaterTask:
-            self.after(50, self._floaterTask)
+        return Task.cont
 
     def _floaterReset(self, event):
-        self._fFloaterTask = 0
+        taskMgr.removeTasksNamed('floaterTask')
         self.velocity = 0.0
         self.scale.set(0.0)
         apply(self.onRelease, self['callbackData'])