You are not logged in Log in Join
You are here: Home » Members » DaddyGravity » ContagiousFun » KutiaPy » wikipage_view

Log in
Name

Password

 
 
FrontPage » ProgrammingForArtists » ContagiousScriptsLibrary »

KutiaPy

#! /usr/bin/python

"""Kutia Turtle graphics program

  ToDo for 1.0
  ============
  * Clear updates on quit
  * Drag to moveTo
  * DragHandle to turn
  * CommandWindow
  * Change current turtle
  * Penup/pendown as radio buttons
  * Documentation
  * Display current angle and position

  Ideas for 2.0
  ============
  Implement turtle language?
  Implement turtle communication, conditionals
  Implement speed based on distance
  Freeze Kutia.  Does it work?  How big is it?
  When move button is used, concatenate adjacent moves into one
  What is a "move"?  An iteration, a line segment, a turn?

  Ideas for 'TurtleTalk' Commands
  ===============================
  Step - run each turtle's next command
  Run - run all commands for all turtles in parallel
  Forward - move turtle (10, 20, 50, random, specify...)
  Turn - (pi/2, pi/3, pi/4, random, specify, change units)
  Pen (up/down, color, width, pattern)
  Turtle(add, remove, select, hide/show)
  Code window for each turtle

  Ideas for Turtle Communication
  ====================
  Send message
  Share data
  Wait for event

  Ideas for Turtle Events
  =============
  Change color
  Pen up/down
  Arrive/Leave/Pass an area
  Intersect a line/color
"""
from math import pi, sin, cos, atan, sqrt, fabs, asin, acos
import whrandom
from Tkinter import *

DEGREE = pi / 180

# test data

house = [
  "retVal.append('self.turn(-90)')",
  "retVal.append('self.move(100)')",
  "retVal.append('self.turn(45)')",
  "retVal.append('self.move(75)')",
  "retVal.append('self.turn(90)')",
  "retVal.append('self.move(75)')",
  "retVal.append('self.turn(45)')",
  "retVal.append('self.move(100)')"]

star = [
  "global star_angle;star_angle =  144",
  "for side in range(5): retVal.append('self.move(50); self.turn(star_angle);')"]

circle = [
  "global circle_angle;circle_angle = 1",
  "for side in range(360): retVal.append('self.move(1); self.turn(circle_angle);')"]

hexagon = [
  "global hex_angle;hex_angle = 60",
  "for side in range(6): retVal.append('self.move(30); self.turn(hex_angle);')"]

square = [
  "global square_angle;square_angle = 90",
  "for side in range(4): retVal.append('self.move(30); self.turn(square_angle);')"]
 
moire = [
  "for side in range(360): retVal.append('self.move(100);self.move(-100);self.turn(1);')"]

random = [
  "for side in range(60): retVal.append('self.moveRandom(); self.turnRandom();')"]

TRUE = 1
FALSE = 0

class Turtle:
  def __init__(self, canvas, name="kutia", commands=[], pencolor='black', x=25, y=15, angle=0.0, speed=4):
    self.color = 'dark green'
    self.highlight = 'green'
    self.outline = 'black'
    self.pencolor = pencolor
    self.degrees = angle
    self.angle = angle * DEGREE
    self.speed = speed
    self.tagList = [0] * 360
    self.cX = x
    self.cY = y
    self.canvas = canvas
    self.visible = TRUE
    self.pendown = TRUE
    self.name = name
    self.commands = self.expand(commands)
    self.currentStep = 0
    self.draw(0)
    self.incrementalTurnFlag = 0 # disable turning animation

  def run(self):
    self.step();
    if not self.doneRunning():
      self.canvas.after(1000/self.speed, self.run);
    else:
      self.currentStep = 0

  def hide(self):
    self.visible = FALSE
    self.hideIcon(int(self.degrees))

  def show(self):
    self.visible = TRUE
    self.showIcon(int(self.degrees))

  def doneRunning(self):
    if (self.currentStep < len(self.commands)):
      return FALSE
    return TRUE

  def step(self):
    #print self.name + " stepping()"
    if not self.doneRunning():
      exec(self.commands[self.currentStep], globals(), locals())
      self.currentStep = self.currentStep + 1
      self.canvas.update()

  def expand(self, commands):
    #print 'Expanding commands for %s' % self.name
    retVal = []
    if commands:
      for line in commands:
        exec(line, globals(), locals())
    #print 'Done expanding'
    return retVal

  def hideIcon(self, index):
    idx = int(index)
    if (self.tagList[idx]):
      self.canvas.move(self.tagList[idx], -(self.cX + 30), -(self.cY + 30))
    else:
      print 'collision in hideIcon()'

  def showIcon(self, index):
    idx = int(index)
    if (self.tagList[idx]):
      #print 'showIcon() thinks there is an icon here'
      self.canvas.move(self.tagList[idx], self.cX + 30, self.cY + 30)
    else:
      #print 'showIcon() thinks it needs to draw the icon'
      self.draw(idx)
    self.currTag = self.tagList[idx]

  def move(self, distance):
    #print 'cX = ' + str(self.cX) + ', cY = ' + str(self.cY)
    newx = self.cX + cos(self.angle) * distance
    newy = self.cY + sin(self.angle) * distance
    if self.pendown:
      height = self.canvas.create_line(self.cX, self.cY, newx, newy, fill=self.pencolor)
      self.top(height) # make sure to stay above the new line we draw
    self.moveTo(newx, newy)

  def moveTo(self, x, y):
    #print 'moving from (%d,%d) to (%d,%d)' % (self.cX, self.cY, x, y)
    self.canvas.move(self.currTag, x - self.cX, y - self.cY)
    self.cX = x
    self.cY = y

  def moveToPoint(self, x, y):
    dX = x - self.cX
    dY = y - self.cY
    distance = sqrt(dX ** 2 + dY ** 2)
    self.turn(self.findAngle(x,y))
    self.move(distance)
    
  def turn(self, angle):
      #self.turnTo(self.degrees + angle)
      inc = 1
      if angle < 0: inc = -1
      if self.incrementalTurnFlag:
        for each in range(int(fabs(angle))):
          self.turnTo(self.degrees + inc)
      else:
        self.turnTo(self.degrees + angle)

  def turnTo(self, angle):
    self.hideIcon(int(self.degrees))
    #print 'turning from %d to %d degrees' % (self.degrees, angle %360)
    self.degrees = angle % 360
    self.angle = angle * DEGREE
    self.showIcon(int(self.degrees))
    self.canvas.update()

  def moveRandom(self, maxDistance=60):
    self.move(whrandom.randint(1, maxDistance))

  def turnRandom(self, maxAngle=360):
    self.turn(whrandom.randint(1, maxAngle))

  def top(self, object):
    #self.canvas.tag_raise(self.currTag, object)
    pass

  def setColor(self, color):
    self.pencolor = color

  def points(self, coords):
      """Rotate points relative to current orientation"""
      cX = self.cX
      cY = self.cY
      retVal = []
      for i in range(len(coords)):
          if (i % 2):
            pass
          else:
            x = coords[i]
            y = coords[i + 1]
            retVal.append(self.convertX(x,y))
            retVal.append(self.convertY(x,y))
      return retVal

  def convertX(self, x, y):

      return int(cos(self.angle)*(x - self.cX) - sin(self.angle)*(y - self.cY) + self.cX)

  def convertY(self, x, y):
      return int(sin(self.angle)*(x - self.cX) + cos(self.angle)*(y - self.cY) + self.cY)

  def draw(self, index):
    #print 'Pre-drawing image cache %d for %s' % (index, self.name)
    #print 'cX = ' + str(self.cX) + ', cY = ' + str(self.cY)
    if self.tagList[index]:
      print 'collision in draw()'
    baseTag = "id" + str(id(self))
    #print "generating image %d" % index 
    self.currTag = baseTag + str(index)
    self.tagList[index] = self.currTag
    #self.turnTo(index)
    self.drawHead()
    self.drawTail()
    self.drawBody()
    self.drawLegs()
    #self.hideIcon(i)
    #self.turnTo(0)
    self.currTag = self.tagList[0]
    #self.moveTo(self.cX + 2, self.cY * 2)
    # self.showIcon(0)
    #print 'Done pre-drawing'
    
  def drawHead(self):
    cX = self.cX
    cY = self.cY
    self.canvas.create_polygon(
      self.points((
        cX + 15, cY + 2,
        cX + 18, cY + 2,
        cX + 18, cY + 4,
        cX + 20, cY + 4,
        cX + 25, cY + 2,
        cX + 25, cY - 2,
        cX + 20, cY - 4,
        cX + 18, cY - 4,
        cX + 18, cY - 2,
        cX + 15, cY - 2
      )),
      tags=(self.currTag))

  def drawTail(self):
    cX = self.cX
    cY = self.cY
    self.canvas.create_polygon(self.points((cX-15, cY+2,
                               cX-25, cY,
                               cX-15, cY)),
                               tags=(self.currTag))
      
  def drawLegs(self):
    cX = self.cX
    cY = self.cY
    # lower left
    self.canvas.create_polygon(self.legCoords(cX-10, cY+8, -1.0, 1.0), tags=(self.currTag))
    # upper left
    self.canvas.create_polygon(self.legCoords(cX-10, cY-8, -1.0, -1.0), tags=(self.currTag))
    # upper right
    self.canvas.create_polygon(self.legCoords(cX+10, cY-8, 1.0, -1.0), tags=(self.currTag))
    # lower right
    self.canvas.create_polygon(self.legCoords(cX+10, cY+8, 1.0, 1.0), tags=(self.currTag))

  def drawBody(self):
    # draw concentric ovals
    cX = self.cX
    cY = self.cY
    #outer oval
    self.canvas.create_polygon(
        self.points((cX - 15, cY + 4,
               cX - 15, cY - 4,
               cX - 8, cY - 10,
               cX + 8, cY - 10,
               cX + 15, cY - 4,
               cX + 15, cY + 4,
               cX + 8, cY + 10,
               cX - 8, cY + 10
        )),
        fill=self.highlight,
        smooth='true',
        outline=self.outline,
        tags=(self.currTag))
    #inner oval
    self.canvas.create_polygon(
        self.points((cX-10,cY+2,
                cX-10,cY-2,
                cX-6,cY-5,
                cX+6,cY-5,
                cX+10,cY-2,
                cX+10,cY+2,
                cX+6,cY+5,
                cX-6,cY+5)),
        fill=self.color,
        outline=self.color,
        smooth='true',
        tags=(self.currTag))
    # draw lines on back
    self.canvas.create_line(self.points((
                cX-9,cY-8,
                cX-5, cY-3,
                cX-5,cY+3,
                cX-9,cY+8)),
        smooth='true',
        #joinstyle=BEVEL,
        width=2,
        tags=(self.currTag))
    self.canvas.create_line(self.points((
                cX+9,cY-8,
                cX+5,cY-3,
                cX+5,cY+3,
                cX+9, cY+8)),
        smooth='true',
        #joinstyle=BEVEL,
        width=2,
        tags=(self.currTag))
    self.canvas.create_line(self.points((cX, cY-10, cX, cY+10)), width=2, tags=(self.currTag))
    self.canvas.create_line(self.points((cX-15, cY, cX+15, cY)), width=2, tags=(self.currTag))

  def pack(self):
    self.canvas.pack()

  def pen_up(self):
    self.pendown = 0

  def pen_down(self):
    self.pendown = 1

  def legCoords(self, x, y, dirX, dirY):
      leg = (
          x,y,
          x + 3 * dirX, y + 3 * dirY,
          x + 5 * dirX, y + 3 * dirY,
          x + 5 * dirX, y + 4 * dirY,
          x + 7 * dirX, y + 4 * dirY,
          x + 7 * dirX, y,
          x + 5 * dirX, y,
          x + 5 * dirX, y + 1 * dirY,
          x + 4 * dirX, y + 1 * dirY,
          x + 2 * dirX, y - 2 * dirY)
      return self.points(leg)

  def center(self, rect):
    self.cX = (rect.bottom - rect.top) / 2
    self.cY = (rect.right - rect.left) / 2

  def round(self, num):
    if num - int(num) >= 0.5: return int(num) + 1
    return int(num)

  def diffAngle(self, angle):
    #print 'Angle: ' + str(angle) + ', current: ' + str(self.degrees)
    newangle = angle + self.degrees
    if newangle > 360: newangle = newangle - 360
    if newangle < -360: newangle = newangle + 360
    if newangle > 180: newangle = newangle - 360
    if newangle < -180: newangle = newangle + 360
    #print 'New angle: ' + str(newangle)
    return - newangle

  def findAngle(self, x, y):
    signx = x - self.cX
    signy = y - self.cY
    if signx > 0 and signy < 0: 
      Q = 1
      #print "NE"
    elif signx < 0 and signy < 0: 
      Q = 2
      #print "NW"
    elif signx < 0 and signy > 0: 
      Q = 3
      #print "SW"
    elif signx > 0 and signy > 0: 
      Q = 4
      #print "SE"
    elif signx == 0 and signy < 0: 
      #print "N"
      return self.diffAngle(90)
    elif signx == 0 and signy > 0: 
      #print "S"
      return self.diffAngle(270)
    elif signx > 0 and signy == 0: 
      #print "E"
      return self.diffAngle(0)
    elif signx < 0 and signy == 0: 
      #print "W"
      return self.diffAngle(180) 
    len_ac = abs(x - self.cX)
    len_bc = abs(y - self.cY)
    len_ab = sqrt(len_ac ** 2 + len_bc ** 2)
    A = self.round(acos(len_bc / len_ab) / DEGREE)
    B = self.round(asin(len_bc / len_ab) / DEGREE)
    #print 'A = ' + str(A) + ', B = ' + str(B)
    if Q == 1: return self.diffAngle(B)
    elif Q == 2: return self.diffAngle(A + 90)
    elif Q == 3: return self.diffAngle(B + 180)
    elif Q == 4: return self.diffAngle(A + 270)
      
def testTurtle():
  root = Tk()
  canvas = Canvas(root)
  canvas.pack()
  icon = Turtle(canvas,"example")
  icon.draw()
  icon.move(50,50)
  icon.turn(60)
  root.mainloop()

class TurtleWindow(Frame):

  def __init__(self, parent=None):
    Frame.__init__(self, parent)
    self.pack()
    self.turtles = []
    self.currTurtle = None
    self.createWidgets()
    self.master.title('Kutia')
    self.master.iconname('Kutia')
    self.demoInitialized = 0

  def createWidgets(self):
    self.addCanvas()
    self.makeMenuBar()
    self.makeButtonBar()
    self.addTurtle()
    #self.addTurtleCommandFrame()

  def makeMenuBar(self):
    self.menubar = Frame(self, relief=RAISED, bd=2)
    self.menubar.pack(side=TOP, fill=X)
    self.turtleMenu()
    self.preferenceMenu()
    self.colorMenu()
    self.demoMenu()

  def makeButtonBar(self):
    toolbar = Frame(self, cursor='hand2', relief=SUNKEN, bd=2)
    toolbar.pack(side=TOP, fill=X)
    Button(toolbar, text='Pen up', command=self.penUp).pack(side=LEFT)
    Button(toolbar, text='Pen down', command=self.penDown).pack(side=LEFT)
    Button(toolbar, text='Clear', command=self.clear).pack(side=LEFT)

  def turtleMenu(self):
    mbutton = Menubutton(self.menubar, text='Turtle', underline=0)
    mbutton.pack(side=LEFT)
    menu = Menu(mbutton)
    self.turtleMenu = menu
    mbutton['menu'] = menu
    return mbutton

  def colorMenu(self):
    mbutton = Menubutton(self.menubar, text='Color', underline=0)
    mbutton.pack(side=LEFT)
    menu = Menu(mbutton)
    colors = ('red', 'orange', 'yellow', 'green', 'blue', 'violet', 'black')
    for color in colors:
      def m_command(self=self, color=color):
        self.changeColor(color)
      menu.add_command(label=color, command=m_command)
    mbutton['menu'] = menu
    return mbutton

  def changeColor(self, color):
    self.currTurtle.setColor(color)

  def demoMenu(self):
    mbutton = Menubutton(self.menubar, text='Demo', underline=0)
    mbutton.pack(side=LEFT)
    menu = Menu(mbutton)
    menu.add_command(label='Square', command=self.squareDemo)
    menu.add_command(label='Hexagon', command=self.hexagonDemo)
    menu.add_command(label='House', command=self.houseDemo)
    menu.add_command(label='Circle', command=self.circleDemo)
    menu.add_command(label='Moire', command=self.moireDemo)
    menu.add_command(label='Random', command=self.randomDemo)
    menu.add_command(label='Demo All', command=self.demo)
    mbutton['menu'] = menu
    return mbutton

  def preferenceMenu(self):
    mbutton = Menubutton(self.menubar, text='Preferences', underline=0)
    mbutton.pack(side=LEFT)
    menu = Menu(mbutton)
    menu.add_command(label='Name turtle...', command=self.doNothing)
    mbutton['menu'] = menu
    return mbutton

  def addCanvas(self):
    self.canvas = Canvas(self, bg="white",height=400,width=600)
    self.canvas.pack(side=BOTTOM)
    self.canvas.bind('<Button-1>', self.turnTo)

  def squareDemo(self):
    turtle = Turtle(self.canvas, name="black", pencolor='black', x=200, y=200, commands=square, speed=10)
    self.turtles.append(turtle)
    turtle.run()
    self.currTurtle = turtle

  def hexagonDemo(self):
    turtle = Turtle(self.canvas, name="blue",pencolor='blue', x=300, y=300, commands=hexagon, speed=20)
    self.turtles.append(turtle)
    turtle.run()
    self.currTurtle = turtle
  
  def houseDemo(self):
    turtle = Turtle(self.canvas, name="red", pencolor='red', x=100, y=400, commands=house, speed=30)
    self.turtles.append(turtle)
    turtle.run()
    self.currTurtle = turtle

  def starDemo(self):
    turtle = Turtle(self.canvas, name="yellow", pencolor='yellow', x=400, y=100, commands=star, speed=40)
    self.turtles.append(turtle)
    turtle.run()
    self.currTurtle = turtle

  def circleDemo(self):
    turtle = Turtle(self.canvas, name="green", pencolor='green', x=100, y=100, commands=circle, speed=1001)
    self.turtles.append(turtle)
    turtle.run()
    self.currTurtle = turtle

  def moireDemo(self):
    turtle = Turtle(self.canvas, name="orange", pencolor='DarkOrange', x=300, y=100, commands=moire, speed=999)
    self.turtles.append(turtle)
    turtle.run()
    self.currTurtle = turtle

  def randomDemo(self):
    turtle = Turtle(self.canvas, name="violet", pencolor='violet', x=300, y=200, commands=random, speed=100)
    self.turtles.append(turtle)
    turtle.run()
    self.currTurtle = turtle

  def initDemo(self):
    if self.demoInitialized: return
    # Create some turtles to test
    self.turtles.append(Turtle(self.canvas, name="black", pencolor='black', x=200, y=200, commands=square, speed=1))
    self.turtles.append(Turtle(self.canvas, name="blue",pencolor='blue', x=300, y=300, commands=hexagon, speed=2))
    self.turtles.append(Turtle(self.canvas, name="red", pencolor='red', x=100, y=400, commands=house, speed=3))
    self.turtles.append(Turtle(self.canvas, name="yellow", pencolor='yellow', x=400, y=100, commands=star, speed=4))
    self.turtles.append(Turtle(self.canvas, name="green", pencolor='green', x=100, y=100, commands=circle, speed=1000))
    self.turtles.append(Turtle(self.canvas, name="orange", pencolor='DarkOrange', x=300, y=100, commands=moire, speed=1000))
    self.turtles.append(Turtle(self.canvas, name="violet", pencolor='violet', x=300, y=200, commands=random, speed=10))
    self.demoInitialized = 1

  def demo(self):
    self.initDemo()
    for turtle in self.turtles:
        turtle.run()

  def doNothing(self):
    pass

  def addTurtle(self):
    self.turtles.append(self.currTurtle)
    self.currTurtle = Turtle(self.canvas, name="Kutia", commands=[], x=100, y=100)

  def forward(self):
    self.currTurtle.commands.append('self.move(20)')
    self.currTurtle.step()

  def turnLeft(self):
    self.currTurtle.turn(-15)

  def turnRight(self):
    self.currTurtle.turn(15)

  def penUp(self):
    self.currTurtle.pen_up()

  def penDown(self):
    self.currTurtle.pen_down()

  def turnTo(self, event):
    #print 'x = ' + str(event.x) + ', y = ' + str(event.y)
    if self.currTurtle:
      self.currTurtle.moveToPoint(event.x, event.y)

  def nameTurtle(self):
    print 'Name turtle called'

  def clear(self):
   self.turtles = []
   for item in self.canvas.find_all():
     self.canvas.delete(item)
   self.canvas.update()
   self.addTurtle()

def testWindow():
  root = Tk()
  window = TurtleWindow(root)
  root.mainloop()

if __name__=='__main__':
    #testTurtle()
    testWindow()