# HG changeset patch # User drewp@bigasterisk.com # Date 1142916635 0 # Node ID d434a74fc068dc6b5914e9652ed1e235e9918d83 # Parent 11597ff6ff6ac80fb199ac4e598c3804043b3081 moves/removes diff -r 11597ff6ff6a -r d434a74fc068 bin/tracker --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/tracker Tue Mar 21 04:50:35 2006 +0000 @@ -0,0 +1,273 @@ +#!/usr/bin/python +from __future__ import division,nested_scopes + +import sys +sys.path.append("../../editor/pour") +sys.path.append("../light8") + +from Submaster import Submaster +from skim.zooming import Zooming,Pair +from math import sqrt,sin,cos +from pygame.rect import Rect +from xmlnodebase import xmlnodeclass,collectiveelement,xmldocfile +from dispatch import dispatcher + +import dmxclient + +import Tkinter as tk + +defaultfont="arial 8" + +def pairdist(pair1,pair2): + return pair1.dist(pair2) + +def canvashighlighter(canvas,obj,attribute,normalval,highlightval): + """creates bindings on a canvas obj that make attribute go + from normal to highlight when the mouse is over the obj""" + canvas.tag_bind(obj,"", + lambda ev: canvas.itemconfig(obj,**{attribute:highlightval})) + canvas.tag_bind(obj,"", + lambda ev: canvas.itemconfig(obj,**{attribute:normalval})) + +class Field(xmlnodeclass): + + """one light has a field of influence. for any point on the + canvas, you can ask this field how strong it is. """ + + def name(self,newval=None): + """light/sub name""" + return self._getorsetattr("name",newval) + def center(self,x=None,y=None): + """x,y float coords for the center of this light in the field. returns + a Pair, although it accepts x,y""" + return Pair(self._getorsettypedattr("x",float,x), + self._getorsettypedattr("y",float,y)) + + def falloff(self,dist=None): + + """linear falloff from 1 at center, to 0 at dist pixels away + from center""" + return self._getorsettypedattr("falloff",float,dist) + + def getdistforintensity(self,intens): + """returns the distance you'd have to be for the given intensity (0..1)""" + return (1-intens)*self.falloff() + + def calc(self,x,y): + """returns field strength at point x,y""" + dist=pairdist(Pair(x,y),self.center()) + return max(0,(self.falloff()-dist)/self.falloff()) + +class Fieldset(collectiveelement): + """group of fields. persistent.""" + def childtype(self): return Field + + def version(self): + """read-only version attribute on fieldset tag""" + return self._getorsetattr("version",None) + + def report(self,x,y): + """reports active fields and their intensities""" + active=0 + for f in self.getall(): + name=f.name() + intens=f.calc(x,y) + if intens>0: + print name,intens, + active+=1 + if active>0: + print + self.dmxsend(x,y) + def dmxsend(self,x,y): + """output lights to dmx""" + levels=dict([(f.name(),f.calc(x,y)) for f in self.getall()]) + dmxlist=Submaster(None,levels).get_dmx_list() + dmxclient.outputlevels(dmxlist) + + + def getbounds(self): + """returns xmin,xmax,ymin,ymax for the non-zero areas of this field""" + r=None + for f in self.getall(): + rad=f.getdistforintensity(0) + fx,fy=f.center() + fieldrect=Rect(fx-rad,fy-rad,rad*2,rad*2) + if r is None: + r=fieldrect + else: + r=r.union(fieldrect) + return r.left,r.right,r.top,r.bottom + +class Fieldsetfile(xmldocfile): + def __init__(self,filename): + self._openornew(filename,topleveltype=Fieldset) + def fieldset(self): + return self._gettoplevel() + +######################################################################## +######################################################################## + +class FieldDisplay: + """the view for a Field.""" + def __init__(self,canvas,field): + self.canvas=canvas + self.field=field + self.tags=[str(id(self))] # canvas tag to id our objects + + def setcoords(self): + """adjust canvas obj coords to match the field""" + # this uses the canvas object ids saved by makeobjs + f=self.field + c=self.canvas + w2c=self.canvas.world2canvas + + # rings + for intens,ring in self.rings.items(): + rad=f.getdistforintensity(intens) + p1=w2c(*(f.center()-Pair(rad,rad))) + p2=w2c(*(f.center()+Pair(rad,rad))) + c.coords(ring,p1[0],p1[1],p2[0],p2[1]) + + # text + p1=w2c(*f.center()) + c.coords(self.txt,*p1) + + def makeobjs(self): + """(re)create the canvas objs (null coords) and make their bindings""" + c=self.canvas + f=self.field + c.delete(self.tags) + + w2c=self.canvas.world2canvas + + # make rings + self.rings={} # rad,canvasobj + for intens,color in (#(1,'white'), + (.8,'gray90'),(.6,'gray80'),(.4,'gray60'),(.2,'gray50'), + (0,'#000080')): + self.rings[intens]=c.create_oval(0,0,0,0, + outline=color,width=2,tags=self.tags, + outlinestipple='gray50') + + # make text + self.txt=c.create_text(0,0,text=f.name(),font=defaultfont+" bold", + fill='white',anchor='c', + tags=self.tags) + + # highlight text bindings + canvashighlighter(c,self.txt,'fill',normalval='white',highlightval='red') + + # position drag bindings + def press(ev): + self._lastmouse=ev.x,ev.y + def motion(ev): + dcan=Pair(*[a-b for a,b in zip((ev.x,ev.y),self._lastmouse)]) + dworld=c.canvas2world_vector(*dcan) + self.field.center(*(self.field.center()+dworld)) + self._lastmouse=ev.x,ev.y + self.setcoords() # redraw + def release(ev): + if hasattr(self,'_lastmouse'): + del self._lastmouse + dispatcher.send("field coord changed") # updates bounds + + c.tag_bind(self.txt,"",press) + c.tag_bind(self.txt,"",motion) + c.tag_bind(self.txt,"",release) + + # radius drag bindings + outerring=self.rings[0] + canvashighlighter(c,outerring, + 'outline',normalval='#000080',highlightval='#4040ff') + def motion(ev): + worldmouse=self.canvas.canvas2world(ev.x,ev.y) + currentdist=pairdist(worldmouse,self.field.center()) + self.field.falloff(currentdist) + self.setcoords() + c.tag_bind(outerring,"",motion) + c.tag_bind(outerring,"",release) # from above + + self.setcoords() + +class Tracker(tk.Frame): + + """whole tracker widget, which is mostly a view for a + Fieldset. tracker makes its own fieldset""" + + # world coords of the visible canvas (preserved even in window resizes) + xmin=0 + xmax=100 + ymin=0 + ymax=100 + + fieldsetfile=None + displays=None # Field : FieldDisplay. we keep these in sync with the fieldset + + def __init__(self,master): + tk.Frame.__init__(self,master) + + self.displays={} + + c=self.canvas=Zooming(self,bg='black',closeenough=5) + c.pack(fill='both',exp=1) + + # preserve edge coords over window resize + c.bind("",self.configcoords) + + c.bind("", + lambda ev: self._fieldset().report(*c.canvas2world(ev.x,ev.y))) + def save(ev): + print "saving" + self.fieldsetfile.save() + master.bind("",save) + dispatcher.connect(self.autobounds,"field coord changed") + + def _fieldset(self): + return self.fieldsetfile.fieldset() + + def load(self,filename): + self.fieldsetfile=Fieldsetfile(filename) + self.displays.clear() + for f in self.fieldsetfile.fieldset().getall(): + self.displays[f]=FieldDisplay(self.canvas,f) + self.displays[f].makeobjs() + self.autobounds() + + def configcoords(self,*args): + # force our canvas coords to stay at the edges of the window + c=self.canvas + cornerx,cornery=c.canvas2world(0,0) + c.move(cornerx-self.xmin, + cornery-self.ymin) + c.setscale(0,0, + c.winfo_width()/(self.xmax-self.xmin), + c.winfo_height()/(self.ymax-self.ymin)) + + def autobounds(self): + """figure out our bounds from the fieldset, and adjust the display zooms. + writes the corner coords onto the canvas.""" + self.xmin,self.xmax,self.ymin,self.ymax=self._fieldset().getbounds() + + self.configcoords() + + c=self.canvas + c.delete('cornercoords') + for x,anc2 in ((self.xmin,'w'),(self.xmax,'e')): + for y,anc1 in ((self.ymin,'n'),(self.ymax,'s')): + pos=c.world2canvas(x,y) + c.create_text(pos[0],pos[1],text="%s,%s"%(x,y), + fill='white',anchor=anc1+anc2, + tags='cornercoords') + [d.setcoords() for d in self.displays.values()] + +######################################################################## +######################################################################## + +root=tk.Tk() +root.wm_geometry('700x350') +tra=Tracker(root) +tra.pack(fill='both',exp=1) + +tra.load("fieldsets/demo") + +root.mainloop() diff -r 11597ff6ff6a -r d434a74fc068 doc/talk.py --- a/doc/talk.py Tue Mar 21 04:45:56 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -def light9_presentation(): - """ - Drew Perttula - - drewp@bigasterisk.com - - http://light9.bigasterisk.com - - - Goals of light9: - - - control the brightness of many lights while playing music - - - allow easy editing of the show - - - allow easy maintenance of the code, even while the show is running - - - - - - - - - """ - - - - - - - - - -def dependencies(): - """ - Twisted - event loop and networking - TwistedWeb - xmlrpc protocol - tk, tix - pympd - my twisted interface to mpd - pydispatcher - signals - - mpd - music player daemon - - swig - interface to C code - darcs - -* - - - - - - - """ - - - - - -def connections(): - """ - (play cmds) - ascoltami --------------> mpd ----------> audio out - | (timing) - v - curvecalc subcomposer keyboardcomposer - | | | - +--- | ----+ - \----- | --------/ - \--+---/ - | (light levels) - v -* dmxserver - | (dmx levels) - ......... v .................... - . chippy . - . | (dmx) . external hardware - . v . - . dmx dimmer . - . | (juice) . - . v . - . light . - ................................ - """ - - -def metrics(): - """ - selected linecounts: - 356 ascoltami (music player) - 318 curvecalc (curve and expression editor) - 279 keyboardcomposer - 189 dmxserver (hardware output) - 153 subcomposer - 17 wavecurve (create smoothed waveforms from .wav) - - 311 light9/curve.py (curve widgets) - 191 light9/FlyingFader.py (enhanced tk.Scale) - 168 light9/Submaster.py -* 151 light9/zoomcontrol.py - 137 light9/dmxchanedit.py - 40 light9/wavepoints.py - - 65 light9/io/parport.c (dmx interface protocol) - 50 light9/io/serport.i (i2c interface to sliders) - - total in project: about 3200 in about 30 files - - """ - - - -def future_projects(): - """ - A submaster server that talks with the other programs and - eliminates all the explicit saving and reloading of subs - - More abstract output layer, to which I can add home lighting, for - example - - Small timed 'clips' that can be triggered - - Generalize to a whizzy, distributed real-time circuit simulator - node network with a 5GL editor and failsafe checkpointing and - redundancy -* - - - - """ - - - - - - - - - - - - - - - - - - - - - - diff -r 11597ff6ff6a -r d434a74fc068 flax/tracker --- a/flax/tracker Tue Mar 21 04:45:56 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,273 +0,0 @@ -#!/usr/bin/python -from __future__ import division,nested_scopes - -import sys -sys.path.append("../../editor/pour") -sys.path.append("../light8") - -from Submaster import Submaster -from skim.zooming import Zooming,Pair -from math import sqrt,sin,cos -from pygame.rect import Rect -from xmlnodebase import xmlnodeclass,collectiveelement,xmldocfile -from dispatch import dispatcher - -import dmxclient - -import Tkinter as tk - -defaultfont="arial 8" - -def pairdist(pair1,pair2): - return pair1.dist(pair2) - -def canvashighlighter(canvas,obj,attribute,normalval,highlightval): - """creates bindings on a canvas obj that make attribute go - from normal to highlight when the mouse is over the obj""" - canvas.tag_bind(obj,"", - lambda ev: canvas.itemconfig(obj,**{attribute:highlightval})) - canvas.tag_bind(obj,"", - lambda ev: canvas.itemconfig(obj,**{attribute:normalval})) - -class Field(xmlnodeclass): - - """one light has a field of influence. for any point on the - canvas, you can ask this field how strong it is. """ - - def name(self,newval=None): - """light/sub name""" - return self._getorsetattr("name",newval) - def center(self,x=None,y=None): - """x,y float coords for the center of this light in the field. returns - a Pair, although it accepts x,y""" - return Pair(self._getorsettypedattr("x",float,x), - self._getorsettypedattr("y",float,y)) - - def falloff(self,dist=None): - - """linear falloff from 1 at center, to 0 at dist pixels away - from center""" - return self._getorsettypedattr("falloff",float,dist) - - def getdistforintensity(self,intens): - """returns the distance you'd have to be for the given intensity (0..1)""" - return (1-intens)*self.falloff() - - def calc(self,x,y): - """returns field strength at point x,y""" - dist=pairdist(Pair(x,y),self.center()) - return max(0,(self.falloff()-dist)/self.falloff()) - -class Fieldset(collectiveelement): - """group of fields. persistent.""" - def childtype(self): return Field - - def version(self): - """read-only version attribute on fieldset tag""" - return self._getorsetattr("version",None) - - def report(self,x,y): - """reports active fields and their intensities""" - active=0 - for f in self.getall(): - name=f.name() - intens=f.calc(x,y) - if intens>0: - print name,intens, - active+=1 - if active>0: - print - self.dmxsend(x,y) - def dmxsend(self,x,y): - """output lights to dmx""" - levels=dict([(f.name(),f.calc(x,y)) for f in self.getall()]) - dmxlist=Submaster(None,levels).get_dmx_list() - dmxclient.outputlevels(dmxlist) - - - def getbounds(self): - """returns xmin,xmax,ymin,ymax for the non-zero areas of this field""" - r=None - for f in self.getall(): - rad=f.getdistforintensity(0) - fx,fy=f.center() - fieldrect=Rect(fx-rad,fy-rad,rad*2,rad*2) - if r is None: - r=fieldrect - else: - r=r.union(fieldrect) - return r.left,r.right,r.top,r.bottom - -class Fieldsetfile(xmldocfile): - def __init__(self,filename): - self._openornew(filename,topleveltype=Fieldset) - def fieldset(self): - return self._gettoplevel() - -######################################################################## -######################################################################## - -class FieldDisplay: - """the view for a Field.""" - def __init__(self,canvas,field): - self.canvas=canvas - self.field=field - self.tags=[str(id(self))] # canvas tag to id our objects - - def setcoords(self): - """adjust canvas obj coords to match the field""" - # this uses the canvas object ids saved by makeobjs - f=self.field - c=self.canvas - w2c=self.canvas.world2canvas - - # rings - for intens,ring in self.rings.items(): - rad=f.getdistforintensity(intens) - p1=w2c(*(f.center()-Pair(rad,rad))) - p2=w2c(*(f.center()+Pair(rad,rad))) - c.coords(ring,p1[0],p1[1],p2[0],p2[1]) - - # text - p1=w2c(*f.center()) - c.coords(self.txt,*p1) - - def makeobjs(self): - """(re)create the canvas objs (null coords) and make their bindings""" - c=self.canvas - f=self.field - c.delete(self.tags) - - w2c=self.canvas.world2canvas - - # make rings - self.rings={} # rad,canvasobj - for intens,color in (#(1,'white'), - (.8,'gray90'),(.6,'gray80'),(.4,'gray60'),(.2,'gray50'), - (0,'#000080')): - self.rings[intens]=c.create_oval(0,0,0,0, - outline=color,width=2,tags=self.tags, - outlinestipple='gray50') - - # make text - self.txt=c.create_text(0,0,text=f.name(),font=defaultfont+" bold", - fill='white',anchor='c', - tags=self.tags) - - # highlight text bindings - canvashighlighter(c,self.txt,'fill',normalval='white',highlightval='red') - - # position drag bindings - def press(ev): - self._lastmouse=ev.x,ev.y - def motion(ev): - dcan=Pair(*[a-b for a,b in zip((ev.x,ev.y),self._lastmouse)]) - dworld=c.canvas2world_vector(*dcan) - self.field.center(*(self.field.center()+dworld)) - self._lastmouse=ev.x,ev.y - self.setcoords() # redraw - def release(ev): - if hasattr(self,'_lastmouse'): - del self._lastmouse - dispatcher.send("field coord changed") # updates bounds - - c.tag_bind(self.txt,"",press) - c.tag_bind(self.txt,"",motion) - c.tag_bind(self.txt,"",release) - - # radius drag bindings - outerring=self.rings[0] - canvashighlighter(c,outerring, - 'outline',normalval='#000080',highlightval='#4040ff') - def motion(ev): - worldmouse=self.canvas.canvas2world(ev.x,ev.y) - currentdist=pairdist(worldmouse,self.field.center()) - self.field.falloff(currentdist) - self.setcoords() - c.tag_bind(outerring,"",motion) - c.tag_bind(outerring,"",release) # from above - - self.setcoords() - -class Tracker(tk.Frame): - - """whole tracker widget, which is mostly a view for a - Fieldset. tracker makes its own fieldset""" - - # world coords of the visible canvas (preserved even in window resizes) - xmin=0 - xmax=100 - ymin=0 - ymax=100 - - fieldsetfile=None - displays=None # Field : FieldDisplay. we keep these in sync with the fieldset - - def __init__(self,master): - tk.Frame.__init__(self,master) - - self.displays={} - - c=self.canvas=Zooming(self,bg='black',closeenough=5) - c.pack(fill='both',exp=1) - - # preserve edge coords over window resize - c.bind("",self.configcoords) - - c.bind("", - lambda ev: self._fieldset().report(*c.canvas2world(ev.x,ev.y))) - def save(ev): - print "saving" - self.fieldsetfile.save() - master.bind("",save) - dispatcher.connect(self.autobounds,"field coord changed") - - def _fieldset(self): - return self.fieldsetfile.fieldset() - - def load(self,filename): - self.fieldsetfile=Fieldsetfile(filename) - self.displays.clear() - for f in self.fieldsetfile.fieldset().getall(): - self.displays[f]=FieldDisplay(self.canvas,f) - self.displays[f].makeobjs() - self.autobounds() - - def configcoords(self,*args): - # force our canvas coords to stay at the edges of the window - c=self.canvas - cornerx,cornery=c.canvas2world(0,0) - c.move(cornerx-self.xmin, - cornery-self.ymin) - c.setscale(0,0, - c.winfo_width()/(self.xmax-self.xmin), - c.winfo_height()/(self.ymax-self.ymin)) - - def autobounds(self): - """figure out our bounds from the fieldset, and adjust the display zooms. - writes the corner coords onto the canvas.""" - self.xmin,self.xmax,self.ymin,self.ymax=self._fieldset().getbounds() - - self.configcoords() - - c=self.canvas - c.delete('cornercoords') - for x,anc2 in ((self.xmin,'w'),(self.xmax,'e')): - for y,anc1 in ((self.ymin,'n'),(self.ymax,'s')): - pos=c.world2canvas(x,y) - c.create_text(pos[0],pos[1],text="%s,%s"%(x,y), - fill='white',anchor=anc1+anc2, - tags='cornercoords') - [d.setcoords() for d in self.displays.values()] - -######################################################################## -######################################################################## - -root=tk.Tk() -root.wm_geometry('700x350') -tra=Tracker(root) -tra.pack(fill='both',exp=1) - -tra.load("fieldsets/demo") - -root.mainloop() diff -r 11597ff6ff6a -r d434a74fc068 light9/Effects.py --- a/light9/Effects.py Tue Mar 21 04:45:56 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -import random -import light9.Submaster as Submaster -from chase import chase as chase_logic - -thirds = 'third-l', 'third-c', 'third-r' -thirds_bounce = 'third-l', 'third-c', 'third-r', 'third-c' -backs = ['back%d' % d for d in range(1, 11)] -rand_flutter = ['scoop-l', 'scoop-c', 'scoop-r', 'down-c', 'down-l', 'down-r', 'cyc', 'zip_blue', 'zip_red', 'zip_green', 'zip_orange'] + backs -rand_flutter *= 10 -random.shuffle(rand_flutter) - -# don't forget to update this! -__all__ = ['chase', 'thirds', 'thirds_bounce', 'rand_flutter', 'backs'] - -def chase(t, ontime=0.5, offset=0.2, onval=1.0, - offval=0.0, names=None, combiner=max): - """names is list of sub or channel names""" - sub_vals = {} - chase_vals = chase_logic(t, ontime, offset, onval, offval, names, combiner) - for name, value in chase_vals.items(): - sub = Submaster.get_sub_by_name(name) - sub_vals[sub] = value - - return Submaster.combine_subdict(sub_vals)