#!/usr/bin/python from __future__ import division from skim.zooming import Zooming,Pair from math import sqrt,sin,cos from pygame.rect import Rect import Tkinter as tk class Field: """one light has a field of influence. for any point on the canvas, you can ask this field how strong it is. """ center=Pair(0,0) falloff=0 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""" 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 def calc(self,x,y): """returns field strength at point x,y""" dist=sqrt( (x-self.center.x)**2 + (y-self.center.y)**2 ) return max(0,(self.falloff-dist)/self.falloff) class FieldSet: """group of fields. persistent.""" 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""" for name,f in self.fields.items(): intens=f.calc(x,y) if intens>0: print name,intens, print def getbounds(self): """returns xmin,xmax,ymin,ymax for the non-zero areas of this field""" r=None for f in self.fields.values(): rad=f.getdistforintensity(0) fx,fy=f.center.x,f.center.y 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 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 draw(self): c=self.canvas f=self.field c.delete(self.tags) w2c=self.canvas.world2canvas 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')): 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""" # world coords of the visible canvas (preserved even in window resizes) xmin=0 xmax=100 ymin=0 ymax=100 def __init__(self,master): tk.Frame.__init__(self,master) self.fieldset=FieldSet() self.displays={} # Field : FieldDisplay c=Zooming(self,bg='black') c.pack(fill='both',exp=1) 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) self.canvas=c c.bind("", lambda ev: self.fieldset.report(*c.canvas2world(ev.x,ev.y))) def configcoords(self,*args): # force canvas coords to line up right 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""" self.xmin,self.xmax,self.ymin,self.ymax=self.fieldset.getbounds() 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() tra=Tracker(root) tra.pack(fill='both',exp=1) 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()