Saturday, August 21, 2010

Setting the turtle free with Sikuli

Have you played with turtle graphics? I enjoyed Logo ages and ages ago. Nowadays there's a turtle module in Python's standard library, plus several other implementations.

But all the implementations I know of keep the poor little turtle stuck inside a dull little window. Last night I realized that, with Sikuli, I can set the turtle free! It can roam freely beyond one little window and have adventures in the big, wide world. Here it is marching right past the edge of TuxPaint and onto GNU Paint.

Note that this is an invisible turtle. But you can see its effects.

For the new free-range turtle, "penDown" and "penUp" are actually "mouse button down" and "mouse button up". If it's walking across a drawing program with a paintbrush turned on, it will paint a line like a traditional Logo turtle. Otherwise, it will click and drag its way across your desktop and your other applications. This turtle can be mischievous!

What really pleased me was just how easy it was to implement with Sikuli. Here's the code in an .skl bundle - it's a zipfile that Sikuli can use directly.

import math

class Turtle(object):
def __init__(self):
self.heading = 0
self.penDown()
self.loc = Env.getMouseLocation()
def forward(self, distance):
sin = math.sin(math.radians(self.heading))
cos = math.cos(math.radians(self.heading))
endpoint = (int(self.loc.x + cos * distance),
int(self.loc.y + sin * distance))
if self.down:
dragDrop(self.loc, endpoint)
else:
mouseMove(endpoint)
self.loc = Location(*endpoint)
def backward(self, distance):
self.forward(distance * -1)
def right(self, degrees):
self.heading = (self.heading + degrees) % 360
def left(self, degrees):
self.right(-1 * degrees)
def penUp(self):
self.down = False
def penDown(self):
self.down = True
def circle(self, radius, extent=360., steps=20):
circ = 2 * math.pi * radius
fraction = extent / 360.
step_length = (circ * fraction) / steps
for i in range(steps):
self.forward(step_length)
self.left(360. * fraction / steps)

def zigzag(startHeading, size, angle, steps):
t = Turtle()
t.heading = startHeading
t.left(angle * 0.5)
for i in range(steps):
t.forward(size)
t.right(angle)
t.forward(size)
t.left(angle)

1 comment:

shi said...

What an original idea! I really like it.