Skip to content

Instantly share code, notes, and snippets.

@kragen
Forked from Arachnid/piclang.py
Created April 30, 2012 19:30
Show Gist options
  • Save kragen/2561867 to your computer and use it in GitHub Desktop.
Save kragen/2561867 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""A DSL for making paths of interesting shapes.
Nick Johnson and Kragen Javier Sitaker
<https://gist.github.com/2555227>
"""
import math
import numbers
from PIL import Image, ImageDraw
class Curve(object):
@classmethod
def wrap(cls, o):
if isinstance(o, Curve):
return o
if callable(o):
return FunctionCurve(o)
if isinstance(o, numbers.Number):
return cls.wrap((o, o))
if isinstance(o, tuple) and len(o) == 2:
return constant(o)
raise TypeError("Expected function, number, or 2-tuple, got %r, a %r" % (o, type(o)))
def __add__(self, other):
return translate(self, other)
def __mul__(self, other):
return scale(self, other)
def __pow__(self, times):
return repeat(self, times)
def __floordiv__(self, steps):
return step(self, steps)
class FunctionCurve(Curve):
def __init__(self, func):
self.func = func
def __call__(self, t):
return self.func(t)
class TwoArgCurve(Curve):
def __init__(self, a, b):
self.a = Curve.wrap(a)
self.b = Curve.wrap(b)
def __call__(self, t):
return self.invoke(self.a(t), self.b(t))
def constant(val):
return FunctionCurve(lambda t: val)
class translate(TwoArgCurve):
def invoke(self, (ax, ay), (bx, by)):
return (ax + bx, ay + by)
class scale(TwoArgCurve):
def invoke(self, (ax, ay), (bx, by)):
return (ax * bx, ay * by)
class rotate(TwoArgCurve):
def invoke(self, (ax, ay), (bx, by)):
return (ax * bx - ay * by, ay * bx + ax * by)
class reverse(Curve):
def __init__(self, func):
self.func = func
def __call__(self, t):
return self.func(1 - t)
class concat(Curve):
def __init__(self, a, b):
self.a = Curve.wrap(a)
self.b = Curve.wrap(b)
def __call__(self, t):
if t < 0.5:
return self.a(t * 2)
else:
return self.b(t * 2 - 1)
class repeat(Curve):
def __init__(self, func, times):
self.func = func
self.times = times
def __call__(self, t):
return self.func((t * self.times) % 1)
class step(Curve):
def __init__(self, func, steps):
self.func =func
self.steps = steps
def __call__(self, t):
return self.func(math.floor(t * self.steps) / self.steps)
@FunctionCurve
def circle(t):
theta = 2 * math.pi * t
return (math.sin(theta), math.cos(theta))
@FunctionCurve
def line(t):
return (t, t)
def boustro(func, times):
return repeat(concat(func, reverse(func)), times/2.0)
def interpolate(f, points):
return [f(x/float(points)) for x in range(points)]
def render(f, points=1000):
size = 800
im = Image.new("RGB", (size, size))
draw = ImageDraw.Draw(im)
draw.line(interpolate(f * (size/2) + size/2, points), fill=(255,255,255))
im.show()
repl_doc = """
Available primitives are circle, line, reverse, concat, boustro,
rotate, numbers, 2-tuples of numbers, +, *, //, and **.
Some examples to try:
circle * line
circle * circle**20
rotate(boustro(circle * line, 30), circle//30)
circle**29 * boustro(line, 30)
circle**29 * line**30
rotate(boustro(line, 32), circle ** 5) * 0.7
circle**5 * line * circle**5 * line * circle**5 * line
circle * (1, 0) + line * (0, 1)
circle * ((circle * (1, 0) + rotate(circle, (0, 1)) * (0, 1)) ** 8 + 1.9) * (1/2.8)
circle**20 + line + (-1, -1)
circle**2 * (line * .3 + .1) + circle**50 * (line * .05 + .05)
circle * (0, 1) // 20 * (line * 2 + -1) ** 20 + line * (1, 0)
circle ** 5 * (0, 1) + circle ** 3 * (1, 0)
"""
def repl():
import sys
print repl_doc,
while True:
print u"€",
try:
input_line = raw_input()
except EOFError:
print
return
try:
formula = eval(input_line)
except:
_, exc, _ = sys.exc_info()
print exc
else:
render(formula, 4000)
if __name__ == '__main__':
repl()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment