This is the complete source code listing for openscad.py. Feel free to copy and paste into a file of that name, and import it into your projects as described in detail in my book, "Python for 3D Printing", by John Clark Craig... Amazon listing



        # openscad.py   

        import os
        import math
        import inspect
        
        # Global commands accumulator
        _cmds = ""
        _use = ""
        
        # Function modifying parameters
        fragments = 31 
        
        # Constants
        tiny = 1e-99
        
        
        # Functions for driving Openscad
        
        
        def startup(s):
            global _use
            _use += f"""{s}\n"""
        
        
        def literally(s):
            global _cmds
            _cmds += f"""{s}\n"""
        
        
        def cyl(diameter, height):
            global _cmds, fragments
            radius = diameter / 2
            _cmds = (
                f"cylinder(h={height},"
                f"r1={radius},r2={radius},"
                f"center=false,$fn={fragments});\n\n"
                ) + _cmds
        
        
        def cylinder(diameter, height):
            global _cmds, fragments
            radius = diameter / 2
            _cmds = (
                f"cylinder(h={height},"
                f"r1={radius},r2={radius},"
                f"center=false,$fn={fragments});\n\n"
                ) + _cmds
        
        
        def cone(diameter, height):
            global _cmds, fragments
            radius = diameter / 2
            _cmds = (
                f"cylinder(h={height},"
                f"r1={radius},r2={0},"
                f"center=false,"
                f"$fn={fragments});\n\n"
                ) + _cmds
        
        
        def cone_truncated(diameter1, diameter2, height): 
            global _cmds, fragments
            radius = diameter1 / 2
            radius2 = diameter2 / 2
            _cmds = (
                f"cylinder(h={height},r1={radius},"
                f"r2={radius2},center=false,"
                f"$fn={fragments});\n\n"
            ) + _cmds
        
        
        def sphere(diameter):
            global _cmds, fragments
            radius = diameter / 2
            _cmds = (f"sphere({radius},"
                f"$fn={fragments});\n\n" ) + _cmds
        
        
        def box(x, y, z):
            global _cmds
            _cmds = (f"cube({[x,y,z]},"
                f"center=false);\n\n") + _cmds
        
        
        def polygon(points_list, height):
            global _cmds, fragments
            _cmds = (
                f"linear_extrude({height})\n"
                f"polygon({points_list},"
                f"convexity=20,$fn={fragments});\n\n"
                ) + _cmds
        
        
        def triangle(point1, point2, point3, height):
            polygon([point1, point2, point3], height)
        
        
        def regular_polygon(sides, radius, height):
            global _cmds
            _cmds = "}\n\n" + _cmds
            for wedge in range(sides):
                p1 = _cart(radius,wedge*360/sides)
                p2 = _cart(radius,(wedge+1)*360/sides)
                triangle([0, 0], p1, p2, height)
            _cmds = "union(){\n" + _cmds
        
        
        def tube(outside_diam, inside_diam, height):
            global _cmds, fragments
            r1 = outside_diam / 2
            r2 = inside_diam / 2
            _cmds = (
                "difference(){\n"
                f"cylinder(h={height},r1={r1},r2={r1},"
                f"center=false,$fn={fragments});\n"
                f"cylinder(h={height*3},r1={r2},r2={r2},"
                f"center=true,$fn={fragments});\n"
                "}\n" ) + _cmds
        
        
        def polyhedron(points_list, faces_list):
            global _cmds
            _cmds = (f"polyhedron({points_list},"
                 f"{faces_list},convexity=20);\n\n"
                 ) + _cmds
        
        
        def text(
            text="",
            size=10,
            font="Liberation Sans",
            halign="left",
            valign="baseline",
            spacing=1,
            direction="ltr",
            language="en",
            script="latin",
            height=1,
        ):
            global _cmds, fragments
            _cmds = (
                f'linear_extrude({height})\n'
                f'text(text="{text}",size={size},'
                f'font="{font}",halign="{halign}",'
                f'valign="{valign}",spacing={spacing},'
                f'direction="{direction}",'
                f'language="{language}",'
                f'script="{script}",'
                f'$fn={fragments});\n\n'
            ) + _cmds
        
        
        def translate(x, y, z):
            global _cmds
            _cmds = f"translate([{x},{y},{z}])\n" + _cmds
        
        
        def rotate(x, y, z):
            global _cmds
            _cmds = f"rotate([{x},{y},{z}])\n" + _cmds
        
        
        def scale(x, y, z):
            global _cmds
            _cmds = f"scale([{x},{y},{z}])\n" + _cmds
        
        
        def resize(x, y, z):
            global _cmds
            _cmds = f"resize([{x},{y},{z}])\n" + _cmds
        
        
        def mirror(x, y, z):
            global _cmds
            _cmds = f"mirror([{x},{y},{z}])\n" + _cmds
        
        
        def color(color_name, alpha=1.0):
            global _cmds
            _cmds = (f'color("{color_name}",'
                f'{alpha})\n') + _cmds
        
        
        def rgb(r, g, b, alpha=1.0):
            global _cmds
            _cmds = (f"color([{r/255},{g/255},"
                f"{b/255},{alpha}])\n") + _cmds
        
        
        def offset_round(distance, height):
            global _cmds, fragments
            _cmds = (
                f"linear_extrude({height})\n"
                f"offset(r={distance},chamfer=false,"
                f"$fn={fragments})\n"
                f"projection()\n" ) + _cmds
        
        
        def offset_straight(distance, height):
            global _cmds
            _cmds = (
                f"linear_extrude({height})\n"
                f"offset(delta={distance},"
                f"chamfer=false)\n"
                f"projection()\n"
                ) + _cmds
        
        
        def offset_chamfer(distance, height):
            global _cmds
            _cmds = (
                f"linear_extrude({height})\n"
                f"offset(delta={distance},"
                f"chamfer=true)\n"
                f"projection()\n"
                ) + _cmds
        
        
        def projection(height):
            global _cmds
            _cmds = (f"linear_extrude({height})"
                f"\nprojection()\n") + _cmds
        
        
        def slice(height):
            global _cmds
            _cmds = (f"linear_extrude({height})\n"
                f"projection(cut=true)\n") + _cmds
        
        
        def spiral(turns, height, scale=1):
            global _cmds, fragments
            _cmds = (
                f"linear_extrude(height={height},"
                f"twist=-{360*turns},"
                f"scale={scale},"
                f"slices={fragments},"
                f"center=false,"
                f"convexity=20,"
                f"$fn={fragments})\n"
                f"projection()\n"
            ) + _cmds
        
        
        def rotate_extrude(angle=360):
            global _cmds, fragments
            _cmds = (
                f"rotate_extrude(angle={angle},"
                f"convexity=20,$fn={fragments})\n"
                f"projection()\n"
            ) + _cmds
        
        
        def union(obj_list):
            global _cmds
            cmd = "union(){\n"
            for obj in obj_list:
                cmd += obj
            cmd += "}\n"
            _cmds = cmd + _cmds
        
        
        def difference(obj_list):
            global _cmds
            cmd = "difference(){\n"
            for obj in obj_list:
                cmd += obj
            cmd += "}\n"
            _cmds = cmd + _cmds
        
        
        def intersection(obj_list):
            global _cmds
            cmd = "intersection(){\n"
            for obj in obj_list:
                cmd += obj
            cmd += "}\n"
            _cmds = cmd + _cmds
        
        
        def hull(obj_list):
            global _cmds
            cmd = "hull(){\n"
            for obj in obj_list:
                cmd += obj
            cmd += "}\n"
            _cmds = cmd + _cmds
        
        
        def minkowski(obj_list):
            global _cmds
            cmd = "minkowski(){\n"
            for obj in obj_list:
                cmd += obj
            cmd += "}\n"
            _cmds = cmd + _cmds
        
        
        def surface(filename,invert="false"):
            global _cmds
            _cmds = (f'surface(file=\"{filename}\",'
                f'convexity=5,'
                f'invert={invert});\n') + _cmds  
        
        
        def result():
            global _cmds
            res = _cmds
            _cmds = ""
            return res
        
        
        def output(cmds_list=""):
            global _use
            calling_file = inspect.stack()[1].filename
            srcfile = calling_file.split("\\")[-1] 
            dstfile = srcfile.replace(".py", ".scad")
            f = open(dstfile, "w")
            if _use:
                f.write(_use)
                _use = ""
            for cmd in cmds_list:
                f.write(cmd)
            f.close()
        
        
        def platonic_cube(n):
            box(n, n, n)
            translate(-n / 2, -n / 2, -n / 2)
        
        
        def platonic_tetrahedron(n):
            n0 = n * (8 / 3) ** -0.5
            n1 = ((8 / 9) ** 0.5) * n0
            n2 = ((2 / 9) ** 0.5) * n0
            n3 = ((2 / 3) ** 0.5) * n0
            n4 = -n0 / 3
            v0 = [n1, 0, n4]
            v1 = [-n2, n3, n4]
            v2 = [-n2, -n3, n4]
            v3 = [0, 0, n0]
            points = [v0, v1, v2, v3]
            faces = [[0, 1, 2], [0, 3, 1],
                     [1, 3, 2], [2, 3, 0]]
            polyhedron(points, faces)
        
        
        def platonic_octahedron(n):
            n0 = n * 2 ** -0.5
            v0 = [n0, 0, 0]
            v1 = [-n0, 0, 0]
            v2 = [0, n0, 0]
            v3 = [0, -n0, 0]
            v4 = [0, 0, n0]
            v5 = [0, 0, -n0]
            points = [v0, v1, v2, v3, v4, v5]
            faces = [
                [0, 4, 2],
                [2, 4, 1],
                [1, 4, 3],
                [3, 4, 0],
                [2, 5, 0],
                [1, 5, 2],
                [3, 5, 1],
                [0, 5, 3],
            ]
            polyhedron(points, faces)
        
        
        def platonic_dodecahedron(n):
            phi = (1 + 5 ** 0.5) / 2
            fac = n * phi / 2
            n0 = phi * fac
            n1 = fac / phi
            v0 = [fac, fac, fac]
            v1 = [-fac, fac, fac]
            v2 = [fac, -fac, fac]
            v3 = [-fac, -fac, fac]
            v4 = [fac, fac, -fac]
            v5 = [-fac, fac, -fac]
            v6 = [fac, -fac, -fac]
            v7 = [-fac, -fac, -fac]
            v8 = [0, n0, n1]
            v9 = [0, -n0, n1]
            v10 = [0, n0, -n1]
            v11 = [0, -n0, -n1]
            v12 = [n1, 0, n0]
            v13 = [n1, 0, -n0]
            v14 = [-n1, 0, n0]
            v15 = [-n1, 0, -n0]
            v16 = [n0, n1, 0]
            v17 = [-n0, n1, 0]
            v18 = [n0, -n1, 0]
            v19 = [-n0, -n1, 0]
            points = [
                v0,
                v1,
                v2,
                v3,
                v4,
                v5,
                v6,
                v7,
                v8,
                v9,
                v10,
                v11,
                v12,
                v13,
                v14,
                v15,
                v16,
                v17,
                v18,
                v19,
            ]
            f1 = [0, 16, 18, 2, 12]
            f2 = [14, 12, 2, 9, 3]
            f3 = [19, 3, 9, 11, 7]
            f4 = [15, 7, 11, 6, 13]
            f5 = [4, 13, 6, 18, 16]
            f6 = [2, 18, 6, 11, 9]
            f7 = [12, 14, 1, 8, 0]
            f8 = [3, 19, 17, 1, 14]
            f9 = [7, 15, 5, 17, 19]
            f10 = [13, 4, 10, 5, 15]
            f11 = [16, 0, 8, 10, 4]
            f12 = [1, 17, 5, 10, 8]
            faces = [f1, f2, f3, f4, f5, f6,
                     f7, f8, f9, f10, f11, f12]
            polyhedron(points, faces)
        
        
        def platonic_icosahedron(n):
            phi = (1 + 5 ** 0.5) / 2
            fac = n / 2
            n0 = phi * fac
            v0 = [0, fac, n0]
            v1 = [0, fac, -n0]
            v2 = [0, -fac, n0]
            v3 = [0, -fac, -n0]
            v4 = [fac, n0, 0]
            v5 = [fac, -n0, 0]
            v6 = [-fac, n0, 0]
            v7 = [-fac, -n0, 0]
            v8 = [n0, 0, fac]
            v9 = [-n0, 0, fac]
            v10 = [n0, 0, -fac]
            v11 = [-n0, 0, -fac]
            points = [v0, v1, v2, v3, v4, v5, v6,
                      v7, v8, v9, v10, v11]
            f1 = [2, 9, 0]
            f2 = [2, 7, 9]
            f3 = [2, 5, 7]
            f4 = [2, 8, 5]
            f5 = [2, 0, 8]
            f6 = [1, 4, 6]
            f7 = [1, 6, 11]
            f8 = [1, 11, 3]
            f9 = [1, 3, 10]
            f10 = [1, 10, 4]
            f11 = [0, 6, 4]
            f12 = [6, 0, 9]
            f13 = [9, 11, 6]
            f14 = [11, 9, 7]
            f15 = [7, 3, 11]
            f16 = [3, 7, 5]
            f17 = [5, 10, 3]
            f18 = [10, 8, 5]
            f19 = [8, 4, 10]
            f20 = [4, 8, 0]
            faces = [
                f1,
                f2,
                f3,
                f4,
                f5,
                f6,
                f7,
                f8,
                f9,
                f10,
                f11,
                f12,
                f13,
                f14,
                f15,
                f16,
                f17,
                f18,
                f19,
                f20,
            ]
            polyhedron(points, faces)
        
        
        # Local utility functions
        
        def _cart(radius, angle):
            rad = math.radians(angle)
            x = radius * math.cos(rad)
            y = radius * math.sin(rad)
            return [x, y]