forked from phooky/SolidConway
-
Notifications
You must be signed in to change notification settings - Fork 0
/
solid_conway.py
executable file
·122 lines (106 loc) · 2.53 KB
/
solid_conway.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/python
import re
import sys
def usage():
print """
Solid Conway is a script for converting a series of RLE files
representing generations in the game of life into an openSCAD
program producing a solid object representing the evolution
of the pattern.
Usage:
solid_conway.py gen1.rle gen2.rle ...
"""
preamble = """
module skew_cube(a,b) {
multmatrix(m = [
[1, 0, a, 0],
[0, 1, b, 0],
[0, 0, 1, 0]
]) cube(size=1.00001);
}
module span_cube(x1,y1,x2,y2,z) {
translate(v=[x1,y1,z])
skew_cube(x2-x1,y2-y1);
}
"""
reXY = re.compile(r"x\s*=\s*([0-9]+),\s*y\s*=\s*([0-9]+)")
rePos = re.compile(r"Pos\s*=\s*(\-?[0-9]+),\s*(\-?[0-9]+)")
def parseOutRLE(raw,row):
count = 0
off = 0
for c in raw:
if c <= '9' and c >= '0':
count = (count * 10) + int(c)
else:
if count == 0: count = 1
if c == 'b':
row[off:off+count] = [0]*count
elif c == 'o':
row[off:off+count] = [1]*count
off = off + count
count = 0
class Generation:
def __init__(self, position, data):
self.pos = position
self.data = data
def has(self,i,j):
i = i - self.pos[0]
j = j - self.pos[1]
if i < 0 or j < 0:
return 0
if j >= len(self.data):
return 0
if i >= len(self.data[j]):
return 0
return self.data[j][i]
def walk(self, fn):
"Walk the tuples of on cells"
for j in range(len(self.data)):
for i in range(len(self.data[j])):
if self.data[j][i]:
fn(i+self.pos[0],j+self.pos[1])
def loadRLE(path):
lines = open(path).readlines()
data = []
pos=(0,0)
dim=(0,0)
y = 0
for line in lines:
posMatch = rePos.search(line)
if posMatch:
pos=(int(posMatch.group(1)),int(posMatch.group(2)))
continue
xyMatch = reXY.match(line)
if xyMatch:
dim=(int(xyMatch.group(1)),int(xyMatch.group(2)))
data=[[0 for i in range(dim[0])] for j in range(dim[1])]
continue
if line and line[0] != "#":
# line is data
rows=line.split("$")
for row in rows:
parseOutRLE(row,data[y])
y = y + 1
#print pos,data
return Generation(pos,data)
paths = sys.argv[1:]
generations = map(loadRLE,paths)
def buildParentsForGen(genAbove,z):
def buildParents(i,j):
# use genAbove
irange = range(i-1,i+2)
jrange = range(j-1,j+2)
for jt in jrange:
for it in irange:
if genAbove.has(it,jt):
print "span_cube({0},{1},{2},{3},{4});".format(i,j,it,jt,z)
return buildParents
if len(generations) < 2:
print "Need at least two generations."
usage()
sys.exit(1)
print preamble
print "union() {"
for idx in range(len(generations)-1):
generations[idx].walk(buildParentsForGen(generations[idx+1],idx))
print "}"