Skip to content

Instantly share code, notes, and snippets.

@roger-

roger-/tikz.py Secret

Last active December 18, 2015 20:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roger-/53910e115d46eca4d4ce to your computer and use it in GitHub Desktop.
Save roger-/53910e115d46eca4d4ce to your computer and use it in GitHub Desktop.
Simple Python -> TikZ wrapper.
# pytikz v0.12
# A simple Python -> TikZ wrapper.
# See main() for sample usage.
#
# 2013, -Roger
from contextlib import contextmanager
__all__ = ['ra', 'Picture']
# change this to use spaces instead of tabs
TAB = '\t'
def ra(radius, angle_deg):
return '%s:%s' % (angle_deg, radius)
def format_pos(pos, coord='abs'):
if isinstance(pos, complex):
pos = (pos.real, pos.imag)
elif isinstance(pos, str):
if pos != 'cycle':
pos = '(%s)' % pos
if coord in ('rel', 'relative'):
coord_type = '+'
elif coord in ('inc', 'increment', 'incremental'):
coord_type = '++'
else:
coord_type = ''
return coord_type + str(pos)
class Path(object):
def __init__(self, style=''):
self.style = style
c = '\\path[%s]' % (self.style)
self.contents = [c]
def add_command(self, cmd, to=False):
# add command to new line if it changes the current path
if to:
self.contents.append('\t' + cmd)
else:
self.contents[-1] += '\t' + cmd
def at(self, pos, name='', coord='abs'):
c = format_pos(pos, coord)
self.add_command(c, to=True)
if name:
self.coord(name=name)
return self
def coord(self, style='', name=''):
c = 'coordinate[%s] (%s)' % (style, name)
self.add_command(c, to=False)
return self
def line_to(self, pos, name='', coord='abs'):
c = '-- %s' % format_pos(pos, coord)
self.add_command(c, to=True)
if name:
self.coord(name=name)
return self
def to(self, pos, style='', name='', coord='abs'):
c = 'to[%s] %s' % (style, format_pos(pos, coord))
self.add_command(c, to=True)
if name:
self.coord(name=name)
return self
def spline_to(self, pos, control1, control2=None, name='', coord='abs'):
if not control2:
control2 = control1
c = '.. controls %s and %s .. %s' % (format_pos(control1, coord), \
format_pos(control2, coord), \
format_pos(pos, coord))
self.add_command(c, to=True)
if name:
self.coord(name=name)
return self
def node(self, text='', style='', name=''):
c = 'node[%s] (%s) {%s}' % (style, name, text)
self.add_command(c, to=False)
return self
def circle(self, radius):
c = 'circle (%s)' % radius
self.add_command(c, to=False)
return self
def arc_to(self, params, coord='abs'):
c = 'arc (%s)' % params
self.add_command(c, to=True)
return self
def rect_to(self, pos, coord='abs'):
c = 'rectangle %s' % format_pos(pos, coord)
self.add_command(c, to=True)
return self
def grid_to(self, pos, coord='abs'):
c = 'grid %s' % format_pos(pos, coord)
self.add_command(c, to=True)
return self
def make(self):
# end current statement
self.contents[-1] += ';'
return self.contents
class Picture(object):
def __init__(self, style=''):
self.style = style
self.contents = []
def add_command(self, text):
self.contents.append('\t' + text)
@contextmanager
def path(self, style='draw'):
path = Path(style)
yield path
c = path.make()
# fit short paths on one line
if len(c) <= 3:
c = [''.join(c).replace('\n', ' ')]
for line in c:
self.add_command(line)
@contextmanager
def new_scope(self, style=''):
scope = Picture(self.style)
yield scope
c = scope.contents
self.add_command('\\begin{scope}[%s]' % style)
for line in c:
self.add_command('\t' + line)
self.add_command('\\end{scope}')
def set_coord(self, pos, options='', name='', coord='abs'):
cmd = '\coordinate[%s] (%s) at %s;' % (options, name, format_pos(pos, coord))
self.add_command(cmd)
def set_node(self, pos, text='', options='', name='', coord='abs'):
cmd = '\node[%s] (%s) at %s {%s};' % (options, name, format_pos(pos, coord), text)
self.add_command(cmd)
def __setitem__(self, item, value):
self.set_coord(pos=value, name=item)
def make(self):
c = '\\begin{tikzpicture}[%s]\n' % (self.style)
c += '\n'.join(self.contents)
c += '\n\\end{tikzpicture}'
# use another character(s) instead of TAB
c = c.replace('\t', TAB)
return c
def main():
pic = Picture()
pic['pointA'] = 5 + 0j
pic['pointB'] = '45:5'
pointC = 3 + 1j
with pic.path('draw') as draw:
draw.at(0 + 0j, name='start').grid_to(6 + 6j)
with pic.path('fill, color=red') as draw:
draw.at('start')\
.line_to('pointA', coord='relative')\
.line_to('pointB').node('hello', style='above, black')\
.line_to(pointC)\
.spline_to(5 + 1j, 2 + 5j).node('spline')\
.line_to('cycle')
with pic.path('fill, color=blue') as draw:
draw.at(pointC).circle(0.3)
print pic.make()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment