diff --git a/traylayout.py b/traylayout.py
new file mode 100755
index 0000000..71a07a1
--- /dev/null
+++ b/traylayout.py
@@ -0,0 +1,343 @@
+#!/usr/bin/python3
+# Copyright (C) 2016 Florian Festi
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import sys, re
+from boxes import *
+import argparse
+
+class Layout(Boxes):
+ """Generate a typetray from a layout file"""
+ def __init__(self, input=None):
+ Boxes.__init__(self)
+ self.buildArgParser("h", "hi")
+ self.argparser.add_argument(
+ "--input", action="store", type=argparse.FileType('r'),
+ help="layout file")
+ self.argparser.add_argument(
+ "--x", action="store", type=int, default=None,
+ help="number of compartments side by side")
+ self.argparser.add_argument(
+ "--y", action="store", type=int, default=None,
+ help="number of compartments back to front")
+
+ def fillDefault(self, x, y):
+ self.x = [0.0] * x
+ self.y = [0.0] * y
+ self.hwalls = [[True for i in range(x)] for j in range(y+1)]
+ self.vwalls = [[True for i in range(x+1)] for j in range(y)]
+ self.floors = [[True for i in range(x)] for j in range(y)]
+
+ def __str__(self):
+ r = []
+ for i, x in enumerate(self.x):
+ r.append(" |" * (i) + " ,> %.1fmm\n" % x)
+ for hwalls, vwalls, floors, y in zip(
+ self.hwalls, self.vwalls, self.floors, self.y):
+ r.append("".join(("+" + " -"[h] for h in hwalls)) + "+\n")
+ r.append("".join((" |"[v] + "X "[f] for v, f in zip(vwalls, floors)))
+ + " |"[vwalls[-1]] + " %.1fmm\n" % y)
+ r.append("".join(("+" + " -"[h] for h in self.hwalls[-1])) + "+\n")
+ return "".join(r)
+
+ def hholes(self, y, start, end):
+ posx = -0.5 * self.thickness
+ for i in range(start, end-1):
+ posx += self.x[i] + self.thickness
+ # XXX if no holes: continue
+ self.fingerHolesAt(posx, 0, self.hi)
+
+ def vholes(self, x, start, end):
+ posy = -0.5 * self.thickness
+ for i in range(start, end-1):
+ posy += self.y[i] + self.thickness
+ # XXX if no holes: continue
+ self.fingerHolesAt(posy, 0, self.hi)
+
+ def vWalls(self, x, y):
+ "Number of vertical walls at a crossing"
+ result = 0
+ if y>0 and self.vwalls[y-1][x]:
+ result += 1
+ if y < len(self.y) and self.vwalls[y][x]:
+ result += 1
+ return result
+
+ def hWalls(self, x, y):
+ "Number of horizontal walls at a crossing"
+ result = 0
+ if x>0 and self.hwalls[y][x-1]:
+ result += 1
+ if x0 and self.floors[y-1][x]:
+ # Inside Wall
+ if self.hwalls[y][x]:
+ self.fingerHolesAt(posx, posy+t2, self.x[x], angle=0)
+ else:
+ # Top edge
+ self.edgeAt(e, posx+self.x[x], posy+t, self.x[x],
+ -180)
+ self.edgeAt("e", posx, posy+t, t, -180)
+ self.edgeAt("e", posx+self.x[x]+t, posy+t, t, -180)
+ elif y>0 and self.floors[y-1][x]:
+ # Bottom Edge
+ self.edgeAt(e, posx, posy, self.x[x])
+ self.edgeAt("e", posx-t, posy, t)
+ self.edgeAt("e", posx+self.x[x], posy, t)
+ posx += self.x[x] + self. thickness
+ posy += self.y[y-1] + self.thickness
+
+ posx = 0
+ for x in range(lx+1):
+ posy = self.thickness
+ for y in range(ly-1, -1, -1):
+ if self.vwalls[y][x]:
+ e = "F"
+ else:
+ e = "e"
+ if x>0 and self.floors[y][x-1]:
+ if x < lx and self.floors[y][x]:
+ # Inside wall
+ if self.vwalls[y][x]:
+ self.fingerHolesAt(posx+t2, posy, self.y[y])
+ else:
+ # Right edge
+ self.edgeAt(e, posx+t, posy, self.y[y], 90)
+ self.edgeAt("e", posx+t, posy-t, t, 90)
+ self.edgeAt("e", posx+t, posy+self.y[y], t, 90)
+ elif x < lx and self.floors[y][x]:
+ # Left edge
+ self.edgeAt(e, posx, posy+self.y[y], self.y[y], -90)
+ self.edgeAt(e, posx, posy+self.y[y]+t, t, -90)
+ self.edgeAt(e, posx, posy, t, -90)
+ posy += self.y[y] + self.thickness
+ if x < lx:
+ posx += self.x[x] + self.thickness
+
+ self.close()
+
+ def parse(self, input):
+ x = []
+ y = []
+ hwalls = []
+ vwalls = []
+ floors = []
+ for line in input:
+ if line[0] == "#":
+ continue
+ m = re.match(r"( \|)* ,>\s*(\d*\.?\d+)\s*mm\s*", line)
+ if m:
+ x.append(float(m.group(2)))
+ continue
+ if line[0] == '+':
+ w = []
+ for n, c in enumerate(line):
+ if n % 2:
+ if c == ' ':
+ w.append(False)
+ elif c == '-':
+ w.append(True)
+ else:
+ pass
+ #raise ValueError(line)
+ else:
+ if c != '+':
+ pass
+ #raise ValueError(line)
+
+
+ hwalls.append(w)
+ if line[0] in " |":
+ w = []
+ f = []
+ for n, c in enumerate(line[:len(x)*2+1]):
+ if n % 2:
+ if c == 'X':
+ f.append(False)
+ elif c == ' ':
+ f.append(True)
+ else:
+ raise ValueError(line)
+ else:
+ if c == ' ':
+ w.append(False)
+ elif c == '|':
+ w.append(True)
+ else:
+ raise ValueError(line)
+
+ floors.append(f)
+ vwalls.append(w)
+ m = re.match(r"([ |][ X])+[ |]\s*(\d*\.?\d+)\s*mm\s*", line)
+ if not m:
+ raise ValueError(line)
+ else:
+ y.append(float(m.group(2)))
+
+ # check sizes
+ lx = len(x)
+ ly = len(y)
+ if len(hwalls) != ly+1:
+ raise ValueError("Wrong number of horizontal wall lines: %i (%i expected)" % (len(hwalls), ly+1))
+ for nr, walls in enumerate(hwalls):
+ if len(walls)!= lx:
+ raise ValueError("Wrong number of horizontal walls in line %i: %i (%i expected)" % (nr, len(walls), lx))
+ if len(vwalls) != ly:
+ raise ValueError("Wrong number of vertical wall lines: %i (%i expected)" % (len(vwalls), ly))
+ for nr, walls in enumerate(vwalls):
+ if len(walls) != lx+1:
+ raise ValueError("Wrong number of vertical walls in line %i: %i (%i expected)" % (nr, len(walls), lx+1))
+
+ self.x = x
+ self.y = y
+ self.hwalls = hwalls
+ self.vwalls = vwalls
+ self.floors = floors
+
+def main():
+ l = Layout()
+ l.parseArgs()
+ if l.x and l.y:
+ l.fillDefault(l.x, l.y)
+ print(l)
+ elif l.input:
+ l.parse(l.input)
+ l.render()
+ print(str(l))
+ else:
+ l.argparser.print_usage()
+
+if __name__ == '__main__':
+ main()