Skip to content

Instantly share code, notes, and snippets.

@louisswarren
Last active March 4, 2021 01:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save louisswarren/29aeab8028cd97444475314cf394b421 to your computer and use it in GitHub Desktop.
Save louisswarren/29aeab8028cd97444475314cf394b421 to your computer and use it in GitHub Desktop.
Svg files from python
from collections import defaultdict
import sys
from svg import *
def log(*a, **k):
print(*a, **k, file=sys.stderr)
ROWS = 64
COLS = ROWS*2-1
BOXSIZE = 10
IMG_HEIGHT = 2000
IMG_WIDTH = IMG_HEIGHT / (ROWS * BOXSIZE + 1) * (COLS * BOXSIZE + 1)
# Basic header
svg_header()
svg_open(width=IMG_WIDTH, height=IMG_HEIGHT,
x_bias=-BOXSIZE*COLS/2, y_bias=-1,
xw=COLS*BOXSIZE+2, yh=ROWS*BOXSIZE+2)
def draw_cell(row, col, value=None):
fill = "grey" if value else "white"
svg_rect((BOXSIZE*(col-0.5), BOXSIZE*row), BOXSIZE, BOXSIZE,
fill=fill, stroke="black")
if len(sys.argv) > 1:
rule = int(sys.argv[1])
else:
rule = 30
cells = defaultdict(lambda: False)
cells[(0,0)] = True
draw_cell(0, 0, cells[(0,0)])
for j in range(1, ROWS):
for i in range(-j, j+1):
prev = 4 * cells[(j-1, i-1)]
prev += 2 * cells[(j-1, i )]
prev += 1 * cells[(j-1, i+1)]
cells[(j,i)] = rule & (1 << prev) != 0
draw_cell(j, i, cells[(j,i)])
# Footer
svg_close()
n = 24
$(n).mp4: stringart.py
$(MAKE) -j 4 $$(for i in $$(seq -w 1 $n); do echo -n "$n-$$i.png "; done)
ffmpeg -r 2 -f image2 -s 800x800 -i $(n)-%02d.png -vcodec libx264 -crf 25 -pix_fmt yuv420p $@
%.png: %.svg
convert $^ $@
$(n)-%.svg: stringart.py
python3 $^ $(n) $* > $@
$(n).svg: stringart.py
python3 $^ > $@
.PHONY: clean
clean:
rm -f *.png *.svg *.mp4
import math
import sys
from svg import *
def log(*a, **k):
print(*a, **k, file=sys.stderr)
def point_to_tick(point, s):
p1 = (point[0] * (1+s), point[1] * (1+s))
p2 = (point[0] * (1-s), point[1] * (1-s))
return p1, p2
def circle(n, s=1, r=1):
for i in range(0, n+1):
t = 2 * math.pi * i * s / n
yield r*math.sin(-t), -r*math.cos(t)
if len(sys.argv) >= 2:
ticks = int(sys.argv[1])
else:
ticks = 24
# Basic header
svg_header()
svg_open(width=800, height=800, x_bias=-110, y_bias=-110, xw=220, yh=220)
# Outer circle
svg_circle((0, 0), 100, fill='none', stroke='black')
# Tick marks
for point in circle(ticks, r=100):
print(svg_line(*point_to_tick(point, 0.03), stroke='black'))
if len(sys.argv) >= 3:
circ = tuple(circle(ticks, int(sys.argv[2]), 100))
svg_poly(*circ[1:], stroke='red', fill='none')
svg_line(*circ[:2], stroke='blue')
else:
# Draw all non-generating cycles
for k in range(2, ticks, 2):
for j in range(2, k+1):
if (ticks % j) == (k % j) == 0:
svg_poly(*circle(ticks, k, 100), stroke='red', fill='none')
log(k)
break
# Footer
svg_close()
def xml(tag, _xml_tag_is_a_singleton=True, **options):
s = f'<{tag}'
kw_attrib = lambda x: x.replace('_', '-')
if options:
s += ' '
s += ' '.join(f'{kw_attrib(k)}="{str(v)}"' for k, v in options.items())
if _xml_tag_is_a_singleton:
s += ' />'
else:
s += '>'
print(s)
def xml_open(*args, **kwargs):
xml(*args, **kwargs, _xml_tag_is_a_singleton=False)
def xml_close(tag):
print(f'</{tag}>')
def svg_header():
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
def svg_open(width, height, x_bias, y_bias, xw, yh):
vb = f'{x_bias} {y_bias} {xw} {yh}'
ns = 'http://www.w3.org/2000/svg'
xml_open('svg', width=width, height=height, viewBox=vb, xmlns=ns)
def svg_close():
xml_close('svg')
def svg_poly(*points, **opts):
point_str = ' '.join(f'{x},{y}' for x, y in points)
xml('polyline', points=point_str, **opts)
def svg_circle(point, radius, **opts):
xml('circle', cx=point[0], cy=point[1], r=radius, **opts)
def svg_line(p1, p2, **opts):
xml('line', x1=p1[0], y1=p1[1], x2=p2[0], y2=p2[1], **opts)
def svg_rect(p, width, height, x_radius=0, y_radius=0, **opts):
xml('rect', x=p[0], y=p[1], width=width, height=height,
rx=x_radius, ry=y_radius, **opts)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment