Changeset - 0a47ec94fc63
[Not reviewed]
default
0 2 0
Drew Perttula - 12 years ago 2013-06-10 09:28:11
drewp@bigasterisk.com
move all point editing into Curve. fix manip to never try to make points have the same X. add check in Curve that points never have the same X or crossing X
Ignore-this: 75ad005fd9d35a61ecef82bf8ddae3f4
2 files changed with 66 insertions and 25 deletions:
0 comments (0 inline, 0 general)
light9/curvecalc/curve.py
Show inline comments
 
@@ -69,12 +69,28 @@ class Curve(object):
 
    def insert_pt(self, new_pt):
 
        """returns index of new point"""
 
        i = bisect(self.points, (new_pt[0],None))
 
        self.points.insert(i,new_pt)
 
        return i
 

	
 
    def set_points(self, updates):
 
        for i, pt in updates:
 
            self.points[i] = pt
 
            
 
        x = None
 
        for p in self.points:
 
            if p[0] <= x:
 
                raise ValueError("overlapping points")
 
            x = p[0]
 

	
 
    def pop_point(self, i):
 
        return self.points.pop(i)
 
            
 
    def remove_point(self, pt):
 
        self.points.remove(pt)
 
            
 
    def indices_between(self, x1, x2, beyond=0):
 
        leftidx = max(0, bisect(self.points, (x1,None)) - beyond)
 
        rightidx = min(len(self.points),
 
                       bisect(self.points, (x2,None)) + beyond)
 
        return range(leftidx, rightidx)
 
        
light9/curvecalc/curveview.py
Show inline comments
 
@@ -53,13 +53,13 @@ class Sketch:
 
            p_right = (x + dx, self.curveview.curve(x + dx))
 

	
 
            if angle_between(pts[i], p_left, p_right) > 160:
 
                to_remove.append(i)
 

	
 
        for i in to_remove:
 
            self.curveview.curve.points.remove(pts[i])
 
            self.curveview.curve.remove_point(pts[i])
 
            finalPoints.remove(pts[i])
 

	
 
        # the simplified curve may now be too far away from some of
 
        # the points, so we'll put them back. this has an unfortunate
 
        # bias toward reinserting the earlier points
 
        for i in to_remove:
 
@@ -150,31 +150,42 @@ class SelectManip(object):
 
    def onMotion(self, item, target_item, event, param):
 
        if hasattr(self, 'dragStartTime'):
 
            origPts = zip(self.getSelectedIndices(), self.origPoints)
 
            left = origPts[0][1][0]
 
            right = origPts[-1][1][0]
 
            width = right - left
 
            dontCross = .001
 

	
 
            clampLo = left if param == 'right' else self.dragRange[0]
 
            clampHi = right if param == 'left' else self.dragRange[1]
 

	
 
            def clamp(x, lo, hi):
 
                return max(lo, min(hi, x))
 
            
 
            mouseT = self.getWorldTime(event.x)
 
            clampedT = max(clampLo, min(clampHi, mouseT))
 
            clampedT = clamp(mouseT, clampLo + dontCross, clampHi - dontCross)
 

	
 
            dt = clampedT - 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)
 
                self.setPoints((
 
                    i,
 
                    (left + dt +
 
                     (orig[0] - left) / width *
 
                     clamp(width - dt, dontCross, right - clampLo - dontCross),
 
                     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)
 
                self.setPoints((
 
                    i,
 
                    (left +
 
                     (orig[0] - left) / width *
 
                     clamp(width + dt, dontCross, clampHi - left - dontCross),
 
                     orig[1])) for i, orig in origPts)
 
            elif param == 'top':
 
                v = self.getWorldValue(event.y)
 
                if self.origMaxValue == 0:
 
                    self.setPoints((i, (orig[0], v)) for i, orig in origPts)
 
                else:
 
                    scl = max(0, min(1 / self.origMaxValue,
 
@@ -183,14 +194,15 @@ class SelectManip(object):
 
                                   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
 
                maxScl = (rad + self.maxPointMove - dontCross) / rad
 
                newWidth = max(dontCross / width,
 
                               min((rad + dt) / rad, maxScl)) * width
 
                self.setPoints((i,
 
                                (tMid +
 
                                 ((orig[0] - left) / width - .5) * newWidth,
 
                                 orig[1])) for i, orig in origPts)
 
                
 

	
 
@@ -462,13 +474,13 @@ class Curveview(object):
 
        left of the current time"""
 
        if curve != self.curve:
 
            return
 
        idx = self.curve.index_before(self.current_time())
 
        if idx is not None:
 
            pos = self.curve.points[idx]
 
            self.curve.points[idx] = (pos[0], value)
 
            self.curve.set_points([(idx, (pos[0], value))])
 
            self.update_curve()
 

	
 
    def slider_in(self, curve, value=None):
 
        """user pushed on a slider. make a new key.  if value is None,
 
        the value will be the same as the last."""
 
        if curve != self.curve:
 
@@ -513,14 +525,17 @@ class Curveview(object):
 
            self.selectManip.destroy()
 
            self.selectManip = None
 

	
 
        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"""
 
        """
 
        if you're dragging these points, what's the most time you can move
 
        left and right before colliding (exactly) 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
 
@@ -532,14 +547,13 @@ class Curveview(object):
 
                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
 
        self.curve.set_points(updates)
 
        self.update_curve()
 
        
 
    def selectionChanged(self):
 
        if self.selectManip:
 
            self.selectManip.update()
 

	
 
@@ -666,19 +680,18 @@ class Curveview(object):
 
            self.culled = True
 
            return
 
        self.culled = False
 
        
 
        self.size = self.canvas.get_allocation()
 
 
 
        cp = self.curve.points
 
        visible_x = (self.world_from_screen(0,0)[0],
 
                     self.world_from_screen(self.size.width, 0)[0])
 

	
 
        visible_idxs = self.curve.indices_between(visible_x[0], visible_x[1],
 
                                                  beyond=1)
 
        visible_points = [cp[i] for i in visible_idxs]
 
        visible_points = [self.curve.points[i] for i in visible_idxs]
 

	
 
        if getattr(self, 'curveGroup', None):
 
            self.curveGroup.remove()
 
        self.curveGroup = goocanvas.Group(parent=self.root)
 

	
 
        # this makes gtk quietly stop working. Getting called too early?
 
@@ -871,13 +884,13 @@ class Curveview(object):
 
        
 
    def remove_point_idx(self, *idxs):
 
        idxs = list(idxs)
 
        while idxs:
 
            i = idxs.pop()
 

	
 
            self.curve.points.pop(i)
 
            self.curve.pop_point(i)
 
            newsel = []
 
            newidxs = []
 
            for si in range(len(self.selected_points)):
 
                sp = self.selected_points[si]
 
                if sp == i:
 
                    continue
 
@@ -933,38 +946,50 @@ class Curveview(object):
 
        self.select_motion(event)
 
        
 
        if not self.dragging_dots:
 
            return
 
        if not event.state & 256:
 
            return # not lmb-down
 
        cp = self.curve.points
 
        moved=0
 

	
 
        # this way is accumulating error and also making it harder to
 
        # undo (e.g. if the user moves far out of the window or
 
        # presses esc or something). Instead, we should be resetting
 
        # the points to their start pos plus our total offset.
 
        cur = self.world_from_screen(event.x, event.y)
 
        if self.last_mouse_world:
 
            delta = (cur[0] - self.last_mouse_world[0],
 
                     cur[1] - self.last_mouse_world[1])
 
        else:
 
            delta = 0,0
 
        self.last_mouse_world = cur
 

	
 
        moved = self.translate_points(delta)
 
        
 
        if moved:
 
            self.update_curve()
 

	
 
    def translate_points(self, delta):
 
        moved = False
 
        
 
        cp = self.curve.points
 
        updates = []
 
        for idx in self.selected_points:
 

	
 
            newp = [cp[idx][0] + delta[0], cp[idx][1] + delta[1]]
 
            
 
            newp[1] = max(0,min(1,newp[1]))
 
            
 
            if idx>0 and newp[0] <= cp[idx-1][0]:
 
                continue
 
            if idx<len(cp)-1 and newp[0] >= cp[idx+1][0]:
 
                continue
 
            moved=1
 
            cp[idx] = tuple(newp)
 
        if moved:
 
            self.update_curve()
 

	
 
            moved = True
 
            updates.append((idx, tuple(newp)))
 
        self.curve.set_points(updates)
 
        return moved
 
            
 
    def unselect(self):
 
        self.select_indices([])
 

	
 
    def onScroll(self, widget, event):
 
        t = self.world_from_screen(event.x, 0)[0]
 
        self.zoomControl.zoom_about_mouse(
0 comments (0 inline, 0 general)