|
|
Line 18: |
Line 18: |
| Download and try [[:Image:City.svg]] in Inscake for an example (this file doesn't display properly in the browser, but that's ok, open it in Inkscape and Run the Seymour extension. Right click and use "Save As..." to save the SVG file on your computer.) | | Download and try [[:Image:City.svg]] in Inscake for an example (this file doesn't display properly in the browser, but that's ok, open it in Inkscape and Run the Seymour extension. Right click and use "Save As..." to save the SVG file on your computer.) |
|
| |
|
| == Turtle Graphics Inkscape Extension ==
| | [[Seymour (Inkscape Turtle Graphics Extension)]] |
| | |
| === Installation ===
| |
| | |
| Unzip the two files contained in: [[File:Seymour.zip]] and copy them to ~/.config/inkscape/extensions[http://wiki.inkscape.org/wiki/index.php/ScriptingHOWTO#Installing]. Restart Inkscape if necessary.
| |
| | |
| === Use ===
| |
| | |
| If properly installed, you should have a "Python" option in your Extensions menu. Select "Seymour..." to use the extension. Place your code in a text box on the SVG page and right-click the text to select "Object Properties" where you can set the id of the element to "code" (the value the Seymour extension expects).
| |
| | |
| === Summary of turtle commands ===
| |
| | |
| Some commands have multiple versions (both fd and forward do the same thing)
| |
| * fd(s), forward(s): Go forward s steps
| |
| * bk(s), backward(s): Go backward s steps
| |
| * lt(d), left(d): Turn left d degrees
| |
| * rt(d), right(d): Turn right d degrees
| |
| * pd(), pendown(): Pen down (and start recording an SVG path)
| |
| * pu(), penup(): Pen up (and draw the SVG path)
| |
| * goto (x, y): Put the turtle at exactly this position (absolute)
| |
| * face(d): Turn the turtle to face this direction (absolute)
| |
| | |
| Some extra's:
| |
| * startgroup([id]): start an SVG group element (g)
| |
| * endgroup(): end the current group element and record to the page
| |
| * rgb(r, g, b): returns a color value based on red, green, and blue values from 0-255 each.
| |
| | |
| Styles
| |
| * styles: Is a a python dictionary and can be used to add/modify SVG style values. The current styles are actually used / recorded when you perform a penup (pu) command.
| |
| | |
| example:
| |
| | |
| styles['fill'] = rgb(128, 128, 0)
| |
| | |
| === Code ===
| |
| | |
| seymour.py
| |
| <source lang="python">
| |
| #!/usr/bin/env python
| |
| #-*- coding:utf-8 -*-
| |
| """
| |
| ==|||+++__<z1*XZY++"|!`+<|=___:+!3X##ZZo3osi_. :.. -.:.-.. ........---..:.
| |
| =|>-=|i|<nqp2*}:.%><i;+|;:;<1vXXSYXmZ##mqXSoSas_=>: .._.::. ::........:...:
| |
| =||=|=<amXnc}si=s,is=; %suWB#gawaa21SSXX#XZXXXoal||iil>:=_==:. ..............
| |
| .:+<+Il*1i%=<<owX2v+=a#ZSYVY1UA#U$W#XapZenZXXdZXXmXXooa>=,_. -...... ......
| |
| ;===iz{il%viawmm#}%%JmeozvX#mmmmX#XXXXXXZZXoSXXXmX#m##Xe|+=.:. ..-.. .... ...
| |
| =|=%|i%iInnnwwdQ2nv=ZZowmWWZ##ZSv1vvlilIvvvI1II12XXXXZXoi.. . . .... .......
| |
| :={|<vnnXXmmmqW$mZvv#omQm#ZXZX11ii|||||+|=+i%}*|iivvnXmZXo>.. ......... . ..
| |
| ==|=vnoodXoX##audpod&mWW##XXo1li>+|====;=<>+==<|l=i+|iX#ZUXa;.. . .... . ..
| |
| :=%vsad#mmmB$8XXWhX#Z#B#ZZXoII|==+==;=>++:==>+====|+==|3##XXz|:... ... .. ..
| |
| .ilo21YYZ#m#D2mmDXmQm##ZZX2ov|i+||==+=:;;i+(====>|.=vnooSZZZous==.. . .. . ..
| |
| iIISo#ZZAYXoXXY!qmWWW#XXXXv%l>|====||>>+|====>|=<inXS111ldmXSX%s;+.: . ....
| |
| S1e}++sa><uenwmWWQWQWmZSSov1i|<>|=====|==:=++==ioXI*+++==+3mZ#oav;;:;... . .
| |
| X2<_owoZcv+mm$WWBmWQW&ZZe1n%iii=|++==+=;==+==||Ii+=;======<V##Xoov%;==:-.. ..
| |
| S(|oXnXX2n>$#qWW#mWBXd2vu1i%l+|=|=+=+=|==||||||+::;;=%wona>=$m#ZS1nai|=::.-..
| |
| vnvoSoXXXnXmm#ZXm##XXn21u1i>|||+<uoool||ii|||>=;:;=<oXmm1vc+)$mmX1XoXs%>;::..
| |
| oXonnSdXoXS#mq#mUX#X2on1vIiasaaoqXY*i+--+=+|=+====|vi=!"X%|==)QWQQmZZXo2s;;::.
| |
| S2XXoo3ooZSmmmm#mZZX221unoXXSX21*++=;::::;:==+:=||swauI}-;:===)QWBm#Zoonvoa,;.
| |
| voXoX2XXSoWWmmmm##ZXXXoo2XX21ii||+===+|||=||l%|=|%nvvi>==+=====]QWm##mmociI1oi_.
| |
| v3n2odX2dmmmm###ZZXZo2ood1lii|||saauoav%ioooon>===||i|=====|=;=;3mm####X2n%|i+<%
| |
| iunnXXnmXm#mBmmZZZZZX22o2vi|||i321#QZqvwwuXSnvi+=;;::=||+||===;;=VWmZmXXo2Xzi:=)
| |
| iI13voXZ#XU#mm##m#ZX2Xoonvi||voS|+!!{X11In21vI>|+==;==:======;:;:=X#W#moo2Svnvs,
| |
| vnvZoZ#UX##Z###mm#XXSooonIii|nXouvv}|++i|llIl%i|i>|++=======:::::=]WmZUXpu+lvIli
| |
| %oXoXXSX####mZWWW#mX2ooovvliiiIli|+=+====Iliiiiili|>+===;:={|>::;;<dmW##mqaini+{
| |
| vnXdXdowZXmmmmmmWW#mXXooovIl|iii|||=====ii|i|ivvvv|======;=<vva===<3m#W#m#1>"s:+
| |
| |3nXod#Sm#m#mmWmWWWmmXo2ovIii|||||+=||ii|||+<oo1li||||+=--:mmmZmo>|l#Q##WZ2c|v.=
| |
| =vnnXXZZdXmWWWmmQWmWQm#Xoonlli||||||<=+|||=;IXS||||||||;=<yQQWWUmmciXm#WmXoo|=
| |
| =onnX2SX###W#WWWWmQQQQQmXXovvii|||||+|=+;==ilvnnawmmmwaauQWWQWU#Q#GvXWWWmm#p=|:_
| |
| |1nvonXXdX####mWWQQQQQQWmZSovvliii||+++++=|i2ZmXmQWWQQWQQW#WDSm2XXudZQWWmmmXXi.
| |
| ::v2vdXXXX####W$WQWQWWWQQQmXXvvvlii||||+++<vqXm#mWWWQQB#Z|iyW#e1InXm#QWQWQZXYv1<
| |
| :<lu{oX1#X#m#$#QWWQQQQQWWW#moovvvlli|=++=vdZmWWWW#UZTmmwdQW#X1sinomBWQWWW#X1|lo
| |
| ..:vvoZln2XXmXZW##WQQWQQQQWQmmXXnvvllli||iommWWW##SqyWmm#W#XS1liivmmQWQQW#WZS|vo
| |
| -+|l3niu2oX#XZ$WmQmWWQWWQWWWWWmmXXnonvivvXXWB#B$Z###XXXXX11%{uqon#WWQW#mWZ#S2nX
| |
| :=|vnv1nSXZ2Z#WWm$QmQQQWQQQQWWmmZmXoonnoom##WXn2XnSnSonvlaumQZSommWQ#Q##XXnIvX
| |
| =<1svIvi3vdXoX##WWQ$QmQWQQQQQQQQWm#m#UXSXmXX##XXvIvIvvvoXX#WQX2XZmQWmW#XXoSu|]#
| |
| -=:-:<>dS12Z3XmWWWQZmmQQQQQQQQQQQQWmm#m#mZmm##oXvonIvv2XoZW#qmmWQQ#md##X1)na%d
| |
| :--.=iidsInvXZ#WWQWQZWQQWWQQQQWQWQWQQQWWmWBQWmQmmmoX2oooX####QWQQQQWmX2Svc+!3S
| |
| |ii%1szXvdXmmQWQQQ#QWWQQQQQWQQQQQQQQWQWmWWQWW#W#m#mQmQ#QmQWWQQQWQm#Xv==:..:
| |
| .I{vXXvnXX#$QWmW#WWBQWWWWQWQQQQQQQQQQQQWWWW#WWWmWmmQWWWWWWQQWWWmZZZzni|=:.
| |
| =|<12XzoXX#mm#X#mYS###QQWWQQQQQQQQQQQWQQQmWQmQQWW#mQWBWWQQQQW$#mweXc"i= ..
| |
| :;.:|InnnnXoXXW##XS1nX#XZ##QQQWQQWQQQQQQQmWQ$mm$WWZ#m#W$#QWW#Z#ZXS2i|-- <
| |
| """
| |
| | |
| 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()
| |
| </source>
| |
| | |
| seymour.inx
| |
| <source lang="xml">
| |
| <inkscape-extension>
| |
| <_name>Seymour</_name>
| |
| <id>org.automatist.filter.seymour</id>
| |
| <dependency type="executable" location="extensions">seymour.py</dependency>
| |
| <dependency type="executable" location="extensions">inkex.py</dependency>
| |
| <param name="codeid" type="string" _gui-text="ID of code element">code</param>
| |
| <effect>
| |
| <object-type>all</object-type>
| |
| <effects-menu>
| |
| <submenu _name="Python"/>
| |
| </effects-menu>
| |
| </effect>
| |
| <script>
| |
| <command reldir="extensions" interpreter="python">seymour.py</command>
| |
| </script>
| |
| </inkscape-extension>
| |
| </source>
| |
|
| |
|
| == Examples == | | == Examples == |