Changeset - 1e4814221a64
[Not reviewed]
default
0 2 0
drewp - 21 years ago 2003-07-06 14:47:01

new tracker program and demo file
2 files changed with 85 insertions and 188 deletions:
0 comments (0 inline, 0 general)
flax/fieldsets/demo
Show inline comments
 
<?xml version='1.0' encoding='UTF-8'?>
 
<fieldset version='1'>
 
  <field y='64.518605176' x='64.4226431666' name='main 2' falloff='10'/>
 
  <field y='71.6297059084' x='64.1501958858' name='main 3' falloff='9.44161122397'/>
 
  <field y='62.2614165793' x='49.4797562176' name='main 4' falloff='9.02748404992'/>
 
  <field y='64.7105473806' x='39.4725745351' name='main 5' falloff='9.02748404992'/>
 
  <field y='70.0008620159' x='45.644638445' name='main 7' falloff='9.02748404992'/>
 
  <field y='70.7801243766' x='53.963092297' name='main 8' falloff='9.02748404992'/>
 
  <field y='68.1676620898' x='74.8416350959' name='main 9' falloff='10.5270359255'/>
 
  <field y='62.0408841822' x='79.8677043872' name='main 10' falloff='9.02748404992'/>
 
</fieldset>
 
light1: x=30 y=40 falloff=10
 
light2: x=60 y=50 falloff=30
 
light3: x=40 y=50 falloff=20
flax/tracker
Show inline comments
 
#!/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,"<Enter>",
 
                    lambda ev: canvas.itemconfig(obj,**{attribute:highlightval}))
 
    canvas.tag_bind(obj,"<Leave>",
 
                    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)
 
        
 
        
 
    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,101 +82,41 @@ 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'),
 
                             (.8,'gray90'),(.6,'gray80'),(.4,'gray60'),(.2,'gray50'),
 
                             (0,'#000080')):
 
            self.rings[intens]=c.create_oval(0,0,0,0,
 
        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')
 

	
 
        # 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')
 
        oval(.01,'white')
 
        for intens,color in ((1,'white'),
 
                             (.8,'gray90'),(.6,'gray80'),(.4,'gray60'),(.2,'gray50'),
 
                             (0,'#000080')):
 

	
 
        # 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,"<ButtonPress-1>",press)
 
        c.tag_bind(self.txt,"<B1-Motion>",motion)
 
        c.tag_bind(self.txt,"<B1-ButtonRelease>",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,"<B1-Motion>",motion)
 
        c.tag_bind(outerring,"<B1-ButtonRelease>",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
 
@@ -200,41 +124,28 @@ class Tracker(tk.Frame):
 
    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("<Configure>",self.configcoords)
 
        
 
        self.canvas=c
 

	
 
        c.bind("<Motion>",
 
               lambda ev: self._fieldset().report(*c.canvas2world(ev.x,ev.y)))
 
        def save(ev):
 
            print "saving"
 
            self.fieldsetfile.save()
 
        master.bind("<Key-s>",save)
 
        dispatcher.connect(self.autobounds,"field coord changed")
 
               lambda ev: self.fieldset.report(*c.canvas2world(ev.x,ev.y)))
 

	
 
    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
 
        # 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()
 

	
 
        self.configcoords()
 
        """figure out our bounds from the fieldset, and adjust the display zooms"""
 
        self.xmin,self.xmax,self.ymin,self.ymax=self.fieldset.getbounds()
 
        
 
        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()
0 comments (0 inline, 0 general)