Changeset - 22529016c4f2
[Not reviewed]
default
0 2 0
drewp@bigasterisk.com - 20 years ago 2005-06-18 04:13:46
drewp@bigasterisk.com
new curve sketching
2 files changed with 94 insertions and 7 deletions:
0 comments (0 inline, 0 general)
bin/curvecalc
Show inline comments
 
@@ -344,13 +344,13 @@ def savekey(*args):
 
    print "saved"
 
    
 
root.bind("<Control-Key-s>",savekey)
 
root.bind("<Control-Key-r>", lambda evt: dispatcher.send('reload all subs'))
 

	
 
create_status_lines(root)
 
for helpline in ["Bindings: C-s save subterms; B1 drag point; C-B1 curve add point; 1..5 add point at time; Esc see current time; S-Esc see curtime to end; Mousewheel zoom; C-p play/pause music at mouse",
 
for helpline in ["Bindings: C-s save subterms; B1 drag point; C-B1 curve add point; S-B1 sketch points; 1..5 add point at time; Esc see current time; S-Esc see curtime to end; Mousewheel zoom; C-p play/pause music at mouse",
 
                 "Available in functions: nsin/ncos period=amp=1; within(a,b) bef(x) aft(x) compare to time; smoove(x) cubic smoothstep; curvename(t) eval curve"]:
 
    tk.Label(root,text=helpline, font="Helvetica -12 italic",
 
             anchor='w').pack(side='top',fill='x')
 

	
 
#def logprint(msg):
 
#    print "log",msg
light9/curve.py
Show inline comments
 
@@ -41,17 +41,89 @@ class Curve:
 
            return self.points[i][1]
 

	
 
        p1,p2 = self.points[i],self.points[i+1]
 
        frac = (t-p1[0])/(p2[0]-p1[0])
 
        y = p1[1]+(p2[1]-p1[1])*frac
 
        return y
 
    __call__=eval
 

	
 
    def insert_pt(self,new_pt):
 
        i = bisect(self.points,(new_pt[0],None))
 
        self.points.insert(i,new_pt)
 
    __call__=eval
 

	
 
    def indices_between(self, x1, x2):
 
        leftidx = max(0, bisect_left(self.points, (x1,None)) - 1)
 
        rightidx = min(len(self.points) - 1,
 
                       bisect_left(self.points, (x2,None)) + 1)
 
        return range(leftidx, rightidx+1)
 
        
 
    def points_between(self, x1, x2):
 
        return [self.points[i] for i in self.indices_between(x1,x2)]
 

	
 
def vlen(v):
 
    return math.sqrt(v[0]*v[0] + v[1]*v[1])
 

	
 
def angle_between(base, p0, p1):
 
    p0 = p0[0] - base[0], p0[1] - base[1]
 
    p1 = p1[0] - base[0], p1[1] - base[1]
 
    p0 = [x/vlen(p0) for x in p0]
 
    p1 = [x/vlen(p1) for x in p1]
 
    dot = p0[0]*p1[0]+p0[1]*p1[1]
 
    dot = max(-1,min(1,dot))
 
    return math.degrees(math.acos(dot))
 

	
 
class Sketch:
 
    """a sketch motion on a curveview, with temporary points while you
 
    draw, and simplification when you release"""
 
    
 
    def __init__(self,curveview,ev):
 
        self.curveview = curveview
 
        self.pts = []
 
        self.last_x = None
 

	
 
    def motion(self,ev):
 
        p = self.curveview.world_from_screen(ev.x, ev.y)
 
        p = p[0], max(0,min(1,p[1]))
 
        if self.last_x is not None and abs(ev.x - self.last_x) < 4:
 
            return
 
        self.last_x = ev.x
 
        self.pts.append(p)
 
        self.curveview.add_point(p)
 

	
 
    def release(self,ev):
 
        pts = self.pts
 
        pts.sort()
 

	
 
        dx = .01
 
        to_remove = []
 
        for i in range(1,len(pts)-1):
 
            x = pts[i][0]
 

	
 
            p_left = (x - dx, self.curveview.curve(x - dx))
 
            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])
 

	
 
        # 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:
 
            p = pts[i]
 
            if abs(self.curveview.curve(p[0]) - p[1]) > .1:
 
                self.curveview.add_point(p)
 
            
 
        self.curveview.update_curve()
 

	
 
    def slope(self,p1,p2):
 
        if p2[0] == p1[0]:
 
            return 0
 
        return (p2[1] - p1[1]) / (p2[0] - p1[0])
 

	
 
class Curveview(tk.Canvas):
 
    def __init__(self,master,curve,**kw):
 
        self.curve=curve
 
        self._time = 0
 
        tk.Canvas.__init__(self,master,width=10,height=10,
 
@@ -88,12 +160,29 @@ class Curveview(tk.Canvas):
 
                  dispatcher.send("music seek",
 
                                  t=self.world_from_screen(ev.x,0)[0]))
 

	
 
        # this binds on c-a-b1, etc
 
        RegionZoom(self, self.world_from_screen, self.screen_from_world)
 

	
 
        self.sketch = None # an in-progress sketch
 
        self.bind("<Shift-ButtonPress-1>", self.sketch_press)
 
        self.bind("<Shift-B1-Motion>", self.sketch_motion)
 
        self.bind("<Shift-ButtonRelease-1>", self.sketch_release)
 

	
 
    def sketch_press(self,ev):
 
        self.sketch = Sketch(self,ev)
 

	
 
    def sketch_motion(self,ev):
 
        if self.sketch:
 
            self.sketch.motion(ev)
 

	
 
    def sketch_release(self,ev):
 
        if self.sketch:
 
            self.sketch.release(ev)
 
            self.sketch = None
 

	
 
    def current_time(self):
 
        return self._time
 

	
 
    def screen_from_world(self,p):
 
        start,end = self.zoom
 
        ht = self.winfo_height()
 
@@ -107,25 +196,23 @@ class Curveview(tk.Canvas):
 
    def input_time(self,val):
 
        t=val
 
        pts = self.screen_from_world((val,0))+self.screen_from_world((val,1))
 
        self.delete('timecursor')
 
        self.create_line(*pts,**dict(width=2,fill='red',tags=('timecursor',)))
 
        self._time = t
 
        
 
    def update_curve(self,*args):
 

	
 
        self.zoom = dispatcher.send("zoom area")[0][1]
 
        cp = self.curve.points
 

	
 
        visible_x = (self.world_from_screen(0,0)[0],
 
                     self.world_from_screen(self.winfo_width(),0)[0])
 

	
 
        visleftidx = max(0,bisect_left(cp,(visible_x[0],None))-1)
 
        visrightidx = min(len(cp)-1,bisect_left(cp,(visible_x[1],None))+1)
 
                             
 
        visible_points = cp[visleftidx:visrightidx+1]
 
        visible_idxs = range(visleftidx,visrightidx+1)
 
        visible_points = self.curve.points_between(*visible_x)
 
        visible_idxs = self.curve.indices_between(*visible_x)
 
        
 
        self.delete('curve')
 

	
 
        if self.winfo_height() < 40:
 
            self._draw_gradient()
 
        else:
0 comments (0 inline, 0 general)