'fleshing out' arm

'fleshing out' arm

use <StepMotor_28BYJ-48.scad>;
use <Hub_28BYJ-48.scad>;
include <Bearing_6807-2RS.scad>;

// global rendering parameters
$fn = 24;

// library examples
// translate([100, 100, 10]) { StepMotor28BYJ(); }
// stepperHub(10, 7, 6, 3.1, 1.5, 2.75);
//translate([0, 0, 8]) bearing6807_2RS();
// bearingInnerStep(bearing6807_2RS_d - iFitAdjust, 2, 2);
//shoulderBase(2, 55, 9.1, 3.1);

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

// shoulder base parameters
shoulderBaseHeight = 3;
shoulderBaseDiameter = 55;
shaftBossDiameter = 10;
mountScrewDiameter = 3.1;
bearingStep = 2; // size in mm of bearing interface step
// joint parameters
hubHeight = 10;
hubRadius = 7;
shaftHeight = 6;
shaftRadius = 3.1;
setScrewRadius = 1.5;
setScrewHeight = 2.75;
spokeWidth = 3;
spokes = 6;
screwTabs = 4;
screwTabHeight = 4;
armLength = 100;
// misc
baseDeckExtension = 50;
boundingBox = 8;
boundingBoxHalf = boundingBox / 2;
// 8 is distance from shaft center to screw center on x axis
// 35 is screw center to screw center on y axis
shaftCenterToMountHoldCenterXAxis = 8;
mountHoleCenterToMountHoleCenter = 35;
leadScrewDiameter = 8;
// forearm
forearmLength = 75;

/* Pieces in this model
** Shoulder:
*** Base plate
*** Top plate
*** Lower arm
*** Upper arm
** Forearm:
*** ?
** Hand:
*** ?
*/

// shoulder base
color([.7, .7, 1])
    shoulderBase(shoulderBaseHeight, shoulderBaseDiameter, shaftBossDiameter, mountScrewDiameter);
// shoulder lower stepper
rotate([0, 180, 0])
    translate([-8, 0, 10])
        StepMotor28BYJ();
// lower shoulder bearing
translate([0, 0, shoulderBaseHeight + bearingStep])
    %bearing6807_2RS();
// shoulder top
color([.6, .6, .9])
    translate([0, 0, 2 * shoulderBaseHeight + (4 * bearing6807_2RS_B) + (8 * bearingStep)])
    rotate([180, 0, 0])
        shoulderBase(shoulderBaseHeight, shoulderBaseDiameter, shaftBossDiameter, mountScrewDiameter);
// upper shoulder bearing
translate([0, 0, shoulderBaseHeight + (3 * bearing6807_2RS_B) + (7 * bearingStep)])
    %bearing6807_2RS();
// shoulder upper stepper
rotate([0, 0, 180])
    translate([-8, 0, 10 + 2 * shoulderBaseHeight + (4 * bearing6807_2RS_B) + (8 * bearingStep)])
        StepMotor28BYJ();
// lower arm including the shoulder - arm joint
//render()
color([1, .7, .7])
    translate([0, 0, shoulderBaseHeight + bearing6807_2RS_B])
        armLower(bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight, armLength);
// upper arm
//render()
color([.9, .7, .7])
    translate([0, 0, shoulderBaseHeight + ( 2*bearing6807_2RS_B) + (12 * bearingStep) - (.5 * bearingStep)])
        rotate([180, 0, 0])
            armLower(bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight, armLength);
// elbow joint pieces
// lower bearing
translate([-armLength, 0, shoulderBaseHeight + bearing6807_2RS_B + bearingStep])
    %bearing6807_2RS();
// arm stepper
rotate([0, 180, 0])
    translate([armLength - 8, 0, bearingStep * 2])
        StepMotor28BYJ();
// forearm pieces
// lower
color([.1, .7, .1])
translate([-armLength, 0, shoulderBaseHeight + (bearing6807_2RS_B * 2) + (4 *bearingStep)])
//translate([-armLength, 0, shoulderBaseHeight + (bearing6807_2RS_B * 2) + (14 *bearingStep)])
    forearmLower(bearing6807_2RS_d - iFitAdjust, bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight + (bearing6807_2RS_B / 2) + (bearingStep/2), hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, forearmLength, boundingBox);
// upper
color([.2, .8, .2])
translate([-armLength, 0, shoulderBaseHeight + (bearing6807_2RS_B * 2) + (4 *bearingStep)])
//translate([-armLength, 0, shoulderBaseHeight + (bearing6807_2RS_B * 2) + (14 *bearingStep)])
    rotate([180, 0, 0])
    forearmLower(bearing6807_2RS_d - iFitAdjust, bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight + (bearing6807_2RS_B / 2) + (bearingStep/2), hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, forearmLength, boundingBox);
// forearm upper bearing
translate([-armLength, 0, shoulderBaseHeight + (2 * bearing6807_2RS_B) + (7 * bearingStep)])
    %bearing6807_2RS();
/*
deprecated
render()
    translate([-armLength, 0, shoulderBaseHeight + bearing6807_2RS_B + (2* bearingStep)])
        armJointSpacer(bearing6807_2RS_d - iFitAdjust, bearing6807_2RS_D + iFitAdjust, bearingStep, shaftBossDiameter, mountScrewDiameter);
*/

//bearingInnerStep(bearing6807_2RS_d - iFitAdjust, 2, 2);
module bearingInnerStep(bearingID, stepHeight, stepWidth) {
    difference() {
        union() {
            // this fits inside of the bearing
            cylinder(h = stepHeight * 2, d = bearingID);
            // this rests on the bearing inner lip
            cylinder(h = stepHeight, d = bearingID + stepWidth);
        }
        // remove center
        cylinder(h = stepHeight * 2, d = bearingID - (stepWidth * 2));
    }
}

//translate([0, 0, 12]) bearingOuterStep(bearing6807_2RS_D + iFitAdjust, 2, 2);
module bearingOuterStep(bearingOD, stepHeight, stepWidth) {
    //render(convexivity = 3)
    difference() {
        cylinder(h = stepHeight * 2, d = bearingOD + stepWidth);
        cylinder(h = stepHeight * 2, d = bearingOD - stepWidth);
        cylinder(h = stepHeight, d = bearingOD);
    }
}

// armJointSpacer(bearing6807_2RS_D - iFitAdjust, bearing6807_2RS_D + iFitAdjust, bearingStep, shaftBossDiameter, mountScrewDiameter);
module armJointSpacer(bearingID, bearingOD, bearingStep, shaftBossDiameter, mountScrewDiameter) {
    union() {
        difference() {
            cylinder(h = bearingStep * 2, d = bearingOD + bearingStep);
            cylinder(h = bearingStep * 2, d = bearingID);
        }
        translate([0, 0, bearingStep * 2])
                bearingInnerStep(bearingID, bearingStep, bearingStep);
        // screw holes for joining to arm
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * bearingStep) - screwTabHeight])
                    difference() {
                        union () {
                            cylinder(h = screwTabHeight, r = setScrewRadius * 2);
                            translate([-2 * setScrewRadius, - 2 * setScrewRadius, 0])
                                cube([setScrewRadius * 2, 4 * setScrewRadius, screwTabHeight], center = false);
                        }
                        cylinder(h = screwTabHeight, r = setScrewRadius);
                    }
    }
}

//shoulderBase(2, 55, 9.1, 3.1);
// WARNING: has some hard-coded non-parametric values in here!
module shoulderBase(shoulderBaseHeight, shoulderBaseDiameter, shaftBossDiameter, mountScrewDiameter) {
    mountHoleDepth = shoulderBaseHeight;

    //render(convexivity = 3)
    difference() {
        union () {
            cylinder(h = shoulderBaseHeight, d = shoulderBaseDiameter);
            translate([0, 0, shoulderBaseHeight])
                bearingInnerStep(bearing6807_2RS_d - iFitAdjust, bearingStep, bearingStep);
            // 40 is the length of deck extension from the baseplate in x
            translate([0, -shoulderBaseDiameter / 2, 0])
                cube([baseDeckExtension, shoulderBaseDiameter, shoulderBaseHeight], center = false);
        }
        // motor shaft hole
        cylinder(h = shoulderBaseHeight + (bearingStep * 2), d = shaftBossDiameter);
        // mounting holes

        translate([shaftCenterToMountHoldCenterXAxis, mountHoleCenterToMountHoleCenter/2, 0])
            cylinder(h = mountHoleDepth, d = mountScrewDiameter);
        translate([shaftCenterToMountHoldCenterXAxis, -mountHoleCenterToMountHoleCenter/2, 0])
            cylinder(h = mountHoleDepth, d = mountScrewDiameter);
        // lead screw hole
        translate([baseDeckExtension - (leadScrewDiameter * 1.5), 0, 0])
            cylinder(h = shoulderBaseHeight, d = leadScrewDiameter + (leadScrewDiameter / 2));
    }
    // NOTE: need to add LM8UU mounts
}

module armLower(bearingOD, stepHeight, stepWidth, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight, armLength) {
    armWidth = bearingOD / 2;
    //render(convexivity = 3)
    difference() {
    union() {
        armInnerJoint(bearingOD, bearingStep, bearingStep, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight);
        translate([-armLength, 0, 0])
            armOuterJointBase(bearingOD, bearingStep, setScrewRadius, screwTabs, screwTabHeight);
        difference() {
            translate([-armLength, -armWidth / 2, 0])
                cube([armLength, armWidth, bearingStep * 2], center=false);
            cylinder(h = bearingStep * 2, d = bearingOD);
            translate([-armLength, 0, 0])
                cylinder(h = bearingStep * 2, d = bearingOD);
            translate([-armLength + (bearingOD / 2) + (boundingBox / 2), -(armWidth / 2) + boundingBoxHalf, 0])
                cube([armLength - bearingOD - (boundingBox + boundingBoxHalf), armWidth - boundingBox, bearingStep * 2], center = false);
        }
        difference() {
            intersection() {

                translate([-armLength + (bearingOD / 2) + (boundingBox / 2), -(armWidth / 2) + boundingBoxHalf, 0])
                    cube([armLength - bearingOD - boundingBox, armWidth - boundingBox, bearingStep * 2], center = false);
                for (i = [ bearingOD / 2 - boundingBoxHalf: armWidth : armLength]) {
                    translate([ -i, 0, bearingStep])
                        rotate([0, 0, 45])
                            cube([spokeWidth, armWidth + boundingBoxHalf, bearingStep * 2], center = true);
                    translate([ -i, 0, bearingStep])
                        rotate([0, 0, -45])
                            cube([spokeWidth, armWidth + boundingBoxHalf, bearingStep * 2], center = true);
                }
            }

        }
   }
           // screw holes for joining to upper
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * stepHeight) - screwTabHeight])
                       cylinder(h = screwTabHeight, r = setScrewRadius);
   }
}

module armOuterJointBase(bearingOD, bearingStep, setScrewRadius, screwTabs, screwTabHeight) {
    mountHoleDepth = bearingStep * 2;

    union() {
        difference() {
            union() {
                translate([0, 0, -(bearingStep * 2)])
                    cylinder(h = bearingStep * 2, d = bearingOD + bearingStep);
                translate([0, 0, bearingStep * 2])
                    rotate([0, 180, 0])
                        bearingOuterStep(bearingOD, bearingStep, bearingStep);
            }
            // motor shaft hole
            translate([0, 0, -(bearingStep * 2)])
                cylinder(h = shoulderBaseHeight + (bearingStep * 2), d = shaftBossDiameter);
            // mounting holes
            translate([0, 0, -(bearingStep * 2)]) {
                translate([shaftCenterToMountHoldCenterXAxis, mountHoleCenterToMountHoleCenter/2, 0])
                cylinder(h = mountHoleDepth, d = mountScrewDiameter);
                translate([shaftCenterToMountHoldCenterXAxis, -mountHoleCenterToMountHoleCenter/2, 0])
                cylinder(h = mountHoleDepth, d = mountScrewDiameter);
            }
        }

    }
}

//armInnerJoint(bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight);
module armInnerJoint(bearingOD, stepHeight, stepWidth, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight) {
    //translate([0, 0, hubHeight - (2 * stepHeight)])
    difference() {
        union() {
         difference() {
            cylinder(h = bearingStep * 2, d = bearingOD + (setScrewRadius * 6));
            cylinder(h = bearingStep * 2, d = bearingOD);
//            translate([0, -bearingOD, 0])
//                cube([bearingOD, bearingOD * 2, bearingStep * 2]);
        }
        bearingOuterStep(bearingOD, stepHeight, stepWidth);
        // hub
        translate([0, 0, -(hubHeight - (2 * stepHeight))])
            stepperHub(hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight);
        // spokes
        radial_array(vec=[0, 0, 1], n = spokes)
                translate([hubRadius - (stepWidth / 2), -(spokeWidth / 2), stepHeight])
                    cube([(bearingOD / 2) - hubRadius, spokeWidth, stepHeight], center = false);
        // screw tabs for joining to upper
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * stepHeight) - screwTabHeight])

                        union () {
                            cylinder(h = screwTabHeight, r = setScrewRadius * 2);
                            translate([-2 * setScrewRadius, - 2 * setScrewRadius, 0])
                                cube([setScrewRadius * 2, 4 * setScrewRadius, screwTabHeight], center = false);
                        }

                    }
        // screw holes for joining to upper
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * stepHeight) - screwTabHeight])
                       cylinder(h = screwTabHeight, r = setScrewRadius);
    }
}

module forearmLower(bearingID, bearingOD, stepHeight, stepWidth, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, forearmLength, boundingBox) {
    armWidth = bearingOD / 2;

    rotate([180, 0, 0]) union() {
        boundingBoxHalf = boundingBox / 2;
        difference() {
            cylinder(h = bearingStep * 2, d = bearingOD + bearingStep);
            cylinder(h = bearingStep * 2, d = bearingID - (2 * bearingStep));
        }
        translate([0, 0, bearingStep * 2])
            bearingInnerStep(bearingID, bearingStep, bearingStep);
        // screw holes for joining to arm
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * bearingStep) - screwTabHeight])
                    difference() {
                        union () {
                            cylinder(h = screwTabHeight, r = setScrewRadius * 2);
                            translate([-2 * setScrewRadius, - 2 * setScrewRadius, 0])
                                cube([setScrewRadius * 2, 4 * setScrewRadius, screwTabHeight], center = false);
                        }
                        cylinder(h = screwTabHeight, r = setScrewRadius);
                    }
        // hub
        translate([0, 0, hubHeight]) rotate([180, 0, 0])
            stepperHub(hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight);
        // spokes
        radial_array(vec=[0, 0, 1], n = spokes)
                translate([hubRadius - (stepWidth / 2), -(spokeWidth / 2), 0])
                    cube([(bearingOD / 2) - hubRadius, spokeWidth, 2 *stepHeight], center = false);
        // forearm extension
             difference() {
            translate([-forearmLength, -armWidth / 2, 0])
                cube([forearmLength, armWidth, bearingStep * 2], center=false);
            cylinder(h = bearingStep * 2, d = bearingOD);
            translate([-forearmLength, 0, 0])
                cylinder(h = bearingStep * 2, d = bearingOD / 2);
                 // 4 on next line comes from dividing the bearingOD on the above line
            translate([-forearmLength + (bearingOD / 4) + (boundingBox / 2), -(armWidth / 2) + boundingBoxHalf, 0])
                cube([forearmLength - (bearingOD/2) - (boundingBox + boundingBoxHalf), armWidth - boundingBox, bearingStep * 2], center = false);
        }

            intersection() {

                translate([-forearmLength + boundingBox * 2, -(armWidth / 2) + boundingBoxHalf, 0])
                    cube([forearmLength - (bearingOD / 2) - boundingBox, armWidth - boundingBox, bearingStep * 2], center = false);
                for (i = [ bearingOD / 2 - boundingBoxHalf: armWidth : forearmLength]) {
                    translate([ -i, 0, bearingStep])
                        rotate([0, 0, 45])
                            cube([spokeWidth, armWidth + boundingBoxHalf, bearingStep * 2], center = true);
                    translate([ -i, 0, bearingStep])
                        rotate([0, 0, -45])
                            cube([spokeWidth, armWidth + boundingBoxHalf, bearingStep * 2], center = true);
                }
            }

    }
}

module copy_mirror(vec=[0,1,0]) {
    children();
    mirror(vec)
        children();
}

module radial_array(vec = [0,0,1], n = 4) {
    for ( i = [0 : n - 1] )
    {
        rotate( i * 360 / n, vec)
            children();
    }
}

use <StepMotor_28BYJ-48.scad>;
use <Hub_28BYJ-48.scad>;
include <Bearing_6807-2RS.scad>;

// global rendering parameters
$fn = 24;

// library examples
// translate([100, 100, 10]) { StepMotor28BYJ(); }
// stepperHub(10, 7, 6, 3.1, 1.5, 2.75);
//translate([0, 0, 8]) bearing6807_2RS();
// bearingInnerStep(bearing6807_2RS_d - iFitAdjust, 2, 2);
//shoulderBase(2, 55, 9.1, 3.1);

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

// shoulder base parameters
shoulderBaseHeight = 3;
shoulderBaseDiameter = 55;
shaftBossDiameter = 10;
mountScrewDiameter = 3.1;
bearingStep = 2; // size in mm of bearing interface step
// joint parameters
hubHeight = 10;
hubRadius = 7;
shaftHeight = 6;
shaftRadius = 3.1;
setScrewRadius = 1.5;
setScrewHeight = 2.75;
spokeWidth = 3;
spokes = 6;
screwTabs = 4;
screwTabHeight = 4;
armLength = 100;
// misc
baseDeckExtension = 50;
boundingBox = 8;
boundingBoxHalf = boundingBox / 2;
// 8 is distance from shaft center to screw center on x axis
// 35 is screw center to screw center on y axis
shaftCenterToMountHoldCenterXAxis = 8;
mountHoleCenterToMountHoleCenter = 35;
leadScrewDiameter = 8;
// forearm
forearmLength = 75;

/* Pieces in this model
** Shoulder:
*** Base plate
*** Top plate
*** Lower arm
*** Upper arm
** Forearm:
*** ?
** Hand:
*** ?
*/

// shoulder base
color([.7, .7, 1])
    shoulderBase(shoulderBaseHeight, shoulderBaseDiameter, shaftBossDiameter, mountScrewDiameter);
// shoulder stepper
rotate([0, 180, 0])
    translate([-8, 0, 10])
        StepMotor28BYJ();
// lower shoulder bearing
translate([0, 0, shoulderBaseHeight + bearingStep])
    %bearing6807_2RS();
// shoulder top
color([.6, .6, .9])
    translate([0, 0, 2 * shoulderBaseHeight + (4 * bearing6807_2RS_B) + (8 * bearingStep)])
    rotate([180, 0, 0])
        shoulderBase(shoulderBaseHeight, shoulderBaseDiameter, shaftBossDiameter, mountScrewDiameter);
// upper shoulder bearing
translate([0, 0, shoulderBaseHeight + (3 * bearing6807_2RS_B) + (7 * bearingStep)])
    %bearing6807_2RS();
// lower arm including the shoulder - arm joint
//render()
color([1, .7, .7])
    translate([0, 0, shoulderBaseHeight + bearing6807_2RS_B])
        armLower(bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight, armLength);
// upper arm
//render()
color([.9, .7, .7])
    translate([0, 0, shoulderBaseHeight + ( 2*bearing6807_2RS_B) + (12 * bearingStep) - (.5 * bearingStep)])
        rotate([180, 0, 0])
            armLower(bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight, armLength);
// elbow joint pieces
// lower bearing
translate([-armLength, 0, shoulderBaseHeight + bearing6807_2RS_B + bearingStep])
    %bearing6807_2RS();
// arm stepper
rotate([0, 180, 0])
    translate([armLength - 8, 0, bearingStep * 2])
        StepMotor28BYJ();
// forearm pieces
// lower
color([.1, .7, .1])
translate([-armLength, 0, shoulderBaseHeight + (bearing6807_2RS_B * 2) + (4 *bearingStep)])
//translate([-armLength, 0, shoulderBaseHeight + (bearing6807_2RS_B * 2) + (14 *bearingStep)])
    forearmLower(bearing6807_2RS_d - iFitAdjust, bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight + (bearing6807_2RS_B / 2) + (bearingStep/2), hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, forearmLength, boundingBox);
// upper
color([.2, .8, .2])
translate([-armLength, 0, shoulderBaseHeight + (bearing6807_2RS_B * 2) + (4 *bearingStep)])
//translate([-armLength, 0, shoulderBaseHeight + (bearing6807_2RS_B * 2) + (14 *bearingStep)])
    rotate([180, 0, 0])
    forearmLower(bearing6807_2RS_d - iFitAdjust, bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight + (bearing6807_2RS_B / 2) + (bearingStep/2), hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, forearmLength, boundingBox);
// forearm upper bearing
translate([-armLength, 0, shoulderBaseHeight + (2 * bearing6807_2RS_B) + (7 * bearingStep)])
    %bearing6807_2RS();
/*
deprecated
render()
    translate([-armLength, 0, shoulderBaseHeight + bearing6807_2RS_B + (2* bearingStep)])
        armJointSpacer(bearing6807_2RS_d - iFitAdjust, bearing6807_2RS_D + iFitAdjust, bearingStep, shaftBossDiameter, mountScrewDiameter);
*/

//bearingInnerStep(bearing6807_2RS_d - iFitAdjust, 2, 2);
module bearingInnerStep(bearingID, stepHeight, stepWidth) {
    difference() {
        union() {
            // this fits inside of the bearing
            cylinder(h = stepHeight * 2, d = bearingID);
            // this rests on the bearing inner lip
            cylinder(h = stepHeight, d = bearingID + stepWidth);
        }
        // remove center
        cylinder(h = stepHeight * 2, d = bearingID - (stepWidth * 2));
    }
}

//translate([0, 0, 12]) bearingOuterStep(bearing6807_2RS_D + iFitAdjust, 2, 2);
module bearingOuterStep(bearingOD, stepHeight, stepWidth) {
    //render(convexivity = 3)
    difference() {
        cylinder(h = stepHeight * 2, d = bearingOD + stepWidth);
        cylinder(h = stepHeight * 2, d = bearingOD - stepWidth);
        cylinder(h = stepHeight, d = bearingOD);
    }
}

// armJointSpacer(bearing6807_2RS_D - iFitAdjust, bearing6807_2RS_D + iFitAdjust, bearingStep, shaftBossDiameter, mountScrewDiameter);
module armJointSpacer(bearingID, bearingOD, bearingStep, shaftBossDiameter, mountScrewDiameter) {
    union() {
        difference() {
            cylinder(h = bearingStep * 2, d = bearingOD + bearingStep);
            cylinder(h = bearingStep * 2, d = bearingID);
        }
        translate([0, 0, bearingStep * 2])
                bearingInnerStep(bearingID, bearingStep, bearingStep);
        // screw holes for joining to arm
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * bearingStep) - screwTabHeight])
                    difference() {
                        union () {
                            cylinder(h = screwTabHeight, r = setScrewRadius * 2);
                            translate([-2 * setScrewRadius, - 2 * setScrewRadius, 0])
                                cube([setScrewRadius * 2, 4 * setScrewRadius, screwTabHeight], center = false);
                        }
                        cylinder(h = screwTabHeight, r = setScrewRadius);
                    }
    }
}

//shoulderBase(2, 55, 9.1, 3.1);
// WARNING: has some hard-coded non-parametric values in here!
module shoulderBase(shoulderBaseHeight, shoulderBaseDiameter, shaftBossDiameter, mountScrewDiameter) {
    mountHoleDepth = shoulderBaseHeight;

    //render(convexivity = 3)
    difference() {
        union () {
            cylinder(h = shoulderBaseHeight, d = shoulderBaseDiameter);
            translate([0, 0, shoulderBaseHeight])
                bearingInnerStep(bearing6807_2RS_d - iFitAdjust, bearingStep, bearingStep);
            // 40 is the length of deck extension from the baseplate in x
            translate([0, -shoulderBaseDiameter / 2, 0])
                cube([baseDeckExtension, shoulderBaseDiameter, shoulderBaseHeight], center = false);
        }
        // motor shaft hole
        cylinder(h = shoulderBaseHeight + (bearingStep * 2), d = shaftBossDiameter);
        // mounting holes

        translate([shaftCenterToMountHoldCenterXAxis, mountHoleCenterToMountHoleCenter/2, 0])
            cylinder(h = mountHoleDepth, d = mountScrewDiameter);
        translate([shaftCenterToMountHoldCenterXAxis, -mountHoleCenterToMountHoleCenter/2, 0])
            cylinder(h = mountHoleDepth, d = mountScrewDiameter);
        // lead screw hole
        translate([baseDeckExtension - (leadScrewDiameter * 1.5), 0, 0])
            cylinder(h = shoulderBaseHeight, d = leadScrewDiameter + (leadScrewDiameter / 2));
    }
    // NOTE: need to add LM8UU mounts
}

module armLower(bearingOD, stepHeight, stepWidth, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight, armLength) {
    armWidth = bearingOD / 2;
    //render(convexivity = 3)
    difference() {
    union() {
        armInnerJoint(bearingOD, bearingStep, bearingStep, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight);
        translate([-armLength, 0, 0])
            armOuterJointBase(bearingOD, bearingStep, setScrewRadius, screwTabs, screwTabHeight);
        difference() {
            translate([-armLength, -armWidth / 2, 0])
                cube([armLength, armWidth, bearingStep * 2], center=false);
            cylinder(h = bearingStep * 2, d = bearingOD);
            translate([-armLength, 0, 0])
                cylinder(h = bearingStep * 2, d = bearingOD);
            translate([-armLength + (bearingOD / 2) + (boundingBox / 2), -(armWidth / 2) + boundingBoxHalf, 0])
                cube([armLength - bearingOD - (boundingBox + boundingBoxHalf), armWidth - boundingBox, bearingStep * 2], center = false);
        }
        difference() {
            intersection() {

                translate([-armLength + (bearingOD / 2) + (boundingBox / 2), -(armWidth / 2) + boundingBoxHalf, 0])
                    cube([armLength - bearingOD - boundingBox, armWidth - boundingBox, bearingStep * 2], center = false);
                for (i = [ bearingOD / 2 - boundingBoxHalf: armWidth : armLength]) {
                    translate([ -i, 0, bearingStep])
                        rotate([0, 0, 45])
                            cube([spokeWidth, armWidth + boundingBoxHalf, bearingStep * 2], center = true);
                    translate([ -i, 0, bearingStep])
                        rotate([0, 0, -45])
                            cube([spokeWidth, armWidth + boundingBoxHalf, bearingStep * 2], center = true);
                }
            }

        }
   }
           // screw holes for joining to upper
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * stepHeight) - screwTabHeight])
                       cylinder(h = screwTabHeight, r = setScrewRadius);
   }
}

module armOuterJointBase(bearingOD, bearingStep, setScrewRadius, screwTabs, screwTabHeight) {
    mountHoleDepth = bearingStep * 2;

    union() {
        difference() {
            union() {
                translate([0, 0, -(bearingStep * 2)])
                    cylinder(h = bearingStep * 2, d = bearingOD + bearingStep);
                translate([0, 0, bearingStep * 2])
                    rotate([0, 180, 0])
                        bearingOuterStep(bearingOD, bearingStep, bearingStep);
            }
            // motor shaft hole
            translate([0, 0, -(bearingStep * 2)])
                cylinder(h = shoulderBaseHeight + (bearingStep * 2), d = shaftBossDiameter);
            // mounting holes
            translate([0, 0, -(bearingStep * 2)]) {
                translate([shaftCenterToMountHoldCenterXAxis, mountHoleCenterToMountHoleCenter/2, 0])
                cylinder(h = mountHoleDepth, d = mountScrewDiameter);
                translate([shaftCenterToMountHoldCenterXAxis, -mountHoleCenterToMountHoleCenter/2, 0])
                cylinder(h = mountHoleDepth, d = mountScrewDiameter);
            }
        }

    }
}

//armInnerJoint(bearing6807_2RS_D + iFitAdjust, bearingStep, bearingStep, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight);
module armInnerJoint(bearingOD, stepHeight, stepWidth, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, screwTabs, screwTabHeight) {
    //translate([0, 0, hubHeight - (2 * stepHeight)])
    difference() {
        union() {
         difference() {
            cylinder(h = bearingStep * 2, d = bearingOD + (setScrewRadius * 6));
            cylinder(h = bearingStep * 2, d = bearingOD);
//            translate([0, -bearingOD, 0])
//                cube([bearingOD, bearingOD * 2, bearingStep * 2]);
        }
        bearingOuterStep(bearingOD, stepHeight, stepWidth);
        // hub
        translate([0, 0, -(hubHeight - (2 * stepHeight))])
            stepperHub(hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight);
        // spokes
        radial_array(vec=[0, 0, 1], n = spokes)
                translate([hubRadius - (stepWidth / 2), -(spokeWidth / 2), stepHeight])
                    cube([(bearingOD / 2) - hubRadius, spokeWidth, stepHeight], center = false);
        // screw tabs for joining to upper
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * stepHeight) - screwTabHeight])

                        union () {
                            cylinder(h = screwTabHeight, r = setScrewRadius * 2);
                            translate([-2 * setScrewRadius, - 2 * setScrewRadius, 0])
                                cube([setScrewRadius * 2, 4 * setScrewRadius, screwTabHeight], center = false);
                        }

                    }
        // screw holes for joining to upper
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * stepHeight) - screwTabHeight])
                       cylinder(h = screwTabHeight, r = setScrewRadius);
    }
}

module forearmLower(bearingID, bearingOD, stepHeight, stepWidth, hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight, spokeWidth, spokes, forearmLength, boundingBox) {
    armWidth = bearingOD / 2;

    rotate([180, 0, 0]) union() {
        boundingBoxHalf = boundingBox / 2;
        difference() {
            cylinder(h = bearingStep * 2, d = bearingOD + bearingStep);
            cylinder(h = bearingStep * 2, d = bearingID - (2 * bearingStep));
        }
        translate([0, 0, bearingStep * 2])
            bearingInnerStep(bearingID, bearingStep, bearingStep);
        // screw holes for joining to arm
        radial_array(vec = [0, 0, 1], n = screwTabs)
                translate([bearingOD / 2 + (setScrewRadius * 2), 0, (2 * bearingStep) - screwTabHeight])
                    difference() {
                        union () {
                            cylinder(h = screwTabHeight, r = setScrewRadius * 2);
                            translate([-2 * setScrewRadius, - 2 * setScrewRadius, 0])
                                cube([setScrewRadius * 2, 4 * setScrewRadius, screwTabHeight], center = false);
                        }
                        cylinder(h = screwTabHeight, r = setScrewRadius);
                    }
        // hub
        translate([0, 0, hubHeight]) rotate([180, 0, 0])
            stepperHub(hubHeight, hubRadius, shaftHeight, shaftRadius, setScrewRadius, setScrewHeight);
        // spokes
        radial_array(vec=[0, 0, 1], n = spokes)
                translate([hubRadius - (stepWidth / 2), -(spokeWidth / 2), 0])
                    cube([(bearingOD / 2) - hubRadius, spokeWidth, 2 *stepHeight], center = false);
        // forearm extension
             difference() {
            translate([-forearmLength, -armWidth / 2, 0])
                cube([forearmLength, armWidth, bearingStep * 2], center=false);
            cylinder(h = bearingStep * 2, d = bearingOD);
            translate([-forearmLength, 0, 0])
                cylinder(h = bearingStep * 2, d = bearingOD / 2);
                 // 4 on next line comes from dividing the bearingOD on the above line
            translate([-forearmLength + (bearingOD / 4) + (boundingBox / 2), -(armWidth / 2) + boundingBoxHalf, 0])
                cube([forearmLength - (bearingOD/2) - (boundingBox + boundingBoxHalf), armWidth - boundingBox, bearingStep * 2], center = false);
        }

            intersection() {

                translate([-forearmLength + boundingBox * 2, -(armWidth / 2) + boundingBoxHalf, 0])
                    cube([forearmLength - (bearingOD / 2) - boundingBox, armWidth - boundingBox, bearingStep * 2], center = false);
                for (i = [ bearingOD / 2 - boundingBoxHalf: armWidth : forearmLength]) {
                    translate([ -i, 0, bearingStep])
                        rotate([0, 0, 45])
                            cube([spokeWidth, armWidth + boundingBoxHalf, bearingStep * 2], center = true);
                    translate([ -i, 0, bearingStep])
                        rotate([0, 0, -45])
                            cube([spokeWidth, armWidth + boundingBoxHalf, bearingStep * 2], center = true);
                }
            }

    }
}

module copy_mirror(vec=[0,1,0]) {
    children();
    mirror(vec)
        children();
}

module radial_array(vec = [0,0,1], n = 4) {
    for ( i = [0 : n - 1] )
    {
        rotate( i * 360 / n, vec)
            children();
    }
}


screws: add support for setscrew head

screws: add support for setscrew head

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) = get(ThreadSize, thread);
function get_screw_head_d(head, thread) = 2 * get(ThreadSize, thread);

module screw(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;
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        tz(head_embed?-head_h:0)
        {
            difference()
            {
                union()
                {
                    if(with_head)
                    {
                        tz(h/2+.01)
                        screw_head(part="pos", head=head, thread=thread_, orient=Z, align=Z);
                    }

                    tz(-h/2+.01)
                    screw_thread(thread=thread_, head=head, h=h+.1, tolerance=tolerance, orient=Z, align=N);
                }
                if(with_head)
                tz(h/2+.01)
                screw_head(part="neg", head=head, thread=thread_, orient=Z, align=Z);
            }

            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);
            }
        }

        if($show_vit)
        {
            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_access=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=Z, align=N)
{
    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)
            {
                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 = 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 = fallback(override_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 = fallback(override_h,head_h)/2, align=Z);
            }
        }
    }
    else if(part=="neg")
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket")
        {
            if(drive == "hex")
            {
                cylindera(d = fn_radius(threadsize, 6), h = 1000, $fn = 6, align=Z);
            }
        }
        else if(head=="button")
        {
            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")
        {
            if(drive == "hex")
            {
                // set head uses -1*mm smaller hex
                tz(-head_h)
                cylindera(d = fn_radius(threadsize-1, 6), h = 1000, $fn = 6, align=Z);
            }

        }
    }
}

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_);
            screw_thread(thread=nut_thread, h=h_+.2);
        }
    }
}

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, trap_offset=10, screw_l=10*mm, screw_offset=0*mm, cut_screw=false, trap_h=10, trap_axis=-Y, orient=Z, align=N)
{
    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)
        {
            tz(-nut_h/2)
            tz(screw_offset)
            screw_cut($show_vit=false, nut=nut, thread=thread_, 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);
        }

        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_, with_nut=false, tolerance=1, h=screw_l, orient=-Z, align=Z);
            }

            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=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);*/
    }

}

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) = get(ThreadSize, thread);
function get_screw_head_d(head, thread) = 2 * get(ThreadSize, thread);

module screw(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;
    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(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);
            }
        }

        if($show_vit)
        {
            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_access=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=Z, align=N)
{
    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)
            {
                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(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 = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread);
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        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);
                }
            }

        }
    }
}

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")
        {
        }
        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_);
            screw_thread(thread=nut_thread, h=h_+.2);
        }
    }
}

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, trap_offset=10, screw_l=10*mm, screw_offset=0*mm, cut_screw=false, trap_h=10, trap_axis=-Y, orient=Z, align=N)
{
    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)
        {
            tz(-nut_h/2)
            tz(screw_offset)
            screw_cut($show_vit=false, nut=nut, thread=thread_, 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);
        }

        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_, with_nut=false, tolerance=1, h=screw_l, orient=-Z, align=Z);
            }

            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=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);

        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);*/
    }

}

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);
        }
    }
}


Base: remove rogue translate so that wheels are in the centre or the wheel slots again

/*
	STL: LogoBotBase_STL

	The printable base plate for the robot.

	The _STL suffix denotes this is a printable part, and is used by the bulk STL generating program

	Local Frame:
		Base lies on XY plane, centered on origin, front of robot is towards y+

	Parameters:
		None

	Returns:
		Base plate, rendered and colored
*/

module LogoBotBase_STL() {

	// turn off explosions inside STL!!!
	$Explode = false;

	printedPart("printedparts/LogoBotBase.scad", "Base", "LogoBotBase_STL()") {

		view(t=[0,0,0], r=[58,0,225], d=681);

		// Color it as a printed plastic part
		color(PlasticColor)
			if (UseSTL) {
				import(str(STLPath, "Base.stl"));
			} else {
				union()
				{
					// Start with an extruded base plate, with all the relevant holes punched in it
					linear_extrude(BaseThickness)
						difference() {
							// Union together all the bits to make the plate
							union() {
								// Base plate
								circle(r=BaseDiameter/2);
							}

/*
							// hole for breadboard
							attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170))
								difference() {
									translate([2,2,0])
										square([Breadboard_Width(Breadboard_170)-4, Breadboard_Depth(Breadboard_170)-4]);

									// attach mounting points, has the effect of not differencing them
									attach(Breadboard_Con_BottomLeft(Breadboard_170),DefCon) {
										circle(r=4/2 + dw);
										translate([0, -2 - dw, 0])
											square([10, 4 + 2*dw]);
									}

									attach(Breadboard_Con_BottomRight(Breadboard_170),DefCon) {
										circle(r=4/2 + dw);
										rotate([0,0,180])
											translate([0, -2 - dw, 0])
											square([10, 4 + 2*dw]);
									}
								}

							// mounting holes for breadboard
							attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170)) {
								attach(Breadboard_Con_BottomLeft(Breadboard_170),DefCon)
									circle(r=4.3/2);

								attach(Breadboard_Con_BottomRight(Breadboard_170),DefCon)
									circle(r=4.3/2);
							}

							// weight loss holes under the motor drivers
							attach(LogoBot_Con_LeftMotorDriver, (ULN2003DriverBoard_Con_UpperLeft))
								roundedSquare([ULN2003Driver_BoardWidth - 2*ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2*ULN2003Driver_HoleInset], tw);

							// Right Motor Driver
							attach(LogoBot_Con_RightMotorDriver, (ULN2003DriverBoard_Con_UpperRight))
								roundedSquare([ULN2003Driver_BoardWidth - 2*ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2*ULN2003Driver_HoleInset], tw);
*/
							// slots for wheels
							for (i=[0:1])
								mirror([i,0,0])
								translate([BaseDiameter/2 + MotorOffsetX, 0, MotorOffsetZ])
								roundedSquare([WheelThickness + tw, WheelDiameter + tw], (WheelThickness + tw)/2, center=true);

							// Centre hole for pen
							circle(r=PenHoleDiameter/2);

							// Chevron at the front so we're clear where the front is!
							*translate([0, BaseBigHolesRadius - 3, 0])
								chevron(width=10, height=6, thickness=3);

							// Caster
							// TODO: Replace with sep "skid" part and pin connector
							//attach(LogoBot_Con_Caster, MarbleCaster_Con_Default)
							//    circle(r = connector_bore(MarbleCaster_Con_Default) / 2);

							if (false) {
								LogoBotBase_RadialHoles();
							} else {
								LogoBotBase_GridHoles();
							}

							// Remove shell fittings
							Shell_TwistLockCutouts();
						}

					// Now union on any other bits (i.e. sticky-up bits!)

					// clips for the motors
					// TODO: Move these into separate printed part
					//LogoBotBase_MotorClips();

					// Not required in new design, they will be integrated with the motor clips
					//LogoBotBase_MotorDriverPosts();

				}
			}
	}
}

module LogoBotBase_GridHoles() {

	pitch = 10;
	xn = round((BaseDiameter / pitch)/2) + 1;
	yn = xn;

	for (x=[-xn:xn], y=[-yn:yn]) {
		if (
			circleInCircle(BaseDiameter/2 - dw, x*pitch, y*pitch, PinDiameter/2) &&
			circleOutsideCircle(PenHoleDiameter/2 + dw, x*pitch, y*pitch, PinDiameter/2) &&
			abs(x*pitch) <= BaseDiameter/2 + MotorOffsetX - 1 - dw - PinDiameter/2
		) {
			translate([x*pitch, y*pitch,0])
				circle(r=PinDiameter/2, $fn=20);
		}

	}

}

function circleInCircle(r1,x,y,r2) = sqrt(sqr(x)+sqr(y))+r2 <= r1;

function circleOutsideCircle(r1,x,y,r2) = !circleInCircle(r1 + 2*r2,x,y,r2);

function circleIntersectSquare(x1,y1,x2,y2,x,y,r) = pointInSquare(x1,y1,x2,y2,x,y) ||
													circleIntersectLine(x1,y1,x2,y1,x,y,r) ||
													circleIntersectLine(x1,y2,x2,y2,x,y,r) ||
													circleIntersectLine(x1,y1,x1,y2,x,y,r) ||
													circleIntersectLine(x2,y1,x2,y2,x,y,r);

function pointInSquare(x1,y1,x2,y2,x,y) = x>=x1 && x<=x2 && y>=y1 && y<=y2;

function circleIntersectLine(x1,y1,x2,y2,x,y,r) = circleInCircle(r,x-x1,y-y1,0) ||
												circleInCircle(r,x-x2,y-y2,0) ||
												false;
												// TODO: fix this!!
												//((abs((x2-x1)*(y-y1) - (x-x1)*(y2-y1)) / sqrt(sqr(x2-x1) + sqr(y2-y1))) <= r);

module LogoBotBase_RadialHoles() {

	// Outer fixing holes - for bumpers, sensors, etc
	for (ang=[-3:3], frontBack=[0,1])
		mirror([0,frontBack,0])
		rotate([0, 0, ang * 15 + 90])
		translate([BaseOuterFixingsRadius, 0, 0])
		circle(r=PinDiameter/2, $fn=20);

	// Mid fixing holes - motor drivers, battery pack
	for (i=[0:360/15])
		rotate([0, 0, i*15])
		translate([BaseMiddleFixingsRadius, 0, 0])
		circle(r=PinDiameter/2, $fn=20);

	// Inner fixing holes (around pen hole) - for pen lift
	for (i=[0:360/30])
		rotate([0, 0, i*30])
		translate([BaseInnerFixingsRadius, 0, 0])
		circle(r=PinDiameter/2, $fn=20);

	// Ring of big holes

	for (i=[0:360/60])
		rotate([0, 0, i*60])
		translate([BaseBigHolesRadius, 0, 0])
		circle(r=BaseBigHoleRadius, $fn=20);

}

module LogoBotBase_MotorClips() {
	// TODO: feed these local variables from the motor vitamin global vars
	mbr = 28/2;  // motor body radius
	mao = 8;     // motor axle offset from centre of motor body
	mth = 7;     // motor tab height

	// clips
	for (i=[0:1], j=[0,1])
		mirror([i,0,0])
		translate([BaseDiameter/2 + MotorOffsetX - 6 - j*12, 0, MotorOffsetZ + mao])
		rotate([-90, 0, 90])
		rotate([0,0, 0])
		linear_extrude(5) {
			difference() {
				union() {

					difference() {
						hull() {
							// main circle
							circle(r=mbr + dw);

							// extension to correctly union to the base plate
							translate([-(mbr * 1.5)/2,  MotorOffsetZ + mao -1,0])
								square([mbr * 1.5, 1]);
						}

						// trim the top off
						rotate([0,0,180 + 30])
							sector(r=mbr+30, a=120);
					}

					// welcoming hands
					for (k=[0,1])
						mirror([k,0,0])
						rotate([0,0,180+30])
						translate([mbr, -dw, 0])
						polygon([[1.5,0],[4,5],[3,5],[0,dw]],4);
				}

				// hollow for the motor
				circle(r=mbr);

			}
		}
}

module LogoBotBase_MotorDriverPosts() {
	// mounting posts for motor drivers
	// using a mirror because the layout is symmetrical

	for (i=[0,1])
		mirror([i,0,0])
			for (j=[0:3])
				attach(offsetConnector(LogoBot_Con_RightMotorDriver, [0,0,-LogoBot_Con_RightMotorDriver[0][2]]),
						ULN2003DriverBoard_Cons[j],
						$Explode=false)
				LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_RightMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);
}

module LogoBotBase_MountingPost(r1=5/2, h1=3, r2=3/2, h2=3) {
	cylinder(r=r1, h=h1);
	translate([0,0,h1-eta])
		cylinder(r1=r2, r2=r2 * 0.7, h=h2 +eta);
}

/*
	STL: LogoBotBase_STL

	The printable base plate for the robot.

	The _STL suffix denotes this is a printable part, and is used by the bulk STL generating program

	Local Frame:
		Base lies on XY plane, centered on origin, front of robot is towards y+

	Parameters:
		None

	Returns:
		Base plate, rendered and colored
*/

module LogoBotBase_STL() {

	// turn off explosions inside STL!!!
	$Explode = false;

	printedPart("printedparts/LogoBotBase.scad", "Base", "LogoBotBase_STL()") {

		view(t=[0,0,0], r=[58,0,225], d=681);

		// Color it as a printed plastic part
		color(PlasticColor)
			if (UseSTL) {
				import(str(STLPath, "Base.stl"));
			} else {
				union()
				{
					// Start with an extruded base plate, with all the relevant holes punched in it
					linear_extrude(BaseThickness)
						difference() {
							// Union together all the bits to make the plate
							union() {
								// Base plate
								circle(r=BaseDiameter/2);
							}

/*
							// hole for breadboard
							attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170))
								difference() {
									translate([2,2,0])
										square([Breadboard_Width(Breadboard_170)-4, Breadboard_Depth(Breadboard_170)-4]);

									// attach mounting points, has the effect of not differencing them
									attach(Breadboard_Con_BottomLeft(Breadboard_170),DefCon) {
										circle(r=4/2 + dw);
										translate([0, -2 - dw, 0])
											square([10, 4 + 2*dw]);
									}

									attach(Breadboard_Con_BottomRight(Breadboard_170),DefCon) {
										circle(r=4/2 + dw);
										rotate([0,0,180])
											translate([0, -2 - dw, 0])
											square([10, 4 + 2*dw]);
									}
								}

							// mounting holes for breadboard
							attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170)) {
								attach(Breadboard_Con_BottomLeft(Breadboard_170),DefCon)
									circle(r=4.3/2);

								attach(Breadboard_Con_BottomRight(Breadboard_170),DefCon)
									circle(r=4.3/2);
							}

							// weight loss holes under the motor drivers
							attach(LogoBot_Con_LeftMotorDriver, (ULN2003DriverBoard_Con_UpperLeft))
								roundedSquare([ULN2003Driver_BoardWidth - 2*ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2*ULN2003Driver_HoleInset], tw);

							// Right Motor Driver
							attach(LogoBot_Con_RightMotorDriver, (ULN2003DriverBoard_Con_UpperRight))
								roundedSquare([ULN2003Driver_BoardWidth - 2*ULN2003Driver_HoleInset, ULN2003Driver_BoardHeight - 2*ULN2003Driver_HoleInset], tw);
*/
							// slots for wheels
							for (i=[0:1])
								mirror([i,0,0])
								translate([BaseDiameter/2 + MotorOffsetX, 0, MotorOffsetZ])
								translate([-1,0,0])
								roundedSquare([WheelThickness + tw, WheelDiameter + tw], (WheelThickness + tw)/2, center=true);

							// Centre hole for pen
							circle(r=PenHoleDiameter/2);

							// Chevron at the front so we're clear where the front is!
							*translate([0, BaseBigHolesRadius - 3, 0])
								chevron(width=10, height=6, thickness=3);

							// Caster
							// TODO: Replace with sep "skid" part and pin connector
							//attach(LogoBot_Con_Caster, MarbleCaster_Con_Default)
							//    circle(r = connector_bore(MarbleCaster_Con_Default) / 2);

							if (false) {
								LogoBotBase_RadialHoles();
							} else {
								LogoBotBase_GridHoles();
							}

							// Remove shell fittings
							Shell_TwistLockCutouts();
						}

					// Now union on any other bits (i.e. sticky-up bits!)

					// clips for the motors
					// TODO: Move these into separate printed part
					//LogoBotBase_MotorClips();

					// Not required in new design, they will be integrated with the motor clips
					//LogoBotBase_MotorDriverPosts();

				}
			}
	}
}

module LogoBotBase_GridHoles() {

	pitch = 10;
	xn = round((BaseDiameter / pitch)/2) + 1;
	yn = xn;

	for (x=[-xn:xn], y=[-yn:yn]) {
		if (
			circleInCircle(BaseDiameter/2 - dw, x*pitch, y*pitch, PinDiameter/2) &&
			circleOutsideCircle(PenHoleDiameter/2 + dw, x*pitch, y*pitch, PinDiameter/2) &&
			abs(x*pitch) <= BaseDiameter/2 + MotorOffsetX - 1 - dw - PinDiameter/2
		) {
			translate([x*pitch, y*pitch,0])
				circle(r=PinDiameter/2, $fn=20);
		}

	}

}

function circleInCircle(r1,x,y,r2) = sqrt(sqr(x)+sqr(y))+r2 <= r1;

function circleOutsideCircle(r1,x,y,r2) = !circleInCircle(r1 + 2*r2,x,y,r2);

function circleIntersectSquare(x1,y1,x2,y2,x,y,r) = pointInSquare(x1,y1,x2,y2,x,y) ||
													circleIntersectLine(x1,y1,x2,y1,x,y,r) ||
													circleIntersectLine(x1,y2,x2,y2,x,y,r) ||
													circleIntersectLine(x1,y1,x1,y2,x,y,r) ||
													circleIntersectLine(x2,y1,x2,y2,x,y,r);

function pointInSquare(x1,y1,x2,y2,x,y) = x>=x1 && x<=x2 && y>=y1 && y<=y2;

function circleIntersectLine(x1,y1,x2,y2,x,y,r) = circleInCircle(r,x-x1,y-y1,0) ||
												circleInCircle(r,x-x2,y-y2,0) ||
												false;
												// TODO: fix this!!
												//((abs((x2-x1)*(y-y1) - (x-x1)*(y2-y1)) / sqrt(sqr(x2-x1) + sqr(y2-y1))) <= r);

module LogoBotBase_RadialHoles() {

	// Outer fixing holes - for bumpers, sensors, etc
	for (ang=[-3:3], frontBack=[0,1])
		mirror([0,frontBack,0])
		rotate([0, 0, ang * 15 + 90])
		translate([BaseOuterFixingsRadius, 0, 0])
		circle(r=PinDiameter/2, $fn=20);

	// Mid fixing holes - motor drivers, battery pack
	for (i=[0:360/15])
		rotate([0, 0, i*15])
		translate([BaseMiddleFixingsRadius, 0, 0])
		circle(r=PinDiameter/2, $fn=20);

	// Inner fixing holes (around pen hole) - for pen lift
	for (i=[0:360/30])
		rotate([0, 0, i*30])
		translate([BaseInnerFixingsRadius, 0, 0])
		circle(r=PinDiameter/2, $fn=20);

	// Ring of big holes

	for (i=[0:360/60])
		rotate([0, 0, i*60])
		translate([BaseBigHolesRadius, 0, 0])
		circle(r=BaseBigHoleRadius, $fn=20);

}

module LogoBotBase_MotorClips() {
	// TODO: feed these local variables from the motor vitamin global vars
	mbr = 28/2;  // motor body radius
	mao = 8;     // motor axle offset from centre of motor body
	mth = 7;     // motor tab height

	// clips
	for (i=[0:1], j=[0,1])
		mirror([i,0,0])
		translate([BaseDiameter/2 + MotorOffsetX - 6 - j*12, 0, MotorOffsetZ + mao])
		rotate([-90, 0, 90])
		rotate([0,0, 0])
		linear_extrude(5) {
			difference() {
				union() {

					difference() {
						hull() {
							// main circle
							circle(r=mbr + dw);

							// extension to correctly union to the base plate
							translate([-(mbr * 1.5)/2,  MotorOffsetZ + mao -1,0])
								square([mbr * 1.5, 1]);
						}

						// trim the top off
						rotate([0,0,180 + 30])
							sector(r=mbr+30, a=120);
					}

					// welcoming hands
					for (k=[0,1])
						mirror([k,0,0])
						rotate([0,0,180+30])
						translate([mbr, -dw, 0])
						polygon([[1.5,0],[4,5],[3,5],[0,dw]],4);
				}

				// hollow for the motor
				circle(r=mbr);

			}
		}
}

module LogoBotBase_MotorDriverPosts() {
	// mounting posts for motor drivers
	// using a mirror because the layout is symmetrical

	for (i=[0,1])
		mirror([i,0,0])
			for (j=[0:3])
				attach(offsetConnector(LogoBot_Con_RightMotorDriver, [0,0,-LogoBot_Con_RightMotorDriver[0][2]]),
						ULN2003DriverBoard_Cons[j],
						$Explode=false)
				LogoBotBase_MountingPost(r1=(ULN2003Driver_HoleDia+2)/2, h1 = LogoBot_Con_RightMotorDriver[0][2], r2=ULN2003Driver_HoleDia/2, h2=3);
}

module LogoBotBase_MountingPost(r1=5/2, h1=3, r2=3/2, h2=3) {
	cylinder(r=r1, h=h1);
	translate([0,0,h1-eta])
		cylinder(r1=r2, r2=r2 * 0.7, h=h2 +eta);
}


Update cushiongrab.scad

Update cushiongrab.scad

// Cushion Grabber - see README.md for details.
// Author: Joseph M. Joy

function range(stop, start=0,  step=1) = [start: step: stop-1];

// Coords of an isoceles right triangle
function ir_tri(base) = [[0,0], [0, base], [base, 0]];

tri_height = 5;
base_thickness = 1;

// Extruded version
module rt_prism(base, height)  {
    linear_extrude(height) polygon(ir_tri(base));
}

// A row of extruded triangles
module prism_rows(base, height, n) {
    for (i = range(n)) {
         translate([3*base*i, 0, 0]) rt_prism(base, height);
    }
}

// pan - upside down pan used for differencing
module pan(width) {
    r = 7;
    ceiling_height = tri_height + base_thickness; // inner height;
    outer_height = r; // height of top of dome from above
    scale([1, ceiling_height/r, 1])
        difference() {
            translate([-r, -r, -r]) cube([width+2*r, 3*outer_height, width+2*r]);
            translate([r, -r, r]) minkowski() {
                cube([width-2*r, r, width-2*r]);
                sphere(r);
            }
        }
}

// Final grabber structure
module cushion_grabber(width) {
    n = width / (3*tri_height); // each row is 3*tri_height, including space
    translate([0, width, 0]) rotate([90, 0, 0])
    difference() {
        union() {
            cube([width, base_thickness, width]);
            translate([tri_height, base_thickness, 0]) prism_rows(tri_height, width, n);
        }
        pan(width);
    }
}

cushion_grabber(125);
//pan(100);

// Cushion Grabber - see README.md for details.
// Author: Joseph M. Joy

function range(stop, start=0,  step=1) = [start: step: stop-1];

// Coords of an isoceles right triangle
function ir_tri(base) = [[0,0], [0, base], [base, 0]];

tri_height = 5;
base_thickness = 1;

// Extruded version
module rt_prism(base, height)  {
    linear_extrude(height) polygon(ir_tri(base));
}

// A row of extruded triangles
module prism_rows(base, height, n) {
    for (i = range(n)) {
         translate([3*base*i, 0, 0]) rt_prism(base, height);
    }
}

// Dome - used for differencing
module dome(width) {
    r = 7;
    ceiling_height = tri_height + base_thickness; // inner height;
    outer_height = r; // height of top of dome from above
    scale([1, ceiling_height/r, 1])
        difference() {
            translate([-r, -r, -r]) cube([width+2*r, 3*outer_height, width+2*r]);
            translate([r, -r, r]) minkowski() {
                cube([width-2*r, r, width-2*r]);
                sphere(r);
            }
        }
}

// Final grabber structure
module cushion_grabber(width) {
    n = width / (3*tri_height); // each row is 3*tri_height, including space
    translate([0, width, 0]) rotate([90, 0, 0])
    difference() {
        union() {
            cube([width, base_thickness, width]);
            translate([tri_height, base_thickness, 0]) prism_rows(tri_height, width, n);
        }
        dome(width);
    }
}

cushion_grabber(125);
//dome(100);


Up issued the version number.

Up issued the version number.

// MicroSwitch v1.1 by Gyrobot
// http://www.gyrobot.co.uk
//
// Default values based upon :
// Farnell P/N : 2352329
// Omron Electronic Components P/N : SS-01GLT
//
// Datum X is centre of mounting hole with X pointing away from mounting slot.
// Datum Y is pointing away from terminals towards the lever.
// Datum Z is in on base of model.
//
// Parameters :
//
// ms_length           = Length of body in X.
// ms_width            = Width of body in Y.
// ms_height           = Height (thickness) of body in Z.
// ms_datxoffset       = X Position of lower left corner of body from Datum hole.
// ms_datyoffset       = Y Position of lower left corner of body from Datum hole.
// ms_orientation      = Defines whether the switch is mounted left or right (0:1).
// ms_hole_diam        = Size of Datum mounting hole and slot width of second hole.
// ms_xholepitch       = Mounting hole X pitch.
// ms_yholepitch       = Mounting hole Y pitch.
// ms_hole_slot_length = Slot length of second mounting hole.
// ms_lever_length     = Length of lever.
// ms_lever_width      = Width of lever.
// ms_lever_thickness  = Thickness of lever.
// ms_lever_height     = Height of lever (OP) from Datum hole.
// ms_tab_length       = Length of terminal tab from Datum hole.
// ms_tab_width        = Width of terminal tab.
// ms_tab_thickness    = Thickness of terminal tab.
// ms_tab_hole_diam    = Diameter of terminal tab hole.
//

module microswitch(ms_length = 19.8, ms_width = 10.2, ms_height = 6.4, ms_datxoffset = -14.6, ms_datyoffset = -2.5, ms_orientation = 0, ms_hole_diam = 2.35, ms_xholepitch = 9.5, ms_yholepitch = 0, ms_hole_slot_length = 0.15, ms_lever_length = 14.5, ms_lever_width = 3.6, ms_lever_thickness = 0.3, ms_lever_height = 8.8, ms_tab_length = 6.4, ms_tab_width = 3.2, ms_tab_thickness = 0.3, ms_tab_hole_diam = 1.6){

	cutout_offset = 0.7;
	foot_offset = ms_datyoffset - 0.4;

	mirror ([ms_orientation, 0, 0]){
		// Mirror to provide a left or right mounted switch.
		//
		translate ([ms_datxoffset, ms_datyoffset, ms_height / 2]){
			//Move datum position.
			//
			color("DarkSlateGray")
			linear_extrude(ms_height, center=true){
				//
				// Body
				//
				difference(){
					union(){
						//Add all Additive profiles together, place any extras here.
						//
						square ([ms_length, ms_width]); //Main body cuboid.
						translate ([-ms_datxoffset, -ms_datyoffset])
							square ([ms_hole_diam, foot_offset * -2], center=true);
						translate ([-ms_datxoffset - ms_xholepitch - ms_hole_slot_length / 2, -ms_datyoffset + ms_yholepitch])
							square ([ms_hole_diam, foot_offset * -2], center=true);

					}
					union(){
						//Add all Subtractive profiles together, place any extras here.
						//
						translate ([ms_length / 2, ms_width - cutout_offset]) // Move corner cutout.
							square ([ms_length, ms_width]); // Corner cutout.
						translate ([-ms_datxoffset, -ms_datyoffset]) // Move datum hole.
							circle (d = ms_hole_diam, $fn = 32); // Remove datum hole.
						translate ([-ms_datxoffset - ms_xholepitch - ms_hole_slot_length / 2, -ms_datyoffset + ms_yholepitch]){ // Move second mounting hole (slot)
							// Second mounting hole (slot)
							hull(){
								circle (d = ms_hole_diam, $fn = 32);
								translate ([ms_hole_slot_length, 0]) // Move circle to create slot.
									circle (d = ms_hole_diam, $fn = 32);
							}
						}
					}
				}
			}
			color("Silver")
			linear_extrude(ms_lever_width, center=true){
				//
				// Lever
				//
				translate([2.5 ,+ ms_lever_height - ms_datyoffset,0]) // Move lever to position
					mirror([0, 1, 0]) // Mirror lever to invert shape
						union (){
							//Add all Additive profiles together, place any extras here.
							//
							square ([ms_lever_length + 2.5 , ms_lever_thickness]);
							square ([ms_lever_thickness,ms_width / 2]);
						}
			}
			for ( x = [1.6, 10.4, 17.7]){
				color ("Silver")
				translate ([x, -ms_tab_length - ms_datyoffset, 0])
					terminal(ms_tab_length, ms_tab_width, ms_tab_thickness, ms_tab_hole_diam);
			}
		}
	}
}

//
// Terminal module for solder pad. Other terminal types are available.
//

module terminal (ms_tab_length, ms_tab_width, ms_tab_thickness, ms_tab_hole_diam){
	rotate ([0,90,0]){
		translate ([ms_tab_width / -2,0,0]) {
			linear_extrude(ms_tab_thickness, center=true){
				difference (){
					square ([ms_tab_width , ms_tab_length]);
					translate ([ms_tab_width / 2, ms_tab_width / 2, 0])
						circle (d = ms_tab_hole_diam, $fn = 32);
				}
			}
		}
	}
}

// MicroSwitch v1.0 by Gyrobot
// http://www.gyrobot.co.uk
//
// Default values based upon :
// Farnell P/N : 2352329
// Omron Electronic Components P/N : SS-01GLT
//
// Datum X is centre of mounting hole with X pointing away from mounting slot.
// Datum Y is pointing away from terminals towards the lever.
// Datum Z is in on base of model.
//
// Parameters :
//
// ms_length           = Length of body in X.
// ms_width            = Width of body in Y.
// ms_height           = Height (thickness) of body in Z.
// ms_datxoffset       = X Position of lower left corner of body from Datum hole.
// ms_datyoffset       = Y Position of lower left corner of body from Datum hole.
// ms_orientation      = Defines whether the switch is mounted left or right (0:1).
// ms_hole_diam        = Size of Datum mounting hole and slot width of second hole.
// ms_xholepitch       = Mounting hole X pitch.
// ms_yholepitch       = Mounting hole Y pitch.
// ms_hole_slot_length = Slot length of second mounting hole.
// ms_lever_length     = Length of lever.
// ms_lever_width      = Width of lever.
// ms_lever_thickness  = Thickness of lever.
// ms_lever_height     = Height of lever (OP) from Datum hole.
// ms_tab_length       = Length of terminal tab from Datum hole.
// ms_tab_width        = Width of terminal tab.
// ms_tab_thickness    = Thickness of terminal tab.
// ms_tab_hole_diam    = Diameter of terminal tab hole.
//

module microswitch(ms_length = 19.8, ms_width = 10.2, ms_height = 6.4, ms_datxoffset = -14.6, ms_datyoffset = -2.5, ms_orientation = 0, ms_hole_diam = 2.35, ms_xholepitch = 9.5, ms_yholepitch = 0, ms_hole_slot_length = 0.15, ms_lever_length = 14.5, ms_lever_width = 3.6, ms_lever_thickness = 0.3, ms_lever_height = 8.8, ms_tab_length = 6.4, ms_tab_width = 3.2, ms_tab_thickness = 0.3, ms_tab_hole_diam = 1.6){

	cutout_offset = 0.7;
	foot_offset = ms_datyoffset - 0.4;

	mirror ([ms_orientation, 0, 0]){
		// Mirror to provide a left or right mounted switch.
		//
		translate ([ms_datxoffset, ms_datyoffset, ms_height / 2]){
			//Move datum position.
			//
			color("DarkSlateGray")
			linear_extrude(ms_height, center=true){
				//
				// Body
				//
				difference(){
					union(){
						//Add all Additive profiles together, place any extras here.
						//
						square ([ms_length, ms_width]); //Main body cuboid.
						translate ([-ms_datxoffset, -ms_datyoffset])
							square ([ms_hole_diam, foot_offset * -2], center=true);
						translate ([-ms_datxoffset - ms_xholepitch - ms_hole_slot_length / 2, -ms_datyoffset + ms_yholepitch])
							square ([ms_hole_diam, foot_offset * -2], center=true);

					}
					union(){
						//Add all Subtractive profiles together, place any extras here.
						//
						translate ([ms_length / 2, ms_width - cutout_offset]) // Move corner cutout.
							square ([ms_length, ms_width]); // Corner cutout.
						translate ([-ms_datxoffset, -ms_datyoffset]) // Move datum hole.
							circle (d = ms_hole_diam, $fn = 32); // Remove datum hole.
						translate ([-ms_datxoffset - ms_xholepitch - ms_hole_slot_length / 2, -ms_datyoffset + ms_yholepitch]){ // Move second mounting hole (slot)
							// Second mounting hole (slot)
							hull(){
								circle (d = ms_hole_diam, $fn = 32);
								translate ([ms_hole_slot_length, 0]) // Move circle to create slot.
									circle (d = ms_hole_diam, $fn = 32);
							}
						}
					}
				}
			}
			color("Silver")
			linear_extrude(ms_lever_width, center=true){
				//
				// Lever
				//
				translate([2.5 ,+ ms_lever_height - ms_datyoffset,0]) // Move lever to position
					mirror([0, 1, 0]) // Mirror lever to invert shape
						union (){
							//Add all Additive profiles together, place any extras here.
							//
							square ([ms_lever_length + 2.5 , ms_lever_thickness]);
							square ([ms_lever_thickness,ms_width / 2]);
						}
			}
			for ( x = [1.6, 10.4, 17.7]){
				color ("Silver")
				translate ([x, -ms_tab_length - ms_datyoffset, 0])
					terminal(ms_tab_length, ms_tab_width, ms_tab_thickness, ms_tab_hole_diam);
			}
		}
	}
}

//
// Terminal module for solder pad. Other terminal types are available.
//

module terminal (ms_tab_length, ms_tab_width, ms_tab_thickness, ms_tab_hole_diam){
	rotate ([0,90,0]){
		translate ([ms_tab_width / -2,0,0]) {
			linear_extrude(ms_tab_thickness, center=true){
				difference (){
					square ([ms_tab_width , ms_tab_length]);
					translate ([ms_tab_width / 2, ms_tab_width / 2, 0])
						circle (d = ms_tab_hole_diam, $fn = 32);
				}
			}
		}
	}
}