view flax/tracker @ 150:1e4814221a64

new tracker program and demo file
author drewp
date Sun, 06 Jul 2003 14:47:01 +0000
parents 45b12307c695
children e6ca7c1f0b1e
line wrap: on
line source

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