diff --git a/light9/curvecalc/curve.py b/light9/curvecalc/curve.py --- a/light9/curvecalc/curve.py +++ b/light9/curvecalc/curve.py @@ -1,5 +1,5 @@ from __future__ import division -import glob, time, logging +import glob, time, logging, ast from bisect import bisect_left,bisect import louie as dispatcher @@ -12,13 +12,12 @@ postPad = 4 class Curve(object): """curve does not know its name. see Curveset""" - points = None # x-sorted list of (x,y) def __init__(self): - self.points = [] + self.points = [] # x-sorted list of (x,y) self._muted = False def __repr__(self): - return "" % len(self.points) + return "<%s (%s points)>" % (self.__class__.__name__, len(self.points)) def muted(): doc = "Whether to currently send levels (boolean, obviously)" @@ -36,7 +35,8 @@ class Curve(object): def load(self,filename): self.points[:]=[] for line in file(filename): - self.points.append(tuple([float(a) for a in line.split()])) + x, y = line.split() + self.points.append((float(x), ast.literal_eval(y))) self.points.sort() dispatcher.send("points changed",sender=self) @@ -46,7 +46,7 @@ class Curve(object): return f = file(filename,'w') for p in self.points: - f.write("%s %s\n" % p) + f.write("%s %r\n" % p) f.close() def eval(self, t, allow_muting=True): @@ -66,9 +66,9 @@ class Curve(object): return y __call__=eval - def insert_pt(self,new_pt): + def insert_pt(self, new_pt): """returns index of new point""" - i = bisect(self.points,(new_pt[0],None)) + i = bisect(self.points, (new_pt[0],None)) self.points.insert(i,new_pt) return i @@ -93,7 +93,12 @@ class Curve(object): if leftidx < 0: return None return leftidx - + +class Markers(Curve): + """Marker is like a point but the y value is a string""" + def eval(self): + raise NotImplementedError() + def slope(p1,p2): if p2[0] == p1[0]: @@ -153,12 +158,19 @@ class Curveset(object): c.load(filename) curvename = curvename.replace('-','_') self.add_curve(curvename,c) + + self.markers = Markers() + try: + self.markers.load("%s.markers" % basename) + except IOError: + print "no marker file found" def save(self,basename): """writes a file for each curve with a name like basename-curvename""" for name,cur in self.curves.items(): cur.save("%s-%s" % (basename,name)) + self.markers.save("%s.markers" % basename) def curveNamesInOrder(self): return sorted(self.curves.keys(), key=self.sorter) diff --git a/light9/curvecalc/curvecalc.glade b/light9/curvecalc/curvecalc.glade --- a/light9/curvecalc/curvecalc.glade +++ b/light9/curvecalc/curvecalc.glade @@ -692,8 +692,8 @@ Mousewheel zoom; C-p play/pause music at mouse -Over a curve: C to collapse; R to rebuild canvas widget -Curve point bindings: B1 drag point; C-B1 curve add point; S-B1 sketch points; 1..5 add point at time cursor; B1 drag select points +Keys in a selected curve: C to collapse; R to rebuild broken canvas widget; 1..5 add point at time cursor; q,w,e,r,t,y set marker at time cursor +Curve point bindings: B1 drag point; C-B1 curve add point; S-B1 sketch points; B1 drag select points Available in functions: nsin/ncos period=amp=1; within(a,b) bef(x) aft(x) compare to time; smoove(x) cubic smoothstep; chan(name); curvename(t) eval curve diff --git a/light9/curvecalc/curveview.py b/light9/curvecalc/curveview.py --- a/light9/curvecalc/curveview.py +++ b/light9/curvecalc/curveview.py @@ -272,7 +272,7 @@ class Curveview(object): """ graphical curve widget only. Please pack .widget """ - def __init__(self, curve, knobEnabled=False, isMusic=False, + def __init__(self, curve, markers, knobEnabled=False, isMusic=False, zoomControl=None): """knobEnabled=True highlights the previous key and ties it to a hardware knob""" @@ -281,6 +281,7 @@ class Curveview(object): self.rebuild() self.curve = curve + self.markers = markers self.knobEnabled = knobEnabled self._isMusic = isMusic self.zoomControl = zoomControl @@ -393,6 +394,8 @@ class Curveview(object): if event.string in list('12345'): x = int(event.string) self.add_point((self.current_time(), (x - 1) / 4.0)) + if event.string in list('qwerty'): + self.add_marker((self.current_time(), event.string)) def onExpose(self, *args): if self.culled: @@ -598,7 +601,9 @@ class Curveview(object): def input_time(self, val, forceUpdate=False): if self._time == val: return - t=val + self.update_time_bar(val) + + def update_time_bar(self, t): if not getattr(self, 'timelineLine', None): self.timelineGroup = goocanvas.Group(parent=self.root) @@ -608,8 +613,8 @@ class Curveview(object): line_width=2, stroke_color='red') self.timelineLine.set_property('points', goocanvas.Points([ - self.screen_from_world((val,0)), - self.screen_from_world((val,1))])) + self.screen_from_world((t, 0)), + self.screen_from_world((t, 1))])) self._time = t if self.knobEnabled: @@ -668,11 +673,15 @@ class Curveview(object): #self.canvas.set_property("background-color", # "gray20" if self.curve.muted else "black") + self.update_time_bar(self._time) if self.size.height < 40: self._draw_line(visible_points, area=True) else: - self._draw_markers(visible_x) + self._draw_time_tics(visible_x) self._draw_line(visible_points) + self._draw_markers( + self.markers.points[i] for i in + self.markers.indices_between(visible_x[0], visible_x[1])) self.dots = {} # idx : canvas rectangle @@ -686,23 +695,39 @@ class Curveview(object): differently)""" return self._isMusic - def _draw_markers(self,visible_x): - mark = self._draw_one_marker + def _draw_markers(self, pts): + colorMap = { + 'q':'#598522', + 'w':'#662285', + 'e':'#852922', + 'r':'#85225C', + 't':'#856B22', + 'y':'#227085', + } + for t, name in pts: + x = int(self.screen_from_world((t,0))[0]) + .5 + goocanvas.polyline_new_line(self.curveGroup, + x, 0, x, self.size.height, + line_width=.4 if name in 'rty' else .8, + stroke_color=colorMap.get(name, 'gray')) - mark(0, "0") + def _draw_time_tics(self,visible_x): + tic = self._draw_one_tic + + tic(0, "0") t1,t2=visible_x if t2-t1<30: for t in range(int(t1),int(t2)+1): - mark(t,str(t)) - mark(introPad, str(introPad)) + tic(t,str(t)) + tic(introPad, str(introPad)) endtimes = dispatcher.send("get max time") if endtimes: endtime = endtimes[0][1] - mark(endtime, "end %.1f"%endtime) - mark(endtime - postPad, "post %.1f" % (endtime - postPad)) + tic(endtime, "end %.1f"%endtime) + tic(endtime - postPad, "post %.1f" % (endtime - postPad)) - def _draw_one_marker(self,t,label): + def _draw_one_tic(self,t,label): x = self.screen_from_world((t,0))[0] ht = self.size.height if not 0 <= x < self.size.width: @@ -811,6 +836,10 @@ class Curveview(object): i = self.curve.insert_pt(p) self.update_curve() self.select_indices([i]) + + def add_marker(self, p): + self.markers.insert_pt(p) + self.update_curve() def remove_point_idx(self, *idxs): idxs = list(idxs) @@ -935,7 +964,7 @@ class CurveRow(object): please pack self.box """ - def __init__(self, name, curve, slider, knobEnabled, zoomControl): + def __init__(self, name, curve, markers, slider, knobEnabled, zoomControl): self.name = name self.box = gtk.VBox() self.box.set_border_width(1) @@ -949,7 +978,7 @@ class CurveRow(object): self.cols.pack_start(controls, expand=False) self.setupControls(controls, name, curve, slider) - self.curveView = Curveview(curve, knobEnabled=knobEnabled, + self.curveView = Curveview(curve, markers, knobEnabled=knobEnabled, isMusic=name in ['music', 'smooth_music'], zoomControl=zoomControl) self.initCurveView() @@ -1098,7 +1127,8 @@ class Curvesetview(object): def add_curve(self, name, slider=None, knobEnabled=False): curve = self.curveset.curves[name] - f = CurveRow(name, curve, slider, knobEnabled, self.zoomControl) + f = CurveRow(name, curve, self.curveset.markers, + slider, knobEnabled, self.zoomControl) self.curvesVBox.pack_start(f.box) f.box.show_all() self.allCurveRows.add(f)