From 555d48c5afd29ef1d69bdc5f8bd039f330b1b484 Mon Sep 17 00:00:00 2001 From: josch Date: Wed, 17 Jul 2013 08:17:52 +0200 Subject: [PATCH] initial commit --- Makefile | 19 +++ partsgen.py | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 Makefile create mode 100644 partsgen.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ab96977 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +BITMAPS:=$(patsubst parts/%.dat, bitmaps/%.png, $(wildcard parts/*.dat)) + +LDFLAGS+=-lm +LINK.o = $(LINK.cc) +CXXFLAGS=-Wall -pedantic + +partsgen: partsgen.o + +clean: + rm -f partsgen.o partsgen + rm -f bitmaps/* + +test: montage.png + +montage.png: $(BITMAPS) + montage -label '%f' $(BITMAPS) $@ + +bitmaps/%.png: parts/%.dat + ~/debpkg/ldview-4.2\~beta1+dfsg/QT/LDView -SaveActualSize=0 -SaveAlpha=1 -SaveWidth=300 -SaveHeight=300 -SaveZoomToFit=0 -SaveSnapShot=$@ $< diff --git a/partsgen.py b/partsgen.py new file mode 100644 index 0000000..7820846 --- /dev/null +++ b/partsgen.py @@ -0,0 +1,339 @@ +from math import sin, cos, pi, copysign +import re + +wrap = lambda l: zip(l, l[1:]+l[:1]) +sign = lambda x: copysign(1, x) + +studsides = 16 + +parts = [ + ("3005" , "Brick 1 x 1"), + ("2453" , "Brick 1 x 1 x 5"), + ("3004" , "Brick 1 x 2"), + ("3245a", "Brick 1 x 2 x 2"), + ("2454" , "Brick 1 x 2 x 5"), + ("3622" , "Brick 1 x 3"), + ("3755" , "Brick 1 x 3 x 5"), + ("3010" , "Brick 1 x 4"), + ("3009" , "Brick 1 x 6"), + ("3754" , "Brick 1 x 6 x 5"), + ("3008" , "Brick 1 x 8"), + ("6111" , "Brick 1 x 10"), + ("6112" , "Brick 1 x 12"), + ("2465" , "Brick 1 x 16"), + ("3003" , "Brick 2 x 2"), + ("2357", "Brick 2 x 2 Corner"), + ("30145", "Brick 2 x 2 x 3"), + ("3002" , "Brick 2 x 3"), + ("3001" , "Brick 2 x 4"), + ("30144", "Brick 2 x 4 x 3"), + ("2456" , "Brick 2 x 6"), + ("6213" , "Brick 2 x 6 x 3"), + ("3007" , "Brick 2 x 8"), + ("3006" , "Brick 2 x 10"), + ("702", "Brick 4 x 4 Corner"), + ("2356" , "Brick 4 x 6"), + ("6212" , "Brick 4 x 10"), + ("4202" , "Brick 4 x 12"), + ("30400", "Brick 4 x 18"), + ("4201" , "Brick 8 x 8"), + ("4204" , "Brick 8 x 16"), + ("733" , "Brick 10 x 10"), + ("3024", "Plate 1 x 1"), + ("3023", "Plate 1 x 2"), + ("3623", "Plate 1 x 3"), + ("3710", "Plate 1 x 4"), + ("3666", "Plate 1 x 6"), + ("3460", "Plate 1 x 8"), + ("4477", "Plate 1 x 10"), + ("60479", "Plate 1 x 12"), + ("3022", "Plate 2 x 2"), + ("2420", "Plate 2 x 2 Corner"), + ("3021", "Plate 2 x 3"), + ("3020", "Plate 2 x 4"), + ("3795", "Plate 2 x 6"), + ("3034", "Plate 2 x 8"), + ("3832", "Plate 2 x 10"), + ("2445", "Plate 2 x 12"), + ("91988", "Plate 2 x 14"), + ("4282", "Plate 2 x 16"), + ("3031", "Plate 4 x 4"), + ("2639", "Plate 4 x 4 Corner"), + ("3032", "Plate 4 x 6"), + ("3035", "Plate 4 x 8"), + ("3030", "Plate 4 x 10"), + ("3029", "Plate 4 x 12"), + ("3958", "Plate 6 x 6"), + ("3036", "Plate 6 x 8"), + ("3033", "Plate 6 x 10"), + ("3028", "Plate 6 x 12"), + ("3456", "Plate 6 x 14"), + ("3027", "Plate 6 x 16"), + ("3026", "Plate 6 x 24"), + ("41539", "Plate 8 x 8"), + ("728", "Plate 8 x 11"), + ("92438", "Plate 8 x 16"), + ("60477", "Slope Brick 18 4 x 1"), + ("30363", "Slope Brick 18 4 x 2"), + ("54200", "Slope Brick 31 1 x 1 x 2/3"), + ("85984", "Slope Brick 31 1 x 2 x 2/3"), + ("3300", "Slope Brick 33 2 x 2 Double"), + ("3299", "Slope Brick 33 2 x 4 Double"), + ("4286", "Slope Brick 33 3 x 1"), + ("4287", "Slope Brick 33 3 x 1 Inverted"), + ("3298", "Slope Brick 33 3 x 2"), + ("3747a", "Slope Brick 33 3 x 2"), + ("4161", "Slope Brick 33 3 x 3"), + ("99301", "Slope Brick 33 3 x 3 Double Concave"), + ("3675", "Slope Brick 33 3 x 3 Double Convex"), + ("3297", "Slope Brick 33 3 x 4"), + ("3048", "Slope Brick 45 2 x 1 Triple"), + ("3040b", "Slope Brick 45 2 x 1"), + ("3044b", "Slope Brick 45 2 x 1 Double"), + ("3665", "Slope Brick 45 2 x 1 Inverted"), + ("3039", "Slope Brick 45 2 x 2"), + ("3043", "Slope Brick 45 2 x 2 Double"), + ("3046", "Slope Brick 45 2 x 2 Double Concave"), + ("962", "Slope Brick 45 2 x 2 Double Concave / Double Convex"), + ("3045", "Slope Brick 45 2 x 2 Double Convex"), + ("3660", "Slope Brick 45 2 x 2 Inverted"), + ("3676", "Slope Brick 45 2 x 2 Inverted Double Convex"), + ("3038", "Slope Brick 45 2 x 3"), + ("3042", "Slope Brick 45 2 x 3 Double"), + ("3037", "Slope Brick 45 2 x 4"), + ("3041", "Slope Brick 45 2 x 4 Double"), + ("4445", "Slope Brick 45 2 x 8"), + ("60481", "Slope Brick 65 2 x 1 x 2"), + ("6678a", "Slope Brick 65 2 x 2 x 2"), + ("4460", "Slope Brick 75 2 x 1 x 3"), + ("2449", "Slope Brick 75 2 x 1 x 3 Inverted"), + ("3684", "Slope Brick 75 2 x 2 x 3"), + ("3685", "Slope Brick 75 2 x 2 x 3 Double Convex"), + ] + +def drawstud(studsx, studsz, x, z, lines, triangles, quads): + # each stud is 4 LDU high and studs are 20 LDU apart + center = ((studsx/2.0 - x)*20 - 10, -4, (studsz/2.0 - z)*20 - 10) + # each stud has a radius of 6 LDU + circle = [(center[0] + sin((side*2*pi)/studsides)*6, + center[2] + cos((side*2*pi)/studsides)*6) + for side in range(studsides)] + # now for each slice of the cake... + for p1, p2 in wrap(circle): + # write the top plate + triangles.append(((center[0], center[1], center[2]), + (p1[0], -4, p1[1]), + (p2[0], -4, p2[1]))) + # write the side + quads.append(((p1[0], -4, p1[1]), (p2[0], -4, p2[1]), + (p2[0], 0, p2[1]), (p1[0], 0, p1[1]))) + # write the lines top and bottom + lines.append(((p1[0], -4, p1[1]), (p2[0], -4, p2[1]))) + lines.append(((p1[0], 0, p1[1]), (p2[0], 0, p2[1]))) + +def render(): + for part in parts: + partid, parttext = part[:2] + m = re.match(r"(?P[A-Za-z0-9 ]+?) (?P\d+)"+ + r" x (?P\d+)(?: x (?P\d+(?:/\d+)?))?"+ + r"(?: (?PCorner)| "+ + r"(?P(?:Double|Triple|Inverted|Concave|Convex| |/)+))?", + parttext) + # sanity checks + if m.group('type') not in ['Brick', 'Plate', 'Slope Brick 18', + 'Slope Brick 31', 'Slope Brick 33', 'Slope Brick 45', + 'Slope Brick 65', 'Slope Brick 75']: + print "not supported part type: %s"%m.group('type') + exit(1) + if m.group('type') == 'Plate' and m.group('height'): + print "plates can't have a height" + exit(1) + if m.group('height') and m.group('corner'): + print "corners can't have a height" + exit(1) + if m.group('corner') and (m.group('studsx') != m.group('studsz')): + print "corners must be squares" + exit(1) + lines = list() + triangles = list() + quads = list() + studsz = int(m.group('studsz')) + studsx = int(m.group('studsx')) + if m.group('type') in ['Brick', 'Slope Brick 18', 'Slope Brick 31', + 'Slope Brick 33', 'Slope Brick 45', 'Slope Brick 65', + 'Slope Brick 75']: + if m.group('height'): + if m.group('height') == '2/3': + height = 2 + else: + height = int(m.group('height'))*3 + else: + height = 3 + else: + height = 1 + if m.group('type') in ['Brick', 'Plate']: + # draw studs + for z in range(studsz): + for x in range(studsx): + if not m.group('corner') or z >= studsz/2 or x >= studsx/2: + drawstud(studsx, studsz, x, z, lines, triangles, quads) + # create top, bottom, inner and outer rectangles + # in case of a corner, draw an L otherwise draw a square + if m.group('corner'): + coords = [(0,0),(1,0),(1,-1),(-1,-1),(-1,1),(0,1)] + else: + coords = [(1,1),(1,-1),(-1,-1),(-1,1)] + outertopcoords = [(studsx*10*x, 0, studsz*10*z) for x,z in coords] + outerbottomcoords = [(x, height*8, z) for x,y,z in outertopcoords] + # walls are 4 LDU thick, use sign() in case x or y are zero + innertopcoords = [(studsx*10*x-sign(x)*4, 4, studsz*10*z-sign(z)*4) for x,z in coords] + innerbottomcoords = [(x, height*8, z) for x,y,z in innertopcoords] + # write outer top plate and lines + # in case of a corner draw two trapezoids, otherwise draw a rectangle + if m.group('corner'): + quads.append(outertopcoords[:4]) + quads.append(outertopcoords[3:]+outertopcoords[:1]) + else: + quads.append(outertopcoords) + for p1, p2 in wrap(outertopcoords): + lines.append((p1, p2)) + # outer sides and lines + for (p1, p2), (p3, p4) in zip(wrap(outertopcoords), wrap(outerbottomcoords)): + quads.append((p1,p2,p4,p3)) + lines.append((p1,p3)) + # write inner top plate and lines + # in case of a corner draw two trapezoids, otherwise draw a rectangle + if m.group('corner'): + quads.append(innertopcoords[:4]) + quads.append(innertopcoords[3:]+innertopcoords[:1]) + else: + quads.append(innertopcoords) + for p1, p2 in wrap(innertopcoords): + lines.append((p1, p2)) + # inner sides and lines + for (p1, p2), (p3, p4) in zip(wrap(innertopcoords), wrap(innerbottomcoords)): + quads.append((p1,p2,p4,p3)) + lines.append((p1,p3)) + # write out bottom with trapezoids and lines + for (p1, p2), (p3, p4) in zip(wrap(innerbottomcoords), wrap(outerbottomcoords)): + quads.append((p1, p2, p4, p3)) + lines.append((p1, p2)) + lines.append((p3, p4)) + elif m.group('type') in ['Slope Brick 18', 'Slope Brick 31', + 'Slope Brick 33', 'Slope Brick 45', 'Slope Brick 65', + 'Slope Brick 75']: + # draw studs + hasstuds = False + if m.group('type') != 'Slope Brick 31' \ + and 'Double' != m.group('slope') \ + and 'Triple' != m.group('slope') \ + and 'Double Concave / Double Convex' != m.group('slope'): + hasstuds = True + if m.group('slope') == 'Inverted': + for z in range(studsz): + for x in range(studsx): + drawstud(studsx, studsz, x, z, lines, triangles, quads) + else: + for x in range(studsx): + drawstud(studsx, studsz, x, 0, lines, triangles, quads) + # create top, bottom, inner and outer rectangles + coords = [(-1,-1),(-1,1),(1,1),(1,-1)] + if hasstuds: + if m.group('slope') == 'Inverted': + outertopcoords = [(studsx*10*x, 0, studsz*10*z) for x,z in coords] + else: + outertopcoords = [(studsx*10*x, 0, z*10+(studsz-1)*10.0) for x,z in coords] + if m.group('slope') == 'Inverted': + outertopcoords2 = [(studsx*10*x, 0, z*10+(studsz-1)*10.0) for x,z in coords] # small + else: + outerbottomcoords2 = [(studsx*10*x, height*8, z*10+(studsz-1)*10.0) for x,z in coords] # small + if m.group('slope') == 'Inverted': + outerbottomcoords = [(studsx*10*x, height*8, z*10+(studsz-1)*10.0) for x,z in coords] + else: + outerbottomcoords = [(studsx*10*x, height*8, studsz*10*z) for x,z in coords] # big + if m.group('slope') != 'Inverted': + if hasstuds: + innertopcoords = [((studsx*10-4)*x, 4, z*6+(studsz-1)*10.0) for x,z in coords] + innerbottomcoords = [(studsx*10*x-sign(x)*4, height*8, studsz*10*z-sign(z)*4) for x,z in coords] + if m.group('slope') == 'Inverted': + noseup = outertopcoords[:1]+outertopcoords[-1:] + nosedown = [(x,4,z) for x,y,z in noseup] + else: + nosedown = outerbottomcoords[:1]+outerbottomcoords[-1:] + noseup = [(x,height*8-4,z) for x,y,z in nosedown] + # write outer top plate and lines + if hasstuds: + quads.append(outertopcoords) + for p1, p2 in wrap(outertopcoords): + lines.append((p1, p2)) + # outer sides and lines + if hasstuds: + if m.group('slope') == 'Inverted': + for (p1, p2), (p3, p4) in zip(wrap(outerbottomcoords)[:3], wrap(outertopcoords2)[:3]): + quads.append((p1,p2,p4,p3)) + for (p1, p2), (p3, p4) in zip(wrap(outerbottomcoords)[1:3], wrap(outertopcoords2)[1:3]): + lines.append((p1,p3)) + else: + for (p1, p2), (p3, p4) in zip(wrap(outertopcoords)[:3], wrap(outerbottomcoords2)[:3]): + quads.append((p1,p2,p4,p3)) + for (p1, p2), (p3, p4) in zip(wrap(outertopcoords)[1:3], wrap(outerbottomcoords2)[1:3]): + lines.append((p1,p3)) + # draw nose + quads.append(noseup+[nosedown[1]]+[nosedown[0]]) + # draw sides and lines to the nose + if hasstuds: + if m.group('slope') == 'Inverted': + quads.append(outerbottomcoords[-1:]+outertopcoords2[-1:]+nosedown[-1:]+noseup[-1:]) + quads.append(outerbottomcoords[:1]+outertopcoords2[:1]+nosedown[:1]+noseup[:1]) + else: + quads.append(outertopcoords[-1:]+outerbottomcoords2[-1:]+nosedown[-1:]+noseup[-1:]) + quads.append(outertopcoords[:1]+outerbottomcoords2[:1]+nosedown[:1]+noseup[:1]) + lines.append(noseup[:1]+nosedown[:1]) + lines.append(noseup[-1:]+nosedown[-1:]) + # draw slope and lines around it + if hasstuds: + if m.group('slope') == 'Inverted': + quads.append(outerbottomcoords[-1:]+outerbottomcoords[:1]+[nosedown[1]]+[nosedown[0]]) + lines.append(nosedown[:1]+outerbottomcoords[:1]) + lines.append(nosedown[-1:]+outerbottomcoords[-1:]) + lines.append(nosedown[:1]+nosedown[-1:]) + else: + quads.append(outertopcoords[-1:]+outertopcoords[:1]+[noseup[1]]+[noseup[0]]) + lines.append(noseup[:1]+outertopcoords[:1]) + lines.append(noseup[-1:]+outertopcoords[-1:]) + lines.append(noseup[:1]+noseup[-1:]) + # write inner top plate and lines + if m.group('slope') == 'Inverted': + quads.append(outerbottomcoords) + else: + if hasstuds: + quads.append(innertopcoords) + for p1, p2 in wrap(innertopcoords): + lines.append((p1, p2)) + # inner sides and lines + if hasstuds: + for (p1, p2), (p3, p4) in zip(wrap(innertopcoords), wrap(innerbottomcoords)): + quads.append((p1,p2,p4,p3)) + lines.append((p1,p3)) + # write out bottom with trapezoids and lines + for (p1, p2), (p3, p4) in zip(wrap(innerbottomcoords), wrap(outerbottomcoords)): + quads.append((p1, p2, p4, p3)) + lines.append((p1, p2)) + lines.append((p3, p4)) + else: + print "not supported part type: %s"%m.group('type') + exit(1) + outfile = open("parts/%s.dat"%partid, 'w') + outfile.write("0 %s\n"%parttext) + for (x1, y1, z1), (x2, y2, z2) in lines: + outfile.write("2 24 %f %f %f %f %f %f\n"%(x1, y1, z1, x2, y2, z2)) + for (x1, y1, z1), (x2, y2, z2), (x3, y3, z3) in triangles: + outfile.write("3 16 %f %f %f %f %f %f %f %f %f\n"%( + x1, y1, z1, x2, y2, z2, x3, y3, z3)) + for (x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4) in quads: + outfile.write("4 16 %f %f %f %f %f %f %f %f %f %f %f %f\n"%( + x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4)) + outfile.close() + +if __name__ == "__main__": + render()