Files @ 990a9474d0e7
Branch filter:

Location: light9/flax/tracker

dmcc
early cue stuff. the CueList will supply the CueFader with the cues to
early cue stuff. the CueList will supply the CueFader with the cues to
work with, and will do crossfading sooner or later. the format of cues
is still very open. cuelist1 is a bogus cuelist for testing.
#!/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("<Configure>",self.configcoords)
        
        self.canvas=c

        c.bind("<Motion>",
               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()