fixup! submodules/libutils: Update (for rcube rename)

include <thing_libutils/attach.scad>
include <thing_libutils/shapes.scad>
include <config.scad>

mount_rod_clamp_conn_rod = [[0,0,0],[1,0,0]];

module mount_rod_clamp_half(rod_d=10, screw_dist=undef, width=4, thick=undef, base_thick=undef, thread=ThreadM4, orient=[0,0,1])
{
    thick_=thick==undef?rod_d:thick;
    outer_d= rod_d+thick_*2;
    thread_dia=lookup(ThreadSize, thread);
    screw_dist_=screw_dist==undef?outer_d+thread_dia+thick_:screw_dist;
    base_thick_=base_thick==undef?thick_*1.5:base_thick;

    orient(orient)
    difference()
    {
        clamp_tolerance = 0.5*mm;
        union()
        {
            // cylinder around rod
            cylindera(d=outer_d, h=width, orient=[0,0,1], align=[0,0,0]);

            // base
            cubea([base_thick_, screw_dist_+thread_dia*2.5, width], align=[1,0,0]);
        }

        // cut bottom of cylinder
        cubea([outer_d/2+.1, screw_dist_+thread_dia*2.5+.1, width+1], align=[-1,0,0], extrasize=[clamp_tolerance,0,0], extrasize_align=[1,0,0]);

        // cut clamp screw holes
        for(i=[-1,1])
        translate([0, i*screw_dist_/2, 0])
        {
            cylindera(d=thread_dia, h=base_thick_*3, orient=[1,0,0]);
        }

        // cut zrod
        cylindera(d=rod_d*rod_fit_tolerance, h=width*2, orient=[0,0,1]);
    }
}

module mount_rod_clamp_full(rod_d=10, screw_dist=undef, width=4, thick=undef, base_thick=undef, thread=ThreadM4, orient=[0,0,1])
{
    thick_=thick==undef?rod_d:thick;
    outer_d= rod_d+thick_*2;
    thread_dia=lookup(ThreadSize, thread);
    screw_dist_=screw_dist==undef?outer_d+thread_dia*2+thick_:screw_dist;
    base_thick_=base_thick==undef?thick_*1.5:base_thick;

    orient(orient)
    difference()
    {
        clamp_tolerance = 0.5*mm;
        base_cut = -rod_d/2 + clamp_tolerance;
        union()
        {
            // cylinder around rod
            cylindera(d=outer_d, h=width, orient=[0,0,1], align=[0,0,0]);

            // base
            translate([base_cut,0,0])
            cubea([base_thick_, screw_dist_+thread_dia*2.5, width], align=[1,0,0]);
        }

        // cut bottom of cylinder
        translate([base_cut,0,0])
            cubea([outer_d/2+.1, outer_d+.1, width+1], align=[-1,0,0]);

        // cut clamp screw holes
        for(i=[-1,1])
        translate([0, i*screw_dist_/2, 0])
        {
            cylindera(d=thread_dia, h=base_thick_*3, orient=[1,0,0]);
        }

        // cut zrod
        cylindera(d=rod_d*rod_fit_tolerance, h=width*2, orient=[0,0,1]);
    }
}

/*translate([20,0,0])*/
/*mount_rod_clamp_half(rod_d=yaxis_rod_d,  width=extrusion_size, thick=4, thread=ThreadM5);*/
/*mount_rod_clamp_full(rod_d=yaxis_rod_d,  width=extrusion_size, thick=4, thread=ThreadM5);*/

include <thing_libutils/attach.scad>
include <config.scad>

mount_rod_clamp_conn_rod = [[0,0,0],[1,0,0]];

module mount_rod_clamp_half(rod_d=10, screw_dist=undef, width=4, thick=undef, base_thick=undef, thread=ThreadM4, orient=[0,0,1])
{
    thick_=thick==undef?rod_d:thick;
    outer_d= rod_d+thick_*2;
    thread_dia=lookup(ThreadSize, thread);
    screw_dist_=screw_dist==undef?outer_d+thread_dia+thick_:screw_dist;
    base_thick_=base_thick==undef?thick_*1.5:base_thick;

    orient(orient)
    difference()
    {
        clamp_tolerance = 0.5*mm;
        union()
        {
            // cylinder around rod
            cylindera(d=outer_d, h=width, orient=[0,0,1], align=[0,0,0]);

            // base
            cubea([base_thick_, screw_dist_+thread_dia*2.5, width], align=[1,0,0]);
        }

        // cut bottom of cylinder
        cubea([outer_d/2+.1, screw_dist_+thread_dia*2.5+.1, width+1], align=[-1,0,0], extrasize=[clamp_tolerance,0,0], extrasize_align=[1,0,0]);

        // cut clamp screw holes
        for(i=[-1,1])
        translate([0, i*screw_dist_/2, 0])
        {
            cylindera(d=thread_dia, h=base_thick_*3, orient=[1,0,0]);
        }

        // cut zrod
        cylindera(d=rod_d*rod_fit_tolerance, h=width*2, orient=[0,0,1]);
    }
}

module mount_rod_clamp_full(rod_d=10, screw_dist=undef, width=4, thick=undef, base_thick=undef, thread=ThreadM4, orient=[0,0,1])
{
    thick_=thick==undef?rod_d:thick;
    outer_d= rod_d+thick_*2;
    thread_dia=lookup(ThreadSize, thread);
    screw_dist_=screw_dist==undef?outer_d+thread_dia*2+thick_:screw_dist;
    base_thick_=base_thick==undef?thick_*1.5:base_thick;

    orient(orient)
    difference()
    {
        clamp_tolerance = 0.5*mm;
        base_cut = -rod_d/2 + clamp_tolerance;
        union()
        {
            // cylinder around rod
            cylindera(d=outer_d, h=width, orient=[0,0,1], align=[0,0,0]);

            // base
            translate([base_cut,0,0])
            cubea([base_thick_, screw_dist_+thread_dia*2.5, width], align=[1,0,0]);
        }

        // cut bottom of cylinder
        translate([base_cut,0,0])
            cubea([outer_d/2+.1, outer_d+.1, width+1], align=[-1,0,0]);

        // cut clamp screw holes
        for(i=[-1,1])
        translate([0, i*screw_dist_/2, 0])
        {
            cylindera(d=thread_dia, h=base_thick_*3, orient=[1,0,0]);
        }

        // cut zrod
        cylindera(d=rod_d*rod_fit_tolerance, h=width*2, orient=[0,0,1]);
    }
}

/*translate([20,0,0])*/
/*mount_rod_clamp_half(rod_d=yaxis_rod_d,  width=extrusion_size, thick=4, thread=ThreadM5);*/
/*mount_rod_clamp_full(rod_d=yaxis_rod_d,  width=extrusion_size, thick=4, thread=ThreadM5);*/


Update, improved tilt servo support

Update, improved tilt servo support

$fn = 150;
//Need to fix servo tilt suport issue. The spheric part does not move with the arm when you change xPos parameter
// RPi supports does not touch the bottom of the case
include <rpi.scad>;
PIcameraDiam = 8;
PIcameraX = 25;
PIcameraY = 25;

mik = 2;
shellThickness = 3;
supportBaseHeight = 5;
//5.4 is the height of the
baseHeight = 31 + shellThickness - 5.4;
tolerance = 1.5;
gimballWidth = 45; //for 50 gives R = 86

R = sqrt(3 * gimballWidth * gimballWidth) / 2;
panSupport = sqrt(R * R - (gimballWidth * gimballWidth / 4));
gimballPivotR = R / 2;
supportY = 2 * sqrt(R * R - (gimballWidth / 2) * (gimballWidth / 2));
supportX = R - gimballWidth / 2;
supportZ = 1.1 * R;

servoX = 11.8;
servoY = 22.2;

servoHornThickness = 5.5;
servoHornWidth = 8;
servoHornLenght = 32;

servoClerance = 31 - 16 - servoHornThickness;
eccentricity = 22.2 / 2 - 5;
servoBodySupport = 20;

screwDiam = 3;
screwLenght = 10;
screwTolerance = 0.3;

Piwidth = 56;
Pilength = 85;
Piheight = 1.5;

boxLength = Pilength + 8;
boxDepth = 2 * R + 10;
boxHeight = 48;
boxThickness = 1;
topCoverHeight = 15;

servoPanZ = boxHeight - 20 + topCoverHeight - servoClerance;
servoPanBoxX = (servoX - 2 * boxThickness - tolerance);
servoPanBoxThickness = 2;

function RatX(x) = sqrt(R * R - (x * x));

//---------------------------------------
module panSupport() {
    //rotate([0,270,0])servoTiltSupport(15,"pan");
    difference() {
        union() {
            color("blue", 0.5) multmatrix(m = [
                [1, 0, 0, 0],
                [0, 1, 0.4, 0],
                [0, 0, 1, 0],
                [0, 0, 0, 1]
            ]) cylinder(h = servoPanZ, d1 = boxDepth, d2 = servoY + 2 * servoPanBoxThickness, center = true);
            //Creates bottom support blocks add -mik to y transaltion if you want to zero the mechanical interference
            translate([0, boxDepth / 2 - 20 / 2 - mik, -servoPanZ / 2 + 5]) cube([boxHeight / 2 + servoPanBoxX, 20, 10], center = true);
            mirror([0, 1, 0]) translate([0, boxDepth / 2 - 20 / 2 - mik, -servoPanZ / 2 + 5]) cube([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 20, 10], center = true);
        }

        //Cuts the support from a cylinder
        translate([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 0, 0]) cube([boxHeight, boxDepth + 10, boxHeight - 20 + topCoverHeight - servoClerance], center = true);
        mirror(1, 0, 0) translate([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 0, 0]) cube([boxHeight, boxDepth + 10, boxHeight - 20 + topCoverHeight - servoClerance], center = true);

        color("blue", 0.5) multmatrix(m = [
            [1, 0, 0, 0],
            [0, 1, 0.4, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ]) cylinder(h = servoPanZ, d1 = boxDepth - 10, d2 = servoY - 10, center = true);

        //Cuts the support to fit case
        translate([0, +eccentricity, 10]) cube([servoPanBoxX + servoPanBoxThickness + 4, servoY + servoPanBoxThickness + 1.5, 20], center = true);
        translate([0, -(boxDepth / 2 + 10 - mik), -servoPanZ / 2 + 5]) cube([boxHeight / 2 + servoPanBoxX, 20, 10], center = true);

        //Slot for video
        translate([0, boxDepth / 2 - 10, -servoPanZ / 2 + 5]) rotate([90, 0, 0]) scale([1.2, 1.5, 1]) cube([8, 20, 20], center = true);

    }
    rotate([0, 90, 90]) translate([3.25, 0, 0]) miscroServoSupport();

}

//---------------------------------------
//This was the verion for RPi v1
module panSupportOLD() {
    //rotate([0,270,0])servoTiltSupport(15,"pan");
    difference() {
        union() {
            color("blue", 0.5) multmatrix(m = [
                [1, 0, 0, 0],
                [0, 1, 0.4, 0],
                [0, 0, 1, 0],
                [0, 0, 0, 1]
            ]) cylinder(h = servoPanZ, d1 = boxDepth, d2 = servoY + 2 * servoPanBoxThickness, center = true);
            //Creates bottom support blocks add -mik to y transaltion if you want to zero the mechanical interference
            translate([0, boxDepth / 2 - 20 / 2 - mik, -servoPanZ / 2 + 5]) cube([boxHeight / 2 + servoPanBoxX, 20, 10], center = true);
            mirror([0, 1, 0]) translate([0, boxDepth / 2 - 20 / 2 - mik, -servoPanZ / 2 + 5]) cube([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 20, 10], center = true);
        }

        //Cuts the support from a cylinder
        translate([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 0, 0]) cube([boxHeight, boxDepth + 10, boxHeight - 20 + topCoverHeight - servoClerance], center = true);
        mirror(1, 0, 0) translate([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 0, 0]) cube([boxHeight, boxDepth + 10, boxHeight - 20 + topCoverHeight - servoClerance], center = true);

        color("blue", 0.5) multmatrix(m = [
            [1, 0, 0, 0],
            [0, 1, 0.4, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ]) cylinder(h = servoPanZ, d1 = boxDepth - 10, d2 = servoY - 10, center = true);

        //Cuts the support to fit case
        translate([0, +eccentricity, 10]) cube([servoPanBoxX + servoPanBoxThickness + 4, servoY + servoPanBoxThickness + 1.5, 20], center = true);
        translate([0, -(boxDepth / 2 + 10 - mik), -servoPanZ / 2 + 5]) cube([boxHeight / 2 + servoPanBoxX, 20, 10], center = true);

        //Slot for video
        translate([0, boxDepth / 2 - 10, -servoPanZ / 2 + 5]) rotate([90, 0, 0]) scale([1.2, 1.5, 1]) cube([8, 20, 20], center = true);

    }
    rotate([0, 90, 90]) translate([3.25, 0, 0]) miscroServoSupport();

}

module miscroServoSupport() {

    translate([-servoBodySupport / 2, 0, eccentricity]) {
        //This is the servo box
        difference() {
            cube([servoBodySupport, 12 + 1 + shellThickness, 22.2 + 1 + shellThickness], center = true);
            cube([servoBodySupport, 12 + 1, 22.2 + 1], center = true);

            //Servo cable slot
            translate([0, 0, -2 * eccentricity]) cube([100, 3, 3], center = true);

        }

    }

}

//--------------------------------
module box(Xdim, Ydim, Zdim, thickness, application)
    //Creates a box, dimensions are internal
    {
        difference() {

            cube([Xdim + thickness, Ydim + thickness, Zdim], center = true);
            translate([0, 0, thickness / 2]) cube([Xdim, Ydim, Zdim], center = true);
            if (application == "servo") {
                translate([-Xdim / 2, 0, -0.5 * Zdim]) cube([5, Ydim + 2 * thickness, Zdim / 2 + thickness], center = true);
                translate([+Xdim / 2, 0, -0.5 * Zdim]) cube([5, Ydim + 2 * thickness, Zdim / 2 + thickness], center = true);
                translate([+Xdim / 2, 0, 0]) cube([thickness / 2, 0.3 * Ydim, Zdim], center = true);
                translate([-Xdim / 2, 0, 0]) cube([thickness / 2, 0.3 * Ydim, Zdim], center = true);
            }
        }
    }

//Baseplate for pan mechanism
module base(servo) {
        difference() {

            {
                minkowski() {
                    cylinder(r = R + mik, h = supportBaseHeight);
                    rotate([90, 0, 0]) cylinder(r = mik, h = 1);
                }

                //This to create a anchoring mechanism with the pan base
                //translate([0, 0,-mik])cylinder(r = R + mik+3, h = 2);
            }
            if (servo == true) {
                translate([0, 0, -supportZ - servoHornThickness - mik]) rotate([0, 90, 0]) servoHorn("cross");
                cylinder(r = 2.5, h = 15, center = true);
            }
        }
    }
    //-------------------------
    //				translate([0, 0, -supportZ - servoHornThickness-mik])rotate([0, 90, 0])servoHorn("cross");

module supportSection() {
        intersection() {
            difference() {
                sphere(R);
                cube([gimballWidth, 2 * gimballWidth, 2 * gimballWidth], center = true);

            }
            translate([R - supportX / 2, 0, 0]) cube([supportX, supportY, 10], center = true);
        }
    }
    //-------------------------

module tiltSupport()
    //This creates the supporting pillar for the camera shell
    {
        union() {
            difference() {
                sphere(R);
                cube([gimballWidth, 2 * gimballWidth, 2 * gimballWidth], center = true);
                translate([-gimballWidth, 0, 0]) cube([2 * gimballWidth, 2 * gimballWidth, 2 * gimballWidth], center = true);

            }

            hull() {
                translate([0, 0, -5]) supportSection();
                translate([0, 0, -supportZ]) supportSection();
            }
        }

    }

module servoTiltSupport(xPos, Pos) {
        //Need to center the servo shaft, the second figure is the distance form border
        supportPosition = xPos;
        //Need to adjust the clearance to account for the Xpos
        supportCurvature = 30;

        rotate([-0, 0, 0]) difference() {

            union() {

                difference() {
                    translate([-shellThickness / 2 + supportPosition, 0, 0]) {
                        cube([shellThickness, 20, 1.8 * RatX(gimballWidth / 2)], center = true);
                    }

                       difference() {
                            translate([-shellThickness / 2 + supportPosition, 0, 15]) cube([shellThickness, 20, 2 * RatX(gimballWidth / 2)], center = true);

                            //The size of the sphere governs where the servo support will be cut. Need to add tolerance if you whant to print as one piece with the shell.
                            sphere(RatX(gimballWidth / 2) + shellThickness + tolerance);

                        }
                    }

                translate([-servoBodySupport / 2 + supportPosition, 0, eccentricity]) {
                    //This is the servo box
                     difference() {
                        cube([servoBodySupport, 12 + 1 + shellThickness, 22.2 + 1 + shellThickness], center = true);
                        cube([servoBodySupport, 12 + 1, 22.2 + 1], center = true);

                        //Servo cable slot
                        translate([0, 0, -2 * eccentricity]) cube([100, 4, 4], center = true);

                    }

                }
            }

            //This is the slot in the servo arm
            translate([servoClerance + 10, 0, eccentricity]) {
                cube([20 + 1, 12, 32.2 + 1], center = true);
                translate([-31 / 2, 0, 0]) cube([31 + 1, 12, 22.2 + 1], center = true);

            }
        }

        //This is the curved extra support for the connection with the gimball internal shell
             translate([0, 0, 0])SUB_tiltwedge(xPos);
             mirror([0,0,1])translate([0, 0, 0])SUB_tiltwedge(xPos);

    }

    //-------------------------

module SUB_tiltwedge(xPos) {
   supportCurvature = 30;

            difference() {
                translate([12, 0, +RatX(xPos) - 10])
                difference() {
                    cube([16, 20, 20], center = true);
                    translate([-supportCurvature, 0, -supportCurvature]) sphere(r = supportCurvature + 20, center=true);

                }

                difference() {
                    sphere(RatX(gimballWidth / 2) + 10 * shellThickness + tolerance);

                    sphere(RatX(gimballWidth / 2) + shellThickness + tolerance);

                }

            }

}

module cameraSupport() {
        difference() {
            difference() {
                sphere(R);
                sphere(R - shellThickness);
            }

            translate([R + gimballWidth / 2, 0, 0]) cube([2 * R, 2 * R, 2 * R], center = true);
            translate([-R - gimballWidth / 2, 0, 0]) cube([2 * R, 2 * R, 2 * R], center = true);
            //Creates camera support
            translate([0, R - 7, 0]) cube([PIcameraX + tolerance, 3, PIcameraY + tolerance], center = true);
            translate([0, R - 7, 0]) cube([2, 3, PIcameraY], center = true);
            //Slot for camera wires
            translate([0, R - 8, -PIcameraY / 2 - 2]) cube([PIcameraX - 5, 3, 3], center = true);

            //Hole for the camera
            translate([0, R, 0]) rotate([90, 0, 0]) cube([PIcameraDiam + tolerance, 10, PIcameraDiam + tolerance], center = true);
		translate([PIcameraX/2-2, 0.9*R, PIcameraY/2-2])rotate([90,0,0])cylinder(r=1.2,h=10, center=true);
		translate([-(PIcameraX/2-2), 0.9*R, PIcameraY/2-2])rotate([90,0,0])cylinder(r=1.2,h=10, center=true);
		translate([PIcameraX/2-2, 0.9*R, -(PIcameraY/2-2)])rotate([90,0,0])cylinder(r=1.2,h=10, center=true);
		translate([-(PIcameraX/2-2), 0.9*R, -(PIcameraY/2-2)])rotate([90,0,0])cylinder(r=1.2,h=10, center=true);         }

        //Need to hardwire the servo dimensions

        {
            servoTiltSupport(gimballWidth/2, "tilt");

        }
        //pivot mechanism
        translate([-gimballWidth / 2, 0, 0]) {
            difference() {
                rotate([0, 90, 0]) cylinder(r = panSupport, h = shellThickness);
                gimballPivot(1);
                rotate([0, 90, 0]) cylinder(r = gimballPivotR - shellThickness, h = shellThickness);

            }
            gimballPivot(1);
        }
    }

    //-------------------------

module servoHorn(type) {
        cube([servoHornThickness, servoHornWidth, servoHornLenght], center = true);
        if (type == "cross") rotate([90, 0, 0]) cube([servoHornThickness, servoHornWidth, servoHornLenght], center = true);
    }
    //-------------------------

module gimballPivot(tolerance) {
        rotate([0, 90, 0])
        difference() {
            cylinder(r = gimballPivotR - tolerance / 2, h = gimballPivotR / 2, center = true);
            cylinder(r = gimballPivotR - 3 + tolerance / 2, h = gimballPivotR / 2, center = true);
        }
    }
    //-------------------------

module support() {
    scaleFactor = (R + tolerance) / R;

    difference() {
        tiltSupport();

        translate([gimballWidth / 2 + servoHornThickness / 2, 0, 0]) rotate([45, 0, 0]) servoHorn("line");
        translate([gimballWidth / 2, 0, 0]) rotate([0, 90, 0]) cylinder(r = 2.5, h = 30);

    }

    //Need to cut holes in the base for servo and camera cables and the screws
    translate([0, 0, -supportBaseHeight / 2])
    difference() {

        {
            color("blue", 0.5) translate([0, 0, -supportZ - supportBaseHeight]) base(true);
            translate([0, 0, -supportZ - servoHornThickness]) rotate([0, 90, 0]) servoHorn("cross");
            SUB_cameracableDuct();
            translate([0, 0, +supportBaseHeight / 2]) mirror([1, 0, 0]) tiltSupport();
            SUB_mountedPillarScrewHoles();
            translate([-gimballWidth / 2 - supportX / 2, 0, 0]) rotate([0, 0, 180])
            SUB_servocableDuct();

        }

    }

}

module mountedPillar() {
    //Pillar for camera. This one needs to be mounted separately
    difference() {
        mirror([1, 0, 0]) tiltSupport();
        translate([-gimballWidth / 2, 0, 0]) gimballPivot(0);
        translate([-gimballWidth / 2, 0, 0]) gimballPivot(3); {
            SUB_cameracableDuct();
            SUB_mountedPillarScrewHoles();
            translate([-gimballWidth / 2 - supportX / 2, 0, -supportX / 2]) rotate([0, 0, 180])
            SUB_servocableDuct();
        }

    }

}

module SUB_screw(tolerance) {
    cylinder(r = screwDiam / 2 + tolerance, h = screwLenght);
    translate([0, 0, -3]) cylinder(r = 2 * screwDiam / 2 + screwTolerance, h = 4);
}

module SUB_mountedPillarScrewHoles() {
    color("blue", 0.5) translate([-R + supportX / 2 + 2, 0, -supportZ - screwLenght / 2]) {
        translate([0, 0.5 * panSupport, 0]) SUB_screw(-screwTolerance);
        mirror([0, 1, 0]) translate([0, 0.5 * panSupport, 0]) SUB_screw(-screwTolerance);
    }
}

module SUB_servocableDuct() {
    //Duct for servo cable
    {
        translate([0, 0, -supportZ + supportX]) cylinder(r = supportX / 3, h = supportZ + supportX / 2, center = true);
        translate([-supportX, 0, 0]) rotate([0, 120, 0]) cylinder(r = supportX / 3, h = supportX, center = false);
        translate([0, 0, -supportZ - supportX / 3]) rotate([0, 90, 180]) cylinder(r = supportX / 3, h = 0.6 * R, center = false);
    }

}

module SUB_cameracableDuct() {
        //Duct for camera connetion
        translate([-gimballWidth / 2 - 0.5 * supportX, 0, 0]) rotate([0, 0, 90]) {
            translate([0, 0, 0]) rotate([90, 0, 0]) cylinder(r = 26 / 2, h = 0.6 * supportX);
            translate([0, 0, -supportZ / 2]) cube([20, 3, supportZ + 1.1 * supportBaseHeight], center = true);
            translate([0, -R * 0.5 + 13, -supportZ - supportBaseHeight]) rotate([90, 0, 0]) cube([20, 5, R * 0.5], center = true);

        }
    }
    //---------------------------

module connectionSocket() {

    cylinder(r = 3, h = 5);
    translate([-9, -3 / 2, 0]) cube([10, 3, 5]);

}

module PiSupport() {
    difference() {
        translate([0,0,1])cube([4 + 3 / 2 - 0.4, 4 + 3 / 2 - 0.2, 8], center = true);
        cylinder(r = 3 / 2 - 0.2, h = 2 * Piheight + 8, $fs = 0.1);
    }

}

module case () {
    translate([0, 0, -boxHeight / 2 - supportZ - 2 * supportBaseHeight]) {
        difference() {
            minkowski() {
                box(boxLength, boxDepth, boxHeight, boxThickness, "PI");
                //sphere(r = mik);
                cylinder(r = mik, h = 2);
            }
            translate([R - 10, -Piwidth / 2, -boxHeight / 2 - 5 / 2]) connectionSocket();
            translate([-R + 10, -Piwidth / 2, -boxHeight / 2 - 5 / 2]) connectionSocket();
            translate([R - 10, Piwidth / 2, -boxHeight / 2 - 5 / 2]) connectionSocket();
            translate([-R + 10, Piwidth / 2, -boxHeight / 2 - 5 / 2]) connectionSocket();

            translate([0, 0, -boxHeight / 2 - 5 / 2]) cylinder(r = 3, h = 5);

translate([-Pilength / 2, -16, -boxHeight / 2 + 5 + shellThickness]) {
                rpi();
            }
//This is the power inlet. The +6 in z is half the z-lenght of the component
translate([-boxLength/2,-boxDepth/2+8,-boxHeight/2+shellThickness+2*mik+tolerance])cube([15+tolerance,9+tolerance,12], center=true);

        }
        translate([-Pilength / 2, -16, -boxHeight / 2 + 1 + shellThickness]) {
translate ([Pilength-3.5, Piwidth-3.5,-0.1]) PiSupport();
translate ([Pilength-3.5, -0+3.5,-0.1]) PiSupport();
translate ([Pilength-58-3.5, Piwidth-3.5,-0.1]) PiSupport();
translate ([Pilength-58, 0+3.5,-0.1]) PiSupport();
          //translate([25.5, 18, 0]) PiSupport();
            //translate([Pilength - 5, Piwidth - 12.5, 0]) PiSupport();

        }

    }
translate([-boxLength/2+15/2+mik,-boxDepth/2+8,-supportZ-boxHeight-4])rotate([0,0,180])SUB_Power();

}

module topLid() {
    tolerance = 0.02;

    difference() {
        difference() {
            {
                minkowski() {
                    translate([0, 0, -supportZ - supportBaseHeight - topCoverHeight / 2]) cube([boxLength + mik - 1, boxDepth + mik - 1, topCoverHeight], center = true);
                    sphere(r = mik); //cylinder(r = mik, h = 2);
                }
                translate([0, 0, -supportZ - supportBaseHeight]) {
                    scale([1 + tolerance, 1 + tolerance, 1]) base("false");
                    scale([0.9, 0.9, 30]) base("false");

                    //This is the grid an hole for mic.need to re-work the placement definition as it will not work for other box dimensions!!!
                    translate([boxLength / 2 - 5 - mik - boxThickness, -(boxDepth / 2 - 5 - mik - boxThickness), +14 - supportBaseHeight - topCoverHeight / 2]) rotate([0, 180, 0]) SUB_Mic();

                }

            }

        }

        case ();

    }

//Need to explain the -1.4*supportBaseHeight
difference(){
translate([0,0,-supportZ-1.4*supportBaseHeight])rotate([0,-90,90])testSupport();
       case ();

}

}

module SUB_Mic() {
    translate([-7, 0, 0]) for (i = [1: 6]) {
        translate([i * 2, 0, 0]) cube([1, 10, 2], center = true);
    }
    cylinder(r = 5, h = 2 * topCoverHeight);

}

module SUB_Power(){
	difference(){
		cube([15,9+3,12], center=true);
		translate([2,0,2])cube([15+tolerance,9+tolerance,12], center=true);
	}
}
module assembly(view)
{

space = (view == "explode") ? 20 : 0;

translate([-space,0,0])cameraSupport();
translate([0,0,-space])support();
translate([-3*space,0,0])mountedPillar();
translate([0,0,-3*space])topLid();

//translate([0,0,-4*space-1.5*supportZ])panSupport();
translate([0,0,-6*space])case ();

}

//-------------------------------
module testSupport(xPos, Pos) {
        //Need to center the servo shaft, the seconf figure is the distance form border
        eccentricity = 22.2 / 2 - 5;
        servoBodySupport = 25;
        //Need to adjust the clearance to account for the Xpos
		armLenght = 1.4*(boxDepth/2);
       difference() {

            union() {

                difference() {
                    translate([-shellThickness / 2, 0, 15]) {
                        cube([shellThickness, servoBodySupport, armLenght], center = true);
                    }

                }

                translate([-servoBodySupport / 2, 0, eccentricity]) {
                    //This is the servo box
                    difference() {
                        cube([servoBodySupport, 12 + 1 + shellThickness, 22.2 + 1 + shellThickness], center = true);
                        cube([servoBodySupport, 12 + 1, 22.2 + 1], center = true);

                        //Servo cable slot
                        translate([0, 0, -2 * eccentricity]) cube([100, 4, 4], center = true);

                    }

                }
            }

            //This is the slot in the servo arm. Need to check why -15 in the translate is needed
translate([-(11)+(32.2/2+12/2+tolerance/2)-15, 0, eccentricity]) {

                cube([20 + tolerance, 12+tolerance/2, 32.2 + tolerance], center = true);
                translate([-31/2, 0, 0]) cube([31 + tolerance, 12+tolerance/2, 22.2 + tolerance], center = true);

            }

}

//Dimensions are hardwired, should be parameters!!!
		translate([-10,0,+armLenght/2+15])rotate([270,0,0])linear_extrude(height = servoBodySupport, center = true, convexity = 10, twist = 0)
			//polygon([[0,0],[8,20],[10,0]], convexity = N);
 polygon([[0,0],[10,armLenght/3],[10,0]], convexity = N);

}

//-------------------------

//assembly("explode");
//case();
//topLid();
//SUB_Power();

//mountedPillar();

servoTiltSupport(gimballWidth/2, "tilt");

//cameraSupport();

$fn = 50;
//Need to fix servo tilt suport issue. The spheric part does not move with the arm when you change xPos parameter
// RPi supports does not touch the bottom of the case
include <rpi.scad>;
PIcameraDiam = 8;
PIcameraX = 25;
PIcameraY = 25;

mik = 2;
shellThickness = 3;
supportBaseHeight = 5;
//5.4 is the height of the
baseHeight = 31 + shellThickness - 5.4;
tolerance = 1.5;
gimballWidth = 45; //for 50 gives R = 86

R = sqrt(3 * gimballWidth * gimballWidth) / 2;
panSupport = sqrt(R * R - (gimballWidth * gimballWidth / 4));
gimballPivotR = R / 2;
supportY = 2 * sqrt(R * R - (gimballWidth / 2) * (gimballWidth / 2));
supportX = R - gimballWidth / 2;
supportZ = 1.1 * R;

servoX = 11.8;
servoY = 22.2;

servoHornThickness = 5.5;
servoHornWidth = 8;
servoHornLenght = 32;

servoClerance = 31 - 16 - servoHornThickness;
eccentricity = 22.2 / 2 - 5;
servoBodySupport = 20;

screwDiam = 3;
screwLenght = 10;
screwTolerance = 0.3;

Piwidth = 56;
Pilength = 85;
Piheight = 1.5;

boxLength = Pilength + 8;
boxDepth = 2 * R + 10;
boxHeight = 48;
boxThickness = 1;
topCoverHeight = 15;

servoPanZ = boxHeight - 20 + topCoverHeight - servoClerance;
servoPanBoxX = (servoX - 2 * boxThickness - tolerance);
servoPanBoxThickness = 2;

function RatX(x) = sqrt(R * R - (x * x));

//---------------------------------------
module panSupport() {
    //rotate([0,270,0])servoTiltSupport(15,"pan");
    difference() {
        union() {
            color("blue", 0.5) multmatrix(m = [
                [1, 0, 0, 0],
                [0, 1, 0.4, 0],
                [0, 0, 1, 0],
                [0, 0, 0, 1]
            ]) cylinder(h = servoPanZ, d1 = boxDepth, d2 = servoY + 2 * servoPanBoxThickness, center = true);
            //Creates bottom support blocks add -mik to y transaltion if you want to zero the mechanical interference
            translate([0, boxDepth / 2 - 20 / 2 - mik, -servoPanZ / 2 + 5]) cube([boxHeight / 2 + servoPanBoxX, 20, 10], center = true);
            mirror([0, 1, 0]) translate([0, boxDepth / 2 - 20 / 2 - mik, -servoPanZ / 2 + 5]) cube([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 20, 10], center = true);
        }

        //Cuts the support from a cylinder
        translate([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 0, 0]) cube([boxHeight, boxDepth + 10, boxHeight - 20 + topCoverHeight - servoClerance], center = true);
        mirror(1, 0, 0) translate([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 0, 0]) cube([boxHeight, boxDepth + 10, boxHeight - 20 + topCoverHeight - servoClerance], center = true);

        color("blue", 0.5) multmatrix(m = [
            [1, 0, 0, 0],
            [0, 1, 0.4, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ]) cylinder(h = servoPanZ, d1 = boxDepth - 10, d2 = servoY - 10, center = true);

        //Cuts the support to fit case
        translate([0, +eccentricity, 10]) cube([servoPanBoxX + servoPanBoxThickness + 4, servoY + servoPanBoxThickness + 1.5, 20], center = true);
        translate([0, -(boxDepth / 2 + 10 - mik), -servoPanZ / 2 + 5]) cube([boxHeight / 2 + servoPanBoxX, 20, 10], center = true);

        //Slot for video
        translate([0, boxDepth / 2 - 10, -servoPanZ / 2 + 5]) rotate([90, 0, 0]) scale([1.2, 1.5, 1]) cube([8, 20, 20], center = true);

    }
    rotate([0, 90, 90]) translate([3.25, 0, 0]) miscroServoSupport();

}

//---------------------------------------
//This was the verion for RPi v1
module panSupportOLD() {
    //rotate([0,270,0])servoTiltSupport(15,"pan");
    difference() {
        union() {
            color("blue", 0.5) multmatrix(m = [
                [1, 0, 0, 0],
                [0, 1, 0.4, 0],
                [0, 0, 1, 0],
                [0, 0, 0, 1]
            ]) cylinder(h = servoPanZ, d1 = boxDepth, d2 = servoY + 2 * servoPanBoxThickness, center = true);
            //Creates bottom support blocks add -mik to y transaltion if you want to zero the mechanical interference
            translate([0, boxDepth / 2 - 20 / 2 - mik, -servoPanZ / 2 + 5]) cube([boxHeight / 2 + servoPanBoxX, 20, 10], center = true);
            mirror([0, 1, 0]) translate([0, boxDepth / 2 - 20 / 2 - mik, -servoPanZ / 2 + 5]) cube([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 20, 10], center = true);
        }

        //Cuts the support from a cylinder
        translate([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 0, 0]) cube([boxHeight, boxDepth + 10, boxHeight - 20 + topCoverHeight - servoClerance], center = true);
        mirror(1, 0, 0) translate([boxHeight / 2 + (servoX - 2 * boxThickness - tolerance), 0, 0]) cube([boxHeight, boxDepth + 10, boxHeight - 20 + topCoverHeight - servoClerance], center = true);

        color("blue", 0.5) multmatrix(m = [
            [1, 0, 0, 0],
            [0, 1, 0.4, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ]) cylinder(h = servoPanZ, d1 = boxDepth - 10, d2 = servoY - 10, center = true);

        //Cuts the support to fit case
        translate([0, +eccentricity, 10]) cube([servoPanBoxX + servoPanBoxThickness + 4, servoY + servoPanBoxThickness + 1.5, 20], center = true);
        translate([0, -(boxDepth / 2 + 10 - mik), -servoPanZ / 2 + 5]) cube([boxHeight / 2 + servoPanBoxX, 20, 10], center = true);

        //Slot for video
        translate([0, boxDepth / 2 - 10, -servoPanZ / 2 + 5]) rotate([90, 0, 0]) scale([1.2, 1.5, 1]) cube([8, 20, 20], center = true);

    }
    rotate([0, 90, 90]) translate([3.25, 0, 0]) miscroServoSupport();

}

module miscroServoSupport() {

    translate([-servoBodySupport / 2, 0, eccentricity]) {
        //This is the servo box
        difference() {
            cube([servoBodySupport, 12 + 1 + shellThickness, 22.2 + 1 + shellThickness], center = true);
            cube([servoBodySupport, 12 + 1, 22.2 + 1], center = true);

            //Servo cable slot
            translate([0, 0, -2 * eccentricity]) cube([100, 3, 3], center = true);

        }

    }

}

//--------------------------------
module box(Xdim, Ydim, Zdim, thickness, application)
    //Creates a box, dimensions are internal
    {
        difference() {

            cube([Xdim + thickness, Ydim + thickness, Zdim], center = true);
            translate([0, 0, thickness / 2]) cube([Xdim, Ydim, Zdim], center = true);
            if (application == "servo") {
                translate([-Xdim / 2, 0, -0.5 * Zdim]) cube([5, Ydim + 2 * thickness, Zdim / 2 + thickness], center = true);
                translate([+Xdim / 2, 0, -0.5 * Zdim]) cube([5, Ydim + 2 * thickness, Zdim / 2 + thickness], center = true);
                translate([+Xdim / 2, 0, 0]) cube([thickness / 2, 0.3 * Ydim, Zdim], center = true);
                translate([-Xdim / 2, 0, 0]) cube([thickness / 2, 0.3 * Ydim, Zdim], center = true);
            }
        }
    }

//Baseplate for pan mechanism
module base(servo) {
        difference() {

            {
                minkowski() {
                    cylinder(r = R + mik, h = supportBaseHeight);
                    rotate([90, 0, 0]) cylinder(r = mik, h = 1);
                }

                //This to create a anchoring mechanism with the pan base
                //translate([0, 0,-mik])cylinder(r = R + mik+3, h = 2);
            }
            if (servo == true) {
                translate([0, 0, -supportZ - servoHornThickness - mik]) rotate([0, 90, 0]) servoHorn("cross");
                cylinder(r = 2.5, h = 15, center = true);
            }
        }
    }
    //-------------------------
    //				translate([0, 0, -supportZ - servoHornThickness-mik])rotate([0, 90, 0])servoHorn("cross");

module supportSection() {
        intersection() {
            difference() {
                sphere(R);
                cube([gimballWidth, 2 * gimballWidth, 2 * gimballWidth], center = true);

            }
            translate([R - supportX / 2, 0, 0]) cube([supportX, supportY, 10], center = true);
        }
    }
    //-------------------------

module tiltSupport()
    //This creates the supporting pillar for the camera shell
    {
        union() {
            difference() {
                sphere(R);
                cube([gimballWidth, 2 * gimballWidth, 2 * gimballWidth], center = true);
                translate([-gimballWidth, 0, 0]) cube([2 * gimballWidth, 2 * gimballWidth, 2 * gimballWidth], center = true);

            }

            hull() {
                translate([0, 0, -5]) supportSection();
                translate([0, 0, -supportZ]) supportSection();
            }
        }

    }

module servoTiltSupport(xPos, Pos) {
        //Need to center the servo shaft, the second figure is the distance form border
        supportPosition = xPos;
        //Need to adjust the clearance to account for the Xpos
        supportCurvature = 30;

        rotate([-0, 0, 0]) difference() {

            union() {

                difference() {
                    translate([-shellThickness / 2 + supportPosition, 0, 15]) {
                        cube([shellThickness, 20, 2 * RatX(gimballWidth / 2)], center = true);
                    }

                    if (Pos == "tilt") {
                        difference() {
                            translate([-shellThickness / 2 + supportPosition, 0, 15]) cube([shellThickness, 20, 2 * RatX(gimballWidth / 2)], center = true);

                            //The size of the sphere governs where the servo support will be cut. Need to add tolerance if you whant to print as one piece with the shell.
                            sphere(RatX(gimballWidth / 2) + shellThickness + tolerance);

                        }
                    }

                }

                translate([-servoBodySupport / 2 + supportPosition, 0, eccentricity]) {
                    //This is the servo box
                    difference() {
                        cube([servoBodySupport, 12 + 1 + shellThickness, 22.2 + 1 + shellThickness], center = true);
                        //cube([servoBodySupport, 12 + 1, 22.2 + 1], center = true);

                        //Servo cable slot
                        translate([0, 0, -2 * eccentricity]) cube([100, 3, 3], center = true);

                    }

                }
            }

            //This is the slot in the servo arm
            translate([servoClerance + 10, 0, eccentricity]) {
                cube([20 + 1, 12, 32.2 + 1], center = true);
                translate([-31 / 2, 0, 0]) cube([31 + 1, 12, 22.2 + 1], center = true);

            }
        }

        //This is the curved extra support for the connection with the gimball internal shell
        if (Pos == "tilt") {
            translate([0, 0, 0])
            difference() {
                translate([3, 0, +RatX(xPos) - 10])
                difference() {
                    cube([20, 20, 20], center = true);
                    translate([-supportCurvature, 0, -supportCurvature]) sphere(r = supportCurvature + 20);

                }

                difference() {
                    sphere(RatX(gimballWidth / 2) + 10 * shellThickness + tolerance);

                    sphere(RatX(gimballWidth / 2) + shellThickness + tolerance);

                }

            }
        }

        //Dimensions are hardwired, should be parameters!!!

        if (Pos == "pan") {

            translate([2 * shellThickness - 1, 0, 45]) rotate([270, 0, 0]) linear_extrude(height = servoBodySupport, center = true, convexity = 10, twist = 0)
            polygon([
                [0, 0],
                [8, 20],
                [10, 0]
            ], convexity = N);
            mirror([0, 0, 1]) translate([2 * shellThickness - 1, 0, 45]) rotate([270, 0, 0]) linear_extrude(height = servoBodySupport, center = true, convexity = 10, twist = 0) polygon([
                [0, 0],
                [7, 28],
                [10, 30],
                [10, 0]
            ], convexity = N);

        }

    }
    //-------------------------
module cameraSupport() {
        difference() {
            difference() {
                sphere(R);
                sphere(R - shellThickness);
            }

            translate([R + gimballWidth / 2, 0, 0]) cube([2 * R, 2 * R, 2 * R], center = true);
            translate([-R - gimballWidth / 2, 0, 0]) cube([2 * R, 2 * R, 2 * R], center = true);
            //Creates camera support
            translate([0, R - 7, 0]) cube([PIcameraX + tolerance, 3, PIcameraY + tolerance], center = true);
            translate([0, R - 7, 0]) cube([2, 3, PIcameraY], center = true);
            //Slot for camera wires
            translate([0, R - 8, -PIcameraY / 2 - 2]) cube([PIcameraX - 5, 3, 3], center = true);

            //Hole for the camera
            translate([0, R, 0]) rotate([90, 0, 0]) cube([PIcameraDiam + tolerance, 10, PIcameraDiam + tolerance], center = true);
        }

        //Need to hardwire the servo dimensions

        {
            servoTiltSupport(15, "tilt");

        }
        //pivot mechanism
        translate([-gimballWidth / 2, 0, 0]) {
            difference() {
                rotate([0, 90, 0]) cylinder(r = panSupport, h = shellThickness);
                gimballPivot(1);
                rotate([0, 90, 0]) cylinder(r = gimballPivotR - shellThickness, h = shellThickness);

            }
            gimballPivot(1);
        }
    }
    //-------------------------

module servoHorn(type) {
        cube([servoHornThickness, servoHornWidth, servoHornLenght], center = true);
        if (type == "cross") rotate([90, 0, 0]) cube([servoHornThickness, servoHornWidth, servoHornLenght], center = true);
    }
    //-------------------------

module gimballPivot(tolerance) {
        rotate([0, 90, 0])
        difference() {
            cylinder(r = gimballPivotR - tolerance / 2, h = gimballPivotR / 2, center = true);
            cylinder(r = gimballPivotR - 3 + tolerance / 2, h = gimballPivotR / 2, center = true);
        }
    }
    //-------------------------

module support() {
    scaleFactor = (R + tolerance) / R;

    difference() {
        tiltSupport();

        translate([gimballWidth / 2 + servoHornThickness / 2, 0, 0]) rotate([45, 0, 0]) servoHorn("line");
        translate([gimballWidth / 2, 0, 0]) rotate([0, 90, 0]) cylinder(r = 2.5, h = 30);

    }

    //Need to cut holes in the base for servo and camera cables and the screws
    translate([0, 0, -supportBaseHeight / 2])
    difference() {

        {
            color("blue", 0.5) translate([0, 0, -supportZ - supportBaseHeight]) base(true);
            translate([0, 0, -supportZ - servoHornThickness]) rotate([0, 90, 0]) servoHorn("cross");
            SUB_cameracableDuct();
            translate([0, 0, +supportBaseHeight / 2]) mirror([1, 0, 0]) tiltSupport();
            SUB_mountedPillarScrewHoles();
            translate([-gimballWidth / 2 - supportX / 2, 0, 0]) rotate([0, 0, 180])
            SUB_servocableDuct();

        }

    }

}

module mountedPillar() {
    //Pillar for camera. This one needs to be mounted separately
    difference() {
        mirror([1, 0, 0]) tiltSupport();
        translate([-gimballWidth / 2, 0, 0]) gimballPivot(0);
        translate([-gimballWidth / 2, 0, 0]) gimballPivot(3); {
            SUB_cameracableDuct();
            SUB_mountedPillarScrewHoles();
            translate([-gimballWidth / 2 - supportX / 2, 0, -supportX / 2]) rotate([0, 0, 180])
            SUB_servocableDuct();
        }

    }

}

module SUB_screw(tolerance) {
    cylinder(r = screwDiam / 2 + tolerance, h = screwLenght);
    translate([0, 0, -3]) cylinder(r = 2 * screwDiam / 2 + screwTolerance, h = 4);
}

module SUB_mountedPillarScrewHoles() {
    color("blue", 0.5) translate([-R + supportX / 2 + 2, 0, -supportZ - screwLenght / 2]) {
        translate([0, 0.5 * panSupport, 0]) SUB_screw(-screwTolerance);
        mirror([0, 1, 0]) translate([0, 0.5 * panSupport, 0]) SUB_screw(-screwTolerance);
    }
}

module SUB_servocableDuct() {
    //Duct for servo cable
    {
        translate([0, 0, -supportZ + supportX]) cylinder(r = supportX / 3, h = supportZ + supportX / 2, center = true);
        translate([-supportX, 0, 0]) rotate([0, 120, 0]) cylinder(r = supportX / 3, h = supportX, center = false);
        translate([0, 0, -supportZ - supportX / 3]) rotate([0, 90, 180]) cylinder(r = supportX / 3, h = 0.6 * R, center = false);
    }

}

module SUB_cameracableDuct() {
        //Duct for camera connetion
        translate([-gimballWidth / 2 - 0.5 * supportX, 0, 0]) rotate([0, 0, 90]) {
            translate([0, 0, 0]) rotate([90, 0, 0]) cylinder(r = 26 / 2, h = 0.6 * supportX);
            translate([0, 0, -supportZ / 2]) cube([20, 3, supportZ + 1.1 * supportBaseHeight], center = true);
            translate([0, -R * 0.5 + 13, -supportZ - supportBaseHeight]) rotate([90, 0, 0]) cube([20, 5, R * 0.5], center = true);

        }
    }
    //---------------------------

module connectionSocket() {

    cylinder(r = 3, h = 5);
    translate([-9, -3 / 2, 0]) cube([10, 3, 5]);

}

module PiSupport() {
    difference() {
        translate([0,0,1])cube([4 + 3 / 2 - 0.4, 4 + 3 / 2 - 0.2, 8], center = true);
        cylinder(r = 3 / 2 - 0.2, h = 2 * Piheight + 8, $fs = 0.1);
    }

}

module case () {
    translate([0, 0, -boxHeight / 2 - supportZ - 2 * supportBaseHeight]) {
        difference() {
            minkowski() {
                box(boxLength, boxDepth, boxHeight, boxThickness, "PI");
                //sphere(r = mik);
                cylinder(r = mik, h = 2);
            }
            translate([R - 10, -Piwidth / 2, -boxHeight / 2 - 5 / 2]) connectionSocket();
            translate([-R + 10, -Piwidth / 2, -boxHeight / 2 - 5 / 2]) connectionSocket();
            translate([R - 10, Piwidth / 2, -boxHeight / 2 - 5 / 2]) connectionSocket();
            translate([-R + 10, Piwidth / 2, -boxHeight / 2 - 5 / 2]) connectionSocket();

            translate([0, 0, -boxHeight / 2 - 5 / 2]) cylinder(r = 3, h = 5);

translate([-Pilength / 2, -16, -boxHeight / 2 + 5 + shellThickness]) {
                rpi();
            }
//This is the power inlet. The +6 in z is half the z-lenght of the component
translate([-boxLength/2,-boxDepth/2+8,-boxHeight/2+shellThickness+2*mik+tolerance])cube([15+tolerance,9+tolerance,12], center=true);

        }
        translate([-Pilength / 2, -16, -boxHeight / 2 + 1 + shellThickness]) {
translate ([Pilength-3.5, Piwidth-3.5,-0.1]) PiSupport();
translate ([Pilength-3.5, -0+3.5,-0.1]) PiSupport();
translate ([Pilength-58-3.5, Piwidth-3.5,-0.1]) PiSupport();
translate ([Pilength-58, 0+3.5,-0.1]) PiSupport();
          //translate([25.5, 18, 0]) PiSupport();
            //translate([Pilength - 5, Piwidth - 12.5, 0]) PiSupport();

        }

    }
translate([-boxLength/2+15/2+mik,-boxDepth/2+8,-supportZ-boxHeight-4])rotate([0,0,180])SUB_Power();

}

module topLid() {
    tolerance = 0.02;

    difference() {
        difference() {
            {
                minkowski() {
                    translate([0, 0, -supportZ - supportBaseHeight - topCoverHeight / 2]) cube([boxLength + mik - 1, boxDepth + mik - 1, topCoverHeight], center = true);
                    sphere(r = mik); //cylinder(r = mik, h = 2);
                }
                translate([0, 0, -supportZ - supportBaseHeight]) {
                    scale([1 + tolerance, 1 + tolerance, 1]) base("false");
                    scale([0.9, 0.9, 30]) base("false");

                    //This is the grid an hole for mic.need to re-work the placement definition as it will not work for other box dimensions!!!
                    translate([boxLength / 2 - 5 - mik - boxThickness, -(boxDepth / 2 - 5 - mik - boxThickness), +14 - supportBaseHeight - topCoverHeight / 2]) rotate([0, 180, 0]) SUB_Mic();

                }

            }

        }

        case ();

    }

//Need to explain the -1.4*supportBaseHeight
difference(){
translate([0,0,-supportZ-1.4*supportBaseHeight])rotate([0,-90,90])testSupport();
       case ();

}

}

module SUB_Mic() {
    translate([-7, 0, 0]) for (i = [1: 6]) {
        translate([i * 2, 0, 0]) cube([1, 10, 2], center = true);
    }
    cylinder(r = 5, h = 2 * topCoverHeight);

}

module SUB_Power(){
	difference(){
		cube([15,9+3,12], center=true);
		translate([2,0,2])cube([15+tolerance,9+tolerance,12], center=true);
	}
}
module assembly(view)
{

space = (view == "explode") ? 20 : 0;

translate([-space,0,0])cameraSupport();
translate([0,0,-space])support();
translate([-3*space,0,0])mountedPillar();
translate([0,0,-3*space])topLid();

//translate([0,0,-4*space-1.5*supportZ])panSupport();
translate([0,0,-6*space])case ();

}

//-------------------------------
module testSupport(xPos, Pos) {
        //Need to center the servo shaft, the seconf figure is the distance form border
        eccentricity = 22.2 / 2 - 5;
        servoBodySupport = 25;
        //Need to adjust the clearance to account for the Xpos
		armLenght = 1.4*(boxDepth/2);
       difference() {

            union() {

                difference() {
                    translate([-shellThickness / 2, 0, 15]) {
                        cube([shellThickness, servoBodySupport, armLenght], center = true);
                    }

                }

                translate([-servoBodySupport / 2, 0, eccentricity]) {
                    //This is the servo box
                    difference() {
                        cube([servoBodySupport, 12 + 1 + shellThickness, 22.2 + 1 + shellThickness], center = true);
                        cube([servoBodySupport, 12 + 1, 22.2 + 1], center = true);

                        //Servo cable slot
                        translate([0, 0, -2 * eccentricity]) cube([100, 4, 4], center = true);

                    }

                }
            }

            //This is the slot in the servo arm. Need to check why -15 in the translate is needed
translate([-(11)+(32.2/2+12/2+tolerance/2)-15, 0, eccentricity]) {

                cube([20 + tolerance, 12+tolerance/2, 32.2 + tolerance], center = true);
                translate([-31/2, 0, 0]) cube([31 + tolerance, 12+tolerance/2, 22.2 + tolerance], center = true);

            }

}

//Dimensions are hardwired, should be parameters!!!
		translate([-10,0,+armLenght/2+15])rotate([270,0,0])linear_extrude(height = servoBodySupport, center = true, convexity = 10, twist = 0)
			//polygon([[0,0],[8,20],[10,0]], convexity = N);
 polygon([[0,0],[10,armLenght/3],[10,0]], convexity = N);

}

//-------------------------

//assembly("explode");
//case();
topLid();
//SUB_Power();


GUANIN

GUANIN


h=2.5mm + loop for printing multiple elements at once

module wedge(width, span, height)
{
  polyhedron(
    points=[
             [0, 0, 0],
             [span, 0, 0],
             [span, width, 0],
             [0, width, 0],
             [0, width, height],
             [span, width, height],
           ],
    faces=[
            // bottom
            [0,1,2],
            [2,3,0],
            // sides
            [5,2,1],
            [0,3,4],
            // back
            [3,2,4],
            [5,4,2],
            // top
            [0,4,5],
            [5,1,0],
          ]
   );
};

module support()
{
  w=119;
  s=21;
  h=2.5;

  difference()
  {
    union()
    {
      wedge(width=w, span=s, height=h);

      // rounded ending
      translate([s/2, w, 0])
        difference()
        {
          cylinder(r=s/2, h=h);
          translate([-s/2, -s/2*2, 0])
            cube([s, s, h]);
        }
    }

    // screw hole
    translate([s/2, w-8/2, 0])
      cylinder(r=8/2, h=h);
  }
}

for(i=[0:0])
  translate(i*[25, 0, 0])
    support(2.5);

module wedge(width, span, height)
{
  polyhedron(
    points=[
             [0, 0, 0],
             [span, 0, 0],
             [span, width, 0],
             [0, width, 0],
             [0, width, height],
             [span, width, height],
           ],
    faces=[
            // bottom
            [0,1,2],
            [2,3,0],
            // sides
            [5,2,1],
            [0,3,4],
            // back
            [3,2,4],
            [5,4,2],
            // top
            [0,4,5],
            [5,1,0],
          ]
   );
};

module support()
{
  h=4;
  w=119;
  s=21;

  difference()
  {
    union()
    {
      wedge(width=w, span=s, height=h);

      // rounded ending
      translate([s/2, w, 0])
        difference()
        {
          cylinder(r=s/2, h=h);
          translate([-s/2, -s/2*2, 0])
            cube([s, s, h]);
        }
    }

    // screw hole
    translate([s/2, w-8/2, 0])
      cylinder(r=8/2, h=h);
  }
}

support();

translate([-5, 0, 0])
  rotate([0, -90, 0])
    support();


screws: add support for square nuts

screws: add support for square nuts

include <system.scad>
include <thread-data.scad>
include <nut-data.scad>
include <materials.scad>

use <shapes.scad>
use <transforms.scad>
use <misc.scad>

// for proper threads
use <naca_sweep.scad>

// naive, assume head height is same as thread size (generally true for cap heads)
function get_screw_head_h(head, thread) =
(head=="socket" || head == "button") ?
    get(ThreadSize, thread)
:
head == "set" ? 0
:
0;

function get_screw_head_d(head, thread) =
(head=="socket" || head == "button") ?
    2 * get(ThreadSize, thread)
:
head == "set" ? 0
:
0;

module screw(part, nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_head=true, nut_offset=0, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw: No nut or thread given");
    assert(head != U, "screw: head == U");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw: Mismatched nut and thread");*/

    head_h = get_screw_head_h(head=head, thread=thread_);
    nut_h = get(NutThickness,nut)*tolerance;
    threadsize = get(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;

    if(part==U)
    {
        difference()
        {
            screw(part="pos", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
            screw(part="neg", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
        }
        if($show_vit)
        %screw(part="vit", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
    }
    else if(part=="pos")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    tz(head_embed?-head_h:0)
    {
        if(with_head)
        {
            tz(h/2+.01)
            screw_head(part=part, head=head, thread=thread_, orient=Z, align=Z);
        }

        tz(-h/2+.01)
        screw_thread(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

        if(with_nut && nut != U)
        {
            tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
            screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);
        }
    }
    else if(part=="neg")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    tz(head_embed?-head_h:0)
    {
        if(with_head)
        tz(h/2+.01)
        screw_head(part=part, head=head, thread=thread_, orient=Z, align=Z);
    }
    else if(part=="vit")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        if(with_nut && nut != U)
        {
            tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
            %screw_nut(nut=nut, align=-Z);
        }
    }
}

module screw_cut(nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_nut_cut=true, with_nut_access=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=Z, align=N)
{
    assert(h>0);

    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw_cut: Mismatched nut and thread");*/

    nut_h = get(NutThickness,nut)*tolerance;
    assert(head != U, "screw_cut: head == U");

    head_h = get_screw_head_h(head=head, thread=thread_);
    assert(head_h != U, "screw_cut: head_h is U!");

    threadsize = get(ThreadSize, thread_);
    assert(threadsize != U, "screw_cut: threadsize is U!");

    s = threadsize*tolerance;
    total_h = h;
    assert(s != U, "screw_cut: s is U!");
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        tz(head_embed?-head_h:0)
        {
            if(with_head)
            {
                tz(h/2+.01)
                screw_head_cut(head=head, thread=thread_, tolerance=tolerance, override_h=head_cut_h, align=Z);
            }

            screw_thread_cut(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

            if(with_nut && nut != U && with_nut_cut)
            {
                tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
                screw_nut_cut(nut=nut, tolerance=tolerance, h=nut_cut_h, with_access=with_nut_access, align=-Z);
            }

            if($show_vit)
            {
                %screw(nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=-Z, align=N);
            }
        }
    }
}

module screw_thread(thread, h=10, tolerance=1.00, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread: thread is undef");
    threadsize = get(ThreadSize, thread);
    d = get(ThreadSize, thread);
    pitch = get(ThreadPitchMm, thread);
    windings = ceil(h/pitch);

    size_align(size=[d,d,h], orient=orient, align=align)
    material(Mat_Steel)
    {
        if($preview_mode)
        {
            cylindera(d=d, h=h, align=Z);
        }
        else
        {
            intersection()
            {
                cylindera(d=d, h=h, align=Z);
                tz(-pitch)
                threading(angle=60, pitch=pitch, d=d, windings=windings, full=true);
            }
            /*inner_d = (get(ThreadInternalMinorDiaMin, thread) + get(ThreadInternalMinorDiaMin, thread))/2;*/
            /*cylindera(d=inner_d, h=h, align=Z);*/
        }
    }
}

module screw_thread_cut(thread, h=10, tolerance=1.05, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread_cut: thread is undef");
    threadsize = get(ThreadSize, thread);
    cylindera(d=threadsize*tolerance, h=h, orient=orient, align=align);
}

module screw_head(part, head="socket", drive="hex", thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head: thread == undef");

    threadsize = get(ThreadSize, thread);
    head_h = fallback(override_h, get_screw_head_h(head=head, thread=thread));
    head_d = get_screw_head_d(head=head, thread=thread);

    if(part==U)
    {
        difference()
        {
            screw_head(part="pos", head=head, drive=drive, thread=thread, tolerance=tolerance, override_h=override_h, orient=orient, align=align);
            screw_head(part="neg", head=head, drive=drive, thread=thread, tolerance=tolerance, override_h=override_h, orient=orient, align=align);
        }
    }
    else if(part=="pos")
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket")
        {
            tz(-head_h/2)
            cylindera(d=head_d, h = head_h, align=Z);
        }
        else if(head=="button")
        {
            c = head_d;
            f = head_h*1.25;
            r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

            d = r - f; // displacement to move sphere

            intersection()
            {
                tz(-head_h/2)
                scale([1,1,.5])
                spherea(r = head_h, align=N);

                tz(-head_h/2)
                cylindera(d=head_d, h = head_h/2, align=Z);
            }
        }
    }
    else if(part=="neg")
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        tz(-head_h/2)
        if(drive=="hex")
        {
            threadsize_cut = threadsize-head_drive_threadsize_minus(head);
            inset = head_drive_inset(head, head_h);
            tz(inset)
            cylindera(d = fn_radius(threadsize_cut, 6), h = head_h, $fn = 6, align=Z);
        }
        else if(part=="neg")
        {
            if(head=="socket")
            {
                difference()
                {
                    tz(-head_h/2)
                    cylindera(d=head_d, h = fallback(override_h,head_h), align=Z);

                    if(drive == "hex")
                    {
                        cylindera(d = fn_radius(threadsize, 6), h = 1000, $fn = 6, align=Z);
                    }
                }
            }
            else if(head=="button")
            {
                c = head_d;
                f = head_h*1.25;
                r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

                d = r - f; // displacement to move sphere

                difference()
                {
                    intersection()
                    {
                        tz(-head_h/2)
                        scale([1,1,.5])
                        spherea(r = head_h, align=N);

                        tz(-head_h/2)
                        cylindera(d=head_d, h = fallback(override_h,head_h)/2, align=Z);
                    }

                    if(drive == "hex")
                    {
                        // button head uses -0.5*mm smaller hex
                        tz(-head_h/2)
                        cylindera(d = fn_radius(threadsize-.5, 6), h = 1000, $fn = 6, align=Z);
                    }
                }
            }
            else if(head=="set")
            {
            }
        }
    }
}

function head_drive_threadsize_minus(head) =
head=="socket"?  0:
head=="button"?  0.5:
head=="set"?     1.5:
0;

function head_drive_inset(head, head_h) =
head=="socket"?  1*mm:
head=="button"?  0.5*mm:
head=="set"?     -head_h/2:
0;

module screw_head_cut(head="socket", thread, tolerance=1.05, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head_cut: thread is undef");

    threadsize = get(ThreadSize, thread);
    head_h = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread)*tolerance;
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket" || head=="button")
        {
            tz(-head_h/2)
            cylindera(d=head_d, h=fallback(override_h, head_h), align=Z);
        }
        else if(head=="set")
        {
            tz(-head_h/2)
            cylindera(d=threadsize, h=fallback(override_h, head_h), align=Z);
        }
        else
        {
            assert("head: not implemented");
        }
    }
}

module screw_nut(nut, thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_facets = get(NutFacets, nut);
    assert(nut_facets>=4, "screw_nut: nut_facets < 4 is not supported");
    nut_dia = nut_dia(nut);
    nut_thread = fallback(thread, get(NutThread, nut));
    h_ = fallback(override_h, nut_thick);
    material(Mat_Steel)
    size_align([nut_dia, nut_dia, h_], orient=orient, align=align)
    {
        difference()
        {
            echo(nut_facets);
            if(nut_facets==4)
            {
                cubea([nut_dia,nut_dia,h_]);
            }
            else
            {
                cylindera($fn=nut_facets, d=nut_dia, h=h_);
            }
            tz(-h_/2-.2)
            screw_thread(thread=nut_thread, h=h_+.1);
        }
    }
}

module screw_nut_cut(nut, tolerance=1.05, h=1000, with_access=true, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut_cut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_dia = nut_dia(nut);
    nut_facets = get(NutFacets, nut);
    assert(nut_facets>=4, "screw_nut: nut_facets < 4 is not supported");
    size_align(size=[nut_dia, nut_dia, nut_thick], orient=orient, align=align)
    {
        tz(nut_thick/2)
        {
            tz(-.1)
            screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);

            if(with_access)
            tz(-nut_thick-.01)
            {
                if(nut_facets==4)
                {
                    cubea([nut_dia+2*mm,nut_dia+2*mm, h], align=-Z);
                }
                else
                {
                    cylindera(d=nut_dia+2*mm, h=h, orient=Z, align=-Z);
                }
            }
        }
    }
}

function nut_radius(nut) = outradius(get(NutWidthMin, nut)/2, get(NutFacets, nut));
function nut_dia(nut) = get(NutFacets, nut) > 4 ?
                2*nut_radius(nut) :
                get(NutWidthMax, nut);

module nut_trap_cut(nut, thread, head="socket", trap_offset=10, screw_l=10*mm, screw_offset=0*mm, cut_screw=false, cut_screw_close=false, trap_h=10, trap_axis=-Y, orient=Z, align=N)
{
    screw_min_nut_dist = 2*mm;
    assert(screw_l+screw_offset >= trap_offset, "nut_trap_cut: screw_l+screw_offset < trap_offset");
    assert(screw_offset-trap_offset <= -screw_min_nut_dist, "nut_trap_cut: screw_offset-trap_offset > -screw_min_nut_dist");

    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "nut_trap_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "nut_trap_cut: Mismatched nut and thread");*/

    threadsize = get(ThreadSize, thread_);
    head_h = get_screw_head_h(head=head, thread=thread);
    nut_h = get(NutThickness, nut) +.5*mm;

    tolerance = -.2*mm;
    nut_width_min = get(NutWidthMin, nut)+tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut)+tolerance;
    total_h = nut_h;
    size_align(size=[nut_dia, nut_dia, total_h], orient=orient, orient_ref=Z, align=align)
    {
        if(cut_screw)
        {
            if(cut_screw_close)
            {
                tz(-nut_h/2)
                tz(-screw_min_nut_dist)
                tz(trap_offset)
                screw_cut($show_vit=false, nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1.1, h=screw_l, orient=Z, align=Z);
            }
            else
            {
                tz(-nut_h/2)
                tz(screw_offset)
                screw_cut($show_vit=false, nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1.1, h=screw_l, orient=Z, align=Z);
            }
        }
        else
        {
            tz(nut_h/2+screw_offset)
            screw_thread_cut($show_vit=false, thread=thread_, tolerance=1.1, h=screw_l, orient=Z, align=Z);
        }

        tz(trap_offset)
        hull()
        {
            orient(axis=Z, axis_ref=orient)
            translate(-.15*mm*trap_axis)
            stack(dist=trap_h, axis=trap_axis)
            {
                orient(axis=orient, axis_ref=Z)
                {
                    if(nut_facets==4)
                    {
                        cubea([nut_dia,nut_dia,h_]);
                    }
                    else
                    {
                        rz(30)
                        cylindera($fn=nut_facets, d=nut_dia, h=nut_h, align=N);
                    }
                }

                orient(axis=orient, axis_ref=Z)
                cubea(size=[nut_width_min,nut_width_min,nut_h], align=N);
            }
        }

        if($show_vit)
        {
            if(cut_screw)
            {
                tz(-nut_h/2)
                tz(screw_offset)
                %screw(nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1, h=screw_l, orient=Z, align=Z);
            }

            tz(trap_offset)
            rotate([0,0,30])
            %screw_nut(nut=nut, thread=thread, tolerance=1, orient=Z, align=N);
        }

    }
}

if(false)
{
    all_axes()
    translate($axis*5*mm)
    color($color)
    screw(nut=NutHexM3, h=10*mm, orient=-$axis, align=$axis);
}

// all nuts
if(false)
{
    for(nuti=[0:1:len(AllNut)-1])
    {
        nut = AllNut[nuti];
        v_threadsize = v_get(AllNut,ThreadSize);
        v_nutwidthmin = v_get(AllNut,NutWidthMin);
        dist = v_cumsum(v_nutwidthmin, 0, nuti)[nuti]*1.2;
        translate(X*dist)
        rotate([0,0,30])
        screw(nut=nut, h=get(NutWidthMax, nut)*5, head_embed=false, orient=-Z, align=Z);
    }
}

// From ParkinBots threading library
// https://www.thingiverse.com/thing:1659079
/*module Threading(D = 0, pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=40)*/
/*{*/
    /*R = D==0?d/2+2*pitch/PI:D/2; */
    /*translate([0,0,-pitch])*/
    /*difference()*/
    /*{*/
        /*translate([0,0,pitch])*/
        /*cylinder (r=R, h =pitch*(windings-helices));*/
        /*threading(pitch, d, windings, helices, angle, steps, true); */
    /*}*/
/*}*/

// From ParkinBots threading library
// https://www.thingiverse.com/thing:1659079
module threading(pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=40, full = false)
{
    // tricky: glue two 180° pieces together to get a proper manifold
    r = d/2;
    Steps = steps/2;
    Pitch = pitch*helices;
    if(full)
    {
        cylinder(r = r-.5-pitch/PI, h=pitch*(windings+helices), $fn=steps);
    }

    sweep(gen_dat(), planar_caps=true);   // half screw
    rz(180)
    tz(Pitch/2)
    sweep(gen_dat(), planar_caps=true);   // half screw

    function gen_dat() =
        let(ang = 180, bar = R_(180, -90, 0, Ty_(-r+.5, vec3D(pitch/PI*Rack(windings, angle)))))
        [for (i=[0:Steps]) Tz_(i/2/Steps*Pitch, -Rz_(i*ang/Steps, bar))];

    function Rack(w, angle) =
        concat([[0, 2]],
            [for (i=[0:w-1], j=[0:3])
            let(t = [ [0, 1], [2*tan(angle/2), -1], [PI/2, -1], [2*tan(angle/2)+PI/2, 1]])
            [t[j][0]+i*PI, t[j][1]]], [[w*PI, 1], [w*PI, 2]
            ]);
}

if(false)
{
    $fn=32;
    all_heads=
    [
        "socket",
        "button",
        "set"
    ];
    for(i=[0:len(all_heads)-1])
    {
        head=all_heads[i];
        tx(i*8*mm)
        screw(nut=NutHexM3, head=head, h=25, orient=-Z);
    }

}

if(false)
{
    $fn=128;
    nut1 = NutHexM3;
    stack(axis=X, dist = 20)
    {
        screw(nut=NutHexM8, head="socket", h=25, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, with_head=1, with_nut=0, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, with_head=0, with_nut=0, orient=-Z);
        screw(nut=NutHexM8, head="set", h=25, with_nut=0, orient=-Z);

        threading(pitch = 1.25, d=8, windings=10, full=true);
    }
}

if(false)
{
    box_w = 150*mm;
    box_d = 10*mm;
    box_h = 10*mm;
    o = 10*mm;

    $show_vit = true;
    /*$fs= 0.2;*/
    /*$fa = 4;*/

    nut1 = NutHexM3;
    nut2 = NutHexM5;
    nut3 = NutKnurlM3_3_42;
    nut4 = NutKnurlM3_5_42;

    /*all_axes()*/
    /*for($axis=[X])*/
    /*color($color)*/
    /*translate($axis*15)*/
    /*echo($axis)*/

    /*orient(axis=$axis, axis_ref=-X)*/
    for(y=[-1,1])
    translate(-y*Y*20*mm)
    difference()
    {
        cubea([box_w,box_d,box_h], align=X+Z);
        test_cuts(orient=y*Z);
        /*%test_cuts(orient=y, axis_ref=-Z);*/
    }

}

if(false)
{
    nut1 = NutHexM5;
    stack(dist=10,axis=X)
    {
        $show_vit=true;
        $preview_mode=true;
        #screw(nut=nut1, h=10, orient=-Z, align=-Z);
        #screw_cut(nut=nut1, h=10, orient=-Z, align=-Z);
        #screw_cut(nut=nut1, h=10, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=10, cut_screw=false, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=10, cut_screw=true, orient=-Z, align=-Z);
        #screw(nut=nut1, h=20, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=20, cut_screw=true, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=20, trap_offset=20, cut_screw=true, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=25, screw_offset=-5, trap_offset=20, cut_screw=true, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=20, cut_screw=true, cut_screw_close=true, orient=-Z, align=-Z);
    }
}

module test_cuts(orient)
{
    union()
    {
        /*offset = 0;*/
        offset = -5;
        /*translate([0,offset,0])*/
        /*translate([5,0,-5])*/
        translate([5,0,0])
        stack(dist=10,axis=X)
        {
            screw_cut(nut=nut1, head="button", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, head="socket", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(thread=ThreadM5, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=orient, align=Z);

            screw_cut(nut=nut1, h=box_h, nut_offset=0*mm, head_embed=true, orient=orient, align=Z);
            screw_cut(nut=nut2, h=box_h, nut_offset=3*mm, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut4, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,-5*mm])
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=-Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=Z, orient=orient, align=N);
            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=-Z, orient=orient, align=N);
        }
    }
}

include <system.scad>
include <thread-data.scad>
include <nut-data.scad>
include <materials.scad>

use <shapes.scad>
use <transforms.scad>
use <misc.scad>

// for proper threads
use <naca_sweep.scad>

// naive, assume head height is same as thread size (generally true for cap heads)
function get_screw_head_h(head, thread) =
(head=="socket" || head == "button") ?
    get(ThreadSize, thread)
:
head == "set" ? 0
:
0;

function get_screw_head_d(head, thread) =
(head=="socket" || head == "button") ?
    2 * get(ThreadSize, thread)
:
head == "set" ? 0
:
0;

module screw(part, nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_head=true, nut_offset=0, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw: No nut or thread given");
    assert(head != U, "screw: head == U");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw: Mismatched nut and thread");*/

    head_h = get_screw_head_h(head=head, thread=thread_);
    nut_h = get(NutThickness,nut)*tolerance;
    threadsize = get(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;

    if(part==U)
    {
        difference()
        {
            screw(part="pos", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
            screw(part="neg", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
        }
        if($show_vit)
        %screw(part="vit", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
    }
    else if(part=="pos")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    tz(head_embed?-head_h:0)
    {
        if(with_head)
        {
            tz(h/2+.01)
            screw_head(part=part, head=head, thread=thread_, orient=Z, align=Z);
        }

        tz(-h/2+.01)
        screw_thread(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

        if(with_nut && nut != U)
        {
            tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
            screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);
        }
    }
    else if(part=="neg")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    tz(head_embed?-head_h:0)
    {
        if(with_head)
        tz(h/2+.01)
        screw_head(part=part, head=head, thread=thread_, orient=Z, align=Z);
    }
    else if(part=="vit")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        if(with_nut && nut != U)
        {
            tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
            %screw_nut(nut=nut, align=-Z);
        }
    }
}

module screw_cut(nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_nut_cut=true, with_nut_access=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=Z, align=N)
{
    assert(h>0);

    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw_cut: Mismatched nut and thread");*/

    nut_h = get(NutThickness,nut)*tolerance;
    assert(head != U, "screw_cut: head == U");

    head_h = get_screw_head_h(head=head, thread=thread_);
    assert(head_h != U, "screw_cut: head_h is U!");

    threadsize = get(ThreadSize, thread_);
    assert(threadsize != U, "screw_cut: threadsize is U!");

    s = threadsize*tolerance;
    total_h = h;
    assert(s != U, "screw_cut: s is U!");
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        tz(head_embed?-head_h:0)
        {
            if(with_head)
            {
                tz(h/2+.01)
                screw_head_cut(head=head, thread=thread_, tolerance=tolerance, override_h=head_cut_h, align=Z);
            }

            screw_thread_cut(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

            if(with_nut && nut != U && with_nut_cut)
            {
                tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
                screw_nut_cut(nut=nut, tolerance=tolerance, h=nut_cut_h, with_access=with_nut_access, align=-Z);
            }

            if($show_vit)
            {
                %screw(nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=-Z, align=N);
            }
        }
    }
}

module screw_thread(thread, h=10, tolerance=1.00, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread: thread is undef");
    threadsize = get(ThreadSize, thread);
    d = get(ThreadSize, thread);
    pitch = get(ThreadPitchMm, thread);
    windings = ceil(h/pitch);

    size_align(size=[d,d,h], orient=orient, align=align)
    material(Mat_Steel)
    {
        if($preview_mode)
        {
            cylindera(d=d, h=h, align=Z);
        }
        else
        {
            intersection()
            {
                cylindera(d=d, h=h, align=Z);
                tz(-pitch)
                threading(angle=60, pitch=pitch, d=d, windings=windings, full=true);
            }
            /*inner_d = (get(ThreadInternalMinorDiaMin, thread) + get(ThreadInternalMinorDiaMin, thread))/2;*/
            /*cylindera(d=inner_d, h=h, align=Z);*/
        }
    }
}

module screw_thread_cut(thread, h=10, tolerance=1.05, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread_cut: thread is undef");
    threadsize = get(ThreadSize, thread);
    cylindera(d=threadsize*tolerance, h=h, orient=orient, align=align);
}

module screw_head(part, head="socket", drive="hex", thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head: thread == undef");

    threadsize = get(ThreadSize, thread);
    head_h = fallback(override_h, get_screw_head_h(head=head, thread=thread));
    head_d = get_screw_head_d(head=head, thread=thread);

    if(part==U)
    {
        difference()
        {
            screw_head(part="pos", head=head, drive=drive, thread=thread, tolerance=tolerance, override_h=override_h, orient=orient, align=align);
            screw_head(part="neg", head=head, drive=drive, thread=thread, tolerance=tolerance, override_h=override_h, orient=orient, align=align);
        }
    }
    else if(part=="pos")
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket")
        {
            tz(-head_h/2)
            cylindera(d=head_d, h = head_h, align=Z);
        }
        else if(head=="button")
        {
            c = head_d;
            f = head_h*1.25;
            r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

            d = r - f; // displacement to move sphere

            intersection()
            {
                tz(-head_h/2)
                scale([1,1,.5])
                spherea(r = head_h, align=N);

                tz(-head_h/2)
                cylindera(d=head_d, h = head_h/2, align=Z);
            }
        }
    }
    else if(part=="neg")
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        tz(-head_h/2)
        if(drive=="hex")
        {
            threadsize_cut = threadsize-head_drive_threadsize_minus(head);
            inset = head_drive_inset(head, head_h);
            tz(inset)
            cylindera(d = fn_radius(threadsize_cut, 6), h = head_h, $fn = 6, align=Z);
        }
        else if(part=="neg")
        {
            if(head=="socket")
            {
                difference()
                {
                    tz(-head_h/2)
                    cylindera(d=head_d, h = fallback(override_h,head_h), align=Z);

                    if(drive == "hex")
                    {
                        cylindera(d = fn_radius(threadsize, 6), h = 1000, $fn = 6, align=Z);
                    }
                }
            }
            else if(head=="button")
            {
                c = head_d;
                f = head_h*1.25;
                r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

                d = r - f; // displacement to move sphere

                difference()
                {
                    intersection()
                    {
                        tz(-head_h/2)
                        scale([1,1,.5])
                        spherea(r = head_h, align=N);

                        tz(-head_h/2)
                        cylindera(d=head_d, h = fallback(override_h,head_h)/2, align=Z);
                    }

                    if(drive == "hex")
                    {
                        // button head uses -0.5*mm smaller hex
                        tz(-head_h/2)
                        cylindera(d = fn_radius(threadsize-.5, 6), h = 1000, $fn = 6, align=Z);
                    }
                }
            }
            else if(head=="set")
            {
            }
        }
    }
}

function head_drive_threadsize_minus(head) =
head=="socket"?  0:
head=="button"?  0.5:
head=="set"?     1.5:
0;

function head_drive_inset(head, head_h) =
head=="socket"?  1*mm:
head=="button"?  0.5*mm:
head=="set"?     -head_h/2:
0;

module screw_head_cut(head="socket", thread, tolerance=1.05, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head_cut: thread is undef");

    threadsize = get(ThreadSize, thread);
    head_h = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread)*tolerance;
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket" || head=="button")
        {
            tz(-head_h/2)
            cylindera(d=head_d, h=fallback(override_h, head_h), align=Z);
        }
        else if(head=="set")
        {
            tz(-head_h/2)
            cylindera(d=threadsize, h=fallback(override_h, head_h), align=Z);
        }
        else
        {
            assert("head: not implemented");
        }
    }
}

module screw_nut(nut, thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut);
    nut_thread = fallback(thread, get(NutThread, nut));
    h_ = fallback(override_h, nut_thick);
    material(Mat_Steel)
    size_align([nut_dia, nut_dia, h_], orient=orient, align=align)
    {
        difference()
        {
            cylindera($fn=nut_facets, d=nut_dia, h=h_);
            tz(-h_/2-.2)
            screw_thread(thread=nut_thread, h=h_+.1);
        }
    }
}

module screw_nut_cut(nut, tolerance=1.05, h=1000, with_access=true, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut_cut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_dia = nut_dia(nut);
    size_align(size=[nut_dia, nut_dia, nut_thick], orient=orient, align=align)
    {
        tz(nut_thick/2)
        {
            tz(-.1)
            screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);

            if(with_access)
            tz(-nut_thick-.01)
            {
                cylindera(d=nut_dia+2*mm, h=h, orient=Z, align=-Z);
            }
        }
    }
}

function nut_radius(nut) = outradius(get(NutWidthMin, nut)/2, get(NutFacets, nut));
function nut_dia(nut) = 2*nut_radius(nut);

module nut_trap_cut(nut, thread, head="socket", trap_offset=10, screw_l=10*mm, screw_offset=0*mm, cut_screw=false, cut_screw_close=false, trap_h=10, trap_axis=-Y, orient=Z, align=N)
{
    screw_min_nut_dist = 2*mm;
    assert(screw_l+screw_offset >= trap_offset, "nut_trap_cut: screw_l+screw_offset < trap_offset");
    assert(screw_offset-trap_offset <= -screw_min_nut_dist, "nut_trap_cut: screw_offset-trap_offset > -screw_min_nut_dist");

    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "nut_trap_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "nut_trap_cut: Mismatched nut and thread");*/

    threadsize = get(ThreadSize, thread_);
    head_h = get_screw_head_h(head=head, thread=thread);
    nut_h = get(NutThickness, nut) +.5*mm;

    tolerance = -.2*mm;
    nut_width_min = get(NutWidthMin, nut)+tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut)+tolerance;
    total_h = nut_h;
    size_align(size=[nut_dia, nut_dia, total_h], orient=orient, orient_ref=Z, align=align)
    {
        if(cut_screw)
        {
            if(cut_screw_close)
            {
                tz(-nut_h/2)
                tz(-screw_min_nut_dist)
                tz(trap_offset)
                screw_cut($show_vit=false, nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1.1, h=screw_l, orient=Z, align=Z);
            }
            else
            {
                tz(-nut_h/2)
                tz(screw_offset)
                screw_cut($show_vit=false, nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1.1, h=screw_l, orient=Z, align=Z);
            }
        }
        else
        {
            tz(nut_h/2+screw_offset)
            screw_thread_cut($show_vit=false, thread=thread_, tolerance=1.1, h=screw_l, orient=Z, align=Z);
        }

        tz(trap_offset)
        hull()
        {
            orient(axis=Z, axis_ref=orient)
            translate(-.15*mm*trap_axis)
            stack(dist=trap_h, axis=trap_axis)
            {
                orient(axis=orient, axis_ref=Z)
                {
                    rotate([0,0,30])
                    cylindera($fn=nut_facets, d=nut_dia, h=nut_h, align=N);
                }

                orient(axis=orient, axis_ref=Z)
                cubea(size=[nut_width_min,nut_width_min,nut_h], align=N);
            }
        }

        if($show_vit)
        {
            if(cut_screw)
            {
                tz(-nut_h/2)
                tz(screw_offset)
                %screw(nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1, h=screw_l, orient=Z, align=Z);
            }

            tz(trap_offset)
            rotate([0,0,30])
            %screw_nut(nut=nut, thread=thread, tolerance=1, orient=Z, align=N);
        }

    }
}

if(false)
{
    all_axes()
    translate($axis*5*mm)
    color($color)
    screw(nut=NutHexM3, h=10*mm, orient=-$axis, align=$axis);
}

// all nuts
if(false)
{
    for(nuti=[0:1:len(AllNut)-1])
    {
        nut = AllNut[nuti];
        v_threadsize = v_get(AllNut,ThreadSize);
        v_nutwidthmin = v_get(AllNut,NutWidthMin);
        dist = v_cumsum(v_nutwidthmin, 0, nuti)[nuti]*1.2;
        translate(X*dist)
        rotate([0,0,30])
        screw(nut=nut, h=get(NutWidthMax, nut)*5, head_embed=false, orient=-Z, align=Z);
    }
}

// From ParkinBots threading library
// https://www.thingiverse.com/thing:1659079
/*module Threading(D = 0, pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=40)*/
/*{*/
    /*R = D==0?d/2+2*pitch/PI:D/2; */
    /*translate([0,0,-pitch])*/
    /*difference()*/
    /*{*/
        /*translate([0,0,pitch])*/
        /*cylinder (r=R, h =pitch*(windings-helices));*/
        /*threading(pitch, d, windings, helices, angle, steps, true); */
    /*}*/
/*}*/

// From ParkinBots threading library
// https://www.thingiverse.com/thing:1659079
module threading(pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=40, full = false)
{
    // tricky: glue two 180° pieces together to get a proper manifold
    r = d/2;
    Steps = steps/2;
    Pitch = pitch*helices;
    if(full)
    {
        cylinder(r = r-.5-pitch/PI, h=pitch*(windings+helices), $fn=steps);
    }

    sweep(gen_dat(), planar_caps=true);   // half screw
    rz(180)
    tz(Pitch/2)
    sweep(gen_dat(), planar_caps=true);   // half screw

    function gen_dat() =
        let(ang = 180, bar = R_(180, -90, 0, Ty_(-r+.5, vec3D(pitch/PI*Rack(windings, angle)))))
        [for (i=[0:Steps]) Tz_(i/2/Steps*Pitch, -Rz_(i*ang/Steps, bar))];

    function Rack(w, angle) =
        concat([[0, 2]],
            [for (i=[0:w-1], j=[0:3])
            let(t = [ [0, 1], [2*tan(angle/2), -1], [PI/2, -1], [2*tan(angle/2)+PI/2, 1]])
            [t[j][0]+i*PI, t[j][1]]], [[w*PI, 1], [w*PI, 2]
            ]);
}

if(false)
{
    $fn=32;
    all_heads=
    [
        "socket",
        "button",
        "set"
    ];
    for(i=[0:len(all_heads)-1])
    {
        head=all_heads[i];
        tx(i*8*mm)
        screw(nut=NutHexM3, head=head, h=25, orient=-Z);
    }

}

if(false)
{
    $fn=128;
    nut1 = NutHexM3;
    stack(axis=X, dist = 20)
    {
        screw(nut=NutHexM8, head="socket", h=25, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, with_head=1, with_nut=0, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, with_head=0, with_nut=0, orient=-Z);
        screw(nut=NutHexM8, head="set", h=25, with_nut=0, orient=-Z);

        threading(pitch = 1.25, d=8, windings=10, full=true);
    }
}

if(false)
{
    box_w = 150*mm;
    box_d = 10*mm;
    box_h = 10*mm;
    o = 10*mm;

    $show_vit = true;
    /*$fs= 0.2;*/
    /*$fa = 4;*/

    nut1 = NutHexM3;
    nut2 = NutHexM5;
    nut3 = NutKnurlM3_3_42;
    nut4 = NutKnurlM3_5_42;

    /*all_axes()*/
    /*for($axis=[X])*/
    /*color($color)*/
    /*translate($axis*15)*/
    /*echo($axis)*/

    /*orient(axis=$axis, axis_ref=-X)*/
    for(y=[-1,1])
    translate(-y*Y*20*mm)
    difference()
    {
        cubea([box_w,box_d,box_h], align=X+Z);
        test_cuts(orient=y*Z);
        /*%test_cuts(orient=y, axis_ref=-Z);*/
    }

}

if(false)
{
    nut1 = NutHexM5;
    stack(dist=10,axis=X)
    {
        $show_vit=true;
        $preview_mode=true;
        #screw(nut=nut1, h=10, orient=-Z, align=-Z);
        #screw_cut(nut=nut1, h=10, orient=-Z, align=-Z);
        #screw_cut(nut=nut1, h=10, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=10, cut_screw=false, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=10, cut_screw=true, orient=-Z, align=-Z);
        #screw(nut=nut1, h=20, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=20, cut_screw=true, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=20, trap_offset=20, cut_screw=true, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=25, screw_offset=-5, trap_offset=20, cut_screw=true, orient=-Z, align=-Z);
        #nut_trap_cut(nut=nut1, screw_l=20, cut_screw=true, cut_screw_close=true, orient=-Z, align=-Z);
    }
}

module test_cuts(orient)
{
    union()
    {
        /*offset = 0;*/
        offset = -5;
        /*translate([0,offset,0])*/
        /*translate([5,0,-5])*/
        translate([5,0,0])
        stack(dist=10,axis=X)
        {
            screw_cut(nut=nut1, head="button", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, head="socket", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(thread=ThreadM5, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=orient, align=Z);

            screw_cut(nut=nut1, h=box_h, nut_offset=0*mm, head_embed=true, orient=orient, align=Z);
            screw_cut(nut=nut2, h=box_h, nut_offset=3*mm, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut4, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,-5*mm])
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=-Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=Z, orient=orient, align=N);
            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=-Z, orient=orient, align=N);
        }
    }
}