cylindertransport.py

Mon, 03 Apr 2017 04:19:45 +0200

author
mdd
date
Mon, 03 Apr 2017 04:19:45 +0200
changeset 4
f62562506053
parent 3
d8745f771267
child 5
c2158ae1dc05
permissions
-rw-r--r--

pylint

"""
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(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():
    """
    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 <cylindertransport.scad>\n"
        self.circles = []
        self.spacings = []
        self.margin = 20

    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):
        """
        Push the circle definition for later rendering
        """
        self.circles.append([
            x, r, txt, size
            ])

    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):
            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])

        # 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)

    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):
        """
        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
        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

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, \
        help='minimum space between cylinders (mm)')

    options = parser.parse_args()

    for test in options.cylinders:
        if not test in CYLINDER.keys():
            print "Cylinder type '%s' is unknown" % test
            sys.exit(1)

    worker = CylinderSpacerCalculator(
        options.cylinders, options.space_min)

    image = worker.render_image()
    image.show()

    print "\n------------ START SCAD SCRIPT ------------"
    print worker.scad + "------------ END SCAD SCRIPT ------------"

mercurial