maddening

maddening

lenseD = 60;
depth = 20;
rim = 4;
center=18;

noseparts();

eyeparts();

module noseparts(){
    translate([25,0,0])
        hingepin();
    translate([-14,0,0])
        clip();
}

module eyeparts(){
translate([-30,0,0]) rotate([0,0,-90])
    lensebody(rotar=360*.3);
mirror([1,0,0])
    translate([-45,0,0]) rotate([0,0,-90])
        lensebody(rotar=360*.3, flip=1);
}

module torus(r=lenseD/2, circ=rim){
	rotate_extrude($fn=96){
		translate([r,0,0])
			circle(r=circ, $fn=36);
		}
}

module head(){
	scale([1.3,1.6,1])
		sphere(r=lenseD, $fn=64);
}

module flange(rad=lenseD/2-3, flare=0.4){
	rotate_extrude($fn=64){
		translate([rad,0,0]) rotate([0,0,0])
			difference(){
				scale([1.6,.5,1])
					circle(r=3, $fn=36);
				translate([-2,0,0]) scale([1.6,1,1])
					circle(r=3, center=true);
			}
	}
}

//***********************************************
// *****  nose parts    **************

nose=18;
base=1.2;
shaft=3;
pinh=1;
pinthk=1;
knobrad=2.2;

module hingepin(fat=0){
// the round part everything attaches to
    pinD=nose/2;
    pinring= pinD - 2;
    cylinder(r=pinD/2+1+fat, h=base, $fn=36);
    translate([0,0,base])
        cylinder(r=pinD/2+fat, h=shaft, $fn=30);
    translate([0,0,base+shaft])
        cylinder(r=pinring/2+fat, h=pinh+fat, $fn=48);
    translate([0,0,base+shaft+pinh])
        cylinder(r1=pinring/2+fat, r2=pinring/2+pinthk+fat, h=pinthk, $fn=48);
}

module clip(){
    // the c-clip that holds it all on the hingepin
    bigrad=(nose-4)/2;
    difference(){
        cylinder(r=bigrad, h=pinh*2, $fn=36);
        translate([0,0,-(base+shaft)+0.05])
            hingepin(fat=.13);
        translate([0,bigrad/2, pinthk])
            cube([nose/2-pinthk*2, bigrad, pinthk*3+.1], center=true);
    }
    //knobbies
    for (i=[44,136]){
    translate([bigrad*cos(i), bigrad*sin(i), 0])
        cylinder(r=knobrad, h=pinthk*4, $fn=24);
    }
}

module noseconnect(cut=1){
    // the plate that hooks to hingepin from the goggle-eye
    vital=.75*nose;
    curve= nose/4;
    intersection(){
        difference(){
            minkowski(){
                cube([vital,vital,shaft - 0.1], center=false);
                cylinder(r=curve, h=0.05, center=false, $fn=32);
            }
            translate([vital-curve/2, vital-curve/2, -0.05 - base]){
                hingepin(fat=0.21);
                translate([0,0,base+shaft/2])
                    cylinder(r=vital*.75, h=shaft/2+.1, $fn=36);
            }

        }
        scale([2.2,1,1]) rotate([0,0,0])
            cylinder(r=vital, h=shaft, $fn=64, center=false);
    }
}

module lensebody(rad=lenseD/2, d=depth, rotar=0){
    translate([0,0,8]){
	    difference(){
            rotate([0,0,rotar])
		    union(){
			    cylinder(r=rad, h=d-10, $fn=128, center=true);
			    translate([0,0,-(d/2)])
				    cylinder(r1=rad-rim, r2=rad, h=d-10, $fn=128, center=true);
			    translate([0,0,-3])
				    torus(r=rad-1, circ=2);
			    translate([0,0,4]) rotate([0,6,0])
				    flange();
			    for (i=[1:20:360]){
				    translate([rad*cos(i), rad*sin(i), 0]) rotate([0,0,i])
					    minkowski(){
						    cube([1.5,0.5,10], center=true);
						    rotate([0,90,0])
							    cylinder(r=.5, h=.1, center=true, $fn=16);
					    }
			    }
                if (flip==1){
                translate([24.5,5,-2]) rotate([180,0,33])
                    noseconnect();
                }
                else {
                translate([24.5,5,-5]) rotate([0,0,-57])
                    noseconnect();
                }
            }
		    translate([0,0,-d+7])
			    cylinder(r=rad-1, h=d/2, $fn=128, center=true);
		    translate([0,0,-d/3])
			    cylinder(r=rad-rim, h=d/4, $fn=128, center=true);
		    translate([0,0,d/2])
			    cylinder(r=rad-1, h=d/2+0.1, $fn=128, center=true);
		    translate([0,0,0])
                assign(lenserad = rad - 2.2){
			    cylinder(r=lenserad, h=d/2+2, $fn=128, center=true);
            echo("lense radius (as rendered) is: ", lenserad);
            echo("so the diameter is: ", 2*lenserad);}
		    translate([0,d*1.2,d*2.9])
			    head();
        }
	}
}

lenseD = 60;
depth = 20;
rim = 4;
center=18;

flange();

mirror([1,0,0])
	translate([-lenseD/2-center/2,0,0])
		lensebody();

translate([-lenseD/2-center/2,0,0])
	lensebody();
//torus();

module torus(r=lenseD/2, circ=rim){
	rotate_extrude($fn=96){
		translate([r,0,0])
			circle(r=circ, $fn=36);
		}
}

module head(){
	scale([4,7,1])
		sphere(r=lenseD, $fn=64);
}

module flange(rad=lenseD/2, flare=0.4){
	rotate_extrude($fn=64){
		translate([rad,0,0]) rotate([0,0,0])
			difference(){
				scale([1.6,1,1])
					circle(r=3, $fn=36);
				translate([-2,0,0]) scale([1.6,1,1])
					circle(r=3, center=true);
			}
	}
}

module lensebody(rad=lenseD/2, d=depth){
	difference(){
		union(){
			cylinder(r=rad, h=d-10, $fn=128, center=true);
			translate([0,0,-(d/2)])
				cylinder(r1=rad-rim, r2=rad, h=d-10, $fn=128, center=true);
			translate([0,0,-3])
				torus(r=rad-1, circ=2);
			translate([0,0,4]) rotate([0,6,0])
				flange();
			for (i=[1:20:360]){
				translate([rad*cos(i), rad*sin(i), 0]) rotate([0,0,i])
					minkowski(){
						cube([1.5,0.5,10], center=true);
						rotate([0,90,0])
							cylinder(r=.5, h=.1, center=true, $fn=16);
					}
			}
		}
		translate([0,0,-d+7])
			cylinder(r=rad-1, h=d/2, $fn=128, center=true);
		translate([0,0,-depth/3])
			cylinder(r=rad-rim, h=d/4, $fn=128, center=true);
		translate([0,0,d/2])
			cylinder(r=rad-1, h=d/2+0.1, $fn=128, center=true);
		translate([0,0,0])
			cylinder(r=rad-3, h=d/2, $fn=128, center=true);
		translate([1.7*rad*2,d*2.1,d*2.8])
			head();
	}
}


fanduct: add fan

fanduct: add fan

include <config.scad>
include <fanduct.h>
use <scad-utils/transformations.scad>
use <scad-utils/lists.scad>
use <scad-utils/shapes.scad>
use <list-comprehension-demos/sweep.scad>
use <scad-utils/shapes.scad>
use <list-comprehension-demos/skin.scad>
use <thing_libutils/misc.scad>
use <thing_libutils/splines.scad>
use <thing_libutils/sweep.scad>
use <thing_libutils/transforms.scad>
use <thing_libutils/shapes.scad>
use <thing_libutils/attach.scad>

use <thing_libutils/Naca_sweep.scad>

include <fan_5015S.scad>

function vec_xyz(v) = [for(v=v) let(v_=v) [v_[0],v_[1], v_[2]]];

// helper functions to work on headered-arrays (where first entry is column headers)
function nSpline_header(S, N) =
let(slice = v_slice(S,start=1))
let(filtered = filter(slice,U))
concat(
    [S[0]],
    nSpline(filtered, N)
    );

// data for duct geometry
// ss parameter is initial size of duct (e.g. for fan throat/output)
// returned data has a header row
// warning, don't set r to 0 or rendering breaks!
function gen_data(ss) = [
    [       "Tx","Ty","Tz",     "Sx",   "Sy", "Rx", "Ry",  "r"],
    [     ss.x/4, -27,  55,   ss.x/2,   ss.y,   13,    0,  .01],
    [ 2.5+ss.x/4, -25,  49, 5+ss.x/2, 1+ss.y,   13,    0,  .1 ],
    [         15, -18,  40,       11,     20,   13,    0,  .4 ],
    [         17,  -8,  20,       11,     17,   11,  -10,  .5 ],
    [         14,  -3,  10,        8,     15,    8,  -25,  .7 ],
    [         11,   0,   4,        6,     13,    0,  -45,  .8 ],
];

fanduct_data = gen_data(fanduct_throat_size_withwall);

module duct(A, fanduct_wallthick=2, N=100, d=2)
{
    // rounded bugs out when rendering if r value is 0 :/
    function shape_profile(size,r) = rounded_rectangle_profile(size=size,r=r);
    /*function shape_profile(size,r) = rectangle_profile(size);*/

    module showcontrols(V)
    {
        function gen_T(V) =
        [
           for (i=[0:len(V)-2])
           let(R = vec3(geth(V,["Rx","Ry"],i)))
           let(T = geth(V,["Tx","Ty","Tz"],i))
           translation(T)*rotation(v_mul(R,[1,-1,1]))
        ];

        T = gen_T(A);
        for (i=[0:len(T)-1])
        let(t=T[i])
        multmatrix(t)
        color("red")
        let(S = [geth(A,"Sx",i),geth(A,"Sy",i)])
        let(r = geth(A,"r",i))
        let(r_ = [.5*r*S.x, .5*r*S.y])
        let(h=.5)
        difference()
        {
            linear_extrude(height=h,center=true)
            polygon(shape_profile(size=S+[1,1], r=r_));

            linear_extrude(height=h+.1,center=true)
            polygon(shape_profile(size=S, r=r_));

            /*torus(r=a[3], radial_width=1.5);*/
        }

        function gen_dat(V) =
           [
           for (i=[0:len(V)-2])
               let(S = geth(V,["Sx","Sy"],i))
               let(R = vec3(geth(V,["Rx","Ry"],i)))
               let(T = geth(V,["Tx","Ty","Tz"],i))
               let(dat = R_(R.x, R.y, R.z, v=to_3d(circle_profile(r=.5))))
               T_(T.x,T.y,T.z, dat)
           ];
        outer = gen_dat(nSpline_header(A,N));    // outer skin
        skin(outer, close = true, slices = true);
    }

    // generate transformed shapes
    function gen_dat(V) =
       [
       for (i=[0:len(V)-2])
           let(S = geth(V,["Sx","Sy"],i))
           let(r = [S.x*.5*geth(V,"r",i), S.y*.5*geth(V,"r",i)])
           let(R = vec3(geth(V,["Rx","Ry"],i)))
           let(T = geth(V,["Tx","Ty","Tz"],i))
           let(dat = R_(R.x, R.y, R.z, v=to_3d(shape_profile(size=S,r=r))))
           T_(T.x,T.y,T.z, dat)
       ];

    // inner skin data: modify tube diameter
    function modify_inner(S, subtract) = array_header_col_subtract(S, ["Sx", "Sy"], subtract);

    module sweepshape(V_outer,interp=true)
    {
        // inner skin is outer, but smaller size and reversed (so it will go back to start and join)
        V_inner = modify_inner(reverse_header(V_outer), fanduct_wallthick);
        if(interp)
        {
            outer = gen_dat(nSpline_header(V_outer,N));    // outer skin
            inner = gen_dat(nSpline_header(V_inner,N));    // inner skin
            sweep(concat(outer, inner), close = true, showslices = false);
            /*sweep(outer, close = true, showslices = false);*/
            /*skin(concat(outer, inner), loop=true);*/
        }
        else
        {
            outer = gen_dat(V_outer);    // outer skin
            inner = gen_dat(V_inner);    // inner skin
            sweep(concat(outer, inner), close = true, showslices = true);
        }
    }

    function mirrorshape(S, size) =
    let(cols = header_col_index(S,["Tx","Rx","Ry"]))
    concat([S[0]],
    [
        for(i=[1:len(S)-1])
        [
        for(j=[0:len(S[0])-1])
            v_contains(cols,j) ? -S[i][j] :
            S[i][j]
        ]
    ]);

    // debug experiment, mirror + join path for both duct arms
    /*A_ = mirrorshape(A);*/
    /*A__ = reverse_header(A_);*/
    /*A___ = concat_header(A__,A);*/
    /*sweepshape(A___,true);*/

    if($preview_mode)
    {
        /*tx(15)*/
        /*sweep(gen_dat(A), close = false, slices = true);*/

        /*tx(-15)*/
        showcontrols(A);

        sweepshape(A,true);
    }
    else
    {
        sweepshape(A,true);
    }
}

module mirrorcopy(axis)
{
    children();
    mirror(axis)
    children();
}

use <thing_libutils/attach.scad>
use <thing_libutils/shapes.scad>
use <thing_libutils/transforms.scad>

module fanduct_throat(throat_seal_h)
{
    difference()
    {
        union()
        {
            hull()
            {
                ty((fanduct_throat_size_withwall.y)/2)
                cubea([fanduct_throat_size.x,fanduct_throat_size.y,throat_seal_h]+XY*fanduct_wallthick,align=Z-Y);

                tz(seal_height)
                ty(fanduct_throat_size.y/2 - 47)
                tz(11.34/2)
                cylindera(h=fanduct_throat_size_withwall.x, d=7, orient=X);
            }

        }

        tz(-.1)
        cubea([fanduct_throat_size.x,fanduct_throat_size.y,1000],align=Z);

        seal_height = 4;
        tz(seal_height)
        {
            ty((fanduct_throat_size.y)/2)
            cubea([fanduct_throat_size.x,1000,1000],align=Z-Y, extra_size=.1*Y, extra_align=-Z);

            ty(-(fanduct_throat_size_withwall.y)/2)
            tz(-5)
            cubea([fanduct_throat_size.x,1000,1000],align=Z-Y, extra_size=.1*Y, extra_align=-Z);

            tz(-seal_height+2*mm)
            cubea([3.8,1000,1000],align=Z+Y);

            ty(fanduct_throat_size.y/2 - 47)
            tz(11.34/2)
            cylindera(h=1000, d=3.65, orient=X);
        }
    }

    ry(45)
    cubea([fanduct_wallthick/sqrt(2),fanduct_throat_size_withwall.y,fanduct_wallthick/sqrt(2)]);
}

module fanduct()
{
    $fn=is_build?$fn:48;

    A = fanduct_data;

    mirrorcopy(X)
    {
        duct(A=A, fanduct_wallthick=fanduct_wallthick, N=$fn, debug=true);
    }

    tx(-geth(A,"Tx",0))
    t(geth(A,["Tx","Ty","Tz"],0))
    r(geth(A,["Rx","Ry","Rz"],0))
    {
        throat_seal_h = 13*mm;

        fanduct_conn_fan = [[0,0,-throat_seal_h/2],-Z];
        attach(fanduct_conn_fan, fan_5015S_conn_flowoutput, 90)
        fan_5015S();

        fanduct_throat(throat_seal_h);
    }
}

module part_fanduct()
{
    ry(180)
    t(-13)
    t(-geth(fanduct_data,["Tx","Ty","Tz"],0))
    r(-geth(fanduct_data,["Rx","Ry","Rz"],0))
    difference()
    fanduct();
}

fanduct();
/*part_fanduct();*/

include <config.scad>
include <fanduct.h>
use <scad-utils/transformations.scad>
use <scad-utils/lists.scad>
use <scad-utils/shapes.scad>
use <list-comprehension-demos/sweep.scad>
use <scad-utils/shapes.scad>
use <list-comprehension-demos/skin.scad>
use <thing_libutils/misc.scad>
use <thing_libutils/splines.scad>
use <thing_libutils/sweep.scad>
use <thing_libutils/transforms.scad>
use <thing_libutils/shapes.scad>
use <thing_libutils/attach.scad>

use <thing_libutils/Naca_sweep.scad>

function vec_xyz(v) = [for(v=v) let(v_=v) [v_[0],v_[1], v_[2]]];

// helper functions to work on headered-arrays (where first entry is column headers)
function nSpline_header(S, N) =
let(slice = v_slice(S,start=1))
let(filtered = filter(slice,U))
concat(
    [S[0]],
    nSpline(filtered, N)
    );

// data for duct geometry
// ss parameter is initial size of duct (e.g. for fan throat/output)
// returned data has a header row
// warning, don't set r to 0 or rendering breaks!
function gen_data(ss) = [
    [       "Tx","Ty","Tz",     "Sx",   "Sy", "Rx", "Ry",  "r"],
    [     ss.x/4, -27,  55,   ss.x/2,   ss.y,   13,    0,  .01],
    [ 2.5+ss.x/4, -25,  49, 5+ss.x/2, 1+ss.y,   13,    0,  .1 ],
    [         15, -18,  40,       11,     20,   13,    0,  .4 ],
    [         17,  -8,  20,       11,     17,   11,  -10,  .5 ],
    [         14,  -3,  10,        8,     15,    8,  -25,  .7 ],
    [         11,   0,   4,        6,     13,    0,  -45,  .8 ],
];

fanduct_data = gen_data(fanduct_throat_size_withwall);

module duct(A, fanduct_wallthick=2, N=100, d=2)
{
    // rounded bugs out when rendering if r value is 0 :/
    function shape_profile(size,r) = rounded_rectangle_profile(size=size,r=r);
    /*function shape_profile(size,r) = rectangle_profile(size);*/

    module showcontrols(V)
    {
        function gen_T(V) =
        [
           for (i=[0:len(V)-2])
           let(R = vec3(geth(V,["Rx","Ry"],i)))
           let(T = geth(V,["Tx","Ty","Tz"],i))
           translation(T)*rotation(v_mul(R,[1,-1,1]))
        ];

        T = gen_T(A);
        for (i=[0:len(T)-1])
        let(t=T[i])
        multmatrix(t)
        color("red")
        let(S = [geth(A,"Sx",i),geth(A,"Sy",i)])
        let(r = geth(A,"r",i))
        let(r_ = [.5*r*S.x, .5*r*S.y])
        let(h=.5)
        difference()
        {
            linear_extrude(height=h,center=true)
            polygon(shape_profile(size=S+[1,1], r=r_));

            linear_extrude(height=h+.1,center=true)
            polygon(shape_profile(size=S, r=r_));

            /*torus(r=a[3], radial_width=1.5);*/
        }

        function gen_dat(V) =
           [
           for (i=[0:len(V)-2])
               let(S = geth(V,["Sx","Sy"],i))
               let(R = vec3(geth(V,["Rx","Ry"],i)))
               let(T = geth(V,["Tx","Ty","Tz"],i))
               let(dat = R_(R.x, R.y, R.z, v=to_3d(circle_profile(r=.5))))
               T_(T.x,T.y,T.z, dat)
           ];
        outer = gen_dat(nSpline_header(A,N));    // outer skin
        skin(outer, close = true, slices = true);
    }

    // generate transformed shapes
    function gen_dat(V) =
       [
       for (i=[0:len(V)-2])
           let(S = geth(V,["Sx","Sy"],i))
           let(r = [S.x*.5*geth(V,"r",i), S.y*.5*geth(V,"r",i)])
           let(R = vec3(geth(V,["Rx","Ry"],i)))
           let(T = geth(V,["Tx","Ty","Tz"],i))
           let(dat = R_(R.x, R.y, R.z, v=to_3d(shape_profile(size=S,r=r))))
           T_(T.x,T.y,T.z, dat)
       ];

    // inner skin data: modify tube diameter
    function modify_inner(S, subtract) = array_header_col_subtract(S, ["Sx", "Sy"], subtract);

    module sweepshape(V_outer,interp=true)
    {
        // inner skin is outer, but smaller size and reversed (so it will go back to start and join)
        V_inner = modify_inner(reverse_header(V_outer), fanduct_wallthick);
        if(interp)
        {
            outer = gen_dat(nSpline_header(V_outer,N));    // outer skin
            inner = gen_dat(nSpline_header(V_inner,N));    // inner skin
            sweep(concat(outer, inner), close = true, showslices = false);
            /*sweep(outer, close = true, showslices = false);*/
            /*skin(concat(outer, inner), loop=true);*/
        }
        else
        {
            outer = gen_dat(V_outer);    // outer skin
            inner = gen_dat(V_inner);    // inner skin
            sweep(concat(outer, inner), close = true, showslices = true);
        }
    }

    function mirrorshape(S, size) =
    let(cols = header_col_index(S,["Tx","Rx","Ry"]))
    concat([S[0]],
    [
        for(i=[1:len(S)-1])
        [
        for(j=[0:len(S[0])-1])
            v_contains(cols,j) ? -S[i][j] :
            S[i][j]
        ]
    ]);

    // debug experiment, mirror + join path for both duct blades
    /*A_ = mirrorshape(A);*/
    /*A__ = reverse_header(A_);*/
    /*A___ = concat_header(A__,A);*/
    /*sweepshape(A___,true);*/

    if($preview_mode)
    {
        /*tx(15)*/
        /*sweep(gen_dat(A), close = false, slices = true);*/

        /*tx(-15)*/
        showcontrols(A);

        sweepshape(A,true);
    }
    else
    {
        sweepshape(A,true);
    }
}

module mirrorcopy(axis)
{
    children();
    mirror(axis)
    children();
}

module fanduct()
{
    $fn=32;

    A = fanduct_data;

    mirrorcopy(X)
    {
        duct(A=A, fanduct_wallthick=fanduct_wallthick, N=$fn, debug=true);
    }

    tx(-geth(A,"Tx",0))
    t(geth(A,["Tx","Ty","Tz"],0))
    r(geth(A,["Rx","Ry","Rz"],0))
    {
        difference()
        {
            union()
            {
                hull()
                {
                    ty((fanduct_throat_size_withwall.y)/2)
                    cubea([fanduct_throat_size.x,fanduct_throat_size.y,13]+[fanduct_wallthick,fanduct_wallthick,0],align=Z-Y);

                    tz(seal_height)
                    ty(fanduct_throat_size.y/2 - 47)
                    tz(11.34/2)
                    cylindera(h=fanduct_throat_size_withwall.x, d=7, orient=X);
                }

            }

            tz(-.1)
            cubea([fanduct_throat_size.x,fanduct_throat_size.y,1000],align=Z);

            seal_height = 4;
            tz(seal_height)
            {
                ty((fanduct_throat_size.y)/2)
                cubea([fanduct_throat_size.x,1000,1000],align=Z-Y, extra_size=.1*Y, extra_align=-Z);

                ty(-(fanduct_throat_size_withwall.y)/2)
                tz(-5)
                cubea([fanduct_throat_size.x,1000,1000],align=Z-Y, extra_size=.1*Y, extra_align=-Z);

                tz(-seal_height+2*mm)
                cubea([3.8,1000,1000],align=Z+Y);

                ty(fanduct_throat_size.y/2 - 47)
                tz(11.34/2)
                cylindera(h=1000, d=3.65, orient=X);
            }
        }

        ry(45)
        cubea([fanduct_wallthick/sqrt(2),fanduct_throat_size_withwall.y,fanduct_wallthick/sqrt(2)]);
    }
}

module part_fanduct()
{
    ry(180)
    t(-13)
    t(-geth(fanduct_data,["Tx","Ty","Tz"],0))
    r(-geth(fanduct_data,["Rx","Ry","Rz"],0))
    difference()
    fanduct();
}

fanduct();
/*part_fanduct();*/


transforms: make size_align tolerate U args for extra* params

/*include <../scad-utils/transformations.scad>*/
include <system.scad>
use <misc.scad>

module t(dist)
{
    translate(dist)
    children();
}

module tx(dist)
{
    translate(X*dist)
    children();
}

module ty(dist)
{
    translate(Y*dist)
    children();
}

module tz(dist)
{
    translate(Z*dist)
    children();
}

// translate children
module position(positions)
{
    assert(positions!=U, "positions==U");
    for(pos=positions)
    translate(pos)
    children();
}

// translate children
module linup(arr=U)
{
    if($children>0)
    {
        for (i = [0 : $children-1])
        translate(arr[i]) children(i);
    }
}

module stack(dist=10, distances=U, axis=Z)
{
    if($children>0)
    {
        if(dist == U && distances != U)
        {
            for (i = [0:len(distances)-1])
            {
                offset = v_sum(distances,i);
                translate(axis*offset)
                    child(i);
            }
        }
        else if(dist != U && distances == U)
        {
            for (i = [0 : $children-1])
                translate(axis*(dist*i))
                    children(i);
        }
    }
}

module orient(axis=U, axis_ref=U, roll=0, extra_roll, extra_roll_orient)
{
    rotate(extra_roll_orient==U||extra_roll==U?0:extra_roll*extra_roll_orient)

    // orient to reference axis
    /*rotate(axis_ref==U?0:_orient_angles(axis_ref))*/
    /*multmatrix(axis_ref==U?0:v_rotate(90, axis_ref))*/
    rotate(axis_ref==U?0:_orient_angles(axis_ref))

    // roll around orient axis
    rotate(axis==U?0:roll*axis)

    // orient to axis
    /*multmatrix(axis==U?0:v_rotate(90, axis))*/
    rotate(axis==U?0:_orient_angles(axis))
    children();
}

module orient_(axis=U, axis_ref=U, roll=0, extra_roll, extra_roll_orient)
{
    rotate(extra_roll_orient==U||extra_roll==U?0:extra_roll*extra_roll_orient)

    // orient to reference axis
    multmatrix(axis_ref==U?0:v_rotate(90, axis_ref))

    // roll around orient axis
    rotate(axis==U?0:roll*axis)

    // orient to axis
    multmatrix(axis_ref==U?0:v_rotate(90, axis))
    children();
}

function _orient_bounds(orient, size) =
    (_rotate_matrix(_orient_angles(orient)) * [size.x,size.y,size.z,1]);

function _orient_t(orient, align, size) =
    let(bounds = _orient_bounds(orient, size))
    (hadamard(align, [abs(bounds.x/2),abs(bounds.y/2),abs(bounds.z/2)]));

module size_align(size=[10,10,10], extra_size=N, align=N, extra_align=N, orient=Z, orient_ref=Z, roll=0, extra_roll, extra_roll_orient)
{
    t = orient==U?N:_orient_t(orient, align, size);
    extra_t = (orient==U||extra_size==U||extra_size==[U,U,U]||extra_size==[0,0,0]||extra_align==U) ?
        N : _orient_t(orient, extra_align, extra_size);
    translate(t+extra_t)
    {
        orient(axis=orient, axis_ref=orient_ref, roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
        {
            children();
        }
    }
}

module hull_pairwise()
{
    for (i= [1:1:$children-1])
    {
        hull()
        {
            children(i-1);
            children(i);
        }
    }
}

module proj_extrude_axis(axis=Z, h=1, offset=0, cut=false)
{
    translate(-offset*axis)
    hull()
    {
        orient_(axis=axis, axis_ref=Z)
        {
            linear_extrude(h, center=false)
            projection(cut=cut)
            orient_(axis=Z, axis_ref=axis)
            translate(offset*axis)
            children();

            orient_(axis=Z, axis_ref=axis)
            translate(offset*axis)
            children();
        }
    }
}

module all_axes()
{
    for($axis=concat(AXES,-AXES))
    {
        $color = v_abs($axis*.3 + v_clamp(v_sign($axis),0,1)*.7);
        children();
    }
}

// test proj_extruder_axis
if(false)
{
    all_axes()
    color($color)
    translate(5*$axis)
    proj_extrude_axis(axis=$axis)
    translate(20*$axis)
    sphere(d=10);
}

if(false)
{
    translate(Y*10)
    proj_extrude_axis(axis=Y, offset=10)
    {
        sphere(d=10);
    }
}

/*include <../scad-utils/transformations.scad>*/
include <system.scad>
use <misc.scad>

module t(dist)
{
    translate(dist)
    children();
}

module tx(dist)
{
    translate(X*dist)
    children();
}

module ty(dist)
{
    translate(Y*dist)
    children();
}

module tz(dist)
{
    translate(Z*dist)
    children();
}

// translate children
module position(positions)
{
    assert(positions!=U, "positions==U");
    for(pos=positions)
    translate(pos)
    children();
}

// translate children
module linup(arr=U)
{
    if($children>0)
    {
        for (i = [0 : $children-1])
        translate(arr[i]) children(i);
    }
}

module stack(dist=10, distances=U, axis=Z)
{
    if($children>0)
    {
        if(dist == U && distances != U)
        {
            for (i = [0:len(distances)-1])
            {
                offset = v_sum(distances,i);
                translate(axis*offset)
                    child(i);
            }
        }
        else if(dist != U && distances == U)
        {
            for (i = [0 : $children-1])
                translate(axis*(dist*i))
                    children(i);
        }
    }
}

module orient(axis=U, axis_ref=U, roll=0, extra_roll, extra_roll_orient)
{
    rotate(extra_roll_orient==U||extra_roll==U?0:extra_roll*extra_roll_orient)

    // orient to reference axis
    /*rotate(axis_ref==U?0:_orient_angles(axis_ref))*/
    /*multmatrix(axis_ref==U?0:v_rotate(90, axis_ref))*/
    rotate(axis_ref==U?0:_orient_angles(axis_ref))

    // roll around orient axis
    rotate(axis==U?0:roll*axis)

    // orient to axis
    /*multmatrix(axis==U?0:v_rotate(90, axis))*/
    rotate(axis==U?0:_orient_angles(axis))
    children();
}

module orient_(axis=U, axis_ref=U, roll=0, extra_roll, extra_roll_orient)
{
    rotate(extra_roll_orient==U||extra_roll==U?0:extra_roll*extra_roll_orient)

    // orient to reference axis
    multmatrix(axis_ref==U?0:v_rotate(90, axis_ref))

    // roll around orient axis
    rotate(axis==U?0:roll*axis)

    // orient to axis
    multmatrix(axis_ref==U?0:v_rotate(90, axis))
    children();
}

function _orient_bounds(orient, size) =
    (_rotate_matrix(_orient_angles(orient)) * [size.x,size.y,size.z,1]);

function _orient_t(orient, align, size) =
    let(bounds = _orient_bounds(orient, size))
    (hadamard(align, [abs(bounds.x/2),abs(bounds.y/2),abs(bounds.z/2)]));

module size_align(size=[10,10,10], extra_size=N, align=N, extra_align=N, orient=Z, orient_ref=Z, roll=0, extra_roll, extra_roll_orient)
{
    t = orient==U?N:_orient_t(orient, align, size);
    /*t_ = orient_ref==U?N:_orient_t(orient_ref, align, size);*/
    extra_t = (orient==U||extra_size==U)?N:_orient_t(orient, extra_align, extra_size);
    translate(t+extra_t)
    {
        orient(axis=orient, axis_ref=orient_ref, roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
        {
            children();
        }
    }
}

module hull_pairwise()
{
    for (i= [1:1:$children-1])
    {
        hull()
        {
            children(i-1);
            children(i);
        }
    }
}

module proj_extrude_axis(axis=Z, h=1, offset=0, cut=false)
{
    translate(-offset*axis)
    hull()
    {
        orient_(axis=axis, axis_ref=Z)
        {
            linear_extrude(h, center=false)
            projection(cut=cut)
            orient_(axis=Z, axis_ref=axis)
            translate(offset*axis)
            children();

            orient_(axis=Z, axis_ref=axis)
            translate(offset*axis)
            children();
        }
    }
}

module all_axes()
{
    for($axis=concat(AXES,-AXES))
    {
        $color = v_abs($axis*.3 + v_clamp(v_sign($axis),0,1)*.7);
        children();
    }
}

// test proj_extruder_axis
if(false)
{
    all_axes()
    color($color)
    translate(5*$axis)
    proj_extrude_axis(axis=$axis)
    translate(20*$axis)
    sphere(d=10);
}

if(false)
{
    translate(Y*10)
    proj_extrude_axis(axis=Y, offset=10)
    {
        sphere(d=10);
    }
}


xaxis/ends: position idleholders better for print

include <MCAD/motors.scad>
include <MCAD/stepper.scad>

include <config.scad>
include <thing_libutils/misc.scad>
include <thing_libutils/bearing.scad>
use <thing_libutils/metric-screw.scad>
use <thing_libutils/timing-belts.scad>

motor_mount_wall_thick = xaxis_pulley[1] - xaxis_pulley[0]/2 + 4*mm;
xaxis_end_motorsize = lookup(NemaSideSize,xaxis_motor);
xaxis_end_motor_offset=[xaxis_end_motorsize/2+zaxis_bearing[1]/2-1*mm,motor_mount_wall_thick-2*mm,0];
xaxis_end_wz = xaxis_rod_distance+zaxis_bearing[2]+5*mm;

// overlap of the X and Z rods
xaxis_end_xz_rod_overlap = 4*mm;

// how much "stop" for the x rods
xaxis_end_rod_stop = 10*mm;

xaxis_endstop_size = [10.3*mm, 20*mm, 6.3*mm];
xaxis_endstop_screw_offset = [-1.8*mm, 0*mm, 0*mm];

module xaxis_end_body(part, with_motor, beltpath_index=0, nut_top=false)
{
    nut_h = zaxis_nut[4];
    wx = zaxis_bearing[1]/2+zaxis_nut[1];
    wx_ = with_motor? xaxis_end_motorsize+xaxis_end_motor_offset[0] - xaxis_end_motorsize/2 : wx;
    bearing_sizey= zaxis_bearing[1]+10*mm;

    if(part==undef)
    {
        difference()
        {
            xaxis_end_body(part="pos", with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top);
            xaxis_end_body(part="neg", with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top);
        }
    }
    else if(part=="pos")
    {
        if(with_motor)
        {
            screw_dist = lookup(NemaDistanceBetweenMountingHoles, xaxis_motor);
            for(x=[-1,1])
            for(z=[-1,1])
            translate([0,0,xaxis_beltpath_z_offsets[beltpath_index]])
            translate(xaxis_end_motor_offset)
            {
                translate([x*screw_dist/2, 0, z*screw_dist/2])
                cylindera(d=lookup(ThreadSize,xaxis_motor_thread)+4*mm, h=motor_mount_wall_thick, orient=[0,1,0], align=[0,-1,0], rounding_radius=2);
            }
        }

        // nut mount
        mirror([0,0,nut_top?1:0])
        translate([zaxis_rod_screw_distance_x, -xaxis_zaxis_distance_y, -xaxis_end_wz/2])
        {
            // main nut support
            cylindera(h=zaxis_nut[4], d=zaxis_nut[0]+5*mm, align=[0,0,1], round_radius=2);

            // nut screw holes support
            for(x=[-1,1])
            translate([x*zaxis_nut[5], 0, 0])
            cylindera(h=zaxis_nut[4], d=lookup(ThreadSize, zaxis_nut[6])+5*mm, align=[0,0,1], round_radius=2);

            // lead screw
            // ensure some support for the leadscrew cutout all the way to the top
            cylindera(h=xaxis_end_wz, d=zaxis_nut[2]*2, align=[0,0,1], round_radius=2);
        }

        // x axis rod holders
        xaxis_rod_d_support = xaxis_rod_d+5*mm;
        for(z=[-1,1])
        translate([-xaxis_end_xz_rod_overlap,0,z*(xaxis_rod_distance/2)])
        cylindera(h=wx_+xaxis_end_xz_rod_overlap, d=xaxis_rod_d_support, orient=[1,0,0], align=[1,0,0], round_radius=2);

        translate([wx_,0,(xaxis_rod_distance/2)+xaxis_rod_d])
        {
            rcubea(xaxis_endstop_size, align=[-1,0,-1]);
        }

        // support around z axis bearings
        translate([0, -xaxis_zaxis_distance_y, 0])
        cylindera(h=xaxis_end_wz,d=bearing_sizey, orient=[0,0,1], align=[0,0,0], round_radius=2);
    }
    else if(part=="neg")
    {
        // cut support around z axis bearings
        translate([0, -xaxis_zaxis_distance_y, 0])
        translate([0,0,.1])
        cubea([bearing_sizey/2+.1, bearing_sizey+.2, xaxis_end_wz+.4], orient=[0,0,1], align=[-1,0,0]);
    }
    else if(part=="vit")
    {
    }
}

module xaxis_end(part, with_motor=false, stop_x_rods=false, beltpath_index=0, show_motor=false, nut_top=false, show_nut=false, show_rods=false, show_bearings=false)
{
    nut_h = zaxis_nut[4];
    wx = zaxis_bearing[1]/2+zaxis_nut[1];
    wx_ = with_motor? xaxis_end_motorsize+xaxis_end_motor_offset[0] - xaxis_end_motorsize/2 : wx;
    extrasize = with_motor?0*mm:0*mm;
    extrasize_align = 1;

    if(part==undef)
    {
        difference()
        {
            xaxis_end(part="pos", with_motor=with_motor, stop_x_rods=stop_x_rods, beltpath_index=beltpath_index, show_motor=show_motor, nut_top=nut_top, show_nut=show_nut, show_rods=show_rods, show_bearings=show_bearings);
            xaxis_end(part="neg", with_motor=with_motor, stop_x_rods=stop_x_rods, beltpath_index=beltpath_index, show_motor=show_motor, nut_top=nut_top, show_nut=show_nut, show_rods=show_rods, show_bearings=show_bearings);
        }
    }
    else if(part=="pos")
    {
        hull()
        {
            xaxis_end_body(with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top);

            // projection against z, ensure easy print
            translate([0,0,-xaxis_end_wz/2])
                linear_extrude(1)
                projection(cut=false)
                xaxis_end_body(with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top);
        }
    }
    else if(part=="neg")
    {
        //endstop mount screw cuts
        translate([wx_,0,(xaxis_rod_distance/2)+xaxis_rod_d])
        translate(xaxis_endstop_screw_offset)
        for(y=[-1,1])
        translate([-5*mm,y*9.5*mm/2,-2*mm])
        nut_trap_cut(nut=MHexNutM3, screw_l=6*mm, screw_l_extra=0*mm, trap_axis=[-1,0,0], orient=[0,0,1], align=[0,0,-1]);

        xaxis_end_beltpath(height=xaxis_beltpath_height_body, width=xaxis_beltpath_width);

        // z smooth bearing mounts
        for(z=[-1,1])
        {
            translate([0,0,z*xaxis_rod_distance/2])
            translate([0, -xaxis_zaxis_distance_y, 0])
            {
                bearing_mount_holes(
                        bearing_type=zaxis_bearing,
                        ziptie_type=ziptie_type,
                        ziptie_bearing_distance=ziptie_bearing_distance,
                        orient=[0,0,1],
                        with_zips=true,
                        show_zips=false
                        );
                /*hull()*/
                /*{*/
                    /*cylindera(d=zaxis_bearing[1],h=xaxis_end_wz);*/

                    /*translate([-5*cm,0,0])*/
                    /*cylindera(d=zaxis_bearing[1],h=xaxis_end_wz);*/
                /*}*/
            }
        }

        // x smooth rods
        x_rod_stop = stop_x_rods ? xaxis_end_rod_stop : 0;
        for(z=[-1,1])
        translate([-xaxis_end_xz_rod_overlap-.1+x_rod_stop,0,z*(xaxis_rod_distance/2)])
        cylindera(h=wx_+xaxis_end_xz_rod_overlap-x_rod_stop+.2,d=xaxis_rod_d+.2*mm, orient=[1,0,0], align=[1,0,0]);

        if(with_motor)
        {
            screw_dist = lookup(NemaDistanceBetweenMountingHoles, xaxis_motor);

            // axle
            translate([0,0,xaxis_beltpath_z_offsets[beltpath_index]])
            translate(xaxis_end_motor_offset)
            {
                round_d=1.1*lookup(NemaRoundExtrusionDiameter, xaxis_motor);
                translate([0, .1, 0])
                translate([0,1,0])
                teardrop(d=round_d,h=motor_mount_wall_thick, tear_orient=[0,0,1], orient=[0,1,0], align=[0,-1,0], roll=90, truncate=0);

                // motor axle
                translate([0, .1, 0])
                cylindera(d=1.2*lookup(NemaAxleDiameter, xaxis_motor), h=lookup(NemaFrontAxleLength, xaxis_motor)+3*mm, orient=[0,1,0], align=[0,1,0]);

                // bearing for offloading force on motor shaft
                translate([0, -2.5*mm-xaxis_pulley[1]-.1, 0])
                scale(1.03)
                cylindera(d=bearing_MR105[1], h=6*mm, orient=[0,1,0], align=[0,-1,0]);

                for(x=[-1,1])
                for(z=[-1,1])
                translate([0,-xaxis_end_motor_offset[1],0])
                translate([x*screw_dist/2, -(xaxis_beltpath_width/2+4*mm), z*screw_dist/2])
                {
                    screw_cut(xaxis_motor_nut, h=25, with_nut=false, orient=[0,1,0], align=[0,1,0]);
                }
            }

        }

        // nut mount
        mirror([0,0,nut_top?1:0])
        translate([zaxis_rod_screw_distance_x, -xaxis_zaxis_distance_y, -xaxis_end_wz/2])
        {
            union()
            {
                translate([0,0,-.1])
                {
                    // nut
                    cylindera(h=nut_h+1, d=zaxis_nut[0]*1.05, align=[0,0,1]);

                    // lead screw
                    translate([0,0,-.1])
                    cylindera(h=lookup(NemaFrontAxleLength,zaxis_motor), d=zaxis_nut[2]*1.5, align=[0,0,1]);

                    for(i=[-1,1])
                    translate([i*zaxis_nut[5], 0, -zaxis_nut[3]])
                    screw_cut(thread=zaxis_nut[5], h=16*mm, with_nut=false, orient=[0,0,1], align=[0,0,1]);
                }
            }
        }
    }
    else if(part=="vit")
    {
        if(with_motor && show_motor)
        {
            translate([0,0,xaxis_beltpath_z_offsets[beltpath_index]])
            translate(xaxis_end_motor_offset)
            {
                // 1mm due to motor offset
                translate([0,-1*mm,0])
                {
                    // 1mm between pulley and motor
                    translate([0,-1*mm,0])
                        pulley(xaxis_pulley, flip=false, orient=[0,1,0], align=[0,-1,0]);
                    motor(xaxis_motor, NemaMedium, dualAxis=false, orientation=[-90,0,0]);
                }
            }
        }

        //endstop
        %if($show_vit)
        {
            translate([wx_,0,(xaxis_rod_distance/2)+xaxis_rod_d])
            {
                difference()
                {
                    rcubea(xaxis_endstop_size, align=[-1,0,1]);

                    translate(xaxis_endstop_screw_offset)
                    for(y=[-1,1])
                    translate([-5*mm,y*9.5*mm/2,xaxis_endstop_size[2]])
                    screw_cut(nut=MHexNutM3, h=10*mm, orient=[0,0,-1], align=[0,0,-1]);
                }
            }
        }

        %if(show_nut)
        {
            mirror([0,0,nut_top?1:0])
            translate([zaxis_rod_screw_distance_x, -xaxis_zaxis_distance_y, -xaxis_end_wz/2-zaxis_nut[3]])
            xaxis_end_znut();
        }

        %if(show_rods)
        {
            for(z=[-1,1])
                translate([0,0,z*xaxis_rod_distance/2])
                translate([0, -xaxis_zaxis_distance_y, 0])
                cylindera(d=zaxis_rod_d, h=zaxis_rod_l, orient=[0,0,1]);
        }

        %if(show_bearings)
        {
            for(z=[-1,1])
            translate([0,0,z*xaxis_rod_distance/2])
            translate([0, -xaxis_zaxis_distance_y, 0])
            bearing(zaxis_bearing);
        }
    }
}

module xaxis_end_beltpath(height, width, length = 1000, align=[0,0,0], orient=[1,0,0])
{
    hull()
    for(z=[-1,1])
    translate([0,0,z*height/2])
    teardrop(h=length, d=width, orient=orient, align=align, roll=-180*min(0,z), truncate=.5);
}

module xaxis_end_idlerholder(part, width=xaxis_beltpath_width, length=10, beltpath_index=0)
{
    width = 20*mm;

    if(part==undef)
    {
        difference()
        {
            xaxis_end_idlerholder(part="pos", width=xaxis_beltpath_width, length=length, beltpath_index=beltpath_index);
            xaxis_end_idlerholder(part="neg", width=xaxis_beltpath_width, length=length, beltpath_index=beltpath_index);
        }
    }
    else if(part=="pos")
    {
        hull()
        {
            // support for x smooth rods
            for(z=[-1,1])
            translate([0,0,z*(xaxis_rod_distance/2)])
            cylindera(h=width, d=xaxis_rod_d+4*mm, orient=[1,0,0], align=[1,0,0], round_radius=2);

            // extra support for pulley
            translate([0,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
            translate([width/2,0,0])
            cylindera(h=8*mm+pulley_height(xaxis_idler_pulley), d=xaxis_idler_pulley[3], orient=[0,1,0], round_radius=2);
        }
    }
    else if(part=="neg")
    {
        for(z=[-1,1])
        {
            translate([0,0,z*(xaxis_rod_distance/2)])
            {
                // x smooth rods
                translate([width/2+.1,0,0])
                cylindera(h=width/2,d=xaxis_rod_d+.2*mm, orient=[1,0,0], align=[1,0,0]);

                screw_cut(nut=MHexNutM4, h=16*mm, with_nut=false, orient=[1,0,0], align=[1,0,0]);

                translate([6*mm,0,0])
                nut_trap_cut(nut=MHexNutM4, screw_l=16*mm, trap_axis=[0,-1,0], orient=[1,0,0], $show_vit=false);
            }
        }

        translate([0,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            translate([width/2,0,0])
                pulley(xaxis_idler_pulley, orient=[0,1,0]);

            translate([width/2,0,0])
                cylindera(d=lookup(ThreadSize, ThreadM5), h=100, orient=[0,1,0]);

            xaxis_end_beltpath(height=xaxis_beltpath_height_holders, width=xaxis_beltpath_width);
        }
    }
    else if(part=="vit")
    {
        %translate([0,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        translate([width/2,0,0])
        pulley(xaxis_idler_pulley, orient=[0,1,0]);
    }
}

module xaxis_end_znut()
{
    difference()
    {
        union()
        {
            cylindera(d=zaxis_nut[1], h=zaxis_nut[3], orient=[0,0,1], align=[0,0,1]);
            cylindera(d=zaxis_nut[0], h=zaxis_nut[4], orient=[0,0,1], align=[0,0,1]);
        }

        translate([0,0,-.1])
        cylindera(d=zaxis_nut[2], h=zaxis_nut[4]+.2, orient=[0,0,1], align=[0,0,1]);
    }
}

include <thing_libutils/misc.scad>;
if(false)
{
    // x axis
    /*translate([0,0,axis_pos_z])*/
    {
        /*if(!preview_mode)*/
        {
            zrod_offset = zmotor_mount_rod_offset_x;
            for(z=[-1,1])
            for(z=xaxis_beltpath_z_offsets)
            translate([-z*3*mm, xaxis_zaxis_distance_y, z])
            belt_path(5*mm+main_width+2*(zrod_offset)+xaxis_end_motor_offset[0], 6, xaxis_pulley_inner_d, orient=[1,0,0], align=[0,0,0]);
        }

        /*translate([axis_pos_x,0,0])*/
        /*{*/
            // x carriage
            /*attach(xaxis_carriage_conn, [[0,-xaxis_zaxis_distance_y,0],[0,0,0]])*/
            /*{*/
                /*x_carriage_full();*/
            /*}*/
        /*}*/

        // x smooth rods
        color(color_rods)
        for(z=[-1,1])
            translate([xaxis_rod_offset_x,xaxis_zaxis_distance_y,z*(xaxis_rod_distance/2)])
                cylindera(h=xaxis_rod_l,d=xaxis_rod_d, orient=[1,0,0]);

        for(x=[-1,1])
        {
            translate([x*(main_width/2), 0, 0])
            {
                translate([0, xaxis_zaxis_distance_y, 0])
                translate([x*zmotor_mount_rod_offset_x, 0, 0])
                mirror([max(0,x),0,0])
                {
                    xaxis_end(with_motor=true, beltpath_index=max(0,x), show_nut=true, show_motor=true, show_nut=true);
                    xaxis_end(part="vit", with_motor=true, beltpath_index=max(0,x), show_nut=true, show_motor=true, show_nut=true);
                }

                translate([x*95, xaxis_zaxis_distance_y, 0])
                mirror([0,0,max(0,x)])
                mirror([max(0,x),0,0])
                xaxis_end_idlerholder();
            }
        }
    }

}

if(false)
{
    translate([0, xaxis_zaxis_distance_y, 0])
    {
        for(x=[-1,1])
        translate([x*55,0,xaxis_end_wz/2])
        mirror([max(0,x),0,0])
        {
            xaxis_end(with_motor=true, beltpath_index=max(0,x), show_motor=false, show_nut=false, show_bearings=false);
            xaxis_end(part="vit", with_motor=true, beltpath_index=max(0,x), show_motor=false, show_nut=false, show_bearings=false);
        }

        for(x=[-1,1])
        /*translate([0,0,xaxis_end_wz/2])*/
        translate([x*45,25,0])
        translate([x*-0,0,0])
        mirror([max(0,x),0,0])
        rotate([0,-90,0])
        xaxis_end_idlerholder(beltpath_index=max(0,x));
    }
}

include <MCAD/motors.scad>
include <MCAD/stepper.scad>

include <config.scad>
include <thing_libutils/misc.scad>
include <thing_libutils/bearing.scad>
use <thing_libutils/metric-screw.scad>
use <thing_libutils/timing-belts.scad>

motor_mount_wall_thick = xaxis_pulley[1] - xaxis_pulley[0]/2 + 4*mm;
xaxis_end_motorsize = lookup(NemaSideSize,xaxis_motor);
xaxis_end_motor_offset=[xaxis_end_motorsize/2+zaxis_bearing[1]/2-1*mm,motor_mount_wall_thick-2*mm,0];
xaxis_end_wz = xaxis_rod_distance+zaxis_bearing[2]+5*mm;

// overlap of the X and Z rods
xaxis_end_xz_rod_overlap = 4*mm;

// how much "stop" for the x rods
xaxis_end_rod_stop = 10*mm;

xaxis_endstop_size = [10.3*mm, 20*mm, 6.3*mm];
xaxis_endstop_screw_offset = [-1.8*mm, 0*mm, 0*mm];

module xaxis_end_body(part, with_motor, beltpath_index=0, nut_top=false)
{
    nut_h = zaxis_nut[4];
    wx = zaxis_bearing[1]/2+zaxis_nut[1];
    wx_ = with_motor? xaxis_end_motorsize+xaxis_end_motor_offset[0] - xaxis_end_motorsize/2 : wx;
    bearing_sizey= zaxis_bearing[1]+10*mm;

    if(part==undef)
    {
        difference()
        {
            xaxis_end_body(part="pos", with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top);
            xaxis_end_body(part="neg", with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top);
        }
    }
    else if(part=="pos")
    {
        if(with_motor)
        {
            screw_dist = lookup(NemaDistanceBetweenMountingHoles, xaxis_motor);
            for(x=[-1,1])
            for(z=[-1,1])
            translate([0,0,xaxis_beltpath_z_offsets[beltpath_index]])
            translate(xaxis_end_motor_offset)
            {
                translate([x*screw_dist/2, 0, z*screw_dist/2])
                cylindera(d=lookup(ThreadSize,xaxis_motor_thread)+4*mm, h=motor_mount_wall_thick, orient=[0,1,0], align=[0,-1,0], rounding_radius=2);
            }
        }

        // nut mount
        mirror([0,0,nut_top?1:0])
        translate([zaxis_rod_screw_distance_x, -xaxis_zaxis_distance_y, -xaxis_end_wz/2])
        {
            // main nut support
            cylindera(h=zaxis_nut[4], d=zaxis_nut[0]+5*mm, align=[0,0,1], round_radius=2);

            // nut screw holes support
            for(x=[-1,1])
            translate([x*zaxis_nut[5], 0, 0])
            cylindera(h=zaxis_nut[4], d=lookup(ThreadSize, zaxis_nut[6])+5*mm, align=[0,0,1], round_radius=2);

            // lead screw
            // ensure some support for the leadscrew cutout all the way to the top
            cylindera(h=xaxis_end_wz, d=zaxis_nut[2]*2, align=[0,0,1], round_radius=2);
        }

        // x axis rod holders
        xaxis_rod_d_support = xaxis_rod_d+5*mm;
        for(z=[-1,1])
        translate([-xaxis_end_xz_rod_overlap,0,z*(xaxis_rod_distance/2)])
        cylindera(h=wx_+xaxis_end_xz_rod_overlap, d=xaxis_rod_d_support, orient=[1,0,0], align=[1,0,0], round_radius=2);

        translate([wx_,0,(xaxis_rod_distance/2)+xaxis_rod_d])
        {
            rcubea(xaxis_endstop_size, align=[-1,0,-1]);
        }

        // support around z axis bearings
        translate([0, -xaxis_zaxis_distance_y, 0])
        cylindera(h=xaxis_end_wz,d=bearing_sizey, orient=[0,0,1], align=[0,0,0], round_radius=2);
    }
    else if(part=="neg")
    {
        // cut support around z axis bearings
        translate([0, -xaxis_zaxis_distance_y, 0])
        translate([0,0,.1])
        cubea([bearing_sizey/2+.1, bearing_sizey+.2, xaxis_end_wz+.4], orient=[0,0,1], align=[-1,0,0]);
    }
    else if(part=="vit")
    {
    }
}

module xaxis_end(part, with_motor=false, stop_x_rods=false, beltpath_index=0, show_motor=false, nut_top=false, show_nut=false, show_rods=false, show_bearings=false)
{
    nut_h = zaxis_nut[4];
    wx = zaxis_bearing[1]/2+zaxis_nut[1];
    wx_ = with_motor? xaxis_end_motorsize+xaxis_end_motor_offset[0] - xaxis_end_motorsize/2 : wx;
    extrasize = with_motor?0*mm:0*mm;
    extrasize_align = 1;

    if(part==undef)
    {
        difference()
        {
            xaxis_end(part="pos", with_motor=with_motor, stop_x_rods=stop_x_rods, beltpath_index=beltpath_index, show_motor=show_motor, nut_top=nut_top, show_nut=show_nut, show_rods=show_rods, show_bearings=show_bearings);
            xaxis_end(part="neg", with_motor=with_motor, stop_x_rods=stop_x_rods, beltpath_index=beltpath_index, show_motor=show_motor, nut_top=nut_top, show_nut=show_nut, show_rods=show_rods, show_bearings=show_bearings);
        }
    }
    else if(part=="pos")
    {
        hull()
        {
            xaxis_end_body(with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top);

            // projection against z, ensure easy print
            translate([0,0,-xaxis_end_wz/2])
                linear_extrude(1)
                projection(cut=false)
                xaxis_end_body(with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top);
        }
    }
    else if(part=="neg")
    {
        //endstop mount screw cuts
        translate([wx_,0,(xaxis_rod_distance/2)+xaxis_rod_d])
        translate(xaxis_endstop_screw_offset)
        for(y=[-1,1])
        translate([-5*mm,y*9.5*mm/2,-2*mm])
        nut_trap_cut(nut=MHexNutM3, screw_l=6*mm, screw_l_extra=0*mm, trap_axis=[-1,0,0], orient=[0,0,1], align=[0,0,-1]);

        xaxis_end_beltpath(height=xaxis_beltpath_height_body, width=xaxis_beltpath_width);

        // z smooth bearing mounts
        for(z=[-1,1])
        {
            translate([0,0,z*xaxis_rod_distance/2])
            translate([0, -xaxis_zaxis_distance_y, 0])
            {
                bearing_mount_holes(
                        bearing_type=zaxis_bearing,
                        ziptie_type=ziptie_type,
                        ziptie_bearing_distance=ziptie_bearing_distance,
                        orient=[0,0,1],
                        with_zips=true,
                        show_zips=false
                        );
                /*hull()*/
                /*{*/
                    /*cylindera(d=zaxis_bearing[1],h=xaxis_end_wz);*/

                    /*translate([-5*cm,0,0])*/
                    /*cylindera(d=zaxis_bearing[1],h=xaxis_end_wz);*/
                /*}*/
            }
        }

        // x smooth rods
        x_rod_stop = stop_x_rods ? xaxis_end_rod_stop : 0;
        for(z=[-1,1])
        translate([-xaxis_end_xz_rod_overlap-.1+x_rod_stop,0,z*(xaxis_rod_distance/2)])
        cylindera(h=wx_+xaxis_end_xz_rod_overlap-x_rod_stop+.2,d=xaxis_rod_d+.2*mm, orient=[1,0,0], align=[1,0,0]);

        if(with_motor)
        {
            screw_dist = lookup(NemaDistanceBetweenMountingHoles, xaxis_motor);

            // axle
            translate([0,0,xaxis_beltpath_z_offsets[beltpath_index]])
            translate(xaxis_end_motor_offset)
            {
                round_d=1.1*lookup(NemaRoundExtrusionDiameter, xaxis_motor);
                translate([0, .1, 0])
                translate([0,1,0])
                teardrop(d=round_d,h=motor_mount_wall_thick, tear_orient=[0,0,1], orient=[0,1,0], align=[0,-1,0], roll=90, truncate=0);

                // motor axle
                translate([0, .1, 0])
                cylindera(d=1.2*lookup(NemaAxleDiameter, xaxis_motor), h=lookup(NemaFrontAxleLength, xaxis_motor)+3*mm, orient=[0,1,0], align=[0,1,0]);

                // bearing for offloading force on motor shaft
                translate([0, -2.5*mm-xaxis_pulley[1]-.1, 0])
                scale(1.03)
                cylindera(d=bearing_MR105[1], h=6*mm, orient=[0,1,0], align=[0,-1,0]);

                for(x=[-1,1])
                for(z=[-1,1])
                translate([0,-xaxis_end_motor_offset[1],0])
                translate([x*screw_dist/2, -(xaxis_beltpath_width/2+4*mm), z*screw_dist/2])
                {
                    screw_cut(xaxis_motor_nut, h=25, with_nut=false, orient=[0,1,0], align=[0,1,0]);
                }
            }

        }

        // nut mount
        mirror([0,0,nut_top?1:0])
        translate([zaxis_rod_screw_distance_x, -xaxis_zaxis_distance_y, -xaxis_end_wz/2])
        {
            union()
            {
                translate([0,0,-.1])
                {
                    // nut
                    cylindera(h=nut_h+1, d=zaxis_nut[0]*1.05, align=[0,0,1]);

                    // lead screw
                    translate([0,0,-.1])
                    cylindera(h=lookup(NemaFrontAxleLength,zaxis_motor), d=zaxis_nut[2]*1.5, align=[0,0,1]);

                    for(i=[-1,1])
                    translate([i*zaxis_nut[5], 0, -zaxis_nut[3]])
                    screw_cut(thread=zaxis_nut[5], h=16*mm, with_nut=false, orient=[0,0,1], align=[0,0,1]);
                }
            }
        }
    }
    else if(part=="vit")
    {
        if(with_motor && show_motor)
        {
            translate([0,0,xaxis_beltpath_z_offsets[beltpath_index]])
            translate(xaxis_end_motor_offset)
            {
                // 1mm due to motor offset
                translate([0,-1*mm,0])
                {
                    // 1mm between pulley and motor
                    translate([0,-1*mm,0])
                        pulley(xaxis_pulley, flip=false, orient=[0,1,0], align=[0,-1,0]);
                    motor(xaxis_motor, NemaMedium, dualAxis=false, orientation=[-90,0,0]);
                }
            }
        }

        //endstop
        %if($show_vit)
        {
            translate([wx_,0,(xaxis_rod_distance/2)+xaxis_rod_d])
            {
                difference()
                {
                    rcubea(xaxis_endstop_size, align=[-1,0,1]);

                    translate(xaxis_endstop_screw_offset)
                    for(y=[-1,1])
                    translate([-5*mm,y*9.5*mm/2,xaxis_endstop_size[2]])
                    screw_cut(nut=MHexNutM3, h=10*mm, orient=[0,0,-1], align=[0,0,-1]);
                }
            }
        }

        %if(show_nut)
        {
            mirror([0,0,nut_top?1:0])
            translate([zaxis_rod_screw_distance_x, -xaxis_zaxis_distance_y, -xaxis_end_wz/2-zaxis_nut[3]])
            xaxis_end_znut();
        }

        %if(show_rods)
        {
            for(z=[-1,1])
                translate([0,0,z*xaxis_rod_distance/2])
                translate([0, -xaxis_zaxis_distance_y, 0])
                cylindera(d=zaxis_rod_d, h=zaxis_rod_l, orient=[0,0,1]);
        }

        %if(show_bearings)
        {
            for(z=[-1,1])
            translate([0,0,z*xaxis_rod_distance/2])
            translate([0, -xaxis_zaxis_distance_y, 0])
            bearing(zaxis_bearing);
        }
    }
}

module xaxis_end_beltpath(height, width, length = 1000, align=[0,0,0], orient=[1,0,0])
{
    hull()
    for(z=[-1,1])
    translate([0,0,z*height/2])
    teardrop(h=length, d=width, orient=orient, align=align, roll=-180*min(0,z), truncate=.5);
}

module xaxis_end_idlerholder(part, width=xaxis_beltpath_width, length=10, beltpath_index=0)
{
    width = 20*mm;

    if(part==undef)
    {
        difference()
        {
            xaxis_end_idlerholder(part="pos", width=xaxis_beltpath_width, length=length, beltpath_index=beltpath_index);
            xaxis_end_idlerholder(part="neg", width=xaxis_beltpath_width, length=length, beltpath_index=beltpath_index);
        }
    }
    else if(part=="pos")
    {
        hull()
        {
            // support for x smooth rods
            for(z=[-1,1])
            translate([0,0,z*(xaxis_rod_distance/2)])
            cylindera(h=width, d=xaxis_rod_d+4*mm, orient=[1,0,0], align=[1,0,0], round_radius=2);

            // extra support for pulley
            translate([0,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
            translate([width/2,0,0])
            cylindera(h=8*mm+pulley_height(xaxis_idler_pulley), d=xaxis_idler_pulley[3], orient=[0,1,0], round_radius=2);
        }
    }
    else if(part=="neg")
    {
        for(z=[-1,1])
        {
            translate([0,0,z*(xaxis_rod_distance/2)])
            {
                // x smooth rods
                translate([width/2+.1,0,0])
                cylindera(h=width/2,d=xaxis_rod_d+.2*mm, orient=[1,0,0], align=[1,0,0]);

                screw_cut(nut=MHexNutM4, h=16*mm, with_nut=false, orient=[1,0,0], align=[1,0,0]);

                translate([6*mm,0,0])
                nut_trap_cut(nut=MHexNutM4, screw_l=16*mm, trap_axis=[0,-1,0], orient=[1,0,0], $show_vit=false);
            }
        }

        translate([0,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            translate([width/2,0,0])
                pulley(xaxis_idler_pulley, orient=[0,1,0]);

            translate([width/2,0,0])
                cylindera(d=lookup(ThreadSize, ThreadM5), h=100, orient=[0,1,0]);

            xaxis_end_beltpath(height=xaxis_beltpath_height_holders, width=xaxis_beltpath_width);
        }
    }
    else if(part=="vit")
    {
        %translate([0,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        translate([width/2,0,0])
        pulley(xaxis_idler_pulley, orient=[0,1,0]);
    }
}

module xaxis_end_znut()
{
    difference()
    {
        union()
        {
            cylindera(d=zaxis_nut[1], h=zaxis_nut[3], orient=[0,0,1], align=[0,0,1]);
            cylindera(d=zaxis_nut[0], h=zaxis_nut[4], orient=[0,0,1], align=[0,0,1]);
        }

        translate([0,0,-.1])
        cylindera(d=zaxis_nut[2], h=zaxis_nut[4]+.2, orient=[0,0,1], align=[0,0,1]);
    }
}

include <thing_libutils/misc.scad>;
if(false)
{
    // x axis
    /*translate([0,0,axis_pos_z])*/
    {
        /*if(!preview_mode)*/
        {
            zrod_offset = zmotor_mount_rod_offset_x;
            for(z=[-1,1])
            for(z=xaxis_beltpath_z_offsets)
            translate([-z*3*mm, xaxis_zaxis_distance_y, z])
            belt_path(5*mm+main_width+2*(zrod_offset)+xaxis_end_motor_offset[0], 6, xaxis_pulley_inner_d, orient=[1,0,0], align=[0,0,0]);
        }

        /*translate([axis_pos_x,0,0])*/
        /*{*/
            // x carriage
            /*attach(xaxis_carriage_conn, [[0,-xaxis_zaxis_distance_y,0],[0,0,0]])*/
            /*{*/
                /*x_carriage_full();*/
            /*}*/
        /*}*/

        // x smooth rods
        color(color_rods)
        for(z=[-1,1])
            translate([xaxis_rod_offset_x,xaxis_zaxis_distance_y,z*(xaxis_rod_distance/2)])
                cylindera(h=xaxis_rod_l,d=xaxis_rod_d, orient=[1,0,0]);

        for(x=[-1,1])
        {
            translate([x*(main_width/2), 0, 0])
            {
                translate([0, xaxis_zaxis_distance_y, 0])
                translate([x*zmotor_mount_rod_offset_x, 0, 0])
                mirror([max(0,x),0,0])
                {
                    xaxis_end(with_motor=true, beltpath_index=max(0,x), show_nut=true, show_motor=true, show_nut=true);
                    xaxis_end(part="vit", with_motor=true, beltpath_index=max(0,x), show_nut=true, show_motor=true, show_nut=true);
                }

                translate([x*95, xaxis_zaxis_distance_y, 0])
                mirror([0,0,max(0,x)])
                mirror([max(0,x),0,0])
                xaxis_end_idlerholder();
            }
        }
    }

}

if(false)
{
    translate([0, xaxis_zaxis_distance_y, 0])
    {
        for(x=[-1,1])
        translate([x*55,0,xaxis_end_wz/2])
        mirror([max(0,x),0,0])
        {
            xaxis_end(with_motor=true, beltpath_index=max(0,x), show_motor=false, show_nut=false, show_bearings=false);
            xaxis_end(part="vit", with_motor=true, beltpath_index=max(0,x), show_motor=false, show_nut=false, show_bearings=false);
        }

        for(x=[-1,1])
        /*translate([0,0,xaxis_end_wz/2])*/
        translate([x*45,25,0])
        translate([x*105,0,0])
        mirror([max(0,x),0,0])
        rotate([0,-90,0])
        xaxis_end_idlerholder(beltpath_index=max(0,x));
    }
}


Debugging ProMicro

Debugging ProMicro

/*
  Vitamin: Pro Micro
  Model of an Arduino Pro Micro

  Derived from: https://github.com/sparkfun/Pro_Micro/
           and: https://github.com/sparkfun/Arduino_Pro_Mini_328/

  Authors:
    Jamie Osborne (@jmeosbn)

  Local Frame:
    Point of origin is the centre of the bottom left pin (9)

  Parameters:
    FIXME: type: can be "mini", or "micro";
           otherwise returns a blank board with header pin holes

  Returns:
    FIXME: Model of an Arduino Pro Mini/Micro with header holes
*/

// PCB Variables
holeDiam  = 1.25;         // The hole diameter in mm
holeSpace = 2.54;         // The spacing of the holes in mm
holeInset = holeSpace/2;  // The spacing of the holes in mm
pcbHeight = .063 * 25.4;  // The thickness of the PCB in mm
pcbLength =  1.3 * 25.4;  // The length of the PCB in mm
pcbWidth  =  0.7 * 25.4;  // The width of the PCB in mm

// Pro Micro/Mini
board_colour = [26/255, 90/255, 160/255];
metal_colour = [220/255, 220/255, 220/255];

// Show board clearance area for components
board_clearance = 3;
show_clearance  = true;

module Micro_USB_Clearance() {
  // Rough dimensions for micro usb header
  micro_usb_area    = [7.7, 5.2];
  micro_usb_height  = 2.7;

  color(metal_colour)
  translate([pcbWidth/2 - holeInset, pcbLength - holeSpace, pcbHeight])
  linear_extrude(height=2.7)
    square(size = micro_usb_area, center = true);
}

module ArduinoPro_PCB() {
  // Bare PCB, origin at location for bottom left hole
  translate([-holeInset, -holeInset, 0]) {
    square(size = [pcbWidth, pcbLength]);
  }
}

module ArduinoPro_Headers() {
  // distance between the two header rows
  rowpitch  = pcbWidth - holeInset*2;
  // length for holes, leaving room for end header and insets
  rowlength = pcbLength - holeSpace - holeInset*2;

  // Add headers to either side (along y)
  for (x = [0, rowpitch], y = [0 : holeSpace : rowlength]) {
    translate([x, y, 0]) {
      // Standard PCB hole
      circle(r = holeDiam/2);
    }
  }

}

module ArduinoPro_Serial_Header() {
  // y position for header
  header_y = pcbLength - holeInset*2;
  // width for holes, leaving room for insets
  rowwidth = pcbWidth - holeInset*2;

  // Add serial header along top of board (along x)
  for (x = [holeInset : holeSpace : rowwidth]) {
    translate([x, header_y, 0]) {
      // Standard PCB hole
      circle(r = holeDiam/2);
    }
  }
}

module ArduinoPro(type = "mini") {
  union() {
    color(board_colour)
    linear_extrude(height=pcbHeight)
    difference() {
      // Common Features for Pro Mini/Micro
      ArduinoPro_PCB();
      ArduinoPro_Headers();

      // Features for Pro Mini
      if (type == "mini") {
        // Pro Mini Serial Header
        ArduinoPro_Serial_Header();
      }
    }

    // Features for Pro Micro
    if (type == "micro") {
      // Pro Micro USB port
      Micro_USB_Clearance();
    }

    // Indicate area for minimum board clearance
    if (show_clearance)
    color(board_colour, 0.1)
    translate([-holeInset, -holeInset, pcbHeight])
    linear_extrude(height=board_clearance)
      square(size = [pcbWidth, pcbLength]);
  }
}

*ArduinoPro("mini");
ArduinoPro("micro");

/*
  Vitamin: Pro Micro
  Model of an Arduino Pro Micro

  Derived from: https://github.com/sparkfun/Pro_Micro/
           and: https://github.com/sparkfun/Arduino_Pro_Mini_328/

  Authors:
    Jamie Osborne (@jmeosbn)

  Local Frame:
    Point of origin is the centre of the bottom left pin (9)

  Parameters:
    FIXME: None

  Returns:
    FIXME: Model of an Arduino Pro Mini/Micro with header holes
*/

include <../config/config.scad>

// PCB Variables
holeDiam  = 1.25;         // The hole diameter in mm
holeSpace = 2.54;         // The spacing of the holes in mm
holeInset = holeSpace/2;  // The spacing of the holes in mm
pcbHeight = .063 * 25.4;  // The thickness of the PCB in mm
pcbLength =  1.3 * 25.4;  // The length of the PCB in mm
pcbWidth  =  0.7 * 25.4;  // The width of the PCB in mm

module ArduinoPro_PCB() {
  // Bare PCB, origin at location for bottom left hole
  translate([-holeInset, -holeInset, 0]) {
    square(size=[pcbWidth, pcbLength]);
  }
}

module ArduinoPro_Headers() {
  // Add headers to either side along y, leaving room for end header
  // distance between the two header rows
  rowpitch=pcbWidth-holeInset*2;
  // amount of holes in each row, leaving room for end header and insets
  rowcount=((pcbLength-holeSpace-holeInset*2)/holeSpace);

  // FIXME: rowcount doesn't get fully interated
  // rowcount=11;

  echo("Count=", rowcount * 10000);
  for (x=[0, rowpitch], y=[0:rowcount]) {
    echo("Loop=", y);
    translate([x, y*holeSpace, 0]) {
      // Standard PCB hole
      circle(r=holeDiam/2);
    }
  }
  echo("Count=", rowcount);
}

module ArduinoPro(type="mini") {
  // Common Features between Pro Mini and Pro Micro
  difference() {
    // Common PCB
    ArduinoPro_PCB();

    // Common headers
    ArduinoPro_Headers();
  }

  // Features for Pro Mini
  if (type=="mini") {
  }

  // Features for Pro Micro
  if (type=="micro") {
  }
}

ArduinoPro("mini");
*ArduinoPro("micro");


Connector array collection

Connector array collection

/*
	Vitamin: ULN2003DriverBoard
	Model of a stepper driver board.
	Based on http://42bots.com/tutorials/28byj-48-stepper-motor-with-uln2003-driver-and-arduino-uno/

	Authors:
		Robert Longbottom

	Local Frame:
		Centred on the bottom left mounting hole

	Parameters:
		None

	Returns:
		A Stepper Driver Board, rendered and colored
*/

ULN2003Driver_BoardHeight = 35;
ULN2003Driver_BoardWidth = 30;
ULN2003Driver_PCBThickness = 1.5;
ULN2003Driver_HoleDia = 3;
ULN2003Driver_HoleInset = 2.5;

// Connectors
ULN2003DriverBoard_Con_LowerLeft	= [ [0, 0, 0], [0, 0, -1], 0, ULN2003Driver_PCBThickness, ULN2003Driver_HoleDia];
ULN2003DriverBoard_Con_LowerRight	= [ [ULN2003Driver_BoardWidth - 2 * ULN2003Driver_HoleInset, 0, 0], [0, 0, -1], 0, ULN2003Driver_PCBThickness, ULN2003Driver_HoleDia];
ULN2003DriverBoard_Con_UpperLeft	= [ [0, ULN2003Driver_BoardHeight - 2 * ULN2003Driver_HoleInset, 0], [0, 0, -1], 0, ULN2003Driver_PCBThickness, ULN2003Driver_HoleDia];
ULN2003DriverBoard_Con_UpperRight	= [ [ULN2003Driver_BoardWidth - 2 * ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2 * ULN2003Driver_HoleInset, 0], [0, 0, -1], 0, ULN2003Driver_PCBThickness, ULN2003Driver_HoleDia];

ULN2003DriverBoard_Cons = [
	ULN2003DriverBoard_Con_LowerLeft,
	ULN2003DriverBoard_Con_LowerRight,
	ULN2003DriverBoard_Con_UpperLeft,
	ULN2003DriverBoard_Con_UpperRight
];

module ULN2003DriverBoard() {

	if (DebugCoordinateFrames) {
		frame();
	}

	if (DebugConnectors) {
		connector(ULN2003DriverBoard_Con_LowerLeft);
		connector(ULN2003DriverBoard_Con_LowerRight);
		connector(ULN2003DriverBoard_Con_UpperLeft);
		connector(ULN2003DriverBoard_Con_UpperRight);
	}

	// offset origin to bottom left
	translate([(ULN2003Driver_BoardWidth / 2) - ULN2003Driver_HoleInset, (ULN2003Driver_BoardHeight / 2) - ULN2003Driver_HoleInset, 0])
	{
		// Base PCB
		linear_extrude(ULN2003Driver_PCBThickness)
			difference() {
				square([ULN2003Driver_BoardWidth, ULN2003Driver_BoardHeight], center=true);

				// Mounting holes
				for (i = [0:1], j = [0:1])
					mirror([i, 0, 0])
					mirror([0, j, 0])
					translate([ULN2003Driver_BoardWidth / 2 - ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight / 2 - ULN2003Driver_HoleInset, 0])
						circle(r = ULN2003Driver_HoleDia / 2);
			}

		// Move us to the bottom left of the PCB
		translate([-ULN2003Driver_BoardWidth / 2, -ULN2003Driver_BoardHeight / 2, 0]) {

			// ULN2003 chip
			color("darkgrey")
			translate([1, 8, ULN2003Driver_PCBThickness - eta])
				cube([20, 10, 8]);

			// Arduino header
			color("darkgrey")
			translate([5, 4.5, ULN2003Driver_PCBThickness - eta])
				cube([10, 3, 15]);

			// Stepper Connection
			color("white")
			translate([1.5, 19, ULN2003Driver_PCBThickness - eta])
				difference() {
					cube([15.5, 6, 8]);
					translate([1, 1, 1])
						cube([13.5, 4, 8]);
				}

			// Power & On/Off Jumper
			color("darkgrey")
			translate([23, 8, ULN2003Driver_PCBThickness - eta])
				cube([3, 10, 15]);

			// LEDS
			translate([6, 29, ULN2003Driver_PCBThickness - eta])
				LED_3mm();
			translate([10, 29, ULN2003Driver_PCBThickness - eta])
				LED_3mm();
			translate([14.5, 29, ULN2003Driver_PCBThickness - eta])
				LED_3mm();
			translate([19, 29, ULN2003Driver_PCBThickness - eta])
				LED_3mm();
		}
	}
}

/*
	Vitamin: ULN2003DriverBoard
	Model of a stepper driver board.
	Based on http://42bots.com/tutorials/28byj-48-stepper-motor-with-uln2003-driver-and-arduino-uno/

	Authors:
		Robert Longbottom

	Local Frame:
		Centred on the bottom left mounting hole

	Parameters:
		None

	Returns:
		A Stepper Driver Board, rendered and colored
*/

ULN2003Driver_BoardHeight = 35;
ULN2003Driver_BoardWidth = 30;
ULN2003Driver_PCBThickness = 1.5;
ULN2003Driver_HoleDia = 3;
ULN2003Driver_HoleInset = 2.5;

// Connectors
ULN2003DriverBoard_Con_LowerLeft	= [ [0, 0, 0], [0, 0, -1], 0, ULN2003Driver_PCBThickness, ULN2003Driver_HoleDia];
ULN2003DriverBoard_Con_LowerRight	= [ [ULN2003Driver_BoardWidth - 2 * ULN2003Driver_HoleInset, 0, 0], [0, 0, -1], 0, ULN2003Driver_PCBThickness, ULN2003Driver_HoleDia];
ULN2003DriverBoard_Con_UpperLeft	= [ [0, ULN2003Driver_BoardHeight - 2 * ULN2003Driver_HoleInset, 0], [0, 0, -1], 0, ULN2003Driver_PCBThickness, ULN2003Driver_HoleDia];
ULN2003DriverBoard_Con_UpperRight	= [ [ULN2003Driver_BoardWidth - 2 * ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2 * ULN2003Driver_HoleInset, 0], [0, 0, -1], 0, ULN2003Driver_PCBThickness, ULN2003Driver_HoleDia];

module ULN2003DriverBoard() {

	if (DebugCoordinateFrames) {
		frame();
	}

	if (DebugConnectors) {
		connector(ULN2003DriverBoard_Con_LowerLeft);
		connector(ULN2003DriverBoard_Con_LowerRight);
		connector(ULN2003DriverBoard_Con_UpperLeft);
		connector(ULN2003DriverBoard_Con_UpperRight);
	}

	// offset origin to bottom left
	translate([(ULN2003Driver_BoardWidth / 2) - ULN2003Driver_HoleInset, (ULN2003Driver_BoardHeight / 2) - ULN2003Driver_HoleInset, 0])
	{
		// Base PCB
		linear_extrude(ULN2003Driver_PCBThickness)
			difference() {
				square([ULN2003Driver_BoardWidth, ULN2003Driver_BoardHeight], center=true);

				// Mounting holes
				for (i = [0:1], j = [0:1])
					mirror([i, 0, 0])
					mirror([0, j, 0])
					translate([ULN2003Driver_BoardWidth / 2 - ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight / 2 - ULN2003Driver_HoleInset, 0])
						circle(r = ULN2003Driver_HoleDia / 2);
			}

		// Move us to the bottom left of the PCB
		translate([-ULN2003Driver_BoardWidth / 2, -ULN2003Driver_BoardHeight / 2, 0]) {

			// ULN2003 chip
			color("darkgrey")
			translate([1, 8, ULN2003Driver_PCBThickness - eta])
				cube([20, 10, 8]);

			// Arduino header
			color("darkgrey")
			translate([5, 4.5, ULN2003Driver_PCBThickness - eta])
				cube([10, 3, 15]);

			// Stepper Connection
			color("white")
			translate([1.5, 19, ULN2003Driver_PCBThickness - eta])
				difference() {
					cube([15.5, 6, 8]);
					translate([1, 1, 1])
						cube([13.5, 4, 8]);
				}

			// Power & On/Off Jumper
			color("darkgrey")
			translate([23, 8, ULN2003Driver_PCBThickness - eta])
				cube([3, 10, 15]);

			// LEDS
			translate([6, 29, ULN2003Driver_PCBThickness - eta])
				LED_3mm();
			translate([10, 29, ULN2003Driver_PCBThickness - eta])
				LED_3mm();
			translate([14.5, 29, ULN2003Driver_PCBThickness - eta])
				LED_3mm();
			translate([19, 29, ULN2003Driver_PCBThickness - eta])
				LED_3mm();
		}
	}
}


prototype of string holder

prototype of string holder

$fn = 64;

// interference fit adjustment for 3D printer
iFitAdjust = .4;
iFitAdjust_d = .25;

// latch buttons
latchButtonDiameter = 13;
latchButtonHeight = 30;
module latch_button() {
    cylinder(h = latchButtonHeight, d = latchButtonDiameter, center = false);
}

// led momentary buttons
ledButtonDiameter = 24;
ledButtonHeight = 33;
module momentary_button() {
    cylinder(h = ledButtonHeight, d = ledButtonDiameter, center = false);
}

// encoders
encoderShaftDiameter = 7;
encoderShaftHeight = 18;
encoderWidth = 18;
encoderLength = 31;
encoderDepth = 8.2;
module encoder() {
    // encoder shaft
    cylinder(h = encoderShaftHeight, d = encoderShaftDiameter, center=false);
    // encoder base
    translate([-encoderWidth/2, -encoderLength/2, -encoderDepth/2])
        cube([encoderWidth, encoderLength, encoderDepth]);
}

// string
stringWallThickness = 2;
stringRecess = 5;
stringLength = 60;
photoresistorWidth = 5;
photoresistorLength = 4;
photoresistorWireSep = 3.5;
ledLaserDiameter = 6;
ledLaserLength = 10;
module photoresistor_holder() {
    difference() {
        cylinder(h = stringRecess + stringWallThickness * 2, d = 2 * stringWallThickness + photoresistorWidth + iFitAdjust_d, center = false);
        translate([0,0,stringWallThickness + .1])
            cylinder(h = stringRecess + stringWallThickness + 1, d = photoresistorWidth + iFitAdjust_d, center = false);
        for (i = [-1,1])
            translate([i * photoresistorWireSep / 2, 0, -1])
                cylinder(h = 20, d = 1, center = false);
    }
}
module led_holder() {
    difference() {
        cylinder(h = ledLaserLength + stringWallThickness * 2, d = 2 * stringWallThickness + ledLaserDiameter + iFitAdjust_d, center = false);
        translate([0,0,stringWallThickness + .1])
            cylinder(h = ledLaserLength + stringWallThickness + 1, d = ledLaserDiameter + iFitAdjust_d, center = false);
        for (i = [-1,1])
            translate([i * photoresistorWireSep / 2, 0, -1])
                cylinder(h = 20, d = 1, center = false);
    }
}
module string() {
    photoresistor_holder();
    translate([0, 0, stringLength])
        rotate([180, 0, 0])
            led_holder();
    difference() {
        translate([-(ledLaserDiameter + 2 * stringWallThickness)/2, - ledLaserDiameter, 0])
            cube([ledLaserDiameter + 2 * stringWallThickness, ledLaserDiameter / 2, stringLength ], center = false);
        cylinder(h=100, d = ledLaserDiameter + iFitAdjust, center=false);
    }
}

// display
i2cOLEDWidth = 27;
i2cOLEDHeight = 16;
i2cOLEDScrewWidth = 20;
i2cOLEDScrewHeight = 22;
module display() {
}

module lid() {
    string();
}

lid();

error: pathspec 'controller/case/controller-case.scad' did not match any file(s) known to git


hardware: extrude the text so it prints!

include <bracket.scad>;
include <turnigy2k.scad>;

$fn=20;

// Plate that the camera mounts into.
module plate () {
  m=6;
  d=7;
  // Corner radius.
  r=2;
  w=59-r*2+m;
  h=42-r*2+m;

  translate([r, r, 0])
    hull () {
      cylinder(d, r, r);
      translate([w, h, 0])
          cylinder(7, r, r);
      translate([0, h, 0])
          cylinder(7, r, r);
      translate([w, 0, 0])
          cylinder(7, r, r);
  }
  // Text on the top.
  color("grey")
      translate([r*2, h+r*2, d*3/4])
      rotate(90, [-1, 0, 0])
      linear_extrude(0.5)
      text("pipoint v1", size=d/2+1);
}

// Camera mount assembly.
module camera_mount() {
    difference () {
        union () {
            bracket();
            translate([-5, 2, 0])
                rotate(90, [1, 0, 0])
                plate();
        }
        translate([-2, -1, 3])
            rotate(90, [1, 0, 0])
            turnigy2k();
    }
}

camera_mount();

include <bracket.scad>;
include <turnigy2k.scad>;

$fn=20;

// Plate that the camera mounts into.
module plate () {
  m=6;
  d=7;
  // Corner radius.
  r=2;
  w=59-r*2+m;
  h=42-r*2+m;

  translate([r, r, 0])
    hull () {
      cylinder(d, r, r);
      translate([w, h, 0])
          cylinder(7, r, r);
      translate([0, h, 0])
          cylinder(7, r, r);
      translate([w, 0, 0])
          cylinder(7, r, r);
  }
  // Text on the top.
  color("grey")
      translate([r*2, h+r*2, d*3/4])
      rotate(90, [-1, 0, 0])
      text("pipoint v1", size=d/2+1);
}

// Camera mount assembly.
module camera_mount() {
    difference () {
        union () {
            bracket();
            translate([-5, 2, 0])
                rotate(90, [1, 0, 0])
                plate();
        }
        translate([-2, -1, 3])
            rotate(90, [1, 0, 0])
            turnigy2k();
    }
}

camera_mount();


mount tooths are not much thicker

mount tooths are not much thicker

size = [92.75, 41, 15];
wall = 1.75;
spacing = 2;
l = 3.2;

module tooth()
{
  size = 4;
  difference()
  {
    cube([l, size, 16]);
    translate([0, 0, 16+l/2.5])
      rotate([0, 45, 0])
        translate([0, 0, -l])
          cube([l, size, 2*l]);
  }
}

// body
difference()
{
  cube(size);
  translate(wall*[1, 0, 1])
    cube(size - wall*[2, 1, 1]);
}

// side teeth
for(dy=[5, 25])
  translate([wall+spacing, dy, wall])
  {
    tooth();
    translate([size[0]-2*(wall+spacing), wall, 0])
      rotate([0, 0, 180])
        tooth();
  }

// upper teeth
for(dx=[0:3])
  translate([wall + 15 + dx*18.5, size[1]-wall-spacing-l, 0])
    translate([0, l, 0])
      rotate([0, 0, -90])
        tooth();

size = [92.75, 41, 15];
wall = 1.75;
spacing = 2;
l = 3.2;

module tooth()
{
  difference()
  {
    cube([l, wall, 16]);
    translate([0, 0, 16+l/2.5])
      rotate([0, 45, 0])
        translate([0, 0, -l])
          cube(l*[1, 1, 2]);
  }
}

// body
difference()
{
  cube(size);
  translate(wall*[1, 0, 1])
    cube(size - wall*[2, 1, 1]);
}

// side teeth
for(dy=[5, 25])
  translate([wall+spacing, dy, wall])
  {
    tooth();
    translate([size[0]-2*(wall+spacing), wall, 0])
      rotate([0, 0, 180])
        tooth();
  }

// upper teeth
for(dx=[0:3])
  translate([wall + 15 + dx*18.5, size[1]-wall-spacing-l, 0])
    translate([0, l, 0])
      rotate([0, 0, -90])
        tooth();


Initial Version Lampenkopf

Initial Version Lampenkopf

/*
 * UW Video Leuchte
 *
 * Breiter Fokus mit viel Lichtleistung
 * Möglichst modular aufgebaut.
 *
 */

//ACHTUNG: Das Glas hat eine Toleranz von ± 0.5mm, was eine Berechnung des
// O-Rings und der dazugehörigen Nut erst ermöglicht,
// wenn das Teil genau ausgemessen ist.

// Aktueller Raum für Elektronik: 48mm Durchmesser, 40mm Länge...

//Rendering information
$fn=200;

// Variablenblock:

// max. Gesamtradius des Gehäuses
gehaeuseRadius = 44;
auflageRadiusGlas = 30;
glasRadius = 35;
gesamtLaenge = 90;

//M3 DIN 912 Innensechskantschrauben
kernlochM3 = 1.25;
durchfuehrungM3 = 1.6;
schraubenkopfM3 = 2.85;
schraubenkopfM3hoehe = 3.0;

//Axiale O-Ringe fuer Scheibe (ID = 60mm, Dicke = 2.5mm)
oRingRadiusInnen = 30.0;
oRingRadiusAussen = oRingRadiusInnen + 2.5;
oRingNutbreite = 3.4;
oRingNutTiefe = 1.9; //(± 0,06)
oRingNutAussenRadius = gehaeuseRadius - 4 -durchfuehrungM3 - schraubenkopfM3 - kernlochM3;
oRingNutInnenRadius = oRingNutAussenRadius - oRingNutbreite ;

kernlochKabelverschraubung = 7;

echo ("O-Ring Nut Maße: ID= ", oRingNutInnenRadius * 2, " AD=", oRingNutAussenRadius *2);

//Konstruktion der Hauptkomponenten:

//Die Deckelscheibe hält das Glas in der Fassung.
translate ([0, 0, 230]){
  deckelscheibe(auflageRadiusGlas, gehaeuseRadius, 6);
}

//Glasscheibe mit Dichtringen
translate ([0, 0, 200]){
  frontGlas (oRingRadiusInnen, oRingRadiusAussen);
}

//Linse vor LED
translate ([0, 0, 160]){
  rotate ([0,0,25]){
    linse ();
  }
}

//LED
translate ([0,0,140]){
  rotate ([0,0,25]){
    led ();
  }
}

//LED Trägerplatte mit Kernlochbohrungen für LED Modul
difference (){
  //LED carrier
  translate ([0,0,130]){
    ledCarrier(30, 5);
  }

  // Bohrungen für LED
  rotate ([0,0,25]){
    for (i=  [[17,17,130-1],
             [-17,17,130-1],
             [17,-17,130-1],
             [-17,-17, 130-1]]){
      translate (i){
        cylinder (h = 11, r = 1.25);
      }
    }
  }
}

korpus (gehaeuseRadius, glasRadius, 30);

//Konstruktion der Deckelscheibe.
// Diese klemmt die Borsilika Scheibe mit zwei O-Ringen.
// Sie wird mit 8 Schrauben auf dem Gehäuse befestigt.
module deckelscheibe (innenRadius, aussenRadius, hoehe){
  difference(){
    cylinder (h = hoehe, r = aussenRadius);
    translate ([0, 0, -0.1]){
      cylinder (h = hoehe + 0.2, r = innenRadius);
    }

    // M3 Durchführung, 1mm vom Rand entfernt
    translate ([0, 0, -0.1]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3, durchfuehrungM3, 8 , hoehe + 0.2);
    }
    // Senkung für M3, DIN 912, Innensechskant Schraubenköpfe
    translate ([0, 0, hoehe-schraubenkopfM3hoehe]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3,
                          schraubenkopfM3, 8 ,
                          schraubenkopfM3hoehe + 0.1);
    }

    //Nut für O-Ring
    translate ([0, 0, -1.0]){
      // 2 mm vom Kernloch der Verschraubung entfernt:
      ring (oRingNutAussenRadius,
            oRingNutInnenRadius,
            oRingNutTiefe + 1.0);
    }
  }
}

//Konstruktion der Borsilikascheibe mit Dichtringen
module frontGlas (dichtringID, dichtringAD){

  // Borsilika Glas
  // ø: 70 (± 0.5) x 12 (± 0.5)

  color("LightBlue"){
    cylinder (h = 12, r = glasRadius);
  }

  //O-Ringe
  color("Black"){
    //axial oben (nicht dichtend)
    translate ([0,0, 20]){
      ring ( dichtringAD, dichtringID, dichtringAD-dichtringID);
    }

    //axial unten
    translate ([0,0,-10]){
      ring ( dichtringAD, dichtringID,  dichtringAD-dichtringID);
    }
  }
}

// Linse für LED
module linse (){
  color("LightSteelBlue"){
    difference (){
      sphere (22.0);
      translate ([-25,-25,-23.0]){
       cube ([50,50,23]);
      }
    }
    translate ([-0,-0, -8]){
      cylinder(r=25,h=8);
    }
  }
}

// Die LED die aufgeschraubt wird
module led(){

  color("SlateGray"){
    difference (){
      cylinder (h = 1.42, r = 28);
      translate ([-20,20,-1.0]){
        cube ( [40, 20, 2.43]);
      }
      translate ([-20,-40,-1]){
        cube ( [40, 20, 3.43]);
      }

      for (i=[[17,17,-1],
             [-17,17,-1],
             [17,-17,-1],
             [ -17,-17, -1]]){
        translate (i){
          cylinder (h = 2.430, r= 1.75);
        }
      }

    }
  }

  color("Gray"){
    difference (){
      translate ([-20, -20, 1.42]){
        cube ( [40, 40, 2.86]);
      }
      translate ([-20, -20, 1.41]){
        for (i=[[0,40,-1],
                [40,0,-1],
                [0,0,-1],
                [40  ,40 ,-1]]){
          translate (i){
            cylinder (h = 3.895, r= 10);
          }
        }
      }
    }
  }

  color ("Yellow"){
    translate ([-10.5, -10.5, 1.43]){
      cube ( [21, 21, 2.88]);
    }
  }

}

//Auf diesen Träger wird die LED geschraubt
module ledCarrier(aussenRadius, hoehe){

  difference (){

    cylinder (h = hoehe, r = aussenRadius);

     // M3 Durchführung, 1mm vom Rand entfernt
    translate ([0, 0, -0.1]){
      bohrungenAufRadius (aussenRadius - 1 - schraubenkopfM3, durchfuehrungM3 , 8 , hoehe + 0.2);
    }
    // Senkung für M3, DIN 912, Innensechskant Schraubenköpfe
    translate ([0, 0, hoehe-schraubenkopfM3hoehe]){
      bohrungenAufRadius (aussenRadius - 1 - schraubenkopfM3,
                          schraubenkopfM3, 8 ,
                          schraubenkopfM3hoehe + 0.1);
    }
  }
}

//Der Hauptlampenkörper
module korpus(aussenRadius, glasAufnahmeRadius, ledAufnahmeRadius){

  difference(){
    cylinder (h = gesamtLaenge, r = aussenRadius);

    // === Innen

    //Glasaufnahme
    translate ([0, 0, gesamtLaenge - 12]){
      cylinder (h = gesamtLaenge, r =  glasAufnahmeRadius + 0.25);
    }

    //Bohrungen für Glasplattenhalter
    translate ([0, 0, gesamtLaenge - 20]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3, kernlochM3, 8 ,gesamtLaenge );
    }

    //Nut für O-Ring
    translate ([0, 0, gesamtLaenge - 12 - oRingNutTiefe]){
      // 2 mm vom Kernloch der Verschraubung entfernt:
      ring (oRingNutAussenRadius,
            oRingNutInnenRadius,
            oRingNutTiefe + 1.0);
    }

    //LED Träger Aufnahme
    translate ([0, 0, gesamtLaenge - 12 - 35]){ //= -glas - ledcarrier
      cylinder (h = gesamtLaenge, r = ledAufnahmeRadius + 0.25);
    }

    //Kernlochbohrungen für Trägeraufnahme M3
    translate ([0, 0, gesamtLaenge - 12 - 35 - 20]){
      bohrungenAufRadius (ledAufnahmeRadius - 1 - schraubenkopfM3, kernlochM3, 8 , 150);
    }

    //Raum elektronik
    translate ([0, 0, 3]){
      cylinder (h = gesamtLaenge, r = 24);
    }

    //Kabelverschraubung
    translate ([0,0,-.05]){
       cylinder (h = 6.1, r = kernlochKabelverschraubung);
    }

    // === Außen

    // Aufnahme für Goodman Befestigung
    translate ([0, 0, gesamtLaenge - 30]){
      ring (aussenRadius + 3.00,
            aussenRadius - 1.0,
            10.0);
    }

    // Kühlrippen
    for (i=[25:4:gesamtLaenge - 34]){
      translate ([0, 0, i]){
        ring (aussenRadius + 3.00,
              ledAufnahmeRadius + 3.0,
              2.5);
      }
    }
    translate ([0, 0, 20]){
      ring (aussenRadius + 3.00,
            ledAufnahmeRadius + 11, //rippenlänge = 8mm
            35);
    }

    translate ([0, 0, -1]){
      ring (aussenRadius + 3.0,  27, 25);
    }
  }

  //Leichte erhöhung für Gewinde der Kabelverschraubung
  difference(){

    cylinder (h = 6, r = kernlochKabelverschraubung *1.6);

    //Gewinde für M16 x 1,5 Verschraubung
    translate ([0,0,-.05]){
      cylinder (h = 6.1, r = kernlochKabelverschraubung);
    }

  }

}

// Zeichnet eine Reihe von Zylindern auf einem festgelegtem Radius.
// Gut geeignet um auf einem Ring Bohrungen anzubringen.
module bohrungenAufRadius (radius, lochRadius, anzahl, tiefe){
  for ( i = [0 : anzahl] ) {
      rotate( i * 360 / anzahl + 1, [0, 0, 1]){
        translate ([radius ,0 , -0.1]){
          cylinder (h = tiefe + 0.1, r = lochRadius);
        }
      }
    }
}

// Ein einfacher Ring
module ring(aussenRadius, innenRadius, hoehe) {
    difference() {
        cylinder(r = aussenRadius, h = hoehe);
        translate([ 0, 0, -1 ]) cylinder(r = innenRadius, h = hoehe+2);
    }
}

error: pathspec 'Video-Lampenkopf.scad' did not match any file(s) known to git