cylindertransport.py

changeset 4
f62562506053
parent 3
d8745f771267
child 5
c2158ae1dc05
equal deleted inserted replaced
3:d8745f771267 4:f62562506053
1 """
2 Calculation of spacer pipes for Scuba cylinder transportation
3 2017 by NeoSoft, mdd
4 Input: see commandline help
5 Output: 2D schematic & 3D OpenSCAD script
6 """
1 from math import sqrt 7 from math import sqrt
2 from PIL import Image, ImageDraw, ImageFont 8 from PIL import Image, ImageDraw, ImageFont
3 import argparse, sys 9 import argparse, sys
4 from data import CYLINDER, PIPES 10 from data import CYLINDER, PIPES
5 from config import FONTBASE 11 from config import FONTBASE
6 12
7 def offset(r1, r2): 13 def offset(r_1, r_2):
8 return (2*sqrt(r1*r2)) 14 """
15 Calculate horizontal center offset of two circles
16 so they tangent each other
17 """
18 return 2 * sqrt(r_1 * r_2)
9 19
10 class CylinderSpacerCalculator: 20 class CylinderSpacerCalculator():
11 def __init__(self, cylinders = ["10", "10"], space_min = 10): 21 """
12 self.cylinders = cylinders 22 Class to calculate transport spacer pipes between
13 self.space_min = space_min 23 Scuba cylinders
14 self.font = FONTBASE + "arial.ttf" 24 """
15 self.scad = "// Color support only in compile mode (F5)\n" +\ 25 def __init__(self, cylinders, space_min=10):
16 "include <cylindertransport.scad>\n" 26 self.cylinders = cylinders
17 self.circles = [] 27 self.space_min = space_min
18 self.spacings = [] 28 self.font = FONTBASE + "arial.ttf"
19 self.margin = 20 29 self.scad = "// Color support only in compile mode (F5)\n" +\
30 "include <cylindertransport.scad>\n"
31 self.circles = []
32 self.spacings = []
33 self.margin = 20
20 34
21 def calc_min(self, r1, r2): 35 def calc_min(self, r_1, r_2):
22 # stupider annaehreungsversuch, bis sich die beiden Tauchflaschen r1 und r2 nicht mehr beruehren 36 """
23 for p in PIPES: 37 stupider annaehreungsversuch, bis sich die beiden
24 i = p[1] / 2 38 Tauchflaschen r_1 und r_2 nicht mehr beruehren
25 if i>r1 or i>r2: 39 Rueckgabe: 3 Zylinderradien und das label der verwendeten Roehre
26 return None 40 """
27 x1 = offset(r1, i) 41 for p in PIPES:
28 x2 = offset(r2, i) 42 i = p[1] / 2
29 x = (x1 + x2) - (r1+r2) 43 x_1 = offset(r_1, i)
30 if x >= self.space_min: 44 x_2 = offset(r_2, i)
31 print "%s Pipe (%.1fmm), Cylinder spacing: %imm" % (p[0], p[1], x) 45 x = (x_1 + x_2) - (r_1 + r_2)
32 return [r1, i, r2, p[0]] 46 if x >= self.space_min:
33 return None 47 print "%s Pipe (%.1fmm), Cylinder spacing: %imm" % (
48 p[0], p[1], x)
49 return [r_1, i, r_2, p[0]]
50 print "Abort: no suitable pipe found"
51 sys.exit(1)
34 52
35 def _circle(self, x, r, txt = "", size = 1.0): 53 def _circle(self, x, r, txt="", size=1.0):
36 self.circles.append([ 54 """
37 x, r, txt, size 55 Push the circle definition for later rendering
38 ]) 56 """
57 self.circles.append([
58 x, r, txt, size
59 ])
39 60
40 def _calculate(self): 61 def _calculate(self):
41 # first bottle spacer 62 """
42 r1 = CYLINDER[self.cylinders[0]][0] / 2 63 Calculate all cylinder and spacer circles
43 r2 = CYLINDER[self.cylinders[1]][0] / 2 64 """
44 r1, r2, r3, dn = self.calc_min(r1, r2) 65 # first bottle spacer
45 x = self.margin + r2 # start offset x 66 r_1 = CYLINDER[self.cylinders[0]][0] / 2
46 self._circle(x, r2, dn, 0.5) 67 r_2 = CYLINDER[self.cylinders[1]][0] / 2
47 self.scad += "spacer(%i, %i, %i, %i);\n" % ( 68 r_1, r_2, r_3, dn = self.calc_min(r_1, r_2)
48 x, r2, r3, CYLINDER[self.cylinders[0]][1]) 69 x = self.margin + r_2 # start offset x
49 x = x + offset(r2, r3) 70 self._circle(x, r_2, dn, 0.5)
71 self.scad += "spacer(%i, %i, %i, %i);\n" % (
72 x, r_2, r_3, CYLINDER[self.cylinders[0]][1])
73 x = x + offset(r_2, r_3)
50 74
51 for i in range(0, len(self.cylinders)-1): 75 for i in range(0, len(self.cylinders)-1):
52 r1 = CYLINDER[self.cylinders[i]][0] / 2 76 r_1 = CYLINDER[self.cylinders[i]][0] / 2
53 r2 = CYLINDER[self.cylinders[i+1]][0] / 2 77 r_2 = CYLINDER[self.cylinders[i+1]][0] / 2
54 r1, r2, r3, dn = self.calc_min(r1, r2) 78 r_1, r_2, r_3, dn = self.calc_min(r_1, r_2)
55 # draw cylinder 79 # draw cylinder
56 self._circle(x, r1, "Tank " + self.cylinders[i]) 80 self._circle(x, r_1, "Tank " + self.cylinders[i])
57 self.scad += "tank(%i, %i, %i);\n" % ( 81 self.scad += "tank(%i, %i, %i);\n" % (
58 x, r1, CYLINDER[self.cylinders[i]][1]) 82 x, r_1, CYLINDER[self.cylinders[i]][1])
59 sx1 = x+r1 83 sx1 = x + r_1
60 x = x + offset(r1, r2) 84 x = x + offset(r_1, r_2)
61 # draw right spacer 85 # draw right spacer
62 self._circle(x, r2, dn, 0.5) 86 self._circle(x, r_2, dn, 0.5)
63 self.scad += "spacer(%i, %i, %i, %i);\n" % ( 87 self.scad += "spacer(%i, %i, %i, %i);\n" % (
64 x, r2, r1, CYLINDER[self.cylinders[i]][1]) 88 x, r_2, r_1, CYLINDER[self.cylinders[i]][1])
65 x = x + offset(r2, r3) 89 x = x + offset(r_2, r_3)
66 sx2 = x-r3 90 sx2 = x - r_3
67 if i == (len(self.cylinders) - 2): 91 if i == (len(self.cylinders) - 2):
68 # draw last bottle 92 # draw last bottle
69 self._circle(x, r3, "Tank " + self.cylinders[i+1]) 93 self._circle(x, r_3, "Tank " + self.cylinders[i + 1])
70 self.scad += "tank(%i, %i, %i);\n" % ( 94 self.scad += "tank(%i, %i, %i);\n" % (
71 x, r3, CYLINDER[self.cylinders[i+1]][1]) 95 x, r_3, CYLINDER[self.cylinders[i + 1]][1])
72 x = x + offset(r2, r3) 96 x = x + offset(r_2, r_3)
73 97
74 self.spacings.append([sx1, sx2]) 98 self.spacings.append([sx1, sx2])
75 99
76 # last bottle spacer pipe 100 # last bottle spacer pipe
77 self._circle(x, r2, dn, 0.5) 101 self._circle(x, r_2, dn, 0.5)
78 self.scad += "spacer(%i, %i, %i, %i);\n" % ( 102 self.scad += "spacer(%i, %i, %i, %i);\n" % (
79 x, r2, r3, CYLINDER[self.cylinders[i+1]][1]) 103 x, r_2, r_3, CYLINDER[self.cylinders[len(self.cylinders)]][1])
80 return int(x + r2 + self.margin) 104 return int(x + r_2 + self.margin)
81 105
82 def render_image(self): 106 def centertext(self, draw, x, y, txt, size):
83 width = self._calculate() 107 font = ImageFont.truetype(self.font, int(24 * size))
84 image = Image.new('1', (width, 250)) # create new image 108 tox, toy = draw.textsize(txt, font=font)
85 draw = ImageDraw.Draw(image) 109 draw.text((x - tox / 2, y - toy / 2), \
86 # draw calculated circles 110 txt, font=font, fill='#ffffff')
87 for circle in self.circles:
88 x, r, txt, size = circle
89 draw.arc([x - r, 0, x + r, 2 * r], 0, 360, 'white')
90 if txt != "":
91 font = ImageFont.truetype(self.font, int(24 * size))
92 tox, toy = draw.textsize(txt, font=font)
93 draw.text((x - tox / 2, r - toy / 2), txt, font=font, fill='#ffffff')
94 111
95 # draw the spacing between cylinders 112 def render_image(self):
96 spacerY1 = 200 113 """
97 spacerY2 = 220 114 Start the calculation and return rendered PIL image object
98 for spacing in self.spacings: 115 """
99 sx1, sx2 = spacing 116 width = self._calculate()
100 draw.line((sx1, spacerY1, sx1, spacerY2), fill='#ffffff') 117 image = Image.new('1', (width, 250)) # create new image
101 draw.line((sx2, spacerY1, sx2, spacerY2), fill='#ffffff') 118 draw = ImageDraw.Draw(image)
102 txt = "%imm" % (sx2 - sx1) 119 # draw calculated circles
103 font = ImageFont.truetype(FONTBASE+"arial.ttf", 12) 120 for circle in self.circles:
104 tox, toy = draw.textsize(txt, font=font) 121 x, r, txt, size = circle
105 draw.text((sx1 + (sx2 - sx1) / 2 - tox / 2, 122 draw.arc([x - r, 0, x + r, 2 * r], 0, 360, 'white')
106 spacerY2 + toy / 2), txt, font=font, fill='#ffffff') 123 if txt != "":
124 self.centertext(draw, x, r, txt, size)
107 125
108 return image 126 # draw the spacing between cylinders
127 spacer_y1 = 200
128 spacer_y2 = 220
129 for sx1, sx2 in self.spacings:
130 draw.line((sx1, spacer_y1, sx1, spacer_y2), fill='#ffffff')
131 draw.line((sx2, spacer_y1, sx2, spacer_y2), fill='#ffffff')
132 self.centertext(draw, sx1 + (sx2 - sx1) / 2, \
133 spacer_y2 + 10, "%imm" % (sx2 - sx1), 0.5)
134
135 return image
109 136
110 if __name__ == "__main__": 137 if __name__ == "__main__":
111 parser = argparse.ArgumentParser(description = \ 138 parser = argparse.ArgumentParser(description=\
112 "Calculate spacer pipes for pressure cylinder transport\n" +\ 139 "Calculate spacer pipes for pressure cylinder transport\n" +\
113 "Known cylinder types:\n" + ", ".join(sorted(CYLINDER.keys())) ) 140 "Known cylinder types:\n" + ", ".join(sorted(CYLINDER.keys())))
114 parser.add_argument('cylinders', metavar = 'cylinder', 141 parser.add_argument('cylinders', metavar='cylinder', \
115 type = str, nargs = '+', help = 'cylinder types') 142 type=str, nargs='+', help='cylinder types')
116 parser.add_argument('--space', dest='space_min', 143 parser.add_argument('--space', dest='space_min', \
117 type = int, default=10, 144 type=int, default=10, \
118 help='minimum space between cylinders (mm)') 145 help='minimum space between cylinders (mm)')
119 146
120 args = parser.parse_args() 147 options = parser.parse_args()
121 148
122 for test in args.cylinders: 149 for test in options.cylinders:
123 if not test in CYLINDER.keys(): 150 if not test in CYLINDER.keys():
124 print "Cylinder type '%s' is unknown" % test 151 print "Cylinder type '%s' is unknown" % test
125 sys.exit(1) 152 sys.exit(1)
126 153
127 obj = CylinderSpacerCalculator( 154 worker = CylinderSpacerCalculator(
128 args.cylinders, args.space_min) 155 options.cylinders, options.space_min)
129 156
130 image = obj.render_image() 157 image = worker.render_image()
131 image.show() 158 image.show()
132 159
133 print "\n------------ START SCAD SCRIPT ------------" 160 print "\n------------ START SCAD SCRIPT ------------"
134 print obj.scad + "------------ END SCAD SCRIPT ------------" 161 print worker.scad + "------------ END SCAD SCRIPT ------------"

mercurial