+light1: x=30 y=40 falloff=10
+light2: x=60 y=50 falloff=30
+light3: x=40 y=50 falloff=20
diff --git a/flax/tracker b/flax/tracker
--- a/flax/tracker
+++ b/flax/tracker
@@ -1,96 +1,80 @@
#!/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 __future__ import division
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):
+class Field:
"""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))
+ center=Pair(0,0)
+ falloff=0
- def falloff(self,dist=None):
+ def setcenter(self,x,y):
+ self.center=Pair(x,y)
+
+ def setfalloff(self,dist):
"""linear falloff from 1 at center, to 0 at dist pixels away
from center"""
- return self._getorsettypedattr("falloff",float,dist)
+ self.falloff=dist
def getdistforintensity(self,intens):
"""returns the distance you'd have to be for the given intensity (0..1)"""
- return (1-intens)*self.falloff()
+ 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())
+ dist=sqrt( (x-self.center.x)**2 + (y-self.center.y)**2 )
+ return max(0,(self.falloff-dist)/self.falloff)
-class Fieldset(collectiveelement):
+class FieldSet:
"""group of fields. persistent."""
- def childtype(self): return Field
-
- def version(self):
- """read-only version attribute on fieldset tag"""
- return self._getorsetattr("version",None)
-
+ fields=None # lightname : Field
+ def __init__(self):
+ self.fields={}
+ def loadfromfile(self,f):
+ for line in f:
+ try:
+ col=line.index(':')
+ newfield=Field()
+ name=line[:col]
+ params={}
+ for param in line[col+1:].split():
+ eq=param.index('=')
+ params[param[:eq]]=float(param[eq+1:])
+ newfield.setcenter(params['x'],params['y'])
+ newfield.setfalloff(params['falloff'])
+ self.addfield(name,newfield)
+ newfield.name=name # fields have names?
+ except ValueError:
+ print "error on line: %s" % line
+ def savetofile(self,f):
+ for name,field in self.fields.items():
+ print >>f,"%s: x=%s y=%s falloff=%s" % (name,
+ field.center.x,field.center.y,
+ field.falloff)
+ def addfield(self,name,f):
+ """consider the given field in this set with the given name"""
+ assert isinstance(f,Field)
+ self.fields[name]=f
def report(self,x,y):
"""reports active fields and their intensities"""
- active=0
- for f in self.getall():
- name=f.name()
+ for name,f in self.fields.items():
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)
-
-
+ print
def getbounds(self):
"""returns xmin,xmax,ymin,ymax for the non-zero areas of this field"""
r=None
- for f in self.getall():
+ for f in self.fields.values():
rad=f.getdistforintensity(0)
- fx,fy=f.center()
+ fx,fy=f.center.x,f.center.y
fieldrect=Rect(fx-rad,fy-rad,rad*2,rad*2)
if r is None:
r=fieldrect
@@ -98,143 +82,70 @@ class Fieldset(collectiveelement):
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"""
+ def draw(self):
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'),
+ def oval(rad,color):
+ p1=w2c(*(f.center-Pair(rad,rad)))
+ p2=w2c(*(f.center+Pair(rad,rad)))
+ c.create_oval(p1[0],p1[1],p2[0],p2[1],
+ outline=color,width=2,tags=self.tags,
+ outlinestipple='gray50')
+
+ oval(.01,'white')
+ 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()
-
+
+ oval(f.getdistforintensity(intens),color)
+ if hasattr(f,'name'):
+ p1=w2c(*f.center)
+ c.create_text(p1[0],p1[1],text=f.name,
+ fill='white',anchor='c')
+
class Tracker(tk.Frame):
"""whole tracker widget, which is mostly a view for a
- Fieldset. tracker makes its own fieldset"""
+ 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={}
+ self.fieldset=FieldSet()
+ self.displays={} # Field : FieldDisplay
- c=self.canvas=Zooming(self,bg='black',closeenough=5)
+ c=Zooming(self,bg='black')
c.pack(fill='both',exp=1)
- # preserve edge coords over window resize
+ for x,y in ((5,5),(5,95),(95,5),(95,95)):
+ c.create_text(x,y,text="%s,%s"%(x,y),fill='white')
+
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")
+ self.canvas=c
- def _fieldset(self):
- return self.fieldsetfile.fieldset()
+ c.bind("",
+ lambda ev: self.fieldset.report(*c.canvas2world(ev.x,ev.y)))
- 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
+ # force canvas coords to line up right
c=self.canvas
cornerx,cornery=c.canvas2world(0,0)
c.move(cornerx-self.xmin,
@@ -244,30 +155,24 @@ class Tracker(tk.Frame):
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()
+ """figure out our bounds from the fieldset, and adjust the display zooms"""
+ 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()]
+ def addfield(self,name,f):
+ """add the named Field to this display"""
+ self.fieldset.addfield(name,f)
+ self.displays[f]=FieldDisplay(self.canvas,f)
+ self.displays[f].draw()
-########################################################################
-########################################################################
-
root=tk.Tk()
-root.wm_geometry('700x350')
+
tra=Tracker(root)
tra.pack(fill='both',exp=1)
-tra.load("fieldsets/demo")
+tra.fieldset.loadfromfile(file("fieldsets/demo"))
+for f in tra.fieldset.fields.values():
+ tra.displays[f]=FieldDisplay(tra.canvas,f)
+ tra.displays[f].draw()
+tra.autobounds()
root.mainloop()