Turtle Graphics
Revision as of 20:49, 6 June 2011 by Michael Murtaugh (talk | contribs)
Turtle graphics refers to a technique of allowing graphics to be drawn, typically on a computer screen, by controlling a virtual "turtle" with simple commands like forward and turn. Turtle graphics were first described using the programming language LOGO, and were created by Seymour Papert as part of constructivist approach to teaching kids geometry by creating simulation environments or "microworlds", where principles could be approached and explored in a direct way, as opposed to classical formal education using pre-determined abstractions.
Turtle graphics are a kind of vector-based graphics system, in contrast to a traditional systems raster-based system like a bitmap.
http://www.greenteapress.com/thinkpython/swampy/
Turtle Graphics Inkscape Extension
#!/usr/bin/env python
#-*- coding:utf-8 -*-
"""
#XSS2qwXBUYv%s+<>dqw6wmq2om#HH$wac=:;==|i|nYUWQQW$WQQQWmBQmWBQWWBWQWWWmBBWWmQQQQ
Z2qpXooo2i+=|aaZW1n2vYwn###ovi>=||=;:;::+=|||{?VTSUQWWBBW#ZWWmmWmWmmWmWmBmW#QWQQ
ZoSXXX2!==>{av1XTTTYX#WTY+..."!""!<%|=========|*nSou3vSZVZXA#WWmBBWWWmmBWWBmWWQQ
W#oX2ov%%nnZX1|++<loG?.=;<_%|_,._,..+*~=i>==|==+;:==||{SYH$QWWBWWBWmWWWWmWBWWWQQ
#ZXXo{vnuvnvI+=::vnS(:i|v%;::::=====|=|===|||==+===::=={uwm#mWWWmmWWBWWmWWWmBQQQ
U2XYo2Iov%|i++:.=i%X:=|+: :;:;==v%lvvvvvvlvs%avv|=====+|YWBWWWWWWWWmQWmWBWWBWQQQ
#XoooIi>|=~-:+ ;:=v(:|---:===|ivvnoX2XoXSoXo1aaunvvsi==:=)3WWWWWBBBWmWBWWWWmQWQQ
#XSZ1>)}+=|=;:}"|=|>=;. .;=||ivnoXXXXXZZZXqomX2ouXS2Xo;==;=lXWBWWWWWBWBWWWWmQWQQ
mX1%**=::`:.__;=-=>:::.:==+<svoXZXXZZZSqw#XXnwXXd&SqXXS>::+={S##WWWBWWBWWBWWWWQQ
Wnz||is,=;=._|::_=- -::==+|iIo2XXXXmwmZZZYodXmXXSo#Y1i|||:==|{IXZ$WQBWWBWWWmWQQQ
1vs>>=:=|==||=ia+` .:==++vvvSoXXZX22Y1qu#XXXSoZX1l|+iivv>:=|={*mw#mWQmQWmWWWWQQ
|%iawu2!3e(v>"' -=|>|ivvSeXoZXXSXXXZ#XXwXX1|<ivdXXXXo>:;;iII####WWBWWWWBQQQ
<|2YI>+={1o+ _ .-..._==<v}1ou1XwoZZZXZZZZXZXXonowmZZZXXX#X,:;=|liYXXX#mWBWBWQQQ
|uni>>|=ii3;;+,._- .==|%{iv1uSSX2XZXXZXXXo2XoS2Xm#ZZZ1+{i*SX; ==|i%*YoX###WmWWQQ
Iiv>|<;=||<-:.:=.;;=<iii{%1SooSS2li|ivooXn2ooXX#Z#X2|==-%IISo,--=i=|=InXZ##WmQQQ
|=iii>+||+=::=::=;==|||%vv1****+=+|aommmmXoXXXZZ#SXioZaw|vo#Xo --==<||*A###WQQ
i|==||(=|=>-.-::-=;+||ii%|++++|vuoXZZ#Z#Z##ZZXXZooe+){suq##XXZo ...;;+<i%|?9$WWQ
l|||i=>|>|..:.:;::==|=i||+=|invoXSZZXXXoXnSnn32ZX1|IvvXXXXX#XZ#c .-:::=|3ns%>2AW
vi|i+|=<+:.:.:::;===|||>|ivno2X2***}|**I1ii|iinmXZwunoXX#XXXXXUZc :;:::<=%YoowzY
n}ii||>:;::.-::;:===|||i|lo2SS}|<%.-:=%"!+||ilnSX#Z##XXS22SXXZ#ZX;--:===i<=i3#Zo
os%|l+=:;==:-::::=;+|=|iivno2c|ioXaa%|i%Ii|ilvnSSXXXZZ#XmXXZ#U###m;:.::||i|l>i3$
1|}=>==;=:;:;.:.:;==||iiivno2%=+{*IvuXXoovnvvnooo2SXXZZZXZX#U###ZZc:-:;:||ovsvvv
1i>|==|==;.==;...::|+|i|Ilvnnvvno2XXXXX#XvnvononvooXXZZZ##Xu1XX##Zo;-.:.:+*1l3qv
1|==|++|;;:.::.: :-+|||ivvvoonooooXZZZX1ooo21IIIvnXXXX#XZ#2Il?SXZX(.: ;::iwwIXw
o%i=<==>::::: .: ...:=||ilvnn222XSXXXSnoS2SSo|<vvv2S2XXXmm#`--==<Xov;.:..:|*SlBh
pvi>=|=:==.....- .. -;=|ilvnnoSo2ooXSSSoSXZn>|oSXno2oXXZZ' ..:.:{1=-:.:=||SXQW
X|li>||=;=:..... - :+||lvnnn2o2SS2XXXXX2vii|!+---"!!+- ._...<l=-..-::)Y1#B
Xvii||===;:.=:... -=||ivvnno2oXXXXXXZ1|===. . ..._/====i|; .::=+nWW
mZs|v|+=|==::... . . :==Iivvnno2S2XXXX>==::.. -:=oe" :<vvi>:: . ;||ivd
Qm2vi%i|%;;;:;.: . :=|<vv1vvo2XSXX1==:. ._;=_:-"=...=vIni>:. ..:=)2v|
WWZzi|<v||=+:==.;.. . ..-:=||lIvvnno2n>=.. .:=|+'_:-;..;|invnv:. ` ..=%nIi
Qmwouc|vi<|+:<:: :. . . .-:=||iiInvI|:....;:..:====|ivnzi+|i;. .:-=;=|i|
QW#XS}ii%%||=|=:..-_ - ..-===+|ii||=:.:=i||||||ilIv}|` _i|:- ...;:==ivl=
Qmov%svvu%i==|=:: . : ..:-;=+|===:;:=|ivIvvIl|=+; =i==. ..:==||{nc;
QWmmZm#2n>|><<>=:. .::- -.:-:-:-::==<i|ivlii|>= ==:.. :==::=%o|*1>
QQ#mmWTon=Ivil==:. = . .-..- - :-++<|>|<=;:;: -=<|Inoai_
QQQQQeXeniIi|I==:. . : . -. . : :-::.-.:.- . .;+lXXQ#mB
QQQQQQWscs=|(>===: :.::.. . ..... -.. ... . ====iinodWm
QQQQQQk2oi|+i>><:.::=..<;_:.. . - - ..;: . . ..::+i|vwumQmW
QQQQQBdW#Xviii|=|<=_.:||%%===;;.. . -...:-, .:::...: :=;==|<vnmmQWWT
"""
import random, lxml, sys
sys.path.append('/usr/share/inkscape/extensions') # or another path, as necessary
import inkex
from simplestyle import *
#########################################################
# lxml utils
def collecttext (elt):
ret = elt.text or ""
ret += "\n".join([collecttext(child) for child in elt])
return ret
#def getcontents (element):
# return (element.text or "") + "".join(map(lxml.etree.tostring, element))
#########################################################
# the main turtle action
import math
defaultpathstyles = {
'fill':'none',
'fill-rule':'evenodd',
'stroke':'#000000',
'stroke-width':'1px',
'stroke-linecap':'butt',
'stroke-linejoin':'miter',
'stroke-opacity':'1'
}
def rgb (r, g, b):
return '#%02x%02x%02x' % (r,g,b)
# <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="%s" />
def pointstodata (points):
try:
data = "M %0.6f,%0.6f" % points[0]
for p in points[1:]:
data += " L %0.6f,%0.6f" % p
return data
except IndexError:
return ""
class Turtle (object):
def __init__(self, x=100.0, y=100.0, heading=0.0, pendown=False, layer=None):
self.x = x
self.y = y
self.heading = heading
self._pendown = pendown
self.points = []
self.stack = []
if layer is not None:
self.stack.append(layer)
self.styles = {}
def goto(self, x, y):
self.x = x
self.y = y
def face(self, d):
self.heading = (d/180.0)*math.pi
def forward (self, steps):
self.x += math.cos(self.heading) * steps
self.y -= math.sin(self.heading) * steps
if self._pendown:
self.points.append((self.x, self.y))
def left (self, d):
d = (d/180.0)*math.pi
self.heading += d
def backward (self, steps):
self.forward(-steps)
def right (self, d):
self.left(-d)
def backward (self, steps):
self.bk(steps)
def pendown (self):
self._pendown = True
self.points.append((self.x, self.y))
def penup (self):
self._pendown = False
if len(self.points) and len(self.stack):
path = inkex.etree.Element(inkex.addNS('path', 'svg'))
# http://stackoverflow.com/questions/1551666/how-can-2-python-dictionaries-become-1/1551878#1551878
styles = dict(defaultpathstyles, **self.styles)
path.set('style', formatStyle(styles))
path.set('d', pointstodata(self.points))
self.stack[-1].append(path)
self.points = []
def startgroup (self, id=None):
g = inkex.etree.Element(inkex.addNS('g', 'svg'))
if id is not None:
g.set('id', str(id))
self.stack[-1].append(g)
self.stack.append(g)
def endgroup (self):
self.stack.pop()
## aliases
def fd (self, steps): self.forward(steps)
def bk (self, steps): self.backward(steps)
def lt (self, d): self.left(d)
def rt (self, d): self.right(d)
def pd (self): self.pendown()
def pu (self): self.penup()
def export_to_dict (self, d):
d['forward'] = self.forward
d['backward'] = self.backward
d['left'] = self.left
d['right'] = self.right
d['pendown'] = self.pendown
d['penup'] = self.penup
d['goto'] = self.goto
d['face'] = self.face
d['startgroup'] = self.startgroup
d['endgroup'] = self.endgroup
d['fd'] = self.fd
d['bk'] = self.bk
d['lt'] = self.lt
d['rt'] = self.rt
d['pd'] = self.pd
d['pu'] = self.pu
d['styles'] = self.styles
#########################################################
# The Inkscape Effect interface
class SeymourEffect(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
# http://docs.python.org/lib/module-optparse.html
self.OptionParser.add_option('-c', '--codeid', action = 'store',
type = 'string', dest = 'codeid', default = 'code',
help = 'ID of code element')
def effect (self):
codeid = self.options.codeid
svg = self.document.getroot()
# svg = self.document.xpath('//svg:svg', namespace = inkex.NSS)[0]
code = self.document.xpath("//*[@id='%s']" % codeid)[0]
src = collecttext(code)
# print src
width = inkex.unittouu(svg.get('width'))
height = inkex.unittouu(svg.attrib['height'])
outputlayer = inkex.etree.SubElement(svg, 'g')
outputlayer.set(inkex.addNS('label', 'inkscape'), 'Seymour Output')
outputlayer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
turtle = Turtle(x=width/2, y=height/2, layer=outputlayer)
d = {}
turtle.export_to_dict(d)
d['rgb'] = rgb
exec src in d
##############################################################################
# This file can be directly applied to an SVG on the commandline using:
# python seymour.py path/to/your.svg
if __name__ == "__main__":
effect = SeymourEffect()
effect.affect()