diff -r d8745f771267 -r f62562506053 cylindertransport.py --- a/cylindertransport.py Mon Apr 03 03:15:12 2017 +0200 +++ b/cylindertransport.py Mon Apr 03 04:19:45 2017 +0200 @@ -1,134 +1,161 @@ +""" +Calculation of spacer pipes for Scuba cylinder transportation +2017 by NeoSoft, mdd +Input: see commandline help +Output: 2D schematic & 3D OpenSCAD script +""" from math import sqrt from PIL import Image, ImageDraw, ImageFont import argparse, sys from data import CYLINDER, PIPES from config import FONTBASE -def offset(r1, r2): - return (2*sqrt(r1*r2)) +def offset(r_1, r_2): + """ + Calculate horizontal center offset of two circles + so they tangent each other + """ + return 2 * sqrt(r_1 * r_2) -class CylinderSpacerCalculator: - def __init__(self, cylinders = ["10", "10"], space_min = 10): - self.cylinders = cylinders - self.space_min = space_min - self.font = FONTBASE + "arial.ttf" - self.scad = "// Color support only in compile mode (F5)\n" +\ - "include \n" - self.circles = [] - self.spacings = [] - self.margin = 20 +class CylinderSpacerCalculator(): + """ + Class to calculate transport spacer pipes between + Scuba cylinders + """ + def __init__(self, cylinders, space_min=10): + self.cylinders = cylinders + self.space_min = space_min + self.font = FONTBASE + "arial.ttf" + self.scad = "// Color support only in compile mode (F5)\n" +\ + "include \n" + self.circles = [] + self.spacings = [] + self.margin = 20 - def calc_min(self, r1, r2): - # stupider annaehreungsversuch, bis sich die beiden Tauchflaschen r1 und r2 nicht mehr beruehren - for p in PIPES: - i = p[1] / 2 - if i>r1 or i>r2: - return None - x1 = offset(r1, i) - x2 = offset(r2, i) - x = (x1 + x2) - (r1+r2) - if x >= self.space_min: - print "%s Pipe (%.1fmm), Cylinder spacing: %imm" % (p[0], p[1], x) - return [r1, i, r2, p[0]] - return None + def calc_min(self, r_1, r_2): + """ + stupider annaehreungsversuch, bis sich die beiden + Tauchflaschen r_1 und r_2 nicht mehr beruehren + Rueckgabe: 3 Zylinderradien und das label der verwendeten Roehre + """ + for p in PIPES: + i = p[1] / 2 + x_1 = offset(r_1, i) + x_2 = offset(r_2, i) + x = (x_1 + x_2) - (r_1 + r_2) + if x >= self.space_min: + print "%s Pipe (%.1fmm), Cylinder spacing: %imm" % ( + p[0], p[1], x) + return [r_1, i, r_2, p[0]] + print "Abort: no suitable pipe found" + sys.exit(1) - def _circle(self, x, r, txt = "", size = 1.0): - self.circles.append([ - x, r, txt, size - ]) + def _circle(self, x, r, txt="", size=1.0): + """ + Push the circle definition for later rendering + """ + self.circles.append([ + x, r, txt, size + ]) - def _calculate(self): - # first bottle spacer - r1 = CYLINDER[self.cylinders[0]][0] / 2 - r2 = CYLINDER[self.cylinders[1]][0] / 2 - r1, r2, r3, dn = self.calc_min(r1, r2) - x = self.margin + r2 # start offset x - self._circle(x, r2, dn, 0.5) - self.scad += "spacer(%i, %i, %i, %i);\n" % ( - x, r2, r3, CYLINDER[self.cylinders[0]][1]) - x = x + offset(r2, r3) + def _calculate(self): + """ + Calculate all cylinder and spacer circles + """ + # first bottle spacer + r_1 = CYLINDER[self.cylinders[0]][0] / 2 + r_2 = CYLINDER[self.cylinders[1]][0] / 2 + r_1, r_2, r_3, dn = self.calc_min(r_1, r_2) + x = self.margin + r_2 # start offset x + self._circle(x, r_2, dn, 0.5) + self.scad += "spacer(%i, %i, %i, %i);\n" % ( + x, r_2, r_3, CYLINDER[self.cylinders[0]][1]) + x = x + offset(r_2, r_3) - for i in range(0, len(self.cylinders)-1): - r1 = CYLINDER[self.cylinders[i]][0] / 2 - r2 = CYLINDER[self.cylinders[i+1]][0] / 2 - r1, r2, r3, dn = self.calc_min(r1, r2) - # draw cylinder - self._circle(x, r1, "Tank " + self.cylinders[i]) - self.scad += "tank(%i, %i, %i);\n" % ( - x, r1, CYLINDER[self.cylinders[i]][1]) - sx1 = x+r1 - x = x + offset(r1, r2) - # draw right spacer - self._circle(x, r2, dn, 0.5) - self.scad += "spacer(%i, %i, %i, %i);\n" % ( - x, r2, r1, CYLINDER[self.cylinders[i]][1]) - x = x + offset(r2, r3) - sx2 = x-r3 - if i == (len(self.cylinders) - 2): - # draw last bottle - self._circle(x, r3, "Tank " + self.cylinders[i+1]) - self.scad += "tank(%i, %i, %i);\n" % ( - x, r3, CYLINDER[self.cylinders[i+1]][1]) - x = x + offset(r2, r3) + for i in range(0, len(self.cylinders)-1): + r_1 = CYLINDER[self.cylinders[i]][0] / 2 + r_2 = CYLINDER[self.cylinders[i+1]][0] / 2 + r_1, r_2, r_3, dn = self.calc_min(r_1, r_2) + # draw cylinder + self._circle(x, r_1, "Tank " + self.cylinders[i]) + self.scad += "tank(%i, %i, %i);\n" % ( + x, r_1, CYLINDER[self.cylinders[i]][1]) + sx1 = x + r_1 + x = x + offset(r_1, r_2) + # draw right spacer + self._circle(x, r_2, dn, 0.5) + self.scad += "spacer(%i, %i, %i, %i);\n" % ( + x, r_2, r_1, CYLINDER[self.cylinders[i]][1]) + x = x + offset(r_2, r_3) + sx2 = x - r_3 + if i == (len(self.cylinders) - 2): + # draw last bottle + self._circle(x, r_3, "Tank " + self.cylinders[i + 1]) + self.scad += "tank(%i, %i, %i);\n" % ( + x, r_3, CYLINDER[self.cylinders[i + 1]][1]) + x = x + offset(r_2, r_3) - self.spacings.append([sx1, sx2]) + self.spacings.append([sx1, sx2]) + + # last bottle spacer pipe + self._circle(x, r_2, dn, 0.5) + self.scad += "spacer(%i, %i, %i, %i);\n" % ( + x, r_2, r_3, CYLINDER[self.cylinders[len(self.cylinders)]][1]) + return int(x + r_2 + self.margin) - # last bottle spacer pipe - self._circle(x, r2, dn, 0.5) - self.scad += "spacer(%i, %i, %i, %i);\n" % ( - x, r2, r3, CYLINDER[self.cylinders[i+1]][1]) - return int(x + r2 + self.margin) + def centertext(self, draw, x, y, txt, size): + font = ImageFont.truetype(self.font, int(24 * size)) + tox, toy = draw.textsize(txt, font=font) + draw.text((x - tox / 2, y - toy / 2), \ + txt, font=font, fill='#ffffff') - def render_image(self): - width = self._calculate() - image = Image.new('1', (width, 250)) # create new image - draw = ImageDraw.Draw(image) - # draw calculated circles - for circle in self.circles: - x, r, txt, size = circle - draw.arc([x - r, 0, x + r, 2 * r], 0, 360, 'white') - if txt != "": - font = ImageFont.truetype(self.font, int(24 * size)) - tox, toy = draw.textsize(txt, font=font) - draw.text((x - tox / 2, r - toy / 2), txt, font=font, fill='#ffffff') + def render_image(self): + """ + Start the calculation and return rendered PIL image object + """ + width = self._calculate() + image = Image.new('1', (width, 250)) # create new image + draw = ImageDraw.Draw(image) + # draw calculated circles + for circle in self.circles: + x, r, txt, size = circle + draw.arc([x - r, 0, x + r, 2 * r], 0, 360, 'white') + if txt != "": + self.centertext(draw, x, r, txt, size) - # draw the spacing between cylinders - spacerY1 = 200 - spacerY2 = 220 - for spacing in self.spacings: - sx1, sx2 = spacing - draw.line((sx1, spacerY1, sx1, spacerY2), fill='#ffffff') - draw.line((sx2, spacerY1, sx2, spacerY2), fill='#ffffff') - txt = "%imm" % (sx2 - sx1) - font = ImageFont.truetype(FONTBASE+"arial.ttf", 12) - tox, toy = draw.textsize(txt, font=font) - draw.text((sx1 + (sx2 - sx1) / 2 - tox / 2, - spacerY2 + toy / 2), txt, font=font, fill='#ffffff') + # draw the spacing between cylinders + spacer_y1 = 200 + spacer_y2 = 220 + for sx1, sx2 in self.spacings: + draw.line((sx1, spacer_y1, sx1, spacer_y2), fill='#ffffff') + draw.line((sx2, spacer_y1, sx2, spacer_y2), fill='#ffffff') + self.centertext(draw, sx1 + (sx2 - sx1) / 2, \ + spacer_y2 + 10, "%imm" % (sx2 - sx1), 0.5) - return image + return image if __name__ == "__main__": - parser = argparse.ArgumentParser(description = \ - "Calculate spacer pipes for pressure cylinder transport\n" +\ - "Known cylinder types:\n" + ", ".join(sorted(CYLINDER.keys())) ) - parser.add_argument('cylinders', metavar = 'cylinder', - type = str, nargs = '+', help = 'cylinder types') - parser.add_argument('--space', dest='space_min', - type = int, default=10, + parser = argparse.ArgumentParser(description=\ + "Calculate spacer pipes for pressure cylinder transport\n" +\ + "Known cylinder types:\n" + ", ".join(sorted(CYLINDER.keys()))) + parser.add_argument('cylinders', metavar='cylinder', \ + type=str, nargs='+', help='cylinder types') + parser.add_argument('--space', dest='space_min', \ + type=int, default=10, \ help='minimum space between cylinders (mm)') - args = parser.parse_args() + options = parser.parse_args() - for test in args.cylinders: - if not test in CYLINDER.keys(): - print "Cylinder type '%s' is unknown" % test - sys.exit(1) + for test in options.cylinders: + if not test in CYLINDER.keys(): + print "Cylinder type '%s' is unknown" % test + sys.exit(1) - obj = CylinderSpacerCalculator( - args.cylinders, args.space_min) + worker = CylinderSpacerCalculator( + options.cylinders, options.space_min) - image = obj.render_image() - image.show() + image = worker.render_image() + image.show() - print "\n------------ START SCAD SCRIPT ------------" - print obj.scad + "------------ END SCAD SCRIPT ------------" + print "\n------------ START SCAD SCRIPT ------------" + print worker.scad + "------------ END SCAD SCRIPT ------------"