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):
|
def loads(cls, data):
|
||||||
paths = []
|
paths = []
|
||||||
for line in data.split('\n'):
|
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 = [tuple(map(float, x.split(','))) for x in path]
|
||||||
path = expand_quadratics(path)
|
path = expand_quadratics(path)
|
||||||
if 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 axi
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import os
|
import os
|
||||||
import sys
|
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
|
LABEL = '#%s' % NUMBER
|
||||||
|
|
||||||
COLUMNS = 6
|
COLUMNS = 8
|
||||||
SECONDS = 5
|
SECONDS = 15
|
||||||
FRAME_OFFSET = 0
|
FRAME_OFFSET = 900
|
||||||
MIN_CHANGES = 1
|
MIN_CHANGES = 2
|
||||||
UNIQUE = False
|
UNIQUE = False
|
||||||
SIMPLIFY = 0
|
SIMPLIFY = 5
|
||||||
|
|
||||||
def simplify_sparkline(values, n):
|
def simplify_sparkline(values, n):
|
||||||
if not n:
|
if not n:
|
||||||
|
@ -52,11 +56,11 @@ def title():
|
||||||
d = d.join_paths(0.01)
|
d = d.join_paths(0.01)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def label():
|
def label(x, y):
|
||||||
d = axi.Drawing(axi.text(LABEL, axi.FUTURAL))
|
d = axi.Drawing(axi.text(LABEL, axi.FUTURAL))
|
||||||
d = d.scale_to_fit_height(0.125)
|
d = d.scale_to_fit_height(0.125)
|
||||||
d = d.rotate(-90)
|
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)
|
d = d.join_paths(0.01)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@ -72,9 +76,9 @@ def main():
|
||||||
lines = filter(None, lines)
|
lines = filter(None, lines)
|
||||||
|
|
||||||
# read values and transpose
|
# 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)
|
data = np.transpose(data)
|
||||||
print '%d series in file' % len(data)
|
print('%d series in file' % len(data))
|
||||||
|
|
||||||
# trim to SECONDS worth of data
|
# trim to SECONDS worth of data
|
||||||
n = len(data[0])
|
n = len(data[0])
|
||||||
|
@ -86,7 +90,7 @@ def main():
|
||||||
|
|
||||||
# remove addresses with too few values
|
# remove addresses with too few values
|
||||||
data = [x for x in data if len(set(x)) > MIN_CHANGES]
|
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
|
# remove duplicate series
|
||||||
if UNIQUE:
|
if UNIQUE:
|
||||||
|
@ -99,16 +103,16 @@ def main():
|
||||||
seen.add(k)
|
seen.add(k)
|
||||||
new_data.append(x)
|
new_data.append(x)
|
||||||
data = new_data
|
data = new_data
|
||||||
print '%d unique series' % len(data)
|
print('%d unique series' % len(data))
|
||||||
|
|
||||||
# delete repetitive stuff
|
# delete repetitive stuff
|
||||||
# del data[136:136+8*14]
|
del data[136:136+8*14]
|
||||||
|
|
||||||
# trim so all rows are full
|
# trim so all rows are full
|
||||||
data = data[:int((len(data) // COLUMNS) * COLUMNS)]
|
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
|
# create sparklines in a grid pattern
|
||||||
paths = []
|
paths = []
|
||||||
|
@ -128,16 +132,21 @@ def main():
|
||||||
y = 1 - value + r * 1.5
|
y = 1 - value + r * 1.5
|
||||||
path.append((x, y))
|
path.append((x, y))
|
||||||
paths.append(path)
|
paths.append(path)
|
||||||
|
|
||||||
d = axi.Drawing(paths)
|
d = axi.Drawing(paths)
|
||||||
|
|
||||||
# add title and label and fit to page
|
# 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 = stack_drawings([d, title()], 0.25)
|
||||||
d = d.rotate(-90)
|
d = d.rotate(-90)
|
||||||
d = d.center(12, 8.5)
|
d = d.center(DW, DH)
|
||||||
d.add(label())
|
_, _, 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
|
# save outputs
|
||||||
dirname = 'nes/%s' % NUMBER
|
dirname = 'nes/%s' % NUMBER
|
||||||
|
@ -146,11 +155,10 @@ def main():
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
d.dump(os.path.join(dirname, 'out.axi'))
|
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'))
|
rotated.dump_svg(os.path.join(dirname, 'out.svg'))
|
||||||
im = rotated.render(
|
x0, y0, x1, y1 = rotated.bounds
|
||||||
scale=109 * 1, line_width=0.3/25.4,
|
im = rotated.render(bounds=(x0 - 1, y0 - 1, x1 + 1, y1 + 1))
|
||||||
show_axi_bounds=False, use_axi_bounds=False)
|
|
||||||
im.write_to_png(os.path.join(dirname, 'out.png'))
|
im.write_to_png(os.path.join(dirname, 'out.png'))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
cairocffi==1.2.0
|
||||||
pyserial
|
cffi==1.14.4
|
||||||
pyhull
|
numpy==1.19.5
|
||||||
Shapely
|
pycparser==2.20
|
||||||
|
pyhull==2015.2.1
|
||||||
|
pyserial==3.5
|
||||||
|
Shapely==1.7.1
|
Loading…
Reference in New Issue