Compare commits
10 Commits
60def75465
...
89ed6011fc
Author | SHA1 | Date |
---|---|---|
|
89ed6011fc | |
|
a5a12f0163 | |
|
bb32105061 | |
|
fbf7f5bdca | |
|
1569f75fd1 | |
|
aa12c04186 | |
|
3ab0e05d2f | |
|
8a8c98a113 | |
|
e65e935f7b | |
|
84cb2f5b32 |
|
@ -32,7 +32,10 @@ class Drawing(object):
|
|||
def loads(cls, data):
|
||||
paths = []
|
||||
for line in data.split('\n'):
|
||||
path = line.strip().split()
|
||||
line = line.strip()
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
path = line.split()
|
||||
path = [tuple(map(float, x.split(','))) for x in path]
|
||||
path = expand_quadratics(path)
|
||||
if path:
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import axi
|
||||
|
||||
W, H = 14, 11
|
||||
BOUNDS = axi.A3_BOUNDS
|
||||
|
||||
def main():
|
||||
paths = [
|
||||
[(0, 0), (W, 0), (W, H), (0, H), (0, 0)]
|
||||
]
|
||||
d = axi.Drawing(paths)
|
||||
d = d.center(*BOUNDS[-2:])
|
||||
d.dump('box.axi')
|
||||
d.render(bounds=BOUNDS).write_to_png('box.png')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,56 @@
|
|||
import axi
|
||||
import math
|
||||
import random
|
||||
|
||||
W, H = axi.A3_SIZE
|
||||
|
||||
def circle_ray_intersection(cx, cy, cr, ox, oy, dx, dy):
|
||||
xd = ox - cx
|
||||
yd = oy - cy
|
||||
a = dx * dx + dy * dy
|
||||
b = 2 * (dx * (ox - cx) + dy * (oy - cy))
|
||||
c = xd * xd + yd * yd - cr * cr
|
||||
d = b * b - 4 * a * c
|
||||
if d < 0:
|
||||
return None
|
||||
t = (-b + math.sqrt(d)) / (2 * a)
|
||||
x = ox + dx * t
|
||||
y = oy + dy * t
|
||||
return (x, y)
|
||||
|
||||
def path(x0, y0, r0, x1, y1, r1):
|
||||
t = random.random()
|
||||
a0 = random.random() * 2 * math.pi
|
||||
a1 = a0 + math.radians(10)
|
||||
n = 100
|
||||
result = []
|
||||
for i in range(n + 1):
|
||||
u = i / n
|
||||
a = a0 + (a1 - a0) * u
|
||||
dx = math.cos(a)
|
||||
dy = math.sin(a)
|
||||
ax, ay = circle_ray_intersection(x0, y0, r0, x0, y0, dx, dy)
|
||||
bx, by = circle_ray_intersection(x1, y1, r1, x0, y0, dx, dy)
|
||||
x = ax + (bx - ax) * t
|
||||
y = ay + (by - ay) * t
|
||||
result.append((x, y))
|
||||
return result
|
||||
|
||||
def main():
|
||||
x0 = 0
|
||||
y0 = 0
|
||||
r0 = 1.5
|
||||
x1 = -0.25
|
||||
y1 = 0
|
||||
r1 = 2
|
||||
paths = []
|
||||
for i in range(500):
|
||||
paths.append(path(x0, y0, r0, x1, y1, r1))
|
||||
d = axi.Drawing(paths).rotate_and_scale_to_fit(W, H)#.sort_paths()
|
||||
im = d.render()
|
||||
im.write_to_png('circles_offset.png')
|
||||
d.dump('circles_offset.axi')
|
||||
# axi.draw(d)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,28 @@
|
|||
from shapely import geometry
|
||||
import axi
|
||||
import sys
|
||||
|
||||
def main():
|
||||
size = axi.A3_SIZE
|
||||
bounds = axi.A3_BOUNDS
|
||||
d = axi.Drawing.load(sys.argv[1])
|
||||
print(len(d.paths[0]))
|
||||
d = d.scale_to_fit(*size).center(*size)
|
||||
d = d.simplify_paths(0.01 / 25.4)
|
||||
print(len(d.paths[0]))
|
||||
|
||||
g = geometry.Polygon(d.paths[0])
|
||||
while True:
|
||||
b = -0.25 / 25.4
|
||||
g = g.buffer(b)
|
||||
if g.is_empty:
|
||||
break
|
||||
g = g.simplify(0.01 / 25.4)
|
||||
d.paths.extend(axi.shapely_to_paths(g))
|
||||
|
||||
print(d.bounds)
|
||||
d.dump('out.axi')
|
||||
d.render(bounds=bounds, line_width=0.2/25.4).write_to_png('out.png')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,75 @@
|
|||
from math import *
|
||||
from shapely import geometry, ops
|
||||
import axi
|
||||
|
||||
W = 6
|
||||
H = 8
|
||||
BOUNDS = (0, 0, W, H)
|
||||
|
||||
BIT_RADIUS = 0.125
|
||||
|
||||
def regular_polygon(n, x, y, r):
|
||||
points = []
|
||||
for i in range(n):
|
||||
t = 2 * pi / n * i
|
||||
points.append((x + r * cos(t), y + r * sin(t)))
|
||||
points.append(points[0])
|
||||
return points
|
||||
|
||||
def polygon_splits(n, x, y, r, b):
|
||||
lines = []
|
||||
for i in range(n):
|
||||
t = 2 * pi / n * (i + 0.5)
|
||||
lines.append([(x, y), (x + r * cos(t), y + r * sin(t))])
|
||||
return geometry.MultiLineString(lines).buffer(b)
|
||||
|
||||
def polygon(n, r, br, notch=False):
|
||||
p = regular_polygon(n, 0, 0, r)
|
||||
g = geometry.Polygon(p)
|
||||
g = g.buffer(br).exterior
|
||||
if notch:
|
||||
g = g.difference(polygon_splits(n, 0, 0, r * 2, br * 2))
|
||||
g = ops.linemerge(g)
|
||||
p = axi.shapely_to_paths(g)
|
||||
return axi.Drawing(p).origin()
|
||||
|
||||
def drawings_to_gcode(ds, zs, z0, f):
|
||||
lines = []
|
||||
lines.append('G90') # absolute coordinates
|
||||
lines.append('G20') # inches
|
||||
lines.append('G0 Z%g' % z0) # jog to z0
|
||||
lines.append('M4') # turn on router
|
||||
lines.append('G4 P2.0') # dwell for N seconds
|
||||
lines.append('F%g' % f) # set feed rate (inches per minute)
|
||||
for d, z in zip(ds, zs):
|
||||
for path in d.paths:
|
||||
# jog to first point
|
||||
x, y = path[0]
|
||||
lines.append('G0 X%g Y%g' % (x, y))
|
||||
# move z down
|
||||
lines.append('G1 Z%g' % z)
|
||||
# draw path
|
||||
for x, y in path[1:]:
|
||||
lines.append('G1 X%g Y%g' % (x, y))
|
||||
# move z up
|
||||
lines.append('G1 Z%g' % z0)
|
||||
lines.append('M8')
|
||||
lines.append('G0 X0 Y0')
|
||||
return '\n'.join(lines)
|
||||
|
||||
def main():
|
||||
d0 = polygon(3, 3 / sqrt(3), BIT_RADIUS, False)
|
||||
d1 = polygon(3, 3 / sqrt(3), BIT_RADIUS, True)
|
||||
|
||||
ds = [d0, d0, d1]
|
||||
ds = [d.translate(1, 1) for d in ds]
|
||||
zs = [-0.25, -0.5, -0.75]
|
||||
|
||||
g = drawings_to_gcode(ds, zs, 0.5, 30)
|
||||
print(g)
|
||||
|
||||
for i, (d, z) in enumerate(zip(ds, zs)):
|
||||
d.render(bounds=BOUNDS).write_to_png('z%g.png' % z)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,20 +1,24 @@
|
|||
from __future__ import division
|
||||
from __future__ import division, print_function
|
||||
|
||||
import axi
|
||||
import numpy as np
|
||||
import os
|
||||
import sys
|
||||
|
||||
NUMBER = '?'
|
||||
TITLE = 'Five Seconds of Donkey Kong'
|
||||
|
||||
W, H = 11-2, 14-2
|
||||
DW, DH = axi.A3_SIZE
|
||||
|
||||
NUMBER = '48'
|
||||
TITLE = 'Fifteen Seconds of The Legend of Zelda'
|
||||
LABEL = '#%s' % NUMBER
|
||||
|
||||
COLUMNS = 6
|
||||
SECONDS = 5
|
||||
FRAME_OFFSET = 0
|
||||
MIN_CHANGES = 1
|
||||
COLUMNS = 8
|
||||
SECONDS = 15
|
||||
FRAME_OFFSET = 900
|
||||
MIN_CHANGES = 2
|
||||
UNIQUE = False
|
||||
SIMPLIFY = 0
|
||||
SIMPLIFY = 5
|
||||
|
||||
def simplify_sparkline(values, n):
|
||||
if not n:
|
||||
|
@ -52,11 +56,11 @@ def title():
|
|||
d = d.join_paths(0.01)
|
||||
return d
|
||||
|
||||
def label():
|
||||
def label(x, y):
|
||||
d = axi.Drawing(axi.text(LABEL, axi.FUTURAL))
|
||||
d = d.scale_to_fit_height(0.125)
|
||||
d = d.rotate(-90)
|
||||
d = d.move(12, 8.5, 1, 1)
|
||||
d = d.move(x, y, 1, 1)
|
||||
d = d.join_paths(0.01)
|
||||
return d
|
||||
|
||||
|
@ -72,9 +76,9 @@ def main():
|
|||
lines = filter(None, lines)
|
||||
|
||||
# read values and transpose
|
||||
data = [map(int, line.split(',')) for line in lines]
|
||||
data = [tuple(map(int, line.split(','))) for line in lines]
|
||||
data = np.transpose(data)
|
||||
print '%d series in file' % len(data)
|
||||
print('%d series in file' % len(data))
|
||||
|
||||
# trim to SECONDS worth of data
|
||||
n = len(data[0])
|
||||
|
@ -86,7 +90,7 @@ def main():
|
|||
|
||||
# remove addresses with too few values
|
||||
data = [x for x in data if len(set(x)) > MIN_CHANGES]
|
||||
print '%d series that changed' % len(data)
|
||||
print('%d series that changed' % len(data))
|
||||
|
||||
# remove duplicate series
|
||||
if UNIQUE:
|
||||
|
@ -99,16 +103,16 @@ def main():
|
|||
seen.add(k)
|
||||
new_data.append(x)
|
||||
data = new_data
|
||||
print '%d unique series' % len(data)
|
||||
print('%d unique series' % len(data))
|
||||
|
||||
# delete repetitive stuff
|
||||
# del data[136:136+8*14]
|
||||
del data[136:136+8*14]
|
||||
|
||||
# trim so all rows are full
|
||||
data = data[:int((len(data) // COLUMNS) * COLUMNS)]
|
||||
print '%d series after trimming' % len(data)
|
||||
print('%d series after trimming' % len(data))
|
||||
|
||||
print '%d data points each' % len(data[0])
|
||||
print('%d data points each' % len(data[0]))
|
||||
|
||||
# create sparklines in a grid pattern
|
||||
paths = []
|
||||
|
@ -128,16 +132,21 @@ def main():
|
|||
y = 1 - value + r * 1.5
|
||||
path.append((x, y))
|
||||
paths.append(path)
|
||||
|
||||
d = axi.Drawing(paths)
|
||||
|
||||
# add title and label and fit to page
|
||||
d = d.scale(8.5 / d.width, (12 - 0.5) / d.height)
|
||||
d = d.scale(W / d.width, (H - 0.5) / d.height)
|
||||
d = stack_drawings([d, title()], 0.25)
|
||||
d = d.rotate(-90)
|
||||
d = d.center(12, 8.5)
|
||||
d.add(label())
|
||||
d = d.center(DW, DH)
|
||||
_, _, lx, ly = d.bounds
|
||||
d.add(label(lx, ly))
|
||||
|
||||
print d.bounds
|
||||
d = d.simplify_paths(0.001)
|
||||
|
||||
print(d.bounds)
|
||||
print(d.size)
|
||||
|
||||
# save outputs
|
||||
dirname = 'nes/%s' % NUMBER
|
||||
|
@ -146,11 +155,10 @@ def main():
|
|||
except Exception:
|
||||
pass
|
||||
d.dump(os.path.join(dirname, 'out.axi'))
|
||||
rotated = d.rotate(90).center(8.5, 12)
|
||||
rotated = d.rotate(90).center(DH, DW)
|
||||
rotated.dump_svg(os.path.join(dirname, 'out.svg'))
|
||||
im = rotated.render(
|
||||
scale=109 * 1, line_width=0.3/25.4,
|
||||
show_axi_bounds=False, use_axi_bounds=False)
|
||||
x0, y0, x1, y1 = rotated.bounds
|
||||
im = rotated.render(bounds=(x0 - 1, y0 - 1, x1 + 1, y1 + 1))
|
||||
im.write_to_png(os.path.join(dirname, 'out.png'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
// D = math.Pi
|
||||
S = 2
|
||||
)
|
||||
|
||||
type Point struct {
|
||||
X, Y float64
|
||||
}
|
||||
|
||||
func (a Point) Lerp(b Point, t float64) Point {
|
||||
x := a.X + (b.X-a.X)*t
|
||||
y := a.Y + (b.Y-a.Y)*t
|
||||
return Point{x, y}
|
||||
}
|
||||
|
||||
type Segment struct {
|
||||
P0, P1 Point
|
||||
}
|
||||
|
||||
type Circle struct {
|
||||
X, Y, R float64
|
||||
}
|
||||
|
||||
func (c Circle) ContainsPoint(p Point) bool {
|
||||
return math.Hypot(p.X-c.X, p.Y-c.Y) < c.R
|
||||
}
|
||||
|
||||
func (c Circle) Discretize(n int) []Point {
|
||||
points := make([]Point, n)
|
||||
for i := range points {
|
||||
t := float64(i) / float64(n-1)
|
||||
a := 2 * math.Pi * t
|
||||
x := math.Cos(a)*c.R + c.X
|
||||
y := math.Sin(a)*c.R + c.Y
|
||||
points[i] = Point{x, y}
|
||||
}
|
||||
|
||||
return points
|
||||
}
|
||||
|
||||
func (c Circle) IntersectLine(p0, p1 Point) (float64, float64, bool) {
|
||||
dx := p1.X - p0.X
|
||||
dy := p1.Y - p0.Y
|
||||
A := dx*dx + dy*dy
|
||||
B := 2 * (dx*(p0.X-c.X) + dy*(p0.Y-c.Y))
|
||||
C := (p0.X-c.X)*(p0.X-c.X) + (p0.Y-c.Y)*(p0.Y-c.Y) - c.R*c.R
|
||||
det := B*B - 4*A*C
|
||||
if A <= 0 || det <= 0 {
|
||||
return 0, 0, false
|
||||
}
|
||||
t0 := (-B + math.Sqrt(det)) / (2 * A)
|
||||
t1 := (-B - math.Sqrt(det)) / (2 * A)
|
||||
return t0, t1, true
|
||||
}
|
||||
|
||||
func makeCircles(circleRadius, visibleRadius float64) []Circle {
|
||||
var circles []Circle
|
||||
a := int(math.Ceil(circleRadius + visibleRadius))
|
||||
for y := -a; y <= a; y++ {
|
||||
for x := -a; x <= a; x++ {
|
||||
cx := float64(x)
|
||||
cy := float64(y)
|
||||
if math.Hypot(cx, cy) <= circleRadius+visibleRadius {
|
||||
circles = append(circles, Circle{cx, cy, circleRadius})
|
||||
}
|
||||
}
|
||||
}
|
||||
return circles
|
||||
}
|
||||
|
||||
func count(circles []Circle, p Point) int {
|
||||
var result int
|
||||
for _, c := range circles {
|
||||
if c.ContainsPoint(p) {
|
||||
result++
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type splitFunc func(Point) bool
|
||||
|
||||
func split(circles []Circle, p0, p1 Point, f splitFunc) []Segment {
|
||||
var ts []float64
|
||||
for _, c := range circles {
|
||||
t0, t1, ok := c.IntersectLine(p0, p1)
|
||||
if ok {
|
||||
ts = append(ts, t0)
|
||||
ts = append(ts, t1)
|
||||
}
|
||||
}
|
||||
sort.Float64s(ts)
|
||||
var segments []Segment
|
||||
for i := 1; i < len(ts); i++ {
|
||||
t0 := ts[i-1]
|
||||
t1 := ts[i]
|
||||
if t1 < 0 || t0 > 1 {
|
||||
continue
|
||||
}
|
||||
t0 = math.Max(t0, 0)
|
||||
t1 = math.Min(t1, 1)
|
||||
t := (t0 + t1) / 2
|
||||
p := p0.Lerp(p1, t)
|
||||
if f(p) {
|
||||
q0 := p0.Lerp(p1, t0)
|
||||
q1 := p0.Lerp(p1, t1)
|
||||
segments = append(segments, Segment{q0, q1})
|
||||
}
|
||||
}
|
||||
return segments
|
||||
}
|
||||
|
||||
func run(path string, d, s float64) error {
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
circles := makeCircles(d/2, s*math.Sqrt(2))
|
||||
|
||||
x0 := -s
|
||||
x1 := s
|
||||
y0 := -s
|
||||
y1 := s
|
||||
|
||||
f := func(p Point) bool {
|
||||
return count(circles, p)%2 == 1
|
||||
}
|
||||
|
||||
g := func(p Point) bool {
|
||||
return math.Hypot(p.X, p.Y) <= s
|
||||
}
|
||||
outer := []Circle{Circle{0, 0, s}}
|
||||
|
||||
const n = 200
|
||||
for i := 0; i <= n; i++ {
|
||||
t := float64(i) / float64(n)
|
||||
y := y0 + (y1-y0)*t
|
||||
p0 := Point{x0, y}
|
||||
p1 := Point{x1, y}
|
||||
segments := split(circles, p0, p1, f)
|
||||
for _, s := range segments {
|
||||
clipped := split(outer, s.P0, s.P1, g)
|
||||
for _, cs := range clipped {
|
||||
fmt.Fprintf(file, "%g,%g %g,%g\n", cs.P0.X, cs.P0.Y, cs.P1.X, cs.P1.Y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range circles {
|
||||
points := c.Discretize(360)
|
||||
for i := 1; i < len(points); i++ {
|
||||
p0 := points[i-1]
|
||||
p1 := points[i]
|
||||
clipped := split(outer, p0, p1, g)
|
||||
for _, cs := range clipped {
|
||||
fmt.Fprintf(file, "%g,%g %g,%g\n", cs.P0.X, cs.P0.Y, cs.P1.X, cs.P1.Y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
points := outer[0].Discretize(360)
|
||||
for _, p := range points {
|
||||
fmt.Fprintf(file, "%g,%g ", p.X, p.Y)
|
||||
}
|
||||
fmt.Fprintf(file, "\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
d0 := 1.0
|
||||
d1 := math.Pi
|
||||
n := 48
|
||||
for i := 0; i < n; i++ {
|
||||
t := float64(i) / float64(n-1)
|
||||
d := d0 + (d1-d0)*t
|
||||
path := fmt.Sprintf("overlapping_circles/%.8f.axi", d)
|
||||
fmt.Println(path)
|
||||
run(path, d, S)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import axi
|
||||
import os
|
||||
|
||||
N_PER_ROW = 8
|
||||
SPACING = 2
|
||||
|
||||
def load(path):
|
||||
d = axi.Drawing.load(path)
|
||||
d = d.scale_to_fit(1.9, 1.9)
|
||||
d = d.join_paths(0.5 / 25.4)
|
||||
d = d.sort_paths()
|
||||
d = d.join_paths(0.5 / 25.4)
|
||||
d = d.origin()
|
||||
return d
|
||||
|
||||
def main():
|
||||
dirname = 'overlapping_circles'
|
||||
i = 0
|
||||
j = 0
|
||||
x = 0
|
||||
y = 0
|
||||
drawing = axi.Drawing([])
|
||||
for filename in sorted(os.listdir(dirname)):
|
||||
if not filename.endswith('.axi'):
|
||||
continue
|
||||
path = os.path.join(dirname, filename)
|
||||
print(path)
|
||||
d = load(path)
|
||||
d = d.translate(x, y)
|
||||
drawing.add(d)
|
||||
x += SPACING
|
||||
i += 1
|
||||
if i == N_PER_ROW:
|
||||
i = 0
|
||||
j += 1
|
||||
x = 0
|
||||
if j % 2:
|
||||
x = SPACING / 2
|
||||
y += SPACING * 0.866
|
||||
d = drawing
|
||||
d = d.center(*axi.A3_SIZE)
|
||||
print(len(d.paths))
|
||||
im = d.render(bounds=axi.A3_BOUNDS, line_width = 0.4 / 25.4)
|
||||
im.write_to_png('overlapping_circles.png')
|
||||
d.dump('overlapping_circles.axi')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,152 @@
|
|||
from collections import *
|
||||
from math import *
|
||||
import axi
|
||||
import fileinput
|
||||
|
||||
BOUNDS = axi.A3_BOUNDS
|
||||
X, Y, W, H = BOUNDS
|
||||
P = 0.25
|
||||
R = 0.125
|
||||
COLS = 16
|
||||
ROWS = 16
|
||||
N = ROWS * COLS
|
||||
|
||||
def rectangle(x, y, w, h):
|
||||
return [
|
||||
(x, y),
|
||||
(x + w, y),
|
||||
(x + w, y + h),
|
||||
(x, y + h),
|
||||
(x, y),
|
||||
]
|
||||
|
||||
def padded_rectangle(x, y, w, h, p):
|
||||
x += p
|
||||
y += p
|
||||
w -= p * 2
|
||||
h -= p * 2
|
||||
return rectangle(x, y, w, h)
|
||||
|
||||
def arc(cx, cy, r, a0, a1, n):
|
||||
path = []
|
||||
for i in range(n+1):
|
||||
t = i / n
|
||||
a = a0 + (a1 - a0) * t
|
||||
x = cx + r * cos(a)
|
||||
y = cy + r * sin(a)
|
||||
path.append((x, y))
|
||||
return path
|
||||
|
||||
def rounded_rectangle(x, y, w, h, r):
|
||||
n = 18
|
||||
x0, x1, x2, x3 = x, x + r, x + w - r, x + w
|
||||
y0, y1, y2, y3 = y, y + r, y + h - r, y + h
|
||||
path = []
|
||||
path.extend([(x1, y0), (x2, y0)])
|
||||
path.extend(arc(x2, y1, r, radians(270), radians(360), n))
|
||||
path.extend([(x3, y1), (x3, y2)])
|
||||
path.extend(arc(x2, y2, r, radians(0), radians(90), n))
|
||||
path.extend([(x2, y3), (x1, y3)])
|
||||
path.extend(arc(x1, y2, r, radians(90), radians(180), n))
|
||||
path.extend([(x0, y2), (x0, y1)])
|
||||
path.extend(arc(x1, y1, r, radians(180), radians(270), n))
|
||||
return path
|
||||
|
||||
def padded_rounded_rectangle(x, y, w, h, r, p):
|
||||
x += p
|
||||
y += p
|
||||
w -= p * 2
|
||||
h -= p * 2
|
||||
return rounded_rectangle(x, y, w, h, r)
|
||||
|
||||
def wall(x, y):
|
||||
return [arc(x+0.5, y+0.5, 0.333, 0, 2*pi, 72)]
|
||||
x0 = x + P
|
||||
y0 = y + P
|
||||
x1 = x + 1 - P
|
||||
y1 = y + 1 - P
|
||||
paths = [rectangle(x0, y0, x1 - x0, y1 - y0)]
|
||||
paths.append([(x0, y0), (x1, y1)])
|
||||
paths.append([(x0, y1), (x1, y0)])
|
||||
return paths
|
||||
|
||||
def xy(i):
|
||||
x = i % 6
|
||||
y = i // 6
|
||||
return (x, y)
|
||||
|
||||
def desc_paths(desc):
|
||||
paths = []
|
||||
lookup = defaultdict(list)
|
||||
for i, c in enumerate(desc):
|
||||
lookup[c].append(i)
|
||||
for c in sorted(lookup):
|
||||
ps = lookup[c]
|
||||
if c == 'o':
|
||||
continue
|
||||
elif c == 'x':
|
||||
for i in ps:
|
||||
x, y = xy(i)
|
||||
paths.extend(wall(x, y))
|
||||
else:
|
||||
stride = ps[1] - ps[0]
|
||||
i0 = ps[0]
|
||||
i1 = ps[-1]
|
||||
x0, y0 = xy(i0)
|
||||
x1, y1 = xy(i1)
|
||||
dx = x1 - x0
|
||||
dy = y1 - y0
|
||||
# paths.append(padded_rounded_rectangle(x0, y0, dx + 1, dy + 1, R, P))
|
||||
if c == 'A':
|
||||
paths.append(padded_rectangle(x0, y0, dx + 1, dy + 1, 0.25))
|
||||
else:
|
||||
paths.append([(x0 + 0.5, y0 + 0.5), (x0 + dx + 0.5, y0 + dy + 0.5)])
|
||||
# if c == 'A':
|
||||
# if stride > 1:
|
||||
if len(ps) == 3:
|
||||
# if stride == 1:
|
||||
# if len(ps) == 2:
|
||||
# if False:
|
||||
paths.append(padded_rectangle(x0, y0, dx + 1, dy + 1, 0.35))
|
||||
# s = 0.1
|
||||
# p = P + s
|
||||
# while p < 0.5:
|
||||
# paths.append(padded_rounded_rectangle(x0, y0, dx + 1, dy + 1, R, p))
|
||||
# p += s
|
||||
return paths
|
||||
|
||||
def main():
|
||||
drawing = axi.Drawing()
|
||||
font = axi.Font(axi.FUTURAL, 12)
|
||||
n = 0
|
||||
for line in fileinput.input():
|
||||
fields = line.strip().split()
|
||||
desc = fields[1]
|
||||
moves = int(fields[0])
|
||||
# if 'x' in desc:
|
||||
# continue
|
||||
paths = desc_paths(desc)
|
||||
d = axi.Drawing(paths)
|
||||
i = n % COLS
|
||||
j = n // COLS
|
||||
d = d.translate(i * 8, j * 10)
|
||||
drawing.add(d)
|
||||
|
||||
d = font.wrap(str(moves), 10)
|
||||
d = font.wrap(bin(moves)[2:].replace('1', '\\').replace('0', '/'), 10)
|
||||
# d = d.scale(0.1, 0.1)
|
||||
d = d.scale_to_fit_height(1)
|
||||
d = d.move(i * 8 + 3, j * 10 + 6.5, 0.5, 0)
|
||||
drawing.add(d)
|
||||
|
||||
n += 1
|
||||
if n == N:
|
||||
break
|
||||
# d = axi.Drawing(paths)
|
||||
d = drawing
|
||||
d = d.rotate_and_scale_to_fit(W, H, step=90)
|
||||
d.dump('rush.axi')
|
||||
d.render(bounds=None, show_bounds=False, scale=300).write_to_png('rush.png')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,4 +1,7 @@
|
|||
cairocffi
|
||||
pyserial
|
||||
pyhull
|
||||
Shapely
|
||||
cairocffi==1.2.0
|
||||
cffi==1.14.4
|
||||
numpy==1.19.5
|
||||
pycparser==2.20
|
||||
pyhull==2015.2.1
|
||||
pyserial==3.5
|
||||
Shapely==1.7.1
|
Loading…
Reference in New Issue