changeset 726:8fba62df5e39

selectmanip more controls, hover highlight Ignore-this: fec790c4c895a987fbd6bcf8dbc836e1
author Drew Perttula <drewp@bigasterisk.com>
date Thu, 14 Jun 2012 06:24:39 +0000
parents e8f9f4815ab4
children 3497a0adff67
files light9/curvecalc/curveview.py
diffstat 1 files changed, 140 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/light9/curvecalc/curveview.py	Thu Jun 14 06:23:31 2012 +0000
+++ b/light9/curvecalc/curveview.py	Thu Jun 14 06:24:39 2012 +0000
@@ -77,7 +77,7 @@
     draws itself on the canvas and edits the points when you drag
     various parts
     """
-    def __init__(self, parent, getSelectedIndices, getWorldPoint, getScreenPoint, getCanvasSize, setPoints, getWorldTime):
+    def __init__(self, parent, getSelectedIndices, getWorldPoint, getScreenPoint, getCanvasSize, setPoints, getWorldTime, getWorldValue, getDragRange):
         """parent goocanvas group"""
         self.getSelectedIndices = getSelectedIndices
         self.getWorldPoint = getWorldPoint
@@ -85,39 +85,112 @@
         self.getCanvasSize = getCanvasSize
         self.setPoints = setPoints
         self.getWorldTime = getWorldTime
+        self.getDragRange = getDragRange
+        self.getWorldValue = getWorldValue
         self.grp = goocanvas.Group(parent=parent)
         
         self.title = goocanvas.Text(parent=self.grp, text="selectmanip", x=10, y=10, fill_color='white', font="ubuntu 10")
 
         self.bbox = goocanvas.Rect(parent=self.grp,
-                                   stroke_color='yellow',
                                    fill_color_rgba=0xffff0030,
-                                   line_width=.7)
+                                   line_width=0)
 
         self.xTrans = goocanvas.Polyline(parent=self.grp, close_path=True,
                                          fill_color_rgba=0xffffff88,
                                          )
-        self.xTrans.connect("button-press-event", self.onXPress)
-        self.xTrans.connect("button-release-event", self.onXRelease)
-        self.xTrans.connect("motion-notify-event", self.onXMotion)
+        self.centerScale = goocanvas.Polyline(parent=self.grp, close_path=True,
+                                              fill_color_rgba=0xffffff88,
+                                         )
+
+        thickLine = lambda: goocanvas.Polyline(parent=self.grp,
+                                               stroke_color_rgba=0xffffccff,
+                                               line_width=6)
+        self.leftScale = thickLine()
+        self.rightScale = thickLine()
+        self.topScale = thickLine()
+        
+        for grp, name in [(self.xTrans, 'x'),
+                          (self.leftScale, 'left'),
+                          (self.rightScale, 'right'),
+                          (self.topScale, 'top'),
+                          (self.centerScale, 'centerScale'),
+                          ]:
+            grp.connect("button-press-event", self.onPress, name)
+            grp.connect("button-release-event", self.onRelease, name)
+            grp.connect("motion-notify-event", self.onMotion, name)
+            grp.connect("enter-notify-event", self.onEnter, name)
+            grp.connect("leave-notify-event", self.onLeave, name)
+            # and hover highlight
         self.update()
 
-    def onXPress(self, item, target_item, event):
+    def onEnter(self, item, target_item, event, param):
+        self.prevColor = item.props.stroke_color_rgba
+        item.props.stroke_color_rgba = 0xff0000ff
+        
+    def onLeave(self, item, target_item, event, param):
+        item.props.stroke_color_rgba = self.prevColor
+        
+    def onPress(self, item, target_item, event, param):
         self.dragStartTime = self.getWorldTime(event.x)
-        self.origPoints = [self.getWorldPoint(i) for i in
-                           self.getSelectedIndices()]
+        idxs = self.getSelectedIndices()
+
+        self.origPoints = [self.getWorldPoint(i) for i in idxs]
+        self.origMaxValue = max(p[1] for p in self.origPoints)
+        moveLeft, moveRight = self.getDragRange(idxs)
+
+        if param == 'centerScale':
+            self.maxPointMove = min(moveLeft, moveRight)
+        
+        self.dragRange = (self.dragStartTime - moveLeft,
+                          self.dragStartTime + moveRight)
         return True
 
-    def onXMotion(self, item, target_item, event):
+    def onMotion(self, item, target_item, event, param):
         if hasattr(self, 'dragStartTime'):
-            dt = self.getWorldTime(event.x) - self.dragStartTime
-            self.setPoints(
-                (i, (orig[0] + dt, orig[1]))
-                for i, orig in zip(self.getSelectedIndices(), self.origPoints))
-            
+            origPts = zip(self.getSelectedIndices(), self.origPoints)
+            left = origPts[0][1][0]
+            right = origPts[-1][1][0]
+            width = right - left
+
+            clampLo = left if param == 'right' else self.dragRange[0]
+            clampHi = right if param == 'left' else self.dragRange[1]
+
+            mouseT = self.getWorldTime(event.x)
+            clampedT = max(clampLo, min(clampHi, mouseT))
+            dt = clampedT - self.dragStartTime
 
-    def onXRelease(self, item, target_item, event):
-        del self.dragStartTime
+            if param == 'x':
+                self.setPoints((i, (orig[0] + dt, orig[1]))
+                               for i, orig in origPts)
+            elif param == 'left':
+                self.setPoints((i, (left + dt +
+                                    (orig[0] - left) / width * (width - dt),
+                                    orig[1])) for i, orig in origPts)
+            elif param == 'right':
+                self.setPoints((i, (left +
+                                    (orig[0] - left) / width * (width + dt),
+                                    orig[1])) for i, orig in origPts)
+            elif param == 'top':
+                v = self.getWorldValue(event.y)
+                scl = max(0, min(1 / self.origMaxValue, v / self.origMaxValue))
+                self.setPoints((i, (orig[0], orig[1] * scl))
+                               for i, orig in origPts)
+
+            elif param == 'centerScale':
+                dt = mouseT - self.dragStartTime
+                rad = width / 2
+                tMid = left + rad
+                maxScl = (rad + self.maxPointMove) / rad
+                newWidth = max(0, min((rad + dt) / rad, maxScl)) * width
+                self.setPoints((i,
+                                (tMid +
+                                 ((orig[0] - left) / width - .5) * newWidth,
+                                 orig[1])) for i, orig in origPts)
+                
+
+    def onRelease(self, item, target_item, event, param):
+        if hasattr(self, 'dragStartTime'):
+            del self.dragStartTime
 
     def update(self):
         """if the view or selection or selected point positions
@@ -133,14 +206,40 @@
         b.height = min(max(p[1] for p in pts) - b.y + margin,
                        self.getCanvasSize().height - b.y - 1)
 
-        b.visibility = goocanvas.ITEM_VISIBLE if len(pts) > 1 else goocanvas.ITEM_HIDDEN
+        multi = (goocanvas.ITEM_VISIBLE if len(pts) > 1 else
+                 goocanvas.ITEM_INVISIBLE)
+        b.visibility = multi
+        self.leftScale.props.visibility = multi
+        self.rightScale.props.visibility = multi
+        self.topScale.props.visibility = multi
+        self.centerScale.props.visibility = multi
 
         self.title.props.text = "%s %s selected" % (
             len(idxs), "point" if len(idxs) == 1 else "points")
 
         centerX = b.x + b.width / 2
 
-        midY = self.getCanvasSize().height / 2
+        midY = self.getCanvasSize().height * .5
+        loY = self.getCanvasSize().height * .8
+
+        self.leftScale.props.points = goocanvas.Points([
+            (b.x, b.y), (b.x, b.y + b.height)])
+        self.rightScale.props.points = goocanvas.Points([
+            (b.x + b.width, b.y), (b.x + b.width, b.y + b.height)])
+
+        self.topScale.props.points = goocanvas.Points([
+            (b.x, b.y), (b.x + b.width, b.y)])
+
+        self.updateXTrans(centerX, midY)
+
+        self.centerScale.props.points = goocanvas.Points([
+            (centerX - 5, loY - 5),
+            (centerX + 5, loY - 5),
+            (centerX + 5, loY + 5),
+            (centerX - 5, loY + 5)])
+            
+
+    def updateXTrans(self, centerX, midY):       
         x1 = centerX - 30
         x2 = centerX - 20
         x3 = centerX + 20
@@ -375,12 +474,14 @@
         if self.selected_points and not self.selectManip:
             self.selectManip = SelectManip(
                 self.root,
-                getSelectedIndices=lambda: self.selected_points,
+                getSelectedIndices=lambda: sorted(self.selected_points),
                 getWorldPoint=lambda i: self.curve.points[i],
                 getScreenPoint=lambda i: self.screen_from_world(self.curve.points[i]),
                 getWorldTime=lambda x: self.world_from_screen(x, 0)[0],
+                getWorldValue=lambda y: self.world_from_screen(0, y)[1],
                 getCanvasSize=lambda: self.size,
                 setPoints=self.setPoints,
+                getDragRange=self.getDragRange,
                 )
         if not self.selected_points and self.selectManip:
             self.selectManip.destroy()
@@ -388,6 +489,25 @@
 
         self.selectionChanged()
 
+    def getDragRange(self, idxs):
+        """if you're dragging these points, what's the most time you
+        can move left and right before colliding with another point"""
+        maxLeft = maxRight = 99999
+        cp = self.curve.points
+        for i in idxs:
+            nextStatic = i
+            while nextStatic >= 0 and nextStatic in idxs:
+                nextStatic -= 1
+            if nextStatic >= 0:
+                maxLeft = min(maxLeft, cp[i][0] - cp[nextStatic][0])
+
+            nextStatic = i
+            while nextStatic <= len(cp) - 1 and nextStatic in idxs:
+                nextStatic += 1
+            if nextStatic <= len(cp) - 1:
+                maxRight = min(maxRight, cp[nextStatic][0] - cp[i][0])
+        return maxLeft, maxRight
+
     def setPoints(self, updates):
         for i, pt in updates:
             self.curve.points[i] = pt