Contents

  1. ./csplot.py
  2. ./mystery.py
  3. ./roomba.py
  4. ./test.py
  5. ./twod_exercises.py

./csplot.py 1/5

[
top][prev][next]
"""
A Tkinter.Canvas derived class with some
helpful things for drawing and updating...
"""

import random
from tkinter import *
import math

class Affine2dTrans:
    """ let's create our own 2d affine class """

    def __init__(self):
        """ the components currently supported """ 
        self.scalex = 1.0  # units of  pixels per worldunit (cm)
        self.scaley = 1.0  # units of  pixels per worldunit (cm)
        self.cx = 0.0      # worldunits (cm)
        self.cy = 0.0      # worldunits (cm)
        self.px = 0.0      # pixels
        self.py = 0.0      # pixels
        self.thr = 0.0     # radians, though the interface is always degrees
        self.sinthr = math.sin(0.0)   # why compute this more than once?
        self.costhr = math.cos(0.0)   # why not say it more than once?

    def transform(self, xxx_todo_changeme):
        (x,y) = xxx_todo_changeme
        return self.transformWorldToPixel((x,y))

    def transformWorldToPixel(self, xxx_todo_changeme1):
        """ outpus pixel coordinates for the input world coords """
        (worldx, worldy) = xxx_todo_changeme1
        offx = worldx - self.cx
        offy = worldy - self.cy
        # next, rotate the resulting vector by theta
        rx = self.costhr * offx - self.sinthr * offy
        ry = self.sinthr * offx + self.costhr * offy
        # next, scale the result
        rx = rx * self.scalex
        ry = ry * self.scaley
        # finally, add the pixel offset
        return ( self.px + rx, self.py + ry )

    def transformPixelToWorld(self, xxx_todo_changeme2):
        """ outputs world coordinates for the input pixel coords """
        (pixelx, pixely) = xxx_todo_changeme2
        rx = pixelx - float(self.px)
        ry = pixely - float(self.py)
        # unscale the result
        rx = rx / self.scalex
        ry = ry / self.scaley
        # unrotate the vector by theta
        offx =  self.costhr * rx + self.sinthr * ry
        offy = -self.sinthr * rx + self.costhr * ry
        # add the world offset to get world coords
        return (offx + self.cx, offy + self.cy)

    def transform_scale(self, length, thd):
        """ returns the new length equivalent in pixels to  the input
            length along the thd direction (in degrees) - if x and y
            are not equal, thd will make a difference... 

            length sould be positive - a positive result will be
            returned in any case

            thd is in global world terms, not relative to the
            stored thr within this transformation """
        # first, compute the offset from the world's center of rotation
        thr = thd * 180.0 / math.pi
        offx = length * math.cos(thr)
        offy = length * math.sin(thr)
        # next, scale the result
        rx = offx * self.scalex
        ry = offy * self.scaley
        # finally, compute the magnitude
        return ( math.sqrt( rx * rx + ry * ry ) )
        
    def deltaWorldRotationCenter(self, deltaworldx, deltaworldy):
        """ moves the world rotation center by the amounts
            provided in the inputs deltaworldx and deltaworldy
        """
        self.cx += deltaworldx
        self.cy += deltaworldy

    def setWorldRotationCenter(self, worldcenterx, worldcentery):
        """ puts the input world point onto the input pixel point
            and make that point the center of any rotation specified """
        # I don't know if rotation should be preserved through this call...
        self.cx = worldcenterx
        self.cy = worldcentery

    def setPixelRotationCenter(self, pixelx, pixely):
        """ puts the input world point onto the input pixel point
            and make that point the center of any rotation specified 
        """
        # I don't know if rotation should be preserved through this call...
        self.px = pixelx
        self.py = pixely

    def setScales(self, scale_x, scale_y):
        """ allows for axis flipping and nonuniform (!) scaling, though
            why you'd want that is beyond me... each of scale_x and 
            scale_y are expressed in pixels per world unit (cm) "
        """
        if scale_x == 0.0 or scale_y == 0.0:
            print("Warning - you don't want a finite world length mapping to 0 pixels!")
        self.scalex = scale_x
        self.scaley = scale_y
    
    def setScalesAbsVal(self, scale_x, scale_y):
        """ this will preserve the signs of self.scalex and self.scaley,
            setting their absolute values to scale_x and scale_y, respectively
        """
        if scale_x == 0.0 or scale_y == 0.0:
            print("Warning - you don't want a finite world length mapping to 0 pixels!")
        if self.scalex > 0: self.scalex = abs(scale_x)
        else:               self.scalex = -abs(scale_x)
        if self.scaley > 0: self.scaley = abs(scale_y)
        else:               self.scaley = -abs(scale_y)

    def multiplyScales(self, mult_x, mult_y):
        """ change the x and y scales by multiplying by the inputs 
            scalex and scaley are in units of pixels per world unit (cm)
        """
        if mult_x == 0.0 or mult_y == 0.0:
            print("Warning - you don't want a finite world length mapping to 0 pixels!")
        self.scalex *= mult_x
        self.scaley *= mult_y

    def setRotationAngle(self, thd):
        """ set the number of degrees which the world coordinates
            are rotated (around the rotation center) before mapping to pixels """
        self.thr = thd * math.pi / 180.0
        self.costhr = math.cos(self.thr)
        self.sinthr = math.sin(self.thr)

    def deltaRotationAngle(self, thd):
        """ changes the number of degrees which the world coordinates
            are rotated (around the rotation center) before mapping to pixels """
        self.thr = self.thr + (thd * math.pi / 180.0)
        self.costhr = math.cos(self.thr)
        self.sinthr = math.sin(self.thr)



class Movable:
    """ Movable is a wrapper around Tkinter.Canvas objects
        it holds their _global_ coordinates, converting to
        pixel coordinates as necessary (saving time, perhaps...)

        It can be subclassed to allow for all sorts of
        composite objects to be treated as one, with their
        own, object-local coordinate system
    """

    def __init__(self, canvas, cx=0.0, cy=0.0, thr=0.0, colorstr='black'):
        """ set up a local coordinate system """
        self.canvas = canvas
        # self.tag = 'notag'  # don't seem to be using this...
        self.cx = cx
        self.cy = cy
        self.thr = thr
        self.colorstr = colorstr
        self.sinthr = math.sin(self.thr)
        self.costhr = math.cos(self.thr)
        self.localCoords = []
        self.updateGlobalCoords() # sets self.globalCoords
        self.canvas.addMovable(self) # adds self object to the canvas's list

    def localToGlobalCoords(self, xxx_todo_changeme3):
        """ transforms one point from object-local to global
            coordinates

            self.cx and self.cy are the global location of the 
            center of the object-local coordinate system, and it's
            facing self.thr
        """
        (x,y) = xxx_todo_changeme3
        return ( (x*self.costhr - y*self.sinthr + self.cx, 
                  x*self.sinthr + y*self.costhr + self.cy) )

    def updateGlobalCoords(self):
        """ create global coordinates if the object-local coordinates
            are centered at (cx,cy) facing thr
            input L: should be a list of tuples

            could be overridden
        """
        self.globalCoords = [ self.localToGlobalCoords(T) for T in self.localCoords ]
        #print 'cx,cy,thd are', self.cx, self.cy, math.degrees(self.thr)
        #print 'globalCoords are', self.globalCoords

    def deltaGlobalPose( self, dx, dy, dthr ):
        """ change the global pose of this object """
        self.setGlobalPose( self.cx+dx, self.cy+dy, self.thr+dthr )

    def setGlobalPose( self, cx, cy, thr ):
        """ set the absolute global pose of this object """
        self.cx = cx
        self.cy = cy
        self.thr = thr
        self.sinthr = math.sin(self.thr)
        self.costhr = math.cos(self.thr)
        self.updateGlobalCoords()
        self.updatePixelCoords()

    def setColor( self, r, g, b ):
        """ this writes the appropriate color string """
        self.colorstr = "#%02x%02x%02x" % (r,g,b)
        self.canvas.itemconfigure( self.itemid, fill=colorstr )

    def setColorstr( self, colorstr ):
        """ this takes a color name """
        self.colorstr = colorstr
        self.canvas.itemconfigure( self.itemid, fill=colorstr )

    def updatePixelCoords( self ):
        """ redraws the object it represents on the canvas
            should be overridden
        """
        print('in Movable\'s updatePixelCoords')

    def createObjects( self ):
        """ computes pixel coordinates and then creates the canvas objects
            should be overridden
        """
        print('in Movable\'s createObjects')
        
    def delete( self ):
        """ deletes the items from the canvas
        """
        # should be the list of items!
        # some have more than 1!
        self.canvas.delete( self.itemid )

        
        
       
class MovablePixelLengthLine(Movable):
    
    def __init__(self, canvas, cx=0.0, cy=0.0, thr=0.0, 
                       colorstr='black', width=2, pixellength=8, arrow=None):
        """ the MovablePixelLengthLine constructor """
        Movable.__init__(self, canvas, cx, cy, thr, colorstr)
        self.width = width
        self.pixellength = pixellength
        self.arrow = arrow
        self.localCoords = [ (0.0,0.0),    # these are object-local coordinates
                             (1.0,0.0) ]   # except the length is wrong...
        self.updateGlobalCoords()        # now in world coords, (cx,cy)
        self.createObjects()             # using self.pixellength
        
    def changeAnchor( self, cx, cy, thr=None ):
        """ changeAnchor moves the starting point of the line, cx, and cy,
            both in world coordinates. thr is optional and will reorient the
            line from its anchor for its designated pixel length
        """
        # can't use   if not thr  below because thr might be 0.0 !!
        if thr == None:
            thr = self.thr
        self.setGlobalPose( cx, cy, thr )
        #print 'global pose set to', cx, cy, math.degrees(thr), 'in changeAnchor'
        self.updatePixelCoords()
        
    def computeEndVertex( self, p ):
        """ returns a tuple with the pixel coordinates of the end vertex
            if the initial vertex has pixel coordinates of p[0][0], p[0][1]
            and the end vertex is currently p[1][0], p[1][1] (the wrong length)
        """
        #print 'p is', p
        length = math.sqrt( (p[1][0] - p[0][0])**2 + (p[1][1] - p[0][1])**2 )
        ratio = self.pixellength / float(length)
        #print 'l and ratio are', length, ratio
        endx = p[0][0] + (p[1][0]-p[0][0])*ratio
        endy = p[0][1] + (p[1][1]-p[0][1])*ratio
        #print 'endx,y are', endx, endy
        return endx, endy
        

    def updatePixelCoords( self ):
        """ redraws the object it represents on the canvas
        """
        # p has the pixel coords of the anchor (starting vertex)
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        # now we need to find the other endpoint by scaling p[1] and p[2]
        # so that the length of their sum is self.;pixellength
        endx, endy = self.computeEndVertex(p)
        self.canvas.coords( self.itemid, p[0][0], p[0][1], 
                                         endx, endy )

    def createObjects( self ):
        """ computes pixel coordinates and then creates the
            canvas objects
        """
        # find pixel coordinates and create the canvas objects
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        endx, endy = self.computeEndVertex(p)
        self.itemid = self.canvas.create_line(p[0][0], p[0][1], 
                                              endx,    endy,
                                              fill=self.colorstr,
                                              width=self.width) 
        if self.arrow:
            self.canvas.itemconfigure( self.itemid, arrow='last',
                                                    capstyle='round' )
        
        
        
        
class MovablePoint(Movable):

    def __init__(self, canvas, cx=0.0, cy=0.0, 
                       colorstr='black', pixelradius=2):
        """ the MovablePoint constructor """
        Movable.__init__(self, canvas, cx, cy, 0.0, colorstr)
        self.pixelradius = int(pixelradius)
        self.localCoords = [ (0,0) ]
        self.updateGlobalCoords()
        self.createObjects()

    def updatePixelCoords( self ):
        """ redraws the object it represents on the canvas
        """
        # we could just delete and then call draw...
        # perhaps (?) it's faster to change the coordinates...
        # so we'll give it a try
        #px,py = self.canvas.tfm.transform( self.globalCoords[0] )
        #radius = self.canvas.tfm.transform_scale(self.radius,0.0)
        #self.canvas.coords( self.itemid, px-radius, py-radius,
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        r = self.pixelradius
        self.canvas.coords( self.itemid, p[0][0]-r, p[0][1]-r, 
                                         p[0][0]+r, p[0][1]+r )

    def createObjects( self ):
        """ computes pixel coordinates and then creates the
            canvas objects
        """
        # find pixel coordinates and create the canvas objects
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        r = self.pixelradius
        self.itemid = self.canvas.create_oval(p[0][0]-r, p[0][1]-r, 
                                              p[0][0]+r, p[0][1]+r,
                                              fill=self.colorstr, 
                                              outline='')
        
        
        
        
class MovableCircle(Movable):

    def __init__(self, canvas, cx=0.0, cy=0.0, thr=0.0, 
                       colorstr='black', radius=10.0):
        """ the MovableCircle constructor """
        Movable.__init__(self, canvas, cx, cy, thr, colorstr)
        self.radius = radius
        self.localCoords = [ (0.0,0.0), (self.radius,0.0) ]
        self.updateGlobalCoords()
        self.createObjects()

    def updatePixelCoords( self ):
        """ redraws the object it represents on the canvas
        """
        # we could just delete and then call draw...
        # perhaps (?) it's faster to change the coordinates...
        # so we'll give it a try
        #px,py = self.canvas.tfm.transform( self.globalCoords[0] )
        #radius = self.canvas.tfm.transform_scale(self.radius,0.0)
        #self.canvas.coords( self.itemid, px-radius, py-radius,
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        r = self.canvas.tfm.transform_scale( self.radius, 0.0 )
        self.canvas.coords( self.itemid, p[0][0]-r, p[0][1]-r, 
                                         p[0][0]+r, p[0][1]+r )
        self.canvas.coords( self.itemid2, p[0][0],p[0][1],
                                          p[1][0],p[1][1])

    def createObjects( self ):
        """ computes pixel coordinates and then creates the
            canvas objects
        """
        # find pixel coordinates and create the canvas objects
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        r = self.canvas.tfm.transform_scale( self.radius, 0.0 )
        self.itemid = self.canvas.create_oval(p[0][0]-r, p[0][1]-r, 
                                              p[0][0]+r, p[0][1]+r,
                                              fill=self.colorstr, 
                                              outline='black')
        self.itemid2 = self.canvas.create_line(p[0][0],p[0][1],
                                               p[1][0],p[1][1],
                                               fill='white',width=2)
                                               


class MovableLine(Movable):

    def __init__(self, canvas, endpoints, cx=0.0, cy=0.0, thr=0.0, 
                       colorstr='black', width=2):
        """ the MovableLine constructor """
        Movable.__init__(self, canvas, cx, cy, thr, colorstr)
        self.width = width
        self.localCoords = endpoints[:]  # should be two 2-tuples
        self.updateGlobalCoords()
        self.createObjects()
        
    def changeCoords( self, newcoords ):
        """ should be called updateWorldCoords!
        """
        # remember this "localCoords" is object-local (NOT pixel)
        self.localCoords = newcoords[:]  # better be the right format!
        self.updateGlobalCoords()
        self.updatePixelCoords()

    def updatePixelCoords( self ):
        """ redraws the object it represents on the canvas
        """
        # we could just delete and then call draw...
        # perhaps (?) it's faster to change the coordinates...
        # so we'll give it a try
        #px,py = self.canvas.tfm.transform( self.globalCoords[0] )
        #radius = self.canvas.tfm.transform_scale(self.radius,0.0)
        #self.canvas.coords( self.itemid, px-radius, py-radius,
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        self.canvas.coords( self.itemid, p[0][0], p[0][1], 
                                         p[1][0], p[1][1] )

    def createObjects( self ):
        """ computes pixel coordinates and then creates the
            canvas objects
        """
        # find pixel coordinates and create the canvas objects
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        self.itemid = self.canvas.create_line(p[0][0], p[0][1], 
                                              p[1][0], p[1][1],
                                              fill=self.colorstr,
                                              width=self.width) 


class MovableRect(Movable):

    def __init__(self, canvas, tlbr_endpoints, cx=0.0, cy=0.0, thr=0.0, 
                       colorstr='black'):
        """ the MovableRect constructor """
        Movable.__init__(self, canvas, cx, cy, thr, colorstr)
        [ (tlx,tly), (brx,bry) ] = tlbr_endpoints
        # need to use a polygon so we can rotate...
        self.localCoords = [ (tlx,tly), (tlx,bry), (brx,bry), (brx,tly) ]
        self.updateGlobalCoords()
        self.createObjects()

    def updatePixelCoords( self ):
        """ redraws the object it represents on the canvas
        """
        # we could just delete and then call draw...
        # perhaps (?) it's faster to change the coordinates...
        # so we'll give it a try
        #px,py = self.canvas.tfm.transform( self.globalCoords[0] )
        #radius = self.canvas.tfm.transform_scale(self.radius,0.0)
        #self.canvas.coords( self.itemid, px-radius, py-radius,
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        self.canvas.coords( self.itemid, p[0][0], p[0][1], 
                                         p[1][0], p[1][1],
                                         p[2][0], p[2][1],
                                         p[3][0], p[3][1] )

    def createObjects( self ):
        """ computes pixel coordinates and then creates the
            canvas objects
        """
        # find pixel coordinates and create the canvas objects
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        self.itemid = self.canvas.create_polygon(p[0][0], p[0][1], 
                                                 p[1][0], p[1][1],
                                                 p[2][0], p[2][1],
                                                 p[3][0], p[3][1],
                                                 fill=self.colorstr,
                                                 outline="black") 
class MovablePoly(Movable):

    def __init__(self, canvas, path_endpoints, cx=0.0, cy=0.0, thr=0.0, 
                       colorstr='black'):
        """ the MovablePoly constructor """
        Movable.__init__(self, canvas, cx, cy, thr, colorstr)
        self.localCoords = path_endpoints[:]
        self.colorstr = colorstr
        self.updateGlobalCoords()
        self.createObjects()
        
    def mappend(self,f,pointlist):
        """ just like mappend in CS 60
        """
        if len(pointlist) == 0: return []
        fp = f(pointlist[0])
        return [fp[0],fp[1]] + self.mappend(f,pointlist[1:])
        

    def updatePixelCoords( self ):
        """ redraws the object it represents on the canvas
        """
        # we could just delete and then call draw...
        # perhaps (?) it's faster to change the coordinates...
        # so we'll give it a try
        #px,py = self.canvas.tfm.transform( self.globalCoords[0] )
        #radius = self.canvas.tfm.transform_scale(self.radius,0.0)
        #self.canvas.coords( self.itemid, px-radius, py-radius,
        #p = map( self.canvas.tfm.transform, self.globalCoords 
        p = self.mappend( self.canvas.tfm.transform, self.globalCoords )
        #print 'p is', p
        # seems to want integral pixel values... fair enough
        p = list(map(int,p))
        #print 'now p is', p
        # it seems that the following call wants p in a flattened list
        # instead of a list of pairs
        self.canvas.coords( self.itemid, tuple(p) )
        # HERE for poly

    def createObjects( self ):
        """ computes pixel coordinates and then creates the
            canvas objects
        """
        # find pixel coordinates and create the canvas objects
        p = list(map( self.canvas.tfm.transform, self.globalCoords ))
        self.itemid = self.canvas.create_polygon(p,
                                                 fill=self.colorstr,
                                                 outline=self.colorstr)
                          # it's good to have the outline be the same color
                          # to minimize gaps...


class ErdosCanvas(Canvas):
    """ This is a dervied class from canvas with support for
        a global transformation (tfm) and multiple objects
    """

    def __init__(self,master,height=400,width=400):
        """ a canvas to show a robot running around and a map
            a key facility is a general transformation allowing
            a user to specify everything in global coordinates
            and leave all of the translation to pixel coordinates
            to the computer (good!)
        """
        Canvas.__init__(self,master)
        # basic data members 
        self.master = master
        self.height = height   # window height
        self.width  = width    # window width
        self.scrollWidth = width
        self.scrollHeight = height  # this we'll work on later...

        # here is the transformation...
        self.tfm = Affine2dTrans()
        self.tfm.setWorldRotationCenter(0,0) # maps the world point 0,0 to 
        self.tfm.setPixelRotationCenter(self.width/2,self.height/2) # the center of the window

        # things in the world...
        self.movables = []   # for possibly moving objects with local coords

        # dictionary for keys pressed
        self.keysdown = {}

        self.configure(bg='white', width=self.width, height=self.height)
        self.bind("<Configure>", self.configcallback)

        # this will bring the focus to the main canvas...
        self.focus_set()
        self.bind("<KeyPress>", self.keypresscallback)
        self.bind("<KeyRelease>", self.keyreleasecallback)
        self.configure(highlightthickness=0)

        # handle all of the button events...
        self.bind("<Button-1>", self.b1_down)
        self.bind("<B1-Motion>", self.b1_move)
        self.bind("<ButtonRelease-1>", self.b1_up)

        self.bind("<Button-2>", self.click_b2_down)
        self.bind("<B2-Motion>", self.click_b2_move)
        self.bind("<ButtonRelease-2>", self.click_b2_up)

        self.bind("<Button-3>", self.click_b3_down)
        self.bind("<B3-Motion>", self.click_b3_move)
        self.bind("<ButtonRelease-3>", self.click_b3_up)

	# end of the __init__ constructor

    def reconfig(self):
        """ check h and w... """
        h = int(self.cget('height'))
        w = int(self.cget('width'))
        #print 'h,w are', h, w
        self.width = w
        self.height = h
        self.tfm.setPixelRotationCenter(self.width/2,self.height/2) 
        self.redraw()

    def configcallback(self, event):
        """ configcallback handles resizing, etc. """
        #print "***** config! *****"
        #print "dir is", dir(event)
        #print 'char, delta, height, keycode are', event.char, event.delta, event.height, event.keycode
        #print 'keysym, keysym_num, num, send_event', event.keysym, event.keysym_num, event.num, event.send_event
        #print 'serial, state, time, type', event.serial, event.state, event.time, event.type
        #print 'widget, width, x, x_root, y, y_root', event.widget, event.width, event.x, event.x_root, event.y, event.y_root
        #print "***** ******* *****"
        self.height = event.height 
        self.width = event.width
        # print 'h,w are', self.height, self.width
        # so that the world rotation center maps to the center of the window...
        self.tfm.setPixelRotationCenter(self.width/2,self.height/2) 
        self.startx = self.width/2    # for GUI events that change tfm
        self.starty = self.height/2
        self.redraw()
    
    def keypresscallback(self, event):
        """ handling keypresses """
        c = event.char
        #print 'keysym is', event.keysym
        #print 'c is', c

        # add an entry to our keysdown dictionary
        self.keysdown[event.keysym] = 1

        if c == '?':
            # way to ask about things on the fly!
            #print 'dir is', dir(tkv.canvas)
            #print 'help is', help(tkv.canvas.create_polygon)
            print('\n\
csplot Help \n\
  Controls \n\
    shift+mousedrag:  zooms in/out as you go towards/away from the center \n\
    x+mousedrag:     zooms only the horizontal axis \n\
    y+mousedrag:     zooms only the vertical axis \n\
    z+mousedrag:     zooms in on the box defined by the cursor \n\
    t+mousedrag:     translates (slides) the canvas around \n\
    ctrl+left click: centers the view at the clicked position \n\
    p+left click:    prints the coordinates of the mouse pointer \n\
    R:               resets the zoom and centers the view\n\
    ?:               displays csplot Help')

        # char is the empty string for special (non-char) keys
        #print 'keysym is', event.keysym # things like 'Shift_R' for right shift key, etc.

        #reset the zoom
        if c == 'R':
            w = _getwin()
            w.canv.tfm.setScales(50,-50)
            w.canv.tfm.setWorldRotationCenter(0,0)
            w.canv.tfm.setPixelRotationCenter(w.canv.width/2, w.canv.height/2)
            self.redraw()

    def keyreleasecallback(self, event):
        """ handling keyreleases """
        k = event.keysym
        #print "key RELEASE of", k
        if event.keysym in self.keysdown:
            self.keysdown[event.keysym] = 0

    def loadMap(self, filename):
        """ loadMap reads in a file of obstacles and places them
            on this (self) canvas
        """
        # first clear the old stuff, if any
        # but keep the robots...
        #for (key,item) in self.world.iteritems():
        #    self.delete(item.itemid)  # deletes from the canvas
        #self.world = {}
        self.delete('all') # ... but will also delete any robots...
        self.movables = []

        # open the file
        # should do some sanilty checking here...
        mapfile = open(filename, 'r')
    
        prevx = 0
        prevy = 0

        max_x_found = -1000000.0
        max_y_found = -1000000.0
        min_x_found =  1000000.0
        min_y_found =  1000000.0
        
        for line in mapfile:
            line = line[0:line.find('#')]  # strip comment
            # strip whitespace and replace tabs with spaces
            line = line.strip().replace('\t',' ')  
            # get just the space-delimited tokens
            linedata = [x for x in line.split(' ') if x != '']

            # print "linedata is", linedata
            # if empty, continue
            if len(linedata) == 0: continue
        
            # check the type field
            if linedata[0] == 'LINE':
                if linedata[1] == 'PREV':
                    # create linked segment
                    x1 = prevx
                    y1 = prevy
                    x2 = float(linedata[2])
                    y2 = float(linedata[3])
                    # colors
                    r = float(linedata[4])
                    g = float(linedata[5])
                    b = float(linedata[6])

                else:
                    # create new segment
                    x1 = float(linedata[1])
                    y1 = float(linedata[2])
                    x2 = float(linedata[3])
                    y2 = float(linedata[4])
                    # colors
                    r = float(linedata[5])
                    g = float(linedata[6])
                    b = float(linedata[7])

                # add this wall line...
                tmpname = MovableLine(self,
                                      endpoints=[(x1,y1),(x2,y2)], 
                                      colorstr=("#%02x%02x%02x" % (r, g, b)) ) 

                prevx = x2
                prevy = y2
                if x1 < min_x_found:  min_x_found = x1
                if y1 < min_y_found:  min_y_found = y1
                if x1 > max_x_found:  max_x_found = x1
                if y1 > max_y_found:  max_y_found = y1
                if x2 < min_x_found:  min_x_found = x2
                if y2 < min_y_found:  min_y_found = y2
                if x2 > max_x_found:  max_x_found = x2
                if y2 > max_y_found:  max_y_found = y2

        self.snugSquareFitNoRotation( min_x_found, max_x_found, min_y_found, max_y_found )

        mapfile.close()
        
    def snugSquareFitNoRotation(self, x_min, x_max, y_min, y_max):
        """ this will set self.tfm so that the input rectangle
            is aligned with the window
            this will not change the rotation
            (but does not handle rotation yet)
        """
        yMultiplier = 0.85*self.height/math.fabs(y_max-y_min)
        xMultiplier = 0.85*self.width/math.fabs(x_max-x_min)
        #print 'y,x mults are', yMultiplier, xMultiplier
        scaleMultiplier = min(yMultiplier,xMultiplier)
        # mapdelta = max( (y_max-y_min), (x_max-x_min) )
        # scaleMultiplier = 0.85*min(self.height,self.width)/mapdelta
        # self.tfm.multiplyScales(scaleMultiplier, scaleMultiplier)
        #
        # this preserves the signs of the scales...
        self.tfm.setScalesAbsVal(scaleMultiplier, scaleMultiplier)

        # and set the center appropriate to the map...
        wx = (x_max + x_min)/2.0
        wy = (y_max + y_min)/2.0
        self.tfm.setWorldRotationCenter(wx, wy)
        self.redraw()  # have to redraw whenever self.tfm changes...

    def b1_down(self, event):
        if "Shift_L" in self.keysdown and self.keysdown["Shift_L"] == 1:
            # print "b1 down with shift"
            self.click_b3_down(event)
        # c for camera/rotation
        # t for translation
        # should have r for rotation...
        elif ("c" in self.keysdown and self.keysdown["c"] == 1) or \
             ("t" in self.keysdown and self.keysdown["t"] == 1) or \
             ("z" in self.keysdown and self.keysdown["z"] == 1):
            self.startx = event.x
            self.starty = event.y
        # nonuniform scaling
        # c for camera/rotation
        # t for translation
        elif ("y" in self.keysdown and self.keysdown["y"] == 1) or \
             ("x" in self.keysdown and self.keysdown["x"] == 1):
            self.startx, self.starty = event.x, event.y
            self.b1_move(event)
            

    def b1_move(self,event):
        if "Shift_L" in self.keysdown and self.keysdown["Shift_L"] == 1:
            # print "b1 move with shift"
            self.click_b3_move(event)

        elif "z" in self.keysdown and self.keysdown["z"] == 1:
            self.endx = event.x
            self.endy = event.y
            #create a box
            self.delete('temporary_box')
            self.create_polygon(self.startx, self.starty,
                                self.startx, self.endy,
                                self.endx, self.endy,
                                self.endx, self.starty,
                                tag = 'temporary_box', fill = "", outline = 'red')

        elif ("c" in self.keysdown and self.keysdown["c"] == 1) or \
             ("t" in self.keysdown and self.keysdown["t"] == 1):
            self.endx = event.x
            self.endy = event.y
            # create a line in pixel space...
            self.delete('temporary_line')
            self.create_line(self.startx,self.starty,self.endx,self.endy,
                                         tag='temporary_line', fill='purple')
                                         
        elif ("y" in self.keysdown and self.keysdown["y"] == 1) or \
             ("x" in self.keysdown and self.keysdown["x"] == 1):
            self.delete('circle')
            x, y = self.width/2.0, self.height/2.0
            radius = self.length((x,y),(event.x,event.y)) 
            self.create_oval(x - radius, y - radius, x + radius, y + radius, 
                             tag="circle", outline="purple")
            # create a line in pixel space...
            self.delete('temporary_line')
            if ("y" in self.keysdown and self.keysdown["y"] == 1):
                self.create_line(x,y-radius,  # from the center
                                 x,y+radius, # for our y extent
                                 tag='temporary_line', fill='purple',
                                 width=3)
            else:
                self.create_line(x-radius,y,  # from the center
                                 x+radius,y, # for our y extent
                                 tag='temporary_line', fill='purple',
                                 width=3)


    def b1_up(self,event):
        #print "up event at pixel coordinate", event.x, event.y
        #print "up event.keysym is", event.keysym
        
        if "Shift_L" in self.keysdown and self.keysdown["Shift_L"] == 1:
            self.click_b3_up(event)
            
        elif "Control_L" in self.keysdown and self.keysdown["Control_L"] == 1:
            (wx,wy) = self.tfm.transformPixelToWorld((event.x, event.y))
            self.tfm.setWorldRotationCenter(wx, wy)
            self.redraw()

      

        elif "z" in self.keysdown and self.keysdown["z"] == 1:
             self.endx = event.x
             self.endy = event.y
             self.delete('temporary_box')
             self.box_zoom(event)
            
        elif "t" in self.keysdown and self.keysdown["t"] == 1:
            #print 'Translating...'
            self.endx = event.x
            self.endy = event.y
            # delete old line
            self.delete('temporary_line')
            sx, sy = self.tfm.transformPixelToWorld( (self.startx,self.starty) )
            #print "starting coordinates are (wx,wy) = ", (sx, sy)
            ex, ey = self.tfm.transformPixelToWorld( (self.endx,self.endy) )
            #print "ending coordinates are (ex,ey) = ", (ex, ey)
            self.tfm.deltaWorldRotationCenter( (sx-ex), (sy-ey) )
            self.redraw() # need to redraw after tfm changes!
            
        elif ("y" in self.keysdown and self.keysdown["y"] == 1) or \
             ("x" in self.keysdown and self.keysdown["x"] == 1):
            self.delete('circle')
            self.delete('temporary_line')
            x, y = self.width/2.0, self.height/2.0
            radius_stop = self.length((x,y),(event.x,event.y)) 
            radius_start = self.length((x,y),(self.startx, self.starty))
            if radius_start == 0.0: scaleMultiplier = 10.0
            else: scaleMultiplier = radius_stop/radius_start
            if ("y" in self.keysdown and self.keysdown["y"] == 1) :
                self.tfm.multiplyScales(1.0, scaleMultiplier)
            else:
                self.tfm.multiplyScales(scaleMultiplier,1.0)
            self.redraw()
            
        elif "c" in self.keysdown and self.keysdown["c"] == 1:
            self.endx = event.x
            self.endy = event.y
            # delete old line
            self.delete('temporary_line')
            wx, wy = self.tfm.transformPixelToWorld( (self.startx,self.starty) )
            # print "world coordinates are (wx,wy) = ", (wx, wy)
            # compute the theta in world space!
            ex, ey = self.tfm.transformPixelToWorld( (self.endx,self.endy) )
            thr = math.atan2( ey-wy, ex-wx)
            thd = (180.0/math.pi) * thr
            # create a camera!
            # self.addMovable...
            self.redraw()
            
        elif "p" in self.keysdown and self.keysdown["p"] == 1:  
            # if 'p' is pressed, we print the coordinates
            #print "\npixel coordinates are (px,py) = ", event.x, event.y
            wx, wy = self.tfm.transformPixelToWorld( (event.x,event.y) )
            print("click at world coordinates of (wx,wy) = ", wx, wy)

        elif "s" in self.keysdown and self.keysdown["s"] == 1:  
            # if 's' is pressed, we try to toggle the cell clicked
            #print 's' 
            wx, wy = self.tfm.transformPixelToWorld( (event.x,event.y) )
            row = int(wy)
            col = int(wx)
            try:
                w = _getwin()
                #print w
                oldcellvalue = w.canv.referenceToGraphicsData[row][col]
                #print 'oldcell value is', oldcellvalue
                w.canv.referenceToGraphicsData[row][col] = 1-oldcellvalue
                #print 'hi1'
                w.showList( w.canv.referenceToGraphicsData )
            except:
                #print 'error!'
                #print sys.exc_info()[0]
                print()
                print('No square detected at (row, col) = (', row, ',', col, ')')
                print()
            #print "click at world coordinates of (wx,wy) = ", wx, wy
            pass

    def click_b2_down(self, event):
        pass

    def click_b2_move(self, event):
        pass

    def click_b2_up(self, event):
        pass

    def length(self, xxx_todo_changeme4, xxx_todo_changeme5):
        (x1, y1) = xxx_todo_changeme4
        (x2, y2) = xxx_todo_changeme5
        return math.sqrt( (x1-x2)**2 + (y1-y2)**2 )

    def click_b3_down(self, event):
        self.startx, self.starty = event.x, event.y
        self.click_b3_move(event)

    def click_b3_move(self, event):
        self.delete('circle')
        x, y = self.width/2.0, self.height/2.0
        radius = self.length((x,y),(event.x,event.y)) 
        self.create_oval(x - radius, y - radius, x + radius, y + radius, tag="circle", outline="purple")

    def click_b3_up(self, event):
        """ scaling... """
        self.delete('circle')
        x, y = self.width/2.0, self.height/2.0
        radius_stop = self.length((x,y),(event.x,event.y)) 
        radius_start = self.length((x,y),(self.startx,self.starty))
        if radius_start == 0.0: scaleMultiplier = 10.0
        else: scaleMultiplier = radius_stop/radius_start
        self.tfm.multiplyScales(scaleMultiplier, scaleMultiplier)
        self.redraw()

    def box_zoom(self, event):
        """ Zooming... """
        self.delete('temporary_box')
        #center view
        #self.startx, self.starty = event.x, event.y
        x = (self.startx + self.endx)/2
        y = (self.starty + self.endy)/2
        (wx,wy) = self.tfm.transformPixelToWorld((x, y))
        self.tfm.setWorldRotationCenter(wx, wy)
        #Zoom
        x = abs(self.startx - self.endx)
        y = abs(self.starty - self.endy)
        screenwidth = self.width
        screenheight = self.height
        xscale = screenwidth/x
        yscale = screenheight/y
        if(xscale < yscale):
	        scaleMultiplier = xscale #math.sqrt(xscale*xscale)
        else:
            scaleMultiplier = yscale #math.sqrt(yscale*yscale)
        #scaleMultiplier = math.minimum(xscale, yscale)
        self.tfm.multiplyScales(scaleMultiplier, scaleMultiplier)
        self.redraw()

    def redraw(self):
        """ this goes through the dictionary and recomputes the
            pixel coordinates
        """
        #for (key,item) in self.movables.iteritems():
        for item in self.movables:
            item.updatePixelCoords()

    def addMovable(self, m):
        # check m's name or itemid - if already there, replace
        # self.movables[m.tag] = m
        # self.movables[m.itemid] = m
        # self.redraw()
        self.movables.append(m)
        
class CS5Canvas(ErdosCanvas):
    """ a class to handle displaying various graphical objects
        (at least initially)
    """
    def __init__(self,master=None):
        """ the constructor for the CS5Canvas class """
        ErdosCanvas.__init__(self,master)
        self.setColors()
        self.nowShowing = []
        self.nextrow = 0  # holds the next vertical value at
        # which to put a row of squares...

        
    def setColors(self, colorDictionary={}):
        """ sets up a mapping from arbitrary data to color strings """
        # here is a default
        if colorDictionary == {}:
            self.colorDictionary = {0:"white", 1:"red", "default":"blue"}
        else:
            self.colorDictionary = colorDictionary
            # check that "default" is defined...  
            
    def setColor(self, data, colorstring):
        """ sets up a mapping from arbitrary data to a color string """
        self.colorDictionary[data] = colorstring
            
    def clear(self):
        """ clears the canvas of its items and sets the nextrow to 0 
        """
        self.delete('all')
        self.nextrow = 0
    
            
    def lookup( self, colordatum, newColors = {} ):
        """ handles lots of different types of color data """
        # if there is a color function, apply it...

        # leave strings alone
        if type(colordatum) is type(''):
            return colordatum
        if type(colordatum) is type(()) and len(colordatum) == 3:
            # treat as r,g,b from 0 to 255 if the first is an int
            # and from 0.0 to 1.0 if the first is a float
            r, g, b = colordatum
            if type(r) is int or type(r) is int:
                r = int(max(0,r))
                r = int(min(255,r))
                g = int(max(0,g))
                g = int(min(255,g))
                b = int(max(0,b))
                b = int(min(255,b))
                return '#%02x%02x%02x' % (r,g,b)
            if type(r) is float:
                r = max(0.0,r)
                r = min(1.0,r)
                g = float(max(0.0,g))
                g = float(min(1.0,g))
                b = float(max(0.0,b))
                b = float(min(1.0,b))
                return '#%02x%02x%02x' % (int(r*255.99),
                                          int(g*255.99),
                                          int(b*255.99))
        # otherwise, try to look it up in the dictionary
        self.tempDict = newColors
        for color in self.tempDict:
            self.colorDictionary[color] = self.tempDict[color]
        if colordatum in self.colorDictionary:
            return self.colorDictionary[colordatum]
        else:
            return self.colorDictionary["default"]
            
    # def show1d - deletes/rescales, add1d - adds/recenters? draw1d - 
    # def show2d - deletes/rescales
    
    def add1d( self, L ):
        """ if self.nextrow == 0, we add a row and rescale
            if not, we add a row and recenter
            if the length changes, we start again...
        """
        # might want to handle tuples and strings by casting them to lists?
        if type(L) is not type([]) or len(L) == 0:
            print('the input to CS5Canvas.show must be a nonempty list')
            print('in this case, L =', L, ', was input.  Returning...')
            
        # we will treat L as a one-dimensional list now
        N = len(L)
        oldN = len(self.nowShowing)
        if N != oldN or self.nextrow == 0:
            self.nextrow = 0
            # we need to delete the old and create the new...
            self.delete('all')  # remember, self is a Tkinter.Canvas
            self.nowShowing = self.create1d( L, self.nextrow )
            self.snugSquareFitNoRotation( 0, N, 0, 1 ) # should place at top
            
        else: # add to self.nextrow
            self.nowShowing = self.create1d( L, self.nextrow )
            #self.snugSquareFitNoRotation( 0, N, 0, 1 ) # should place at top
        
        wx = N/2.0
        wy = 0.5 + self.nextrow
        #print 'new wx, wy!'
        #print 'old scalex, scaley', self.tfm.scalex, self.tfm.scaley
        self.tfm.setWorldRotationCenter(wx, wy)
        #print 'old scalex, scaley', self.tfm.scalex, self.tfm.scaley
        self.redraw()  # have to redraw whenever self.tfm changes...
        self.nextrow += 1


    def show2d( self, L, newColors):
        """ display the list L as a set of rectangles
              input L: a list of data
            if L's data are strings, it passes those
            strings on and hopes they are colors...
            otherwise it looks up the data in the
            self.colorDictionary data member, using 
            self.colorDictionary["default"] if it's not there.
            vertical is the lower y-coordinate of the row of squares
            
            returns the list of canvas items it creates (or has changed...)
        """
        # might want to handle tuples and strings by casting them to lists?
        if type(L) is not type([]) or len(L) == 0:
            print('the input to CS5Canvas.show2d must be a nonempty list')
            print('in this case, L =', L, ', was input. Going to default.')
            L = [ [0,1], [1,0] ]  # a 2x2 default list

        firstRow = L[0]
        if type(firstRow) is not type([]) or len(firstRow) == 0:
            print('the input to CS5Canvas.show2d must be a nonempty list')
            print('in this case, L =', L, ', was input. Using default data.')
            L = [ [2,1], [1,2] ]

        # we keep a copy of the reference to the current data,
        # so that a mouse click will change the actual data...
        self.referenceToGraphicsData = L

        needToRecreateGraphics = False

        #print 'self.nowShowing is', self.nowShowing
        #print 'self.nowShowing[0] is', self.nowShowing[0]
        NROWS = len(L)
        NCOLS = len(L[0])

        # is self.nowShowing of the corect format?
        if len(self.nowShowing) < 1:
            needToRecreateGraphics = True
            
        elif type(self.nowShowing[0]) is not type([]):
            needToRecreateGraphics = True

        else:
            # we will treat L as a one-dimensional list now
            oldNROWS = len(self.nowShowing)
            oldNCOLS = len(self.nowShowing[0])
            if NROWS != oldNROWS or NCOLS != oldNCOLS:
                needToRecreateGraphics = True

        if needToRecreateGraphics:
            # we need to delete the old and create the new...
            self.delete('all')  # remember, self is a Tkinter.Canvas
            self.nowShowing = self.create2d( L, 0, NROWS, NCOLS, newColors )
            self.snugSquareFitNoRotation( 0, NCOLS, 0, NROWS )
            self.nextrow = 0
            
        else: # the length of L has not changed!
            # just change the colors
            # we could, in fact, change only the colors that need changing
            for row in range(NROWS):
                for col in range(NCOLS):
                    self.nowShowing[row][col].setColorstr( \
                        self.lookup(L[row][col],
                        newColors) )
                
            
    def show1d( self, L, newColors):
        """ display the list L as a set of rectangles
              input L: a list of data
            if L's data are strings, it passes those
            strings on and hopes they are colors...
            otherwise it looks up the data in the
            self.colorDictionary data member, using 
            self.colorDictionary["default"] if it's not there.
            vertical is the lower y-coordinate of the row of squares
            
            returns the list of canvas items it creates (or has changed...)
        """
        # might want to handle tuples and strings by casting them to lists?
        if type(L) is not type([]) or len(L) == 0:
            print('the input to CS5Canvas.show must be a nonempty list')
            print('in this case, L =', L, ', was input.  Returning...')

        # make self.nowShowing one-dimensional, if it's not
        if len(self.nowShowing) > 0:
            if type(self.nowShowing[0]) is type([]):
                self.nowShowing = []  
            
        # set up vertical
            
        # we will treat L as a one-dimensional list now
        N = len(L)
        oldN = len(self.nowShowing)
        if N != oldN:
            # we need to delete the old and create the new...
            self.delete('all')  # remember, self is a Tkinter.Canvas
            self.nowShowing = self.create1d( L, 0 , newColors)
            self.snugSquareFitNoRotation( 0, N, 0, 1 )
            self.nextrow = 0
            
        else: # the length of L has not changed!
            # just change the colors
            # we could, in fact, change only the colors that need changing
            for i in range(N):
                self.nowShowing[i].setColorstr( self.lookup(L[i], newColors) )
                
    def create1d( self, L, y, newColors):
        """ creates squares from y to y+1 according to L
            returns the newly created canvas items in a list
        """
        return [ MovableRect(self,
                             tlbr_endpoints=[(x,y),(x+1,y+1)],
                             colorstr=self.lookup(L[x], newColors) )
                 for x in range(len(L)) ]

    def create2d( self, L, y, NROWS, NCOLS, newColors):
        """ creates squares from y to y+1 according to L
            returns the newly created canvas items in a list
        """
        twoDarray = []
        for row in range(NROWS):
            twoDarray.append([ MovableRect(self,
                                 tlbr_endpoints=[(x,y+row),(x+1,y+row+1)],
                                 colorstr=self.lookup(L[row][x], newColors) ) \
                                 for x in range(NCOLS) ])
        return twoDarray
        
        
class CS5Frame(Frame):
    """ There was a split here to handle scroll bars, but
        then I decided against scroll bars.
        Yet the two classes (CS5Frame and CS5Win) remain...
    """
    
    def __init__(self,master=None):
        """ CS5Frame constructor """
        Frame.__init__(self,master)
        top=self.winfo_toplevel()
        top.rowconfigure(0, weight=1)
        top.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)
        self.grid(sticky=N+S+E+W)
        self.canv = CS5Canvas( self )
        self.canv.grid( row=0, column=0, sticky=N+S+E+W )
        self.canv.tfm.setScales(1,-1)
        self.canv.tfm.setWorldRotationCenter(0,0)

    def updateList(self, L = None):
        """ this is for evolving in time... probably not for CS 5 this year... """
        if not L:
            # show1d
            self.canv.add1d( [ [random.randrange(0,256) for i in range(3)] for i in range(5) ] ) 
        else:
            self.canv.add1d( L )
        #self.quit()
        
    def showList(self, L = None, newColors = {}):
        """ displays a list L as a row of squares of color """
        if not L:
            # we'll just make up a 3x3 with 3 random colors...
            self.canv.show2d( [ [random.randrange(0,3) for i in range(3)] for i in range(3) ] ) 
        # is it one- or two- dimensional?
        elif type(L) is type([]):
            if len(L) > 0:
                if type(L[0]) is type([]):  # 2d
                    self.canv.show2d( L , newColors)
                else:
                    self.canv.show1d( L , newColors)
            else:
                print('The list is empty! Not displaying...')
        #self.quit()
        
    def makeAPlot(self,Y,leftx,dx,plottype='s', color = 'black'):
        """ this plotting function has four options (final input):
            's'  for line segments
            'p'  for points
            'recL' for rectangles with left endpoints on the curve
            'recU' for rectangles with right endpoints on the curve
	    color allows the user to change the color of the plotted line
        """
        # hmmm...
        N = len(Y)
        if N < 2:
            print('Cannot plot fewer than 2 points')
            return
        if dx == 0.0:
            print('Cannot plot with a stepsize of 0.0')
            return
        
        # make sure the plottype is a string
        if type(plottype) != type(''):
            print('Unrecognized plottype:', plottype)
            plottype = 's'
        
        if 'recL' in plottype:
            plotElements = [ MovableRect(self.canv, 
                                         tlbr_endpoints=[(leftx+dx*i,Y[i]),
                                                         (leftx+dx*(i+1),0)], 
                                         colorstr='blue') for i in range(N) ]
        if 'recU' in plottype:
             plotElements = [ MovableRect(self.canv, 
                                         tlbr_endpoints=[(leftx+dx*i,Y[i+1]),
                                                         (leftx+dx*(i+1),0)], 
                                         colorstr='cyan') for i in range(N-1) ]
            
        if 's' in plottype:
            # only go up to N-1 since we are adding one already to
            # obtain the right endpoint of each segment
            plotElements = [ MovableLine(self.canv, [ [leftx+dx*i,Y[i]],
                             [leftx+dx*(i+1),Y[i+1]] ], colorstr = color)  for i in range(N-1) ]
                             
        if 'p' in plottype:
            plotElements = [ MovablePoint(self.canv, cx=leftx+dx*i, cy=Y[i],
                              colorstr='red') for i in range(N) ]
           
                         
        # values for scaling
        maxy = max(Y)
        miny = min(Y)
        # include the x-axis if rectangles were drawn
        if 'rec' in plottype:
            maxy = max(maxy,0.0)
            miny = min(miny,0.0)
        # the x limits are easier
        minx = leftx
        maxx = leftx + N*dx
        #add x and y axes
        plotElements.extend([MovableLine(self.canv, [[-1000 + minx,0],[1000 + maxx, 0]], colorstr = 'grey'), MovableLine(self.canv, [[0,-1000 + miny],[0, 1000 + maxy]],colorstr = 'grey')])
        self.canv.tfm.setScales(1,-1)
        self.canv.snugSquareFitNoRotation( minx, maxx, miny, maxy )
        #self.canv.redraw()
        return
        
        


### 
#
#   All of this stuff is adapted from turtle.py and graphics.py
#
#   These are 
#
###

import time   # to wait for a mouse click...

# If we reload, this will close any window that was remaining from before...
try:
    global _window
    w = _window
    if w != None:
        # if the window exists here, we destroy it to complete the reload
        # of this module (it must be a reload...)
        w.tkroot.destroy()  
except NameError:
    #print 'No window... setting to None'
    pass

#
# Here is the global window object, _window
#
_window = None

def _getwin():
    """ a utility function to obtain the global window 
        and create it if it doesn't yet exist
    """
    global _window
    w = _window
    if w == None: w = CS5Win()
    _window = w
    return w
    
#
# I had trouble when this class was above the global _window declaration...
#
class CS5Win(CS5Frame):  
    """ CS5Win is meant to be an automatically appearing window, as needed """
    # as mentioned above in CS5Frame, it probably should not be separate...
    # these two classes (CS5Win and CS5Frame) should be combined
    
    def __init__(self):
        """ CS5Win's constructor """
        global _window
        #if _window is None: # for next two lines?
        self.tkroot = Tk()
        self.tkroot.wm_protocol("WM_DELETE_WINDOW", self._destroy)
        self.tkroot.title('Connect Four')
        CS5Frame.__init__(self, self.tkroot)
        _window = self
        # make sure we're watching for clicks (on top of other 
        # callbacks that might also be watching...
        self.bind_all("<Button>", self.onClick, add='+')
        
    def onClick(self, event):
        wx, wy = self.canv.tfm.transformPixelToWorld( (event.x,event.y) )
        self.clickedWorldX = wx
        self.clickedWorldY = wy
        
    def clearLastMouseClick(self):
        self.clickedWorldX = None
        self.clickedWorldY = None

    def _destroy(self):
        """ _destroy sets _window to None """ 
        #if self.idlerID:
        #    self.after_cancel(self.idlerID)
        global _window
        _window = None
        self.tkroot.destroy()
    
#
# These next three functions are for Lab 2
#
    
def plot(Y,low=0.0,hi=1.0,s='s', color = 'black'):
    """ plot draws line segments between the values in the list Y
        The x coordinates are evenly spaced in [low,hi)
        The coordinate system is chosen to leave a margin around the plot
    """
    w = _getwin()
    LY = len(Y)
    if LY == 0: return
    dx = (hi-low)/float(LY)
    w.makeAPlot(Y,low,dx,s, color)
    w.update()
    
def clear():
    """ erases everything and resets the coordinate system """
    w = _getwin()
    w.canv.tfm.setScales(1,-1)
    w.canv.tfm.setWorldRotationCenter(0,0)
    w.canv.tfm.setPixelRotationCenter(w.canv.width/2, w.canv.height/2)
    w.canv.clear()
    
def setColor(x,colorstring):
    """ sets the color of item x """
    w = _getwin()
    w.canv.setColor(x,colorstring)
    
#
# These next three functions are for Labs
#

def openWindow():
    w = _getwin()
    return
    
def show(L, newColors = {}): 
    """ displays a 1d line of colored squares """
    w = _getwin()
    w.showList(L, newColors)
    w.update()       # forces an update of the graphics...

def getKeysDown():
    """ returns the string of keys currently pressed """
    w = _getwin()
    d = w.canv.keysdown # get dictionary
    L = []  # list of active keys
    for i in list(d.keys()):
        if d[i]: L.append( i )  # make sure they're down
    return ''.join(L)   # return a string
    

def winput(promptstr=''): 
    """ winput gets a mouse click and returns the world coordinates """
    w = _getwin()
    if not w:
        print('No window available!')
        return [ 0, 0 ]
    if promptstr: print(promptstr, end=' ')
    w.clearLastMouseClick()
    while True:
        cx = w.clickedWorldX
        cy = w.clickedWorldY
        if cx != None and cy != None: break
        w.update()
        time.sleep(0.1)
    if promptstr: print()
    return [ cx, cy ]
    
def sqinput(promptstr=''):
    """ sqinput gets a click and returns the index of the square clicked on """
    w = _getwin()
    if not w:
        print('No window available!')
        return -1
    N = len(w.canv.nowShowing[0])
    cx, cy = winput(promptstr)
    if N == 0: return -1
    sqnum = int(cx)
    if sqnum <= 0: return 0
    if sqnum >= N: return N-1
    return sqnum


def sqinput2(promptstr=''):
    """ sqinput gets a click and returns the index of the square clicked on """
    w = _getwin()
    if not w:
        print('No window available!')
        return -1
    NROWS = len(w.canv.nowShowing)
    try:
        NCOLS = len(w.canv.nowShowing[0])
    except:
        print('The data does not seem 2d.')
        print('Try using sqinput instead.')
        return -1,-1
    cx, cy = winput(promptstr)
    if NROWS == 0 or NCOLS == 0: return -1, -1
    
    colnum = int(cx)
    if colnum <= 0: colnum = 0
    if colnum >= NCOLS: colnum = NCOLS-1

    rownum = int(cy)
    if rownum <= 0: rownum = 0
    if rownum >= NROWS: rownum = NROWS-1
    
    return colnum, rownum
    
# I don't know why Y seems to be changing, but we'll set it to None...
Y = None

# I just like to have this for pasting into the console...
# import csplot ; reload(csplot) ; from csplot import *




./mystery.py 2/5

[
top][prev][next]
# Solve the mystery of what this code does
# by Sara Sprenkle

def main():
    matrix = createMatrix()
    print("Before:")
    print(matrix)
    mystery(matrix)
    print("After:")
    print(matrix)

def mystery(a):
    """ What does this function do? 
    TODO: write an appropriate doc string """
    for row in range( len(a) ):
        for col in range( len(a[row]) ):
            if row == col:
                a[row][col] = 42
            else:
                a[row][col] += 1

def createMatrix():
    """
    Creates and returns the 2d list for practicing this problem
    """
    a0 = list(range(1,5))
    a1 = list(range(5,9))
    a2 = list(range(9,13))
    a = []
    a.append(a0)
    a.append(a1)
    a.append(a2)
    return a

main()

./roomba.py 3/5

[
top][prev][next]
# I found this on the old computer science server.  
# I don't remember the source.
# Uses csplot to show a simulation of the Roomba

import random
import csplot
from time import sleep

def main():

    WIDTH = 3
    HEIGHT = 3

    room = []

    for x in range(WIDTH):
        row = []
        for y in range(HEIGHT):
            row.append(0)
        room.append(row)

    for x in range(WIDTH):
        room[x][x]=5

    r = Room(room)
    roomba = Roomba(r)

    roomba.clean()

class Roomba:

    def __init__(self, room):        
        self.room = room
        self.xpos = 0
        self.ypos = 0

    def __str__(self):
        return "I am roomba"

    def clean(self):
        while not self.room.isClean():
            # move
            if self.xpos == 0:
                moveLeft = random.randint(0,1)
            elif self.xpos == self.room.getWidth()-1:
                moveLeft = random.randint(-1,0)
            else:
                moveLeft = random.randint(-1,1)

            if self.ypos == 0:
                moveUp = random.randint(0,1)
            elif self.ypos == self.room.getHeight()-1:
                moveUp = random.randint(-1,0)
            else:
                moveUp = random.randint(-1,1)
            self.xpos += moveLeft
            self.ypos += moveUp
            # clean spot
            self.room.cleanSpot(self.xpos, self.ypos)
            print("Room: ", self.room)
            self.room.showRoom()
            sleep(.1)
            


class Room:
    def __init__(self, room):
        self.room = room
        self.width = len(room)
        self.height = len(room[0])
        self.colorMap = {-1:"brown", 0:"white", 1:"green", 2:"green2", 3:"green3", "default":"gray"}

    def __str__(self):
        return str(self.room)

    def getWidth(self):
        return self.width

    def getHeight(self):
        return self.height

    def isClean(self):
        for x in range(len(self.room)):
            for y in range(len(self.room[x])):
                if self.room[x][y] > 0:
                    return False
        return True

    def readRoom(self, filename):
        """ TODO: Read in the file and set up the room"""
        return

    def isImmobile(self, xpos, ypos):
        if room[xpos][ypos] == -1:
            return True
        return False

    def cleanSpot(self, xpos, ypos):
        if xpos > self.width or xpos < 0:
            print("Error: xpos out of bounds")
            return
        if ypos > self.height or ypos < 0:
            print("Error: ypos out of bounds")
            return
        print("Cleaning spot:", xpos, ypos)
        if self.room[xpos][ypos] > 0:
            self.room[xpos][ypos] -= 1

    def showRoom(self):
        csplot.show(self.room, self.colorMap)
        

main()

./test.py 4/5

[
top][prev][next]
# From How to Think Like a Computer Scientist textbook

def testEqual(actual,expected,places=5):
    '''
    Does the actual value equal the expected value?
    For floats, places indicates how many places, right of the decimal, must be correct
    '''
    if isinstance(expected,float):
        if abs(actual-expected) < 10**(-places):
            print('\tPass')
            return True
    else:
        if actual == expected:
            print('\tPass')
            return True
    print('\tTest Failed: expected {} but got {}'.format(expected,actual))
    return False

./twod_exercises.py 5/5

[
top][prev][next]
# Practice with creating 2D Lists
# Sara Sprenkle

import test

def main():
    #rows = int(input("How many rows? "))
    #columns = int(input("How many columns? "))

    rows = 3
    columns = 4

    print("\nCorrect Matrix Creation:")
    print('-'*30)
    matrix = create2DList(rows, columns)
    print(matrix)

    print("\nAssigning matrix[1][2]=3")
    matrix[1][2] = 3
    
    print("Result: ")
    print(matrix)

    print("\n" + "*"*55)
    print("Incorrect Matrix Creation:")
    print('-'*30)

    matrix = noCreate2DList(rows, columns)
    print(matrix)

    print("\nAssigning matrix[1][2]=3")
    matrix[1][2] = 3

    print("Result: ")
    print(matrix)


def create2DList(rows, cols):
    """Returns a two-dimensional list filled with 0s that is 'rows' tall and
    'cols' wide."""
    twodlist = []
    for rowPos in range(rows):
        # creates a new list
        row = []
        for colPos in range(cols):
            row.append(0)
        twodlist.append(row)
    return twodlist

def noCreate2DList(rows, cols):
    """Does not create a 2D list because each 'row' points to the same list in
    memory."""
    
    twodlist = []
    # creates one list
    onerow = []
    for colPos in range(cols):
        onerow.append(0)
    for rowPos in range(rows):
        twodlist.append(onerow)
        
    return twodlist

def testCreation():
    rows = 3
    cols = 4
    expected = [ [0]*cols, [0]*cols, [0]*cols ] 
    
    working = create2DList(rows, cols)
    # this should pass
    test.testEqual( working , expected)
    
    notworking = noCreate2DList(rows, cols)
    # this passes but it doesn't mean that the code worked
    # we're not testing if the lists are different memory
    test.testEqual( notworking, expected)
    
    # if we modify the lists created, we'll see the problem
    expected[1][2] = 3
    working[1][2] = 3
    notworking[1][2] = 3
    
    # this should pass
    test.testEqual( working , expected)
    # this should not
    test.testEqual( notworking, expected)
    
#testCreation()

main()

Generated by GNU Enscript 1.6.5.90.