improved cage - relocate ESC forward

improved cage - relocate ESC forward

//$fs = 0.1;
//$fa = 5;
$fs = 0.25;
$fa = 8;

use <hcsr04.scad>

module wheel() {
    color("black") rotate([90, 90, 0]) difference() {
        #cylinder(d=47, h=23, center=true);
        cylinder(d=30, h=26, center=true);
    }
}

module base() {

    // 4 wheels
    translate([77.5, 60, 47/2]) wheel();
    translate([-77.5, 60, 47/2]) wheel();
    translate([77.5, -60, 47/2]) wheel();
    translate([-77.5, -60, 47/2]) wheel();

    // front axe assemby - slightly longer
    translate([77.5-5, 0, 47/2]) color("gray") cube(size=[25, 103, 26], center=true);
    // rear axe assemby
    translate([-77.5, 0, 47/2]) color("gray") cube(size=[26, 97, 26], center=true);

    color("gray") translate ([0, 0, 12]) {

        // base plate
        linear_extrude(height = 2) base_plate();
        linear_extrude(height = 7) difference() {
            base_plate();
            offset(-2) base_plate();
        }

        // shaft
        translate ([0, 0, 23/2]) cube(size=[158, 12, 23], center=true);
        //shaft ribs
        translate ([14, 4, 23]) cube(size=[67, 2, 4], center=false);
        translate ([14, -6, 23]) cube(size=[67, 2, 4], center=false);
        translate ([0, 0, 23/2]) cube(size=[158, 12, 23], center=true);
        translate ([-81, 4, 23]) cube(size=[45, 2, 6], center=false);
        translate ([-81, -6, 23]) cube(size=[45, 2, 6], center=false);
        // servo
        linear_extrude(height = 15)  polygon([
            [29,0], [79,0],
            [79,-23], [70,-23],[49,-40],[29,-40]
        ]);

        // motor
        translate ([-79+30, -16, 23/2+2]) rotate([0, 90, 0]) cylinder(d=23, h=32, center=false);

        // gear
        translate ([-79+23, 0, 34/2]) rotate([0, 90, 0]) cylinder(d=34, h=6, center=false);

        // battery
        translate ([-5/2, 30/2+7, 16/2+2]) cube(size=[84, 30, 16], center=true);

        // battery holder
        translate ([-79+23, 20, 0]) cylinder(d=7, h=26, center=false);
        translate ([79-29, 20, 0]) cylinder(d=7, h=26, center=false);
        translate ([-5/2, 20, 22]) cube(size=[110, 13, 2], center=true);

        // front shocks
        translate ([162/2, 0, 0]) rotate([90, 0, 90]) linear_extrude(height = 16)
            polygon([[23,44],[18,48],[5,41],[-5,41],[-18,48],[-23,44], [-38,0],[38,0]]);

        // rear shocks
        translate ([-162/2-16, 0, 0]) rotate([90, 0, 90]) linear_extrude(height = 16)
            polygon([[25,48],[20,48],[5,38],[-5,38],[-20,48],[-25,48], [-38,0],[38,0]]);
    }
}

module base_plate() {
    polygon([
        [-79,25], [-70,25],[-54,40],[49,40],[70,23], [79,23],
        [79,-23], [70,-23],[49,-40],[-54,-40],[-70,-25], [-79,-25]
    ]);
}

// size is the XY plane size, height in Z
module hexagon(size, height) {
  boxWidth = size/1.75;
  for (r = [-60, 0, 60]) rotate([0,0,r]) cube([boxWidth, size, height], true);
}

module platform() {

    difference() {
    color("black") translate ([0, 0, 12+32+4]) difference() {
        union() {
            #linear_extrude(height = 3) intersection() {
                difference() {
                    offset(-10) offset(30) offset(-20)
                       polygon([
                         [-200,25], [-70,25],[-44,60],[34,60],[70,25], [200,25], // extra wide front
                         [200,-25], [70,-25],[34,-60],[-44,-60],[-70,-25], [-200,-25]
                       ]);

                    // central hole
                    translate ([-12.5, 0, 0])
                    offset(-5) offset(10) offset(-5) difference() {

                        polygon([
                         // left
                         [-45,-20], [-31,-37], // bevel [-45,-35],
                         [ 32,-37], [ 45,-27], // bevel [45,-35],

                         [48,-27], [48,27],

                         // right
                         [ 45, 27], [ 32, 37], // bevel [45,35],
                         [-31, 37], [-45, 20]  // bevel [-45,35]
                        ]);

                        //translate ([-45, -35, 0]) circle(r=7.5, center=true);
                        //translate ([-45,  35, 0]) circle(r=7.5, center=true);
                        //translate ([ 45, -35, 0]) circle(r=7.5, center=true);
                        //translate ([ 45,  35, 0]) circle(r=7.5, center=true);
                   }

                }
                square(size=[162,120], center=true); // cut off extra length
            }

            intersection() { translate ([0, 0, 100]) cube(size=[200,200,200], center=true); union() {
            // shock tower mount points
            // rear
            #translate ([-81, -25, 0]) {
                hull() {
                    cube(size=[4, 7, 12-3.5], center=false);
                    translate ([4/2, 3.5, 12-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
                }
                translate ([8/2, 3.5, 12-3.5-6]) rotate([90, 0, 90]) cylinder(d=6, h=8, center=true);
            }
            #translate ([-81, 25-7, 0]) {
                hull() {
                    cube(size=[4, 7, 12-3.5], center=false);
                    translate ([4/2, 7-3.5, 12-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
                }
                translate ([8/2, 7-3.5, 12-3.5-6]) rotate([90, 0, 90]) cylinder(d=6, h=8, center=true);
            }

            // front
            #translate ([81-6.5, -25, 0]) {
                cube(size=[6.5, 11, 4], center=false);
                translate ([6.5/2, 5.5, 4]) rotate([90, 0, 90]) cylinder(d=11, h=6.5, center=true);
            }
            #translate ([81-6.5, 25-11, 0]) {
                cube(size=[6.5, 11, 4], center=false);
                translate ([6.5/2, 5.5, 4]) rotate([90, 0, 90]) cylinder(d=11, h=6.5, center=true);
            }
            }}
        }

        // wire tie holes
        #translate ([-45, -45, 0]) cylinder(d=5, h=20, center=true);
        #translate ([-45,  45, 0]) cylinder(d=5, h=20, center=true);
        #translate ([-35, -45, 0]) cylinder(d=5, h=20, center=true);
        #translate ([-35,  45, 0]) cylinder(d=5, h=20, center=true);

        // esc power wires
        #hull() {
            translate ([48-12.5-5, 23, 0]) cylinder(d=6, h=20, center=true);
            translate ([54-12.5,   23, 0]) cylinder(d=6, h=20, center=true);
        }

        // esc motor wires
        #hull() {
            translate ([48-12.5-5, -23,   0]) cylinder(d=6, h=20, center=true);
            translate ([54-12.5,   -23+4, 0]) cylinder(d=6, h=20, center=true);
            translate ([54-12.5,   -23,   0]) cylinder(d=6, h=20, center=true);
            translate ([48-12.5,   -23+4, 0]) cylinder(d=6, h=20, center=true);
        }

        // space save
        #hull() {
            translate ([52, -10, 0]) cylinder(d=5, h=20, center=true);
            translate ([42, -10, 0]) cylinder(d=5, h=20, center=true);
            translate ([42,  10, 0]) cylinder(d=5, h=20, center=true);
            translate ([52,  10, 0]) cylinder(d=5, h=20, center=true);
        }

        // space save
        #hull() {
            translate ([-72, -10, 0]) cylinder(d=5, h=20, center=true);
            translate ([-67, -10, 0]) cylinder(d=5, h=20, center=true);
            translate ([-67,  10, 0]) cylinder(d=5, h=20, center=true);
            translate ([-72,  10, 0]) cylinder(d=5, h=20, center=true);
        }

        // board mount holes
        #translate ([-(45-2.5)-10,  (35-2.5), 2.5]) screw2x6bore();
        #translate ([-(45-2.5)-10, -(35-2.5), 2.5]) screw2x6bore();
        #translate ([ (45-2.5)-10,  (35-2.5), 2.5]) screw2x6bore();
        #translate ([ (45-2.5)-10, -(35-2.5), 2.5]) screw2x6bore();

        // mount holes
        // front
        // M3 hex nut in front H=2.4, Dmin = 5.4
        // left
        #translate ([81, 42.5/2-3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([81-4-5/2, 42.5/2-3/2, 8-2-3/2]) rotate([90, 90, 90]) hexagon(5.6, 6);
        // right
        #translate ([81,-42.5/2+3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([81-4-5/2,-42.5/2+3/2, 8-2-3/2]) rotate([90, 90, 90]) hexagon(5.6, 6);

        // rear
        // left
        #translate ([-81, 46.5/2-3/2, 12-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([-81,-46.5/2+3/2, 12-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
        // right
        #translate ([-81,-46.5/2+3/2, 12-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([-81, 46.5/2-3/2, 12-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
    }
    #arrangebottom(30)
    {
        hbaseholes(center=true, right=1, left=1);
        hbaseholes(center=true, right=0, left=1);
        hbaseholes(center=true, right=0, left=1);
        hbaseholes(center=true, right=1, left=0);
        hbaseholes(center=true, right=1, left=0);
    }
    // cage holes
    #cage(mode=1/*cut*/);

    //arrangebottom(45) hbaseholes();
    }
}

module board() {

    translate ([-12.5, 0, 47]) {
    // base
    color("yellow") cube(size=[90, 70, 1.2], center=true);

    // teensy
    color("green") translate ([-12, 0, 5]) cube(size=[63, 18, 10], center=true);

    // MPU-9250
    color("red") translate ([-24, 21, 5]) cube(size=[18.5, 19, 10], center=true);

    // servo conn
    color("black") translate ([27, -21, 1.5]) cube(size=[10, 7.5, 3], center=true);
    }
}

module esc() {
    color("green") cube(size=[36, 26, 2], center=true);
    color("black") translate ([36/2, 0, 0]) rotate([0, 90, 0]) cylinder(d=8, h=16, center=false);

    // esc support box ?
    // hcolor("black") translate ([0, 0, -11]) cube(size=[30, 20, 20], center=true);
}

module vsonar() {

    // vertical sonar
    translate ([0, -10, 45]) rotate([0, 90, 0]) hcsr04();

    // mount plate
    color("gray") translate ([1.5, -10, -6]) cube(size=[3, 20, 51], center=false);
    color("gray") translate ([-8.5, -10, -6]) cube(size=[10, 20, 5], center=false);

    // sonar ray
    //color("white") translate ([20+1000/2, 0, 22.5]) rotate([0, 90, 0]) cylinder(d1=40, d2=520,h=1000, center=true);
}

module hbaseholes(center = true, left = 2, right = 2)
{
    if (center) translate ([-5,    0, -15]) cylinder(d=3.2, h=15, center=false);

    if (left >= 1) translate ([-5,  9.5, -15]) cylinder(d=3.2, h=15, center=false);
    if (left >= 2) translate ([-5,  19, -15]) cylinder(d=3.2, h=15, center=false);

    if (right >= 1) translate ([-5, -9.5, -15]) cylinder(d=3.2, h=15, center=false);
    if (right >= 2) translate ([-5, -19, -15]) cylinder(d=3.2, h=15, center=false);
}

module hmount() {
    // mount plate
    color ("gray") {

        // front
        difference() {
            translate ([1.5, -48/2, -6]) #hull() {
                cube(size=[3, 48, 25], center=false);
                translate ([0, 48-3, 25]) rotate([90, 0, 90]) cylinder(r=3, h=3, center=false);
                translate ([0, 3, 25]) rotate([90, 0, 90]) cylinder(r=3, h=3, center=false);
            }

            translate ([0, -45/2, 0]) rotate([90, 0, 90]) {

                // sonar
                translate([45/2-(42-16)/2,18/2+1,1.3]) cylinder(d=17, h=15);
                translate([45/2+(42-16)/2,18/2+1,1.3]) cylinder(d=17, h=15);

                //xtal(s)
                hull () {
                    translate([45/2-3.25,20-3.5,1.3])
                    {
                        translate([0,0,0]) cylinder(d=6, h=5);
                        translate([6.5,0,0]) cylinder(d=6, h=5);
                    }
                    translate([45/2-3.25, 4,1.3]) hull()
                    {
                        //translate([0,0,0]) cylinder(d=6, h=5);
                        //translate([6.5,0,0]) cylinder(d=6, h=5);
                        translate([0,-2,0]) cylinder(d=6, h=5);
                        translate([6.5,-2,0]) cylinder(d=6, h=5);
                    }
                }

                // screws
                translate([   1.0,    1.0, 4.5-0.5]) screw2x6bore();
                translate([45-1.0, 20-1.5, 4.5-0.5]) screw2x6bore();
                translate([45-1.0,    1.0, 4.5-0.5]) screw2x6bore();
                translate([   1.0, 20-1.5, 4.5-0.5]) screw2x6bore();
            }

            translate ([3, 0, 10])
                cube(size=[5, 20, 11], center=true);
        }

        //base
        difference() {
            #hull() {
                translate ([-3.5, -48/2, -6]) cube(size=[5, 48, 5], center=false);
                translate ([-3.5, -48/2+5, -6]) cylinder(r=5, h=5, center=false);
                translate ([-3.5, 48/2-5, -6]) cylinder(r=5, h=5, center=false);
            }
            hbaseholes();
        }
    }
}

module screw2x6bore() {
    rotate([180, 0, 0]) {
        cylinder(d=2.2, h=6);
        translate ([0, 0, -1.3])
            cylinder(d1=2*3.6, d2=0, h=2*1.3);
    }
}

module hsonar() {

    // horizontal sonar
    translate ([0, -45/2, 0]) rotate([90, 0, 90]) hcsr04();

    hmount();

    // sonar ray
    //color("white") translate ([20+1000/2, 0, 10]) rotate([0, 90, 0]) cylinder(d1=40, d2=520,h=1000, center=true);
}

module barbox(p1, p2, s=1, r=1) {

    a = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]];
    l = norm(a);
    b = [a[0]/l, a[1]/l, a[2]/l];

    l1 = l+s-2*r;

    c = cross([0,0,1], b);

    az = atan2(c[0], c[1]);
    ax = acos(b[2]);

    o = s/2-r;
    s1 = s-2*r;

    translate(p1)
        rotate([0, 0, -az])
           rotate([0, ax, 0])
               translate([0, 0, l/2]) {
                    cube(size=[s1, s, l1], center=true);
                    cube(size=[s, s1, l1], center=true);

                    for (x=[o, -o])
                        for (y=[o, -o]) {
                            translate([x,y,0]) cylinder(r=r, h=l1, center=true);
                            for (z=[l1/2, -l1/2]) {
                                translate([x,y,z]) sphere(r=r, center=true);
                            }
                        }

                    for (z=[l1/2, -l1/2]) {
                        translate([0,0,z]) cube(size=[s1, s1, 2*r], center=true);

                        translate([ 0, o, z]) rotate([0,90,0]) cylinder(r=r, h=s1, center=true);
                        translate([ 0,-o, z]) rotate([0,90,0]) cylinder(r=r, h=s1, center=true);
                        translate([ o, 0, z]) rotate([90,0,0]) cylinder(r=r, h=s1, center=true);
                        translate([-o, 0, z]) rotate([90,0,0]) cylinder(r=r, h=s1, center=true);
                    }

               }
}

module bar(p1, p2, s=1) {

    a = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]];
    l = norm(a);
    b = [a[0]/l, a[1]/l, a[2]/l];

    c = cross([0,0,1], b);

    az = atan2(c[0], c[1]);
    ax = acos(b[2]);

    translate(p1) sphere(d=s, center=true);
    translate(p2) sphere(d=s, center=true);

    translate(p1)
        rotate([0, 0, -az])
           rotate([0, ax, 0])
               cylinder(d=s, h=l, center=false);
}

module ball8(p1, s=1, r=1) {

    o = s/2-r;

    translate(p1)
        for (x=[o, -o])
            for (y=[o, -o])
                for (z=[o, -o])
                    translate([x,y,z])
                        sphere(r=r, center=true);
}

module skew(p1, p2) { // TODO - not working

    a = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]];
    b = [a[0]/a[2], a[1]/a[2]];

    matrix = [
        [ 1, 0, b[1], 0 ],
        [ 0, 1, b[2], 0 ],
        [ 0, 0, 1, 0 ],
        [ 0, 0, 0, 1 ]
    ];

    translate(p1)
        multmatrix(matrix)
                children();
}

module cage_leg(p1, p2, d, db, r, mode)
{
    if (mode == 2) {
        bar(p1, p2, d);
    }
    else {
        hull() {
            ball8(p2, d, r);
            translate([0, 0, -d/2]) translate(p1) difference() {
                sphere(d=db, center=true);
                translate([0, 0, -db/2]) cube(size=[db, db, db], center=true);
            }
        }
    }
}

module cage_pin(p1, p2, d, pd, ph, mode)
{
    extra = mode == 1 /*cut*/ ? 0.5 : 0;

    if (mode == 1)
    {
        bar(p1, p2, d+extra);
    }
    else
    difference() {
        bar(p1, p2, d+extra);

        #translate ([0,0,-ph])
            translate (p1)
                rotate([0,90,0])
                    cylinder(d=pd, h=2*d, center=true);
    }
}

module lcd_mount(p1, x1, x2)
{
    translate(p1) difference() {
        #translate([(x1-x2)/2,0,-1-0.5/2]) cube(size=[x1+x2, 6, 2.5], center=true);
        #translate([0,0,-0.5]) screw2x6bore();
    }
}

module cage(mode = 0)
{
    // mode 0 - full, mode 1 - cutout for platform, 2 - simple full

    d = 4;
    r1 = 1;
    n = 4;
    db = 6;
    hb = d;
    pd = 1;
    ph = d/2 + 3 /*platform*/+ 1/*freespace*/;

    // measurements
    top = 82;

    top_side = 30;

    top_front = 20;
    top_rear = -40;

    top_esc = top_front+26/*esc size*/;
    top_lcd = top_rear+27/*lcd size*/+d+1;

    bottom = 51/*platform top*/+d/2;

    bottom_front = 27;
    bottom_rear = -50;

    bottom_front_side = 40;
    bottom_rear_side = 40;

    pin = 8;
    pin_base = bottom - pin;

    mid = (top+bottom)/2;
    mid_side = 15;
    mid_front = top_esc+10;

    // points
    top_front_left = [top_front, top_side, top];
    top_front_right = [top_front, -top_side, top];

    top_esc_left = [top_esc, top_side, top];
    top_esc_right = [top_esc, -top_side, top];

    top_lcd_left = [top_lcd, top_side, top];
    top_lcd_right = [top_lcd, -top_side, top];

    top_rear_left = [top_rear, top_side, top];
    top_rear_right = [top_rear, -top_side, top];

    mid_front_left = [mid_front, mid_side, mid];
    mid_front_right = [mid_front_left[0], -mid_front_left[1], mid_front_left[2]];

    bottom_front_left = [bottom_front, bottom_front_side, bottom];
    bottom_front_right = [bottom_front, -bottom_front_side, bottom];

    bottom_rear_left = [bottom_rear, bottom_rear_side, bottom];
    bottom_rear_right = [bottom_rear, -bottom_rear_side, bottom];

    pin_front_left = [bottom_front, bottom_front_side, pin_base];
    pin_front_right = [bottom_front, -bottom_front_side, pin_base];

    pin_rear_left = [bottom_rear, bottom_rear_side, pin_base];
    pin_rear_right = [bottom_rear, -bottom_rear_side, pin_base];

    // bars
    if (mode != 1 /*cut*/)
    {
        barbox(top_front_left, top_front_right, d, r1);
        barbox(top_rear_left, top_rear_right, d, r1);
        barbox(top_esc_left, top_rear_left, d, r1);
        barbox(top_esc_right, top_rear_right, d, r1);

        barbox(top_esc_left, top_esc_right, d, r1);
        barbox(top_lcd_left, top_lcd_right, d, r1);

        bar(mid_front_left, top_esc_left, d);
        bar(mid_front_right, top_esc_right, d);

        lcd_mount([top_rear+d/2+2.5+23+0.5,  23.5/2, top+d/2], 4, 2);
        lcd_mount([top_rear+d/2+2.5+23+0.5, -23.5/2, top+d/2], 4, 2);
        lcd_mount([top_rear+d/2+2.5-0.5,     23.5/2, top+d/2], 2, 4);
        lcd_mount([top_rear+d/2+2.5-0.5,    -23.5/2, top+d/2], 2, 4);

        cage_leg(bottom_front_left, top_front_left, d, db, r1, mode);
        cage_leg(bottom_front_right, top_front_right, d, db, r1, mode);
        cage_leg(bottom_rear_left, top_rear_left, d, db, r1, mode);
        cage_leg(bottom_rear_right, top_rear_right, d, db, r1, mode);

        // lcd lead protector
        translate([top_lcd+1,0,top-3]) cube(size=[2, 12, 6], center=true);
        translate([top_lcd+1,0,top-5]) cube(size=[2, 16, 2], center=true);
    }

    cage_pin(bottom_front_left, pin_front_left, d, pd, ph, mode);
    cage_pin(bottom_front_right, pin_front_right, d, pd, ph, mode);
    cage_pin(bottom_rear_left, pin_rear_left, d, pd, ph, mode);
    cage_pin(bottom_rear_right, pin_rear_right, d, pd, ph, mode);
}

module car()
{
    base();
    platform();
    board();
    color("white") cage(mode=2/*simple*/);
    translate ([33, -8, 79]) rotate([0, 0, 90]) esc();
    lcd();
}

module arrangebottom(a) {
    translate ([68, 0, 57]) if ($children == 1) children(); else children(0);

    translate ([45, 42, 57]) rotate([0, 0, a]) if ($children == 1) children(); else children(1);
    translate ([-5, 45, 57]) rotate([0, 0, 2*a]) if ($children == 1) children(); else children(2);

    translate ([45, -42, 57]) rotate([0, 0, -a]) if ($children == 1) children(); else children(3);
    translate ([-5, -45, 57]) rotate([0, 0, -2*a]) if ($children == 1) children(); else children(4);
}

module lcd() {
    color("blue") translate([-40+2+27.5/2+0.5,0,81]) cube(size=[27.5, 27.5, 2], center=true);
}

module full() {
    car();
    arrangebottom(30) hsonar();

}

//car();

//cage();

//board();
//platform();

full();

//barbox([0,0,0], [0,0,50], 10, 3);

//translate ([0, 0, 4.5]) rotate([0, 90, 0]) hmount();

/* // small front
intersection() {
    platform();
    translate ([80, -15, 0]) cube(size=[40, 35, 400], center=true);
}*/

/* // full front
intersection() {
    platform();
    #translate ([100+20, 0, 0]) cube(size=[200, 200, 400], center=true);
}*/

/* // rear
intersection() {
    platform();
    translate ([-80, 0, 0]) cube(size=[20, 200, 400], center=true);
}*/

//screw2x6bore();

//rotate([0,0,180]) projection(cut=true) translate ([0, 0, -49]) platform();
//projection() translate ([0, 0, 10]) hsonar();

//translate([0,0,90]) rotate([180,0,0]) cage();

//$fs = 0.1;
//$fa = 5;
$fs = 0.25;
$fa = 8;

use <hcsr04.scad>

module wheel() {
    color("black") rotate([90, 90, 0]) difference() {
        #cylinder(d=47, h=23, center=true);
        cylinder(d=30, h=26, center=true);
    }
}

module base() {

    // 4 wheels
    translate([77.5, 60, 47/2]) wheel();
    translate([-77.5, 60, 47/2]) wheel();
    translate([77.5, -60, 47/2]) wheel();
    translate([-77.5, -60, 47/2]) wheel();

    // front axe assemby - slightly longer
    translate([77.5-5, 0, 47/2]) color("gray") cube(size=[25, 103, 26], center=true);
    // rear axe assemby
    translate([-77.5, 0, 47/2]) color("gray") cube(size=[26, 97, 26], center=true);

    color("gray") translate ([0, 0, 12]) {

        // base plate
        linear_extrude(height = 2) base_plate();
        linear_extrude(height = 7) difference() {
            base_plate();
            offset(-2) base_plate();
        }

        // shaft
        translate ([0, 0, 23/2]) cube(size=[158, 12, 23], center=true);
        //shaft ribs
        translate ([14, 4, 23]) cube(size=[67, 2, 4], center=false);
        translate ([14, -6, 23]) cube(size=[67, 2, 4], center=false);
        translate ([0, 0, 23/2]) cube(size=[158, 12, 23], center=true);
        translate ([-81, 4, 23]) cube(size=[45, 2, 6], center=false);
        translate ([-81, -6, 23]) cube(size=[45, 2, 6], center=false);
        // servo
        linear_extrude(height = 15)  polygon([
            [29,0], [79,0],
            [79,-23], [70,-23],[49,-40],[29,-40]
        ]);

        // motor
        translate ([-79+30, -16, 23/2+2]) rotate([0, 90, 0]) cylinder(d=23, h=32, center=false);

        // gear
        translate ([-79+23, 0, 34/2]) rotate([0, 90, 0]) cylinder(d=34, h=6, center=false);

        // battery
        translate ([-5/2, 30/2+7, 16/2+2]) cube(size=[84, 30, 16], center=true);

        // battery holder
        translate ([-79+23, 20, 0]) cylinder(d=7, h=26, center=false);
        translate ([79-29, 20, 0]) cylinder(d=7, h=26, center=false);
        translate ([-5/2, 20, 22]) cube(size=[110, 13, 2], center=true);

        // front shocks
        translate ([162/2, 0, 0]) rotate([90, 0, 90]) linear_extrude(height = 16)
            polygon([[23,44],[18,48],[5,41],[-5,41],[-18,48],[-23,44], [-38,0],[38,0]]);

        // rear shocks
        translate ([-162/2-16, 0, 0]) rotate([90, 0, 90]) linear_extrude(height = 16)
            polygon([[25,48],[20,48],[5,38],[-5,38],[-20,48],[-25,48], [-38,0],[38,0]]);
    }
}

module base_plate() {
    polygon([
        [-79,25], [-70,25],[-54,40],[49,40],[70,23], [79,23],
        [79,-23], [70,-23],[49,-40],[-54,-40],[-70,-25], [-79,-25]
    ]);
}

// size is the XY plane size, height in Z
module hexagon(size, height) {
  boxWidth = size/1.75;
  for (r = [-60, 0, 60]) rotate([0,0,r]) cube([boxWidth, size, height], true);
}

module platform() {

    difference() {
    color("black") translate ([0, 0, 12+32+4]) difference() {
        union() {
            #linear_extrude(height = 3) intersection() {
                difference() {
                    offset(-10) offset(30) offset(-20)
                       polygon([
                         [-200,25], [-70,25],[-44,60],[34,60],[70,25], [200,25], // extra wide front
                         [200,-25], [70,-25],[34,-60],[-44,-60],[-70,-25], [-200,-25]
                       ]);

                    // central hole
                    translate ([-12.5, 0, 0])
                    offset(-5) offset(10) offset(-5) difference() {

                        polygon([
                         // left
                         [-45,-20], [-31,-37], // bevel [-45,-35],
                         [ 32,-37], [ 45,-27], // bevel [45,-35],

                         [48,-27], [48,27],

                         // right
                         [ 45, 27], [ 32, 37], // bevel [45,35],
                         [-31, 37], [-45, 20]  // bevel [-45,35]
                        ]);

                        //translate ([-45, -35, 0]) circle(r=7.5, center=true);
                        //translate ([-45,  35, 0]) circle(r=7.5, center=true);
                        //translate ([ 45, -35, 0]) circle(r=7.5, center=true);
                        //translate ([ 45,  35, 0]) circle(r=7.5, center=true);
                   }

                }
                square(size=[162,120], center=true); // cut off extra length
            }

            intersection() { translate ([0, 0, 100]) cube(size=[200,200,200], center=true); union() {
            // shock tower mount points
            // rear
            #translate ([-81, -25, 0]) {
                hull() {
                    cube(size=[4, 7, 12-3.5], center=false);
                    translate ([4/2, 3.5, 12-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
                }
                translate ([8/2, 3.5, 12-3.5-6]) rotate([90, 0, 90]) cylinder(d=6, h=8, center=true);
            }
            #translate ([-81, 25-7, 0]) {
                hull() {
                    cube(size=[4, 7, 12-3.5], center=false);
                    translate ([4/2, 7-3.5, 12-3.5]) rotate([90, 0, 90]) cylinder(d=7, h=4, center=true);
                }
                translate ([8/2, 7-3.5, 12-3.5-6]) rotate([90, 0, 90]) cylinder(d=6, h=8, center=true);
            }

            // front
            #translate ([81-6.5, -25, 0]) {
                cube(size=[6.5, 11, 4], center=false);
                translate ([6.5/2, 5.5, 4]) rotate([90, 0, 90]) cylinder(d=11, h=6.5, center=true);
            }
            #translate ([81-6.5, 25-11, 0]) {
                cube(size=[6.5, 11, 4], center=false);
                translate ([6.5/2, 5.5, 4]) rotate([90, 0, 90]) cylinder(d=11, h=6.5, center=true);
            }
            }}
        }

        // wire tie holes
        #translate ([-45, -45, 0]) cylinder(d=5, h=20, center=true);
        #translate ([-45,  45, 0]) cylinder(d=5, h=20, center=true);
        #translate ([-35, -45, 0]) cylinder(d=5, h=20, center=true);
        #translate ([-35,  45, 0]) cylinder(d=5, h=20, center=true);

        // esc power wires
        #hull() {
            translate ([48-12.5-5, 23, 0]) cylinder(d=6, h=20, center=true);
            translate ([54-12.5,   23, 0]) cylinder(d=6, h=20, center=true);
        }

        // esc motor wires
        #hull() {
            translate ([48-12.5-5, -23,   0]) cylinder(d=6, h=20, center=true);
            translate ([54-12.5,   -23+4, 0]) cylinder(d=6, h=20, center=true);
            translate ([54-12.5,   -23,   0]) cylinder(d=6, h=20, center=true);
            translate ([48-12.5,   -23+4, 0]) cylinder(d=6, h=20, center=true);
        }

        // space save
        #hull() {
            translate ([52, -10, 0]) cylinder(d=5, h=20, center=true);
            translate ([42, -10, 0]) cylinder(d=5, h=20, center=true);
            translate ([42,  10, 0]) cylinder(d=5, h=20, center=true);
            translate ([52,  10, 0]) cylinder(d=5, h=20, center=true);
        }

        // space save
        #hull() {
            translate ([-72, -10, 0]) cylinder(d=5, h=20, center=true);
            translate ([-67, -10, 0]) cylinder(d=5, h=20, center=true);
            translate ([-67,  10, 0]) cylinder(d=5, h=20, center=true);
            translate ([-72,  10, 0]) cylinder(d=5, h=20, center=true);
        }

        // board mount holes
        #translate ([-(45-2.5)-10,  (35-2.5), 2.5]) screw2x6bore();
        #translate ([-(45-2.5)-10, -(35-2.5), 2.5]) screw2x6bore();
        #translate ([ (45-2.5)-10,  (35-2.5), 2.5]) screw2x6bore();
        #translate ([ (45-2.5)-10, -(35-2.5), 2.5]) screw2x6bore();

        // mount holes
        // front
        // M3 hex nut in front H=2.4, Dmin = 5.4
        // left
        #translate ([81, 42.5/2-3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([81-4-5/2, 42.5/2-3/2, 8-2-3/2]) rotate([90, 90, 90]) hexagon(5.6, 6);
        // right
        #translate ([81,-42.5/2+3/2, 8-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([81-4-5/2,-42.5/2+3/2, 8-2-3/2]) rotate([90, 90, 90]) hexagon(5.6, 6);

        // rear
        // left
        #translate ([-81, 46.5/2-3/2, 12-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([-81,-46.5/2+3/2, 12-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
        // right
        #translate ([-81,-46.5/2+3/2, 12-2-3/2]) rotate([90, 0, 90]) cylinder(d=3, h=20, center=true);
        #translate ([-81, 46.5/2-3/2, 12-2-3/2-6]) rotate([90, 0, 90]) cylinder(d=3, h=12, center=true);
    }
    #arrangebottom(30)
    {
        hbaseholes(center=true, right=1, left=1);
        hbaseholes(center=true, right=0, left=1);
        hbaseholes(center=true, right=0, left=1);
        hbaseholes(center=true, right=1, left=0);
        hbaseholes(center=true, right=1, left=0);
    }
    // cage holes
    #cage(cut=true);

    //arrangebottom(45) hbaseholes();
    }
}

module board() {

    translate ([-12.5, 0, 47]) {
    // base
    color("yellow") cube(size=[90, 70, 1.2], center=true);

    // teensy
    color("green") translate ([-12, 0, 5]) cube(size=[63, 18, 10], center=true);

    // MPU-9250
    color("red") translate ([-24, 21, 5]) cube(size=[18.5, 19, 10], center=true);

    // servo conn
    color("black") translate ([27, -21, 1.5]) cube(size=[10, 7.5, 3], center=true);
    }
}

module esc() {
    color("green") cube(size=[36, 26, 2], center=true);
    color("black") translate ([36/2, 0, 0]) rotate([0, 90, 0]) cylinder(d=8, h=16, center=false);

    // esc support box ?
    // hcolor("black") translate ([0, 0, -11]) cube(size=[30, 20, 20], center=true);
}

module vsonar() {

    // vertical sonar
    translate ([0, -10, 45]) rotate([0, 90, 0]) hcsr04();

    // mount plate
    color("gray") translate ([1.5, -10, -6]) cube(size=[3, 20, 51], center=false);
    color("gray") translate ([-8.5, -10, -6]) cube(size=[10, 20, 5], center=false);

    // sonar ray
    //color("white") translate ([20+1000/2, 0, 22.5]) rotate([0, 90, 0]) cylinder(d1=40, d2=520,h=1000, center=true);
}

module hbaseholes(center = true, left = 2, right = 2)
{
    if (center) translate ([-5,    0, -15]) cylinder(d=3.2, h=15, center=false);

    if (left >= 1) translate ([-5,  9.5, -15]) cylinder(d=3.2, h=15, center=false);
    if (left >= 2) translate ([-5,  19, -15]) cylinder(d=3.2, h=15, center=false);

    if (right >= 1) translate ([-5, -9.5, -15]) cylinder(d=3.2, h=15, center=false);
    if (right >= 2) translate ([-5, -19, -15]) cylinder(d=3.2, h=15, center=false);
}

module hmount() {
    // mount plate
    color ("gray") {

        // front
        difference() {
            translate ([1.5, -48/2, -6]) #hull() {
                cube(size=[3, 48, 25], center=false);
                translate ([0, 48-3, 25]) rotate([90, 0, 90]) cylinder(r=3, h=3, center=false);
                translate ([0, 3, 25]) rotate([90, 0, 90]) cylinder(r=3, h=3, center=false);
            }

            translate ([0, -45/2, 0]) rotate([90, 0, 90]) {

                // sonar
                translate([45/2-(42-16)/2,18/2+1,1.3]) cylinder(d=17, h=15);
                translate([45/2+(42-16)/2,18/2+1,1.3]) cylinder(d=17, h=15);

                //xtal(s)
                hull () {
                    translate([45/2-3.25,20-3.5,1.3])
                    {
                        translate([0,0,0]) cylinder(d=6, h=5);
                        translate([6.5,0,0]) cylinder(d=6, h=5);
                    }
                    translate([45/2-3.25, 4,1.3]) hull()
                    {
                        //translate([0,0,0]) cylinder(d=6, h=5);
                        //translate([6.5,0,0]) cylinder(d=6, h=5);
                        translate([0,-2,0]) cylinder(d=6, h=5);
                        translate([6.5,-2,0]) cylinder(d=6, h=5);
                    }
                }

                // screws
                translate([   1.0,    1.0, 4.5-0.5]) screw2x6bore();
                translate([45-1.0, 20-1.5, 4.5-0.5]) screw2x6bore();
                translate([45-1.0,    1.0, 4.5-0.5]) screw2x6bore();
                translate([   1.0, 20-1.5, 4.5-0.5]) screw2x6bore();
            }

            translate ([3, 0, 10])
                cube(size=[5, 20, 11], center=true);
        }

        //base
        difference() {
            #hull() {
                translate ([-3.5, -48/2, -6]) cube(size=[5, 48, 5], center=false);
                translate ([-3.5, -48/2+5, -6]) cylinder(r=5, h=5, center=false);
                translate ([-3.5, 48/2-5, -6]) cylinder(r=5, h=5, center=false);
            }
            hbaseholes();
        }
    }
}

module screw2x6bore() {
    rotate([180, 0, 0]) {
        cylinder(d=2.2, h=6);
        translate ([0, 0, -1.3])
            cylinder(d1=2*3.6, d2=0, h=2*1.3);
    }
}

module hsonar() {

    // horizontal sonar
    translate ([0, -45/2, 0]) rotate([90, 0, 90]) hcsr04();

    hmount();

    // sonar ray
    //color("white") translate ([20+1000/2, 0, 10]) rotate([0, 90, 0]) cylinder(d1=40, d2=520,h=1000, center=true);
}

module barbox(p1, p2, s=1, r=1) {

    a = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]];
    l = norm(a);
    b = [a[0]/l, a[1]/l, a[2]/l];

    c = cross([0,0,1], b);

    az = atan2(c[0], c[1]);
    ax = acos(b[2]);

    o = s/2-r;
    s1 = s-2*r;

    translate(p1)
        rotate([0, 0, -az])
           rotate([0, ax, 0])
               translate([0, 0, l/2]) {
                    cube(size=[s1, s, l], center=true);
                    cube(size=[s, s1, l], center=true);

                    for (x=[o, -o])
                        for (y=[o, -o]) {
                            translate([x,y,0]) cylinder(r=r, h=l, center=true);
                            for (z=[l/2, -l/2]) {
                                translate([x,y,z]) sphere(r=r, center=true);
                            }
                        }

                    for (z=[l/2, -l/2]) {
                        translate([0,0,z]) cube(size=[s1, s1, 2*r], center=true);

                        translate([ 0, o, z]) rotate([0,90,0]) cylinder(r=r, h=s1, center=true);
                        translate([ 0,-o, z]) rotate([0,90,0]) cylinder(r=r, h=s1, center=true);
                        translate([ o, 0, z]) rotate([90,0,0]) cylinder(r=r, h=s1, center=true);
                        translate([-o, 0, z]) rotate([90,0,0]) cylinder(r=r, h=s1, center=true);
                    }

               }
}

module bar(p1, p2, s=1) {

    a = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]];
    l = norm(a);
    b = [a[0]/l, a[1]/l, a[2]/l];

    c = cross([0,0,1], b);

    az = atan2(c[0], c[1]);
    ax = acos(b[2]);

    translate(p1) sphere(d=s, center=true);
    translate(p2) sphere(d=s, center=true);

    translate(p1)
        rotate([0, 0, -az])
           rotate([0, ax, 0])
               cylinder(d=s, h=l, center=false);
}

module ball8(p1, s=1, r=1) {

    o = s/2-r;

    translate(p1)
        for (x=[o, -o])
            for (y=[o, -o])
                for (z=[o, -o])
                    translate([x,y,z])
                        sphere(r=r, center=true);
}

module skew(p1, p2) { // TODO - not working

    a = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]];
    b = [a[0]/a[2], a[1]/a[2]];

    matrix = [
        [ 1, 0, b[1], 0 ],
        [ 0, 1, b[2], 0 ],
        [ 0, 0, 1, 0 ],
        [ 0, 0, 0, 1 ]
    ];

    translate(p1)
        multmatrix(matrix)
                children();
}

module cage_leg(p1, p2, d, db, r)
{
//    bar(p1, p2, d);

    hull() {
        ball8(p2, d, r);
        translate([0, 0, -d/2]) translate(p1) difference() {
            sphere(d=db, center=true);
            translate([0, 0, -db/2]) cube(size=[db, db, db], center=true);
        }
    }
}

module cage_pin(p1, p2, d, pd, ph, cut)
{
    if (cut)
    {
        bar(p1, p2, d+0.5);
    }
    else
    difference() {
        bar(p1, p2, d);

        translate ([0,0,-ph])
            translate (p1)
                rotate([90,0,0])
                    cylinder(d=pd, h=2*d, center=true);
    }
}

module lcd_mount(p1, x1, x2)
{
    translate(p1) difference() {
        #translate([(x1-x2)/2,0,-1]) cube(size=[x1+x2, 6, 2], center=true);
        #cylinder(d=2.2, h=6, center=true);
    }
}

module cage(cut = false)
{

    d = 4;
    r1 = 1;
    n = 4;
    db = 6;
    hb = d;
    pd = 1;
    ph = d/2 + 3 /*platform*/+ 1/*freespace*/;

    // measurements
    top = 82;

    top_side = 30;

    top_front = 20;
    top_rear = -40;

    top_esc = top_front-26/*esc size*/+d;
    top_lcd = top_rear+27/*lcd size*/+d+1;

    bottom = 51/*platform top*/+d/2;

    bottom_front = 27;
    bottom_rear = -50;

    bottom_front_side = 40;
    bottom_rear_side = 40;

    pin = 8;
    pin_base = bottom - pin;

    // points
    top_front_left = [top_front, top_side, top];
    top_front_right = [top_front, -top_side, top];

    top_esc_left = [top_esc, top_side, top];
    top_esc_right = [top_esc, -top_side, top];

    top_lcd_left = [top_lcd, top_side, top];
    top_lcd_right = [top_lcd, -top_side, top];

    top_rear_left = [top_rear, top_side, top];
    top_rear_right = [top_rear, -top_side, top];

    //mid_front_left = [(top_front+bottom_front)/2, (top_side+bottom_front_side)/2, (top+bottom)/2];
    //mid_front_right = [mid_front_left[0], -mid_front_left[1], mid_front_left[2]];

    bottom_front_left = [bottom_front, bottom_front_side, bottom];
    bottom_front_right = [bottom_front, -bottom_front_side, bottom];

    bottom_rear_left = [bottom_rear, bottom_rear_side, bottom];
    bottom_rear_right = [bottom_rear, -bottom_rear_side, bottom];

    pin_front_left = [bottom_front, bottom_front_side, pin_base];
    pin_front_right = [bottom_front, -bottom_front_side, pin_base];

    pin_rear_left = [bottom_rear, bottom_rear_side, pin_base];
    pin_rear_right = [bottom_rear, -bottom_rear_side, pin_base];

    // bars
    if (!cut)
    {
        barbox(top_front_left, top_front_right, d, r1);
        barbox(top_rear_left, top_rear_right, d, r1);
        barbox(top_front_left, top_rear_left, d, r1);
        barbox(top_front_right, top_rear_right, d, r1);

        barbox(top_esc_left, top_esc_right, d, r1);
        barbox(top_lcd_left, top_lcd_right, d, r1);

        lcd_mount([top_rear+d/2+2.5+23+0.5,  23.5/2, top+d/2], 4, 2);
        lcd_mount([top_rear+d/2+2.5+23+0.5, -23.5/2, top+d/2], 4, 2);
        lcd_mount([top_rear+d/2+2.5-0.5,     23.5/2, top+d/2], 2, 4);
        lcd_mount([top_rear+d/2+2.5-0.5,    -23.5/2, top+d/2], 2, 4);

        //bar(mid_front_left, mid_front_right, d);  // unprintable

        cage_leg(bottom_front_left, top_front_left, d, db, r1);
        cage_leg(bottom_front_right, top_front_right, d, db, r1);
        cage_leg(bottom_rear_left, top_rear_left, d, db, r1);
        cage_leg(bottom_rear_right, top_rear_right, d, db, r1);
    }

    cage_pin(bottom_front_left, pin_front_left, d, pd, ph, cut);
    cage_pin(bottom_front_right, pin_front_right, d, pd, ph, cut);
    cage_pin(bottom_rear_left, pin_rear_left, d, pd, ph, cut);
    cage_pin(bottom_rear_right, pin_rear_right, d, pd, ph, cut);
}

module car()
{
    base();
    platform();
    board();
    color("white") cage();

    translate ([9, -8, 79]) rotate([0, 0, 90]) esc();
}

module arrangebottom(a) {
    translate ([68, 0, 57]) if ($children == 1) children(); else children(0);

    translate ([45, 42, 57]) rotate([0, 0, a]) if ($children == 1) children(); else children(1);
    translate ([-5, 45, 57]) rotate([0, 0, 2*a]) if ($children == 1) children(); else children(2);

    translate ([45, -42, 57]) rotate([0, 0, -a]) if ($children == 1) children(); else children(3);
    translate ([-5, -45, 57]) rotate([0, 0, -2*a]) if ($children == 1) children(); else children(4);
}

module lcd1602() {
translate([-15,0,78]) difference() {
    union() {
        translate([0,0,5+8/2]) color("blue") cube(size=[27, 72, 8], center=true);
        translate([0,0,4+1/2]) color("green") cube(size=[36, 80, 1], center=true);
    }
    translate([ 31/2, 75/2,0]) cylinder(d=2.5, h=20, center=false);
    translate([-31/2, 75/2,0]) cylinder(d=2.5, h=20, center=false);
    translate([ 31/2,-75/2,0]) cylinder(d=2.5, h=20, center=false);
    translate([-31/2,-75/2,0]) cylinder(d=2.5, h=20, center=false);
}
}

module lcd() {
    color("blue") translate([-40+2+27.5/2+0.5,0,81]) cube(size=[27.5, 27.5, 2], center=true);
}

module fullb() {
    car();
    arrangebottom(30) hsonar();
    cage();
    lcd();
}

//car();

//cage();

//board();
//platform();

//fullb();

//barbox([0,0,0], [0,0,50], 10, 3);

//translate ([0, 0, 4.5]) rotate([0, 90, 0]) hmount();

/* // small front
intersection() {
    platform();
    translate ([80, -15, 0]) cube(size=[40, 35, 400], center=true);
}*/

/* // full front
intersection() {
    platform();
    #translate ([100+20, 0, 0]) cube(size=[200, 200, 400], center=true);
}*/

/* // rear
intersection() {
    platform();
    translate ([-80, 0, 0]) cube(size=[20, 200, 400], center=true);
}*/

//screw2x6bore();

//rotate([0,0,180]) projection(cut=true) translate ([0, 0, -49]) platform();
//projection() translate ([0, 0, 10]) hsonar();

translate([0,0,90]) rotate([180,0,0]) cage();


Fix bug in bin size, add untested locating holes.

/* Note that the factory bins have draft which make them injection moldable; we
 * don't need that. */

/* The little circles that the legs fit into to keep the bins from moving around */
gLegCircleSize = 18;

/* Most of the bins have X in this orientation. */
gGridX = 40;
gGridY = 55;

/* So the bins don't fit *too* tightly together */
gTopSlop = 0.6;
// TODO I'm not sure this works, maybe because of poylgon circles?
gExtWidth = 0.45;

// TODO test print for HF, Stanley sizes
//gHeight=47.8; // for HF
gHeight=40.9; // for Stanley

gPegHole = 2.5;
gPegHead = 6;
gPegCircle = 18; // measure hole in base, and subtract 1mm for intentional slop
gPegDepth = 8;

module RoundRect(w,h,r=1) {
    hull()
    for(x_scale=[1,-1]) for(y_scale=[1,-1])
    scale([x_scale,y_scale])
    translate([w/2-r,h/2-r]) circle(r=r);
}
module Corner(r,e=1) {
    translate([-r,-r]) difference() {
        square([r+e,r+e]);
        circle(r=r,$fn=128);
    }
}
module Ess(w,h,r) {
    difference() {
        square([w,h]);
        // cut corner
        translate([w,h]) Corner(r);
    }
    // add corners
    translate([w,0]) rotate([0,0,180]) Corner(r,0);
    translate([0,h]) rotate([0,0,180]) Corner(r,0);
}

module Fillet(a, r) {
    intersection() {
        difference() {
            hull() {
                translate([-a,a]) circle(r=a);
                translate([a,-a]) circle(r=a);
            }
            hull() {
                translate([a,r]) circle(r=r);
                translate([r,a]) circle(r=r);
            }
        }
        square([a,a]);
    }
}

module Bin(x=1,y=1,wall=gExtWidth*3,feet=true,cutaway=false) {
    inside_wall_w = (x*gGridX)-(wall*2)-gTopSlop;
    inside_wall_h = (y*gGridY)-(wall*2)-gTopSlop;
    difference() {
        union() {
            linear_extrude(height=gHeight,convexity=4) difference() {
                RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=128);
                RoundRect(inside_wall_w,inside_wall_h,3-wall,$fn=32);
            }
            // Floor
            linear_extrude(height=2)
            RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=32);
            if(feet) {
                linear_extrude(height=gPegDepth+2,convexity=4)
                for(x_scale=[1,-1]) for(y_scale=[1,-1])
                    scale([x_scale,y_scale])
                    translate([-inside_wall_w/2,-inside_wall_h/2]) Fillet(10, 3, $fn=32);
                //Ess(6,6,3);
            }
        }
        if(feet) {
            for(x_scale=[1,-1]) for(y_scale=[1,-1])
                scale([x_scale,y_scale])
                translate([(x*gGridX-gTopSlop)/2,(y*gGridY-gTopSlop)/2,-1])
                rotate([0,0,180+45]) translate([(gPegCircle-gPegHead)/2,0])
                cylinder(d=gPegHole,h=gPegDepth+1,$fn=32);
        }
        if(cutaway) {
            translate([-inside_wall_w/2,-inside_wall_h/2,-1])
            rotate([0,0,-45]) translate([0,-1,0]) cube([12,12,20]);
        }
    }
}

Bin(4,1);

/* Note that the factory bins have draft which make them injection moldable; we
 * don't need that. */

/* The little circles that the legs fit into to keep the bins from moving around */
gLegCircleSize = 18;

/* Most of the bins have X in this orientation. */
gGridX = 40;
gGridY = 55;

/* So the bins don't fit *too* tightly together */
gTopSlop = 0.6;
// TODO I'm not sure this works, maybe because of poylgon circles?
gExtWidth = 0.45;

//gHeight=47.8; // for HF
gHeight=40.9; // for Stanley

module RoundRect(w,h,r=1) {
    hull()
    for(x_scale=[1,-1]) for(y_scale=[1,-1])
    scale([x_scale,y_scale])
    translate([(w-r)/2,(h-r)/2]) circle(r=r);
}

// TODO h for HF
// TODO locating feet, maybe a really short screw?
// TODO bottom inside rounding
module Bin(x=1,y=1,wall=gExtWidth*3) {
    linear_extrude(height=gHeight) difference() {
        RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=32);
        RoundRect((x*gGridX)-(wall*2)-gTopSlop,(y*gGridY)-(wall*2)-gTopSlop,3-wall,$fn=32);
    }
    // Floor
    linear_extrude(height=2)
    RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=32);
}

Bin(4,1);


Grey->Gray for better compatibility with a translator.

// fishbox.scad
//  - Enclosure design for zebrafish Skinner box
//
//  Author: Mark Liffiton
//  Date: Oct, 2014 - Nov, 2015
//
//  Units are mm

// Set thickness to account for thickness of material
// PLUS needed clearance for cuts into which material should fit.

// 0.100" Acrylic:
acrylic_thickness = 2.4;
    // 2.4mm = 0.094" (looks good based on cut test piece for acrylic slotting into itself)

// 1/8" Hardboard:
hardboard_thickness = 3.5;
    // 2.8mm = 0.110"
    //   * looked good based on cut test piece for hardboard slotting into hardboard
    //   * 2015-03-06: Hanging supports didn't fit into cutouts (or did only with lots of force)
    // 3.1mm for some breathing room
    //   * 2016-02-26: Still tighter than is ideal for sliding parts together.
    // 3.3mm for just a smidge more...
    //   * 2016-06-17: 3.3 is *still* too tight in a few connections!
    //                 Most are fine, but we need to handle the variability
    //                 in material thickness, so...
    // 3.5mm.  (Final?)

// Assign materials: window/back wall and everything else
//window_thickness = acrylic_thickness;
window_thickness = hardboard_thickness;
thickness = hardboard_thickness;

// Interior box dimensions
// The variables are used below to control placement of **centerpoints** centerpoints of walls,
// so material thickness is added to account for that.
width = 290 + thickness;    // x = 29cm wall-to-wall
depth = 348 + thickness;    // y = 34.8cm to accomodate 34.8cm tank length
i_height = 195 + thickness; // z = 19.5cm to accomodate 16cm tank height plus wires for shock
                            // (i_height because 'height' is used for height of entire box)

// amount faces extend past each other at corners
overhang = 15;

// amount horizontal support tabs extend past side supports (technically, it will go this distance minus thickness/2)
outset = thickness+2;

// tank base plate
base_height = overhang/2;

// create height var to account for raised base
height = i_height + base_height;

// height of insert for rear tank support
insert_height = height/6;

// hanging support thickness and drop (distance it "sits down" after in place)
support_w = 10;
support_drop = support_w/2;

// useful tank measurements
tank_width = 70;
tank_foot_offset_left = 105;
tank_foot_offset_right = 101;

// x-position of mask piece inside box
mask_loc = tank_width + thickness;  // center-to-center
// dimensions for mask view opening
view_opening_left = 65;
view_opening_right = 78;
view_opening_width = depth-thickness - view_opening_left - view_opening_right;
view_opening_bottom = 24;
view_opening_height = 96;

// camera mounting measurements
camera_hole_diameter = 1.7;       // bolts are m2; 2mm nominal, 1.9mm actual, and a bit less for the beam width
camera_hole_horiz_offset = 21/2;  // 21mm between mounting holes, horizontally (so half that away from the camera center
camera_hole_vert_offset = 12.5;   // 12.5mm between mounting holes, vertically (one set on the camera center, one set up that much from center)

// position of rpi and related parts
rpi_ypos = depth/2 + 60;

// for slight offsets/tweaks
epsilon = 1;

// set precision of all arcs/spheres/cylinders/etc.
$fn = 50;

// vars to control projection for DXF export (see makefile)
if (DXF_TOP) {
    projection() top_cover(spacing=1);
} else if (DXF_BOTTOM) {
    projection() tank_base();
} else if (DXF_SIDE1) {
    projection() rotate(a=[90,0,0]) side(y=0);
} else if (DXF_SIDE2) {
    projection() rotate(a=[90,0,0]) side(y=depth);
} else if (DXF_END1) {
    projection() rotate(a=[0,90,0]) vert_face(x=0);
} else if (DXF_END2) {
    projection() rotate(a=[0,90,0]) vert_face(x=width);
} else if (DXF_MASK) {
    projection() rotate(a=[0,90,0]) mask(x=mask_loc);
} else if (DXF_TANK_SUPPORT) {
    projection() tank_support_layer1();
    projection() translate([tank_width+thickness,0,0]) tank_support_layer2();
} else if (DXF_RPI_SUPPORT) {
    projection() rotate(a=[90,0,0]) rpi_support();
} else if (DXF_REAR_SUPPORT) {
    projection() rotate(a=[90,0,0]) rear_support(1);
    translate([overhang*3,0,0])
    projection() rotate(a=[90,0,0]) rear_support(2);
    translate([overhang*6,0,0])
    projection() rotate(a=[90,0,0]) rear_support(3);
}
else {
    // 3D model
    // components to include (comment out unwanted)
    vert_face(x=0);      // "near face, transparent for monitor
    vert_face(x=width);  // "far" face, with camera, rpi, etc.
    mask(x=mask_loc);
    side(y=0);
    side(y=depth);
    tank_base();
    tank_support();
    rpi_support(x=width, y=rpi_ypos-10, z=height/2);
    mock_rpi(x=width, y=rpi_ypos, z=height/2);
    top_cover();
    rear_support();
}

//////////////////////////////////////////////////////////////////
// Module definitions
//

module mask(x, tab_extra=0) {
    difference() {
        vert_face_base(x, top_offset=thickness, bottom_offset=base_height-thickness);
        side(y=0);
        side(y=depth);
        translate([x-thickness/2, -overhang, 0]) {
            translate([0, 0, 0])
                rounded_truncation(width=overhang-thickness/2, up=false);
            translate([0, depth+overhang+thickness/2, 0])
                rounded_truncation(width=overhang-thickness/2, up=false);
        }
        // opening for tank view
        translate([0,view_opening_left+thickness/2, view_opening_bottom+base_height+thickness/2])
            cube([width, view_opening_width, view_opening_height]);
        // bottom opening for wires
        translate([0, depth-thickness/2, base_height+thickness])
        rotate(a=[0,90,0])
            cylinder(r=10, h=width);
        // top openings for wires (electrical stim)
        translate([0, depth-15, height-thickness/2])
        rotate(a=[0,90,0])
            cylinder(r=5, h=width);
        translate([0, 15, height-thickness/2])
        rotate(a=[0,90,0])
            cylinder(r=5, h=width);
        // tabs for rigidity w/ bottom panel
        cutouts(4,depth-thickness,outset*2,rot=[-90,0,90],trans=[x+thickness/2,depth/2,base_height+thickness/2], extra=tab_extra);
    }
}

module vert_face(x=0) {
    difference() {
        if (x == 0) {
            vert_face_base(0, window_thickness);
        }
        else {
            vert_face_base(x);
        }
        camera_opening();
        // round=false so we get the full width of the piece in the cutout, not reduced because we hit the rounded corner
        rpi_support(x=width+thickness*2, y=rpi_ypos-10, z=height/2+support_drop, round=false);
        translate([thickness+epsilon,0,-insert_height/2])
            rear_support();
        wire_opening();
        power_jack_cutout();
        // CAUTION: OpenSCAD won't let me make a projection of vert_face(x=0)
        //   if tank_base is put after side(y=depth) here... bug.  :(
        tank_base();
        top_cover();
        side(y=0);
        side(y=depth);
        translate([x-thickness/2, -overhang, 0]) {
            translate([0, depth+overhang+thickness/2, 0])
                rounded_truncation(width=overhang-thickness/2, up=false);
            translate([0, 0, 0])
                rounded_truncation(width=overhang-thickness/2, up=false);
        }
    }
}

module vert_face_base(x, face_thickness=thickness, top_offset=0, bottom_offset=0) {
    translate([x-face_thickness/2, -overhang, bottom_offset])
        cube([face_thickness,depth+overhang*2,height-top_offset-bottom_offset]);
}

module rounded_truncation(width, up, rot=[0,0,0]) {
    truncation_height=height/4;
    rotate(a=rot)
    difference() {
        translate([-50,-epsilon,0])
            cube([100, width+2*epsilon, truncation_height]);
        if (up) {
            translate([-50, width/2, 0])
            rotate(a=[0, 90, 0])
                cylinder(d=width, h=100);
        }
        else {
            translate([-50, width/2, truncation_height])
            rotate(a=[0, 90, 0])
                cylinder(d=width, h=100);
        }
    }
}

module camera_opening() {
    translate([width, depth/2, height/2])
    union() {
        cube([thickness*2,10,10], center=true);
        translate([0, camera_hole_horiz_offset, 0])
        rotate(a=[0, 90, 0])
            cylinder(d=camera_hole_diameter, h=100, center=true);
        translate([0, -camera_hole_horiz_offset, 0])
        rotate(a=[0, 90, 0])
            cylinder(d=camera_hole_diameter, h=100, center=true);
        translate([0, camera_hole_horiz_offset, camera_hole_vert_offset])
        rotate(a=[0, 90, 0])
            cylinder(d=camera_hole_diameter, h=100, center=true);
        translate([0, -camera_hole_horiz_offset, camera_hole_vert_offset])
        rotate(a=[0, 90, 0])
            cylinder(d=camera_hole_diameter, h=100, center=true);
    }
}

module rpi_support(x, y, z, round=true) {
    hanging_support(x, y, z, out=4, up=85, round=round);
}

module hanging_support(x, y, z, out, up, round=true) {
    inner_w = out+thickness;
    inner_h = up;
    color("Red", alpha=0.5)
    translate([x,y,z])
    // push inner opening up against vert face
    translate([out/2,0,0])
    difference() {
        if (round) {
            rounded_rect(inner_w+support_w*2, thickness, inner_h+support_w*2, center=true, radius=support_w);
        }
        else {
            cube([inner_w+support_w*2, thickness, inner_h+support_w*2], center=true);
        }
        // cutout for object
        cube([inner_w, thickness*2, inner_h], center=true);
        // cutout for passing through vertface
        translate([-(inner_w/2+support_w/2),0,-support_drop/2])
            cube([support_w+2*epsilon, thickness*2, inner_h-support_drop], center=true);
        // bottom cutout
        translate([-out/2,0,-(inner_h/2+support_w-support_drop/2)])
            cube([thickness, thickness*2, support_drop], center=true);
    }
}

// rounded corners in x,z directions
module rounded_rect(x,y,z, center, radius) {
    if (center) {
        rounded_rect_centered(x,y,z, radius);
    }
    else {
        translate([x/2, y/2, z/2])
            rounded_rect_centered(x,y,z, radius);
    }
}

module rounded_rect_centered(x,y,z, radius) {
    difference() {
        cube([x,y,z], center=true);

        translate([x/2-radius, 50, z/2-radius])
        rotate(a=[90,0,0])
        difference() {
            cube([radius,radius,100]);
            cylinder(r=radius, h=100);
        }
        translate([-x/2+radius, 50, z/2-radius])
        rotate(a=[90,-90,0])
        difference() {
            cube([radius,radius,100]);
            cylinder(r=radius, h=100);
        }
        translate([x/2-radius, 50, -z/2+radius])
        rotate(a=[90,90,0])
        difference() {
            cube([radius,radius,100]);
            cylinder(r=radius, h=100);
        }
        translate([-x/2+radius, 50, -z/2+radius])
        rotate(a=[90,180,0])
        difference() {
            cube([radius,radius,100]);
            cylinder(r=radius, h=100);
        }
    }
}

module top_cover(spacing=0) {
    // width1 = main cover ; width2 = tank section cover
    width1 = width - mask_loc - thickness/2;
    width2 = mask_loc - thickness/2;
    color("Gray", alpha=0.5)
    union() {
        // back piece (over empty space)
        // spacing added to make separate pieces for laser cutting
        translate([spacing,-outset,height-thickness])
        difference() {
            translate([mask_loc,0,0])
                cube([width1+thickness+outset,depth+outset*2,thickness]);
            cutouts(4,width1,outset,rot=0,trans=[width1/2+mask_loc,0,0]);
            cutouts(4,width1,outset,rot=180,trans=[width1/2+mask_loc,depth+outset*2,0]);
            cutouts(5,depth-thickness,outset,rot=90,trans=[width+outset,depth/2+outset,0]);
        }
        // front piece (over tank)
        translate([-outset-thickness/2,-overhang,height-thickness])
        difference() {
            cube([width2+thickness+outset,depth+overhang*2,thickness]);
            cutouts(5,depth-thickness,outset,rot=90,trans=[outset+thickness/2,depth/2+overhang,0]);
        }
    }
}

module tank_base() {
    difference() {
        translate([-outset,thickness/2,base_height])
            difference() {
                cube([width+outset*2, depth-thickness, thickness]);
                cutouts(5,depth,outset,rot=-90,trans=[0,(depth-thickness)/2,0]);
                cutouts(5,depth,outset,rot=90,trans=[width+outset*2,(depth-thickness)/2,0]);
            }
        mask(x=mask_loc, tab_extra=5);
    }
}

module tank_support() {
    translate([thickness/2, thickness/2, base_height+thickness])
    union() {
        tank_support_layer1();
        color("Gray", alpha=0.5)
            tank_support_layer2();
    }
}
// Two strips of hardboard on either side of LED strip
module tank_support_layer1() {
    // front bottom strip, w/ space for wires
    translate([0, 0, 0])
        cube([tank_width/3, depth-thickness, thickness]);
    // back bottom strip, full-depth
    translate([2*tank_width/3, 0, 0])
        cube([tank_width/3, depth-thickness-20, thickness]);
}
// Clear acrylic over LED strip
module tank_support_layer2() {
    translate([0, 0, thickness])
    difference() {
        cube([tank_width, depth-thickness, thickness]);
        translate([tank_width/2, depth-tank_width/2, 0])
            cylinder(d=25, h=100, center=true);
    }
}

module cutouts(num, width, outset, rot, trans, extra=0) {
    // "extra" is *removed* from width of cubes
    // so that resulting tabs (after differencing this) are *wider*
    w = width / ((num-1) * 2);
    translate(trans)
    rotate([0,0,1], a=rot)
    for (i = [0 : num-1]) {
        translate([-width/2 - w/2 + i*2*w + extra/2, -thickness/2, -thickness/2])
            cube([w-extra, outset+thickness, thickness*2]);
    }
}

module wire_opening() {
    translate([width, rpi_ypos+20, overhang+20])
    scale([1,2,1])
    rotate(a=[0,90,0])
        cylinder(d=12, h=thickness+epsilon, center=true);
}

module power_jack_cutout() {
    translate([width, depth-40, overhang+30])
    rotate(a=[0,90,0])
        cylinder(d=8, h=thickness+epsilon, center=true);
}

module side(y=0) {
    difference() {
        side_base(y);
        translate([0,0,height/2])
        scale([1,1,0.5])
            vert_face_base(x=mask_loc);
        translate([0,0,height/2])
        scale([1,1,0.5])
            vert_face_base(0, window_thickness);
        translate([0,0,height/2])
        scale([1,1,0.5])
            vert_face_base(width);
        top_cover();
    }
}

module side_base(y) {
    translate([-overhang,y-thickness/2,0])
    difference() {
        cube([width+overhang*2, thickness, height]);
        translate([width+overhang+thickness/2, 0, 3/4*height])
            rounded_truncation(width=overhang-thickness/2, up=true, rot=[0,0,-90]);
        translate([0, 0, 3/4*height])
            rounded_truncation(width=overhang-window_thickness/2, up=true, rot=[0,0,-90]);
    }
}

module rear_support(num_high=1) {
    support_height = num_high*height;
    ypos = depth/2-thickness/2;
    color("Orange")
    translate([width+thickness/2, ypos, 0])
    union() {
        // entering-tank part
        translate([-overhang, 0, insert_height/2+overhang])
        difference() {
            rounded_rect(overhang*2, thickness, insert_height, radius=overhang/2);
            translate([overhang-thickness, -thickness/2, insert_height/2])
                cube([thickness, thickness*2, height]);
            translate([overhang-thickness, -thickness/2, insert_height/2])
                difference() {
                    translate([0,0,overhang*0.8])
                        cube([thickness+overhang, thickness*2, height]);
                    translate([overhang+thickness, 0, overhang*0.8])
                    rotate(a=[90,0,0])
                        cylinder(d=overhang*2, h=100, center=true);
                }
        }
        // connect and fill in one of the rounded corners
        cube([overhang, thickness, insert_height/2+overhang+overhang]);
        // below-tank part
        translate([0, 0, -support_height])
            cube([overhang, thickness, support_height]);
    }
}

module mock_rpi(x, y, z) {
    color("Blue", alpha=0.5)
    translate([x+thickness/2, y, z])
        rotate(a=[0,-90,180])
            translate([-85/2, -56/2, 0])
                union() {
                    cube([85, 56, 2]);
                    // USB1
                    translate([67, 41, 2])
                        cube([20, 12, 12]);
                    // USB2
                    translate([67, 23, 2])
                        cube([20, 12, 12]);
                }
}

// fishbox.scad
//  - Enclosure design for zebrafish Skinner box
//
//  Author: Mark Liffiton
//  Date: Oct, 2014 - Nov, 2015
//
//  Units are mm

// Set thickness to account for thickness of material
// PLUS needed clearance for cuts into which material should fit.

// 0.100" Acrylic:
acrylic_thickness = 2.4;
    // 2.4mm = 0.094" (looks good based on cut test piece for acrylic slotting into itself)

// 1/8" Hardboard:
hardboard_thickness = 3.5;
    // 2.8mm = 0.110"
    //   * looked good based on cut test piece for hardboard slotting into hardboard
    //   * 2015-03-06: Hanging supports didn't fit into cutouts (or did only with lots of force)
    // 3.1mm for some breathing room
    //   * 2016-02-26: Still tighter than is ideal for sliding parts together.
    // 3.3mm for just a smidge more...
    //   * 2016-06-17: 3.3 is *still* too tight in a few connections!
    //                 Most are fine, but we need to handle the variability
    //                 in material thickness, so...
    // 3.5mm.  (Final?)

// Assign materials: window/back wall and everything else
//window_thickness = acrylic_thickness;
window_thickness = hardboard_thickness;
thickness = hardboard_thickness;

// Interior box dimensions
// The variables are used below to control placement of **centerpoints** centerpoints of walls,
// so material thickness is added to account for that.
width = 290 + thickness;    // x = 29cm wall-to-wall
depth = 348 + thickness;    // y = 34.8cm to accomodate 34.8cm tank length
i_height = 195 + thickness; // z = 19.5cm to accomodate 16cm tank height plus wires for shock
                            // (i_height because 'height' is used for height of entire box)

// amount faces extend past each other at corners
overhang = 15;

// amount horizontal support tabs extend past side supports (technically, it will go this distance minus thickness/2)
outset = thickness+2;

// tank base plate
base_height = overhang/2;

// create height var to account for raised base
height = i_height + base_height;

// height of insert for rear tank support
insert_height = height/6;

// hanging support thickness and drop (distance it "sits down" after in place)
support_w = 10;
support_drop = support_w/2;

// useful tank measurements
tank_width = 70;
tank_foot_offset_left = 105;
tank_foot_offset_right = 101;

// x-position of mask piece inside box
mask_loc = tank_width + thickness;  // center-to-center
// dimensions for mask view opening
view_opening_left = 65;
view_opening_right = 78;
view_opening_width = depth-thickness - view_opening_left - view_opening_right;
view_opening_bottom = 24;
view_opening_height = 96;

// camera mounting measurements
camera_hole_diameter = 1.7;       // bolts are m2; 2mm nominal, 1.9mm actual, and a bit less for the beam width
camera_hole_horiz_offset = 21/2;  // 21mm between mounting holes, horizontally (so half that away from the camera center
camera_hole_vert_offset = 12.5;   // 12.5mm between mounting holes, vertically (one set on the camera center, one set up that much from center)

// position of rpi and related parts
rpi_ypos = depth/2 + 60;

// for slight offsets/tweaks
epsilon = 1;

// set precision of all arcs/spheres/cylinders/etc.
$fn = 50;

// vars to control projection for DXF export (see makefile)
if (DXF_TOP) {
    projection() top_cover(spacing=1);
} else if (DXF_BOTTOM) {
    projection() tank_base();
} else if (DXF_SIDE1) {
    projection() rotate(a=[90,0,0]) side(y=0);
} else if (DXF_SIDE2) {
    projection() rotate(a=[90,0,0]) side(y=depth);
} else if (DXF_END1) {
    projection() rotate(a=[0,90,0]) vert_face(x=0);
} else if (DXF_END2) {
    projection() rotate(a=[0,90,0]) vert_face(x=width);
} else if (DXF_MASK) {
    projection() rotate(a=[0,90,0]) mask(x=mask_loc);
} else if (DXF_TANK_SUPPORT) {
    projection() tank_support_layer1();
    projection() translate([tank_width+thickness,0,0]) tank_support_layer2();
} else if (DXF_RPI_SUPPORT) {
    projection() rotate(a=[90,0,0]) rpi_support();
} else if (DXF_REAR_SUPPORT) {
    projection() rotate(a=[90,0,0]) rear_support(1);
    translate([overhang*3,0,0])
    projection() rotate(a=[90,0,0]) rear_support(2);
    translate([overhang*6,0,0])
    projection() rotate(a=[90,0,0]) rear_support(3);
}
else {
    // 3D model
    // components to include (comment out unwanted)
    vert_face(x=0);      // "near face, transparent for monitor
    vert_face(x=width);  // "far" face, with camera, rpi, etc.
    mask(x=mask_loc);
    side(y=0);
    side(y=depth);
    tank_base();
    tank_support();
    rpi_support(x=width, y=rpi_ypos-10, z=height/2);
    mock_rpi(x=width, y=rpi_ypos, z=height/2);
    top_cover();
    rear_support();
}

//////////////////////////////////////////////////////////////////
// Module definitions
//

module mask(x, tab_extra=0) {
    difference() {
        vert_face_base(x, top_offset=thickness, bottom_offset=base_height-thickness);
        side(y=0);
        side(y=depth);
        translate([x-thickness/2, -overhang, 0]) {
            translate([0, 0, 0])
                rounded_truncation(width=overhang-thickness/2, up=false);
            translate([0, depth+overhang+thickness/2, 0])
                rounded_truncation(width=overhang-thickness/2, up=false);
        }
        // opening for tank view
        translate([0,view_opening_left+thickness/2, view_opening_bottom+base_height+thickness/2])
            cube([width, view_opening_width, view_opening_height]);
        // bottom opening for wires
        translate([0, depth-thickness/2, base_height+thickness])
        rotate(a=[0,90,0])
            cylinder(r=10, h=width);
        // top openings for wires (electrical stim)
        translate([0, depth-15, height-thickness/2])
        rotate(a=[0,90,0])
            cylinder(r=5, h=width);
        translate([0, 15, height-thickness/2])
        rotate(a=[0,90,0])
            cylinder(r=5, h=width);
        // tabs for rigidity w/ bottom panel
        cutouts(4,depth-thickness,outset*2,rot=[-90,0,90],trans=[x+thickness/2,depth/2,base_height+thickness/2], extra=tab_extra);
    }
}

module vert_face(x=0) {
    difference() {
        if (x == 0) {
            vert_face_base(0, window_thickness);
        }
        else {
            vert_face_base(x);
        }
        camera_opening();
        // round=false so we get the full width of the piece in the cutout, not reduced because we hit the rounded corner
        rpi_support(x=width+thickness*2, y=rpi_ypos-10, z=height/2+support_drop, round=false);
        translate([thickness+epsilon,0,-insert_height/2])
            rear_support();
        wire_opening();
        power_jack_cutout();
        // CAUTION: OpenSCAD won't let me make a projection of vert_face(x=0)
        //   if tank_base is put after side(y=depth) here... bug.  :(
        tank_base();
        top_cover();
        side(y=0);
        side(y=depth);
        translate([x-thickness/2, -overhang, 0]) {
            translate([0, depth+overhang+thickness/2, 0])
                rounded_truncation(width=overhang-thickness/2, up=false);
            translate([0, 0, 0])
                rounded_truncation(width=overhang-thickness/2, up=false);
        }
    }
}

module vert_face_base(x, face_thickness=thickness, top_offset=0, bottom_offset=0) {
    translate([x-face_thickness/2, -overhang, bottom_offset])
        cube([face_thickness,depth+overhang*2,height-top_offset-bottom_offset]);
}

module rounded_truncation(width, up, rot=[0,0,0]) {
    truncation_height=height/4;
    rotate(a=rot)
    difference() {
        translate([-50,-epsilon,0])
            cube([100, width+2*epsilon, truncation_height]);
        if (up) {
            translate([-50, width/2, 0])
            rotate(a=[0, 90, 0])
                cylinder(d=width, h=100);
        }
        else {
            translate([-50, width/2, truncation_height])
            rotate(a=[0, 90, 0])
                cylinder(d=width, h=100);
        }
    }
}

module camera_opening() {
    translate([width, depth/2, height/2])
    union() {
        cube([thickness*2,10,10], center=true);
        translate([0, camera_hole_horiz_offset, 0])
        rotate(a=[0, 90, 0])
            cylinder(d=camera_hole_diameter, h=100, center=true);
        translate([0, -camera_hole_horiz_offset, 0])
        rotate(a=[0, 90, 0])
            cylinder(d=camera_hole_diameter, h=100, center=true);
        translate([0, camera_hole_horiz_offset, camera_hole_vert_offset])
        rotate(a=[0, 90, 0])
            cylinder(d=camera_hole_diameter, h=100, center=true);
        translate([0, -camera_hole_horiz_offset, camera_hole_vert_offset])
        rotate(a=[0, 90, 0])
            cylinder(d=camera_hole_diameter, h=100, center=true);
    }
}

module rpi_support(x, y, z, round=true) {
    hanging_support(x, y, z, out=4, up=85, round=round);
}

module hanging_support(x, y, z, out, up, round=true) {
    inner_w = out+thickness;
    inner_h = up;
    color("Red", alpha=0.5)
    translate([x,y,z])
    // push inner opening up against vert face
    translate([out/2,0,0])
    difference() {
        if (round) {
            rounded_rect(inner_w+support_w*2, thickness, inner_h+support_w*2, center=true, radius=support_w);
        }
        else {
            cube([inner_w+support_w*2, thickness, inner_h+support_w*2], center=true);
        }
        // cutout for object
        cube([inner_w, thickness*2, inner_h], center=true);
        // cutout for passing through vertface
        translate([-(inner_w/2+support_w/2),0,-support_drop/2])
            cube([support_w+2*epsilon, thickness*2, inner_h-support_drop], center=true);
        // bottom cutout
        translate([-out/2,0,-(inner_h/2+support_w-support_drop/2)])
            cube([thickness, thickness*2, support_drop], center=true);
    }
}

// rounded corners in x,z directions
module rounded_rect(x,y,z, center, radius) {
    if (center) {
        rounded_rect_centered(x,y,z, radius);
    }
    else {
        translate([x/2, y/2, z/2])
            rounded_rect_centered(x,y,z, radius);
    }
}

module rounded_rect_centered(x,y,z, radius) {
    difference() {
        cube([x,y,z], center=true);

        translate([x/2-radius, 50, z/2-radius])
        rotate(a=[90,0,0])
        difference() {
            cube([radius,radius,100]);
            cylinder(r=radius, h=100);
        }
        translate([-x/2+radius, 50, z/2-radius])
        rotate(a=[90,-90,0])
        difference() {
            cube([radius,radius,100]);
            cylinder(r=radius, h=100);
        }
        translate([x/2-radius, 50, -z/2+radius])
        rotate(a=[90,90,0])
        difference() {
            cube([radius,radius,100]);
            cylinder(r=radius, h=100);
        }
        translate([-x/2+radius, 50, -z/2+radius])
        rotate(a=[90,180,0])
        difference() {
            cube([radius,radius,100]);
            cylinder(r=radius, h=100);
        }
    }
}

module top_cover(spacing=0) {
    // width1 = main cover ; width2 = tank section cover
    width1 = width - mask_loc - thickness/2;
    width2 = mask_loc - thickness/2;
    color("Grey", alpha=0.5)
    union() {
        // back piece (over empty space)
        // spacing added to make separate pieces for laser cutting
        translate([spacing,-outset,height-thickness])
        difference() {
            translate([mask_loc,0,0])
                cube([width1+thickness+outset,depth+outset*2,thickness]);
            cutouts(4,width1,outset,rot=0,trans=[width1/2+mask_loc,0,0]);
            cutouts(4,width1,outset,rot=180,trans=[width1/2+mask_loc,depth+outset*2,0]);
            cutouts(5,depth-thickness,outset,rot=90,trans=[width+outset,depth/2+outset,0]);
        }
        // front piece (over tank)
        translate([-outset-thickness/2,-overhang,height-thickness])
        difference() {
            cube([width2+thickness+outset,depth+overhang*2,thickness]);
            cutouts(5,depth-thickness,outset,rot=90,trans=[outset+thickness/2,depth/2+overhang,0]);
        }
    }
}

module tank_base() {
    difference() {
        translate([-outset,thickness/2,base_height])
            difference() {
                cube([width+outset*2, depth-thickness, thickness]);
                cutouts(5,depth,outset,rot=-90,trans=[0,(depth-thickness)/2,0]);
                cutouts(5,depth,outset,rot=90,trans=[width+outset*2,(depth-thickness)/2,0]);
            }
        mask(x=mask_loc, tab_extra=5);
    }
}

module tank_support() {
    translate([thickness/2, thickness/2, base_height+thickness])
    union() {
        tank_support_layer1();
        color("Grey", alpha=0.5)
            tank_support_layer2();
    }
}
// Two strips of hardboard on either side of LED strip
module tank_support_layer1() {
    // front bottom strip, w/ space for wires
    translate([0, 0, 0])
        cube([tank_width/3, depth-thickness, thickness]);
    // back bottom strip, full-depth
    translate([2*tank_width/3, 0, 0])
        cube([tank_width/3, depth-thickness-20, thickness]);
}
// Clear acrylic over LED strip
module tank_support_layer2() {
    translate([0, 0, thickness])
    difference() {
        cube([tank_width, depth-thickness, thickness]);
        translate([tank_width/2, depth-tank_width/2, 0])
            cylinder(d=25, h=100, center=true);
    }
}

module cutouts(num, width, outset, rot, trans, extra=0) {
    // "extra" is *removed* from width of cubes
    // so that resulting tabs (after differencing this) are *wider*
    w = width / ((num-1) * 2);
    translate(trans)
    rotate([0,0,1], a=rot)
    for (i = [0 : num-1]) {
        translate([-width/2 - w/2 + i*2*w + extra/2, -thickness/2, -thickness/2])
            cube([w-extra, outset+thickness, thickness*2]);
    }
}

module wire_opening() {
    translate([width, rpi_ypos+20, overhang+20])
    scale([1,2,1])
    rotate(a=[0,90,0])
        cylinder(d=12, h=thickness+epsilon, center=true);
}

module power_jack_cutout() {
    translate([width, depth-40, overhang+30])
    rotate(a=[0,90,0])
        cylinder(d=8, h=thickness+epsilon, center=true);
}

module side(y=0) {
    difference() {
        side_base(y);
        translate([0,0,height/2])
        scale([1,1,0.5])
            vert_face_base(x=mask_loc);
        translate([0,0,height/2])
        scale([1,1,0.5])
            vert_face_base(0, window_thickness);
        translate([0,0,height/2])
        scale([1,1,0.5])
            vert_face_base(width);
        top_cover();
    }
}

module side_base(y) {
    translate([-overhang,y-thickness/2,0])
    difference() {
        cube([width+overhang*2, thickness, height]);
        translate([width+overhang+thickness/2, 0, 3/4*height])
            rounded_truncation(width=overhang-thickness/2, up=true, rot=[0,0,-90]);
        translate([0, 0, 3/4*height])
            rounded_truncation(width=overhang-window_thickness/2, up=true, rot=[0,0,-90]);
    }
}

module rear_support(num_high=1) {
    support_height = num_high*height;
    ypos = depth/2-thickness/2;
    color("Orange")
    translate([width+thickness/2, ypos, 0])
    union() {
        // entering-tank part
        translate([-overhang, 0, insert_height/2+overhang])
        difference() {
            rounded_rect(overhang*2, thickness, insert_height, radius=overhang/2);
            translate([overhang-thickness, -thickness/2, insert_height/2])
                cube([thickness, thickness*2, height]);
            translate([overhang-thickness, -thickness/2, insert_height/2])
                difference() {
                    translate([0,0,overhang*0.8])
                        cube([thickness+overhang, thickness*2, height]);
                    translate([overhang+thickness, 0, overhang*0.8])
                    rotate(a=[90,0,0])
                        cylinder(d=overhang*2, h=100, center=true);
                }
        }
        // connect and fill in one of the rounded corners
        cube([overhang, thickness, insert_height/2+overhang+overhang]);
        // below-tank part
        translate([0, 0, -support_height])
            cube([overhang, thickness, support_height]);
    }
}

module mock_rpi(x, y, z) {
    color("Blue", alpha=0.5)
    translate([x+thickness/2, y, z])
        rotate(a=[0,-90,180])
            translate([-85/2, -56/2, 0])
                union() {
                    cube([85, 56, 2]);
                    // USB1
                    translate([67, 41, 2])
                        cube([20, 12, 12]);
                    // USB2
                    translate([67, 23, 2])
                        cube([20, 12, 12]);
                }
}


more modeling work

more modeling work


Added comment

Added comment

// Housings to hold hexagonal nut for a "nut drive": two nuts with
// reverse threads on a threaded rod.
// All dimensions in mm.
use <../common/extrusions.scad>
EPSILON = 0.01;
LARGE = 100;

function poly_coords(order, r=1)  =
    let(angles=[ for (i = [0:order-1]) i*(360/order) ])
 	[ for (th=angles) [r*cos(th), r*sin(th)] ];

 module regular_polygon(order, r=1){
 	polygon(poly_coords(order, r));
 }

block_w = 30; // Width of block - it's a (rounded) square block
block_ro = 3; // outer radii of rounded block
screw_hole_r = 2.05; // For 3/32 (imperial) screws
holes_major_r = (block_w  - screw_hole_r)/2; // Radius of circle that defines
                                // location of screw holes around center
end_h = 3;
// A rounded square block that fits within {w}x{w} and centered on the origin.
// {r} is the rounding radius.
module block2d(w, r) {
    offset(r = r) square([w-2*r, w-2*r], center=true);
}

// A set of 4 holes positioned on a circle of radius {major_r}.
// Each hole has radius {r}
module holes2d(major_r, r) {
    hole_centers = poly_coords(4, major_r);
    rotate([0, 0, 45]) for (i = [0:3]) {
                translate(hole_centers[i])
                    circle(r);
            }
}

// The center piece of the nut block. This holds the hex nut
// of dia {hex_dia}  and height (thickness of nut) {h}.
// Center is the origin. {hex_dia} is the distance between opposite *corners*
// of the hexagon.
module center_piece(hex_dia, h) {
    linear_extrude(height = h) {
        difference() {
            block2d(block_w, block_ro);
            regular_polygon(6, hex_dia/2);
            holes2d(holes_major_r, screw_hole_r);
        }
    }
}

// Creates an end piece, which matches the center_piece, but with
// an additional rounded triangular piece to hold an anchor screw.
module end_piece() {
    linear_extrude(height = end_h) {
        center_hole_r = 5;
        anchor_offset = 5; // from top of block
        anchor_hole_r = screw_hole_r;
        anchor_wall = 5; // min wall surrounding anchor - this needs to be enough to
                         // take half of the total load on the anchor
        anchor_center = [0, block_w/2 + anchor_offset];
        difference() {
                 //linear_extrude(height = block_h + 2*EPSILON)
            hull() {
                block2d(block_w, block_ro);
                translate(anchor_center)
                    circle(anchor_wall + anchor_hole_r);
            }
            holes2d(holes_major_r, screw_hole_r);
            circle(center_hole_r);
            translate(anchor_center)
                circle(anchor_hole_r);

        }
    }
}

$fn = 50;

// Brass right-handed-thread (RHT) nut dimensions:
//  side 14.12 (measured) == corner 16.304
//      (using https://rechneronline.de/pi/hexagon.php)
//  nut thickness (measured): 8.52
RHT_hex_dia = 16.304 + 0.4; // 0.4 for clearance
center_h = 8.52 + 0.4; // 0.4 for clearance

// Steel left-hand-thread (LHT) nut dimensions:
// side: 14.17 measured) == corner 16.362
// nut thickness: 8.4
center_piece(RHT_hex_dia, center_h);
translate([block_w + 10, 0, 0]) end_piece();
// translate([0, block_w + 10, 0]) end_piece();

// Housings to hold hexagonal nut for a "nut drive": two nuts with
// reverse threads on a threaded rod.
// All dimensions in mm.
use <../common/extrusions.scad>
EPSILON = 0.01;
LARGE = 100;

function poly_coords(order, r=1)  =
    let(angles=[ for (i = [0:order-1]) i*(360/order) ])
 	[ for (th=angles) [r*cos(th), r*sin(th)] ];

 module regular_polygon(order, r=1){
 	polygon(poly_coords(order, r));
 }

block_w = 30; // Width of block - it's a (rounded) square block
block_ro = 3; // outer radii of rounded block
screw_hole_r = 2.05; // For 3/32 (imperial) screws
holes_major_r = (block_w  - screw_hole_r)/2; // Radius of circle that defines
                                // location of screw holes around center
end_h = 3;
// A rounded square block that fits within {w}x{w} and centered on the origin.
// {r} is the rounding radius.
module block2d(w, r) {
    offset(r = r) square([w-2*r, w-2*r], center=true);
}

// A set of 4 holes positioned on a circle of radius {major_r}.
// Each hole has radius {r}
module holes2d(major_r, r) {
    hole_centers = poly_coords(4, major_r);
    rotate([0, 0, 45]) for (i = [0:3]) {
                translate(hole_centers[i])
                    circle(r);
            }
}

// The center piece of the nut block. This holds the hex nut
// of dia {hex_dia}  and height (thickness of nut) {h}.
// Center is the origin. {hex_dia} is the distance between opposite *corners*
// of the hexagon.
module center_piece(hex_dia, h) {
    linear_extrude(height = h) {
        difference() {
            block2d(block_w, block_ro);
            regular_polygon(6, hex_dia/2);
            holes2d(holes_major_r, screw_hole_r);
        }
    }
}

// Creates an end piece, which matches the center_piece, but with
// an additional rounded triangular piece to hold an anchor screw.
module end_piece() {
    linear_extrude(height = end_h) {
        center_hole_r = 5;
        anchor_offset = 5; // from top of block
        anchor_hole_r = screw_hole_r;
        anchor_wall = 5; // min wall surrounding anchor - this needs to be enough to
                         // take half of the total load on the anchor
        anchor_center = [0, block_w/2 + anchor_offset];
        difference() {
                 //linear_extrude(height = block_h + 2*EPSILON)
            hull() {
                block2d(block_w, block_ro);
                translate(anchor_center)
                    circle(anchor_wall + anchor_hole_r);
            }
            holes2d(holes_major_r, screw_hole_r);
            circle(center_hole_r);
            translate(anchor_center)
                circle(anchor_hole_r);

        }
    }
}

$fn = 50;

// Brass right-handed-thread (RHT) nut dimensions:
//  side 14.12 (measured) == corner 16.304
//      (using https://rechneronline.de/pi/hexagon.php)
//  nut thickness (measured): 8.52
RHT_hex_dia = 16.304 + 0.4; // 0.4 for clearance
center_h = 8.52 + 0.4; // 0.4 for clearance

center_piece(RHT_hex_dia, center_h);
translate([block_w + 10, 0, 0]) end_piece();
translate([0, block_w + 10, 0]) end_piece();


main: use bearing orient param

main: use bearing orient param

include <thing_libutils/metric-thread.scad>;
include <thing_libutils/metric-hexnut.scad>;
use <thing_libutils/shapes.scad>;
use <thing_libutils/misc.scad>;
use <thing_libutils/attach.scad>;
use <thing_libutils/triangles.scad>
use <thing_libutils/linear-extrusion.scad>;

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

include <config.scad>
include <extruder-direct.scad>

axis_pos_x = -225/2*mm;
axis_range_z=[85,380];
axis_pos_z = axis_range_z[1]*mm/2;

main();

module main()
{
    color(extrusion_color)
    gantry_lower();

    color(extrusion_color)
    translate([0,0,-main_lower_dist_z])
    gantry_lower();

    color(extrusion_color)
    for(i=[-1,1])
    translate([0,i*(main_upper_dist_y/2),0])
    gantry_upper();

    // x axis
    translate([0,0,axis_pos_z])
    {
        translate([axis_pos_x,0,0])
        {
            xaxis_bearing_dist = 10;
            xcarriage_padding=10;
            xcarriage_width = xaxis_bearing[2]*2 + xaxis_bearing_dist + xcarriage_padding;
            // x carriage
            translate([0,xaxis_zaxis_distance_y,0])
            translate([0,-xaxis_bearing[1]/2,0])
            cubea([xcarriage_width, 5, xaxis_rod_distance+xaxis_bearing[1]+xcarriage_padding], align=[0,-1,0]);

            attach([[-motor_offset_x-motorWidth(extruder_motor)/2, 10, -17], [0,0,0]], extruder_conn_xcarriage)
            /*#cubea([10,10,10]);*/
                extruder();

            // x carriage bearings
            for(j=[-1,1])
            translate([j*(xaxis_bearing_dist+xaxis_bearing[2])/2,xaxis_zaxis_distance_y,(xaxis_rod_distance/2)])
                bearing(xaxis_bearing, orient=[1,0,0]);

            translate([0, xaxis_zaxis_distance_y, -(xaxis_rod_distance/2)])
                bearing(xaxis_bearing, orient=[1,0,0]);
        }

        // x smooth rods
        for(i=[-1,1])
            translate([0,xaxis_zaxis_distance_y,i*(xaxis_rod_distance/2)])
                fncylindera(h=xaxis_rod_l,d=xaxis_rod_d, orient=[1,0,0]);
    }

    // y smooth rods
    translate([0,0,yaxis_bearing[0]/2])
    {
        for(i=[-1,1])
        translate([i*(yaxis_rod_distance/2), 0, 0])
        {
            fncylindera(h=yaxis_rod_l,d=yaxis_rod_d, orient=[0,1,0]);

            for(j=[-1,1])
                translate([0,j*(yaxis_rod_distance/2),0])
                    bearing(yaxis_bearing, orient=[0,1,0]);
        }

        translate([0,0,yaxis_bearing[1]/2])
        {
            // y axis plate
            cubea(ycarriage_size, align=[0,0,1]);
        }
    }

    // z axis
    for(i=[-1,1])
    translate([i*(main_width/2 + lookup(NemaSideSize,zaxis_motor)/2), 0, 0])
    {
        translate([0,0,zaxis_motor_offset_z])
        mirror([i==-1?1:0,0,0])
        {
            zmotor_mount();

            attach([[0,0,0],[0,0,0]],zmotor_mount_conn_motor)
            {
                // z motor/leadscrews
                motor(zaxis_motor, NemaMedium, dualAxis=false, orientation=[0,180,0]);
            }
        }

        // z smooth rods
        translate([i*(zaxis_rod_screw_distance_x+zmotor_mount_motor_offset),0,0])
        {
            translate([0,0,zaxis_motor_offset_z])
                fncylindera(h=zaxis_rod_l,d=zaxis_rod_d,align=[0,0,1]);

            for(j=[-1,1])
                translate([0,0,axis_pos_z-j*xaxis_rod_distance/2])
                    bearing(zaxis_bearing);
        }

        translate([i*zmotor_mount_motor_offset, 0, axis_pos_z-xaxis_rod_distance/2-10])
        {
            difference()
            {
                fncylindera(h=10, d=zaxis_nut[1], align=[0,0,0]);
                union()
                {
                    fncylindera(h=10+1, d=zaxis_nut[0], center= false);
                    translate([0, 13.5, -1]) fncylindera(h=5, r=1.6);
                    translate([0, -13.5, -1]) fncylindera(h=5, r=1.6);
                }
            }
        }
    }
}

zmotor_mount_motor_offset=5;
zmotor_mount_conn_motor=[[-zmotor_mount_motor_offset, 0, 0],[0,1,0]];

module zmotor_mount()
{
    motor_w = lookup(NemaSideSize,zaxis_motor);

    mount_thickness = 5;
    mount_thickness_h = 8;
    mount_thread_dia = lookup(ThreadSize, extrusion_thread);
    mount_width = motor_w+mount_thickness*2 + mount_thread_dia*8;
    mount_h = main_lower_dist_z+extrusion_size+zaxis_motor_offset_z;

    // side triangles
    translate([-motor_w/2, 0, 0])
    for(i=[-1,1])
    {
        translate([mount_thickness, i*((motor_w/2)+mount_thickness/2), 0])
        rotate([90,90,0])
        Right_Angled_Triangle(a=motor_w-mount_thickness+zmotor_mount_motor_offset, b=main_lower_dist_z+extrusion_size+zaxis_motor_offset_z, height=mount_thickness, centerXYZ=[0,0,1]);

        translate([0, i*((motor_w/2)+mount_thickness/2), 0])
        cubea([mount_thickness, mount_thickness, mount_h], align=[1,0,-1]);
    }

    // top mount plate
    difference()
    {
        translate([-motor_w/2,0,-extrusion_size-zaxis_motor_offset_z])
            cubea([mount_thickness, mount_width, extrusion_size], align=[1,0,1]);

        for(i=[-1,1])
            translate([-motor_w/2,i*(motor_w/2+mount_thread_dia*3),-extrusion_size])
                fncylindera(h=mount_thickness*3,d=mount_thread_dia, orient=[1,0,0]);
    }

    // bottom mount plate
    difference()
    {
        translate([-motor_w/2,0,-main_lower_dist_z-extrusion_size-zaxis_motor_offset_z])
            cubea([mount_thickness, mount_width, extrusion_size], align=[1,0,1]);

        for(i=[-1,1])
            translate([-motor_w/2,i*(motor_w/2+mount_thread_dia*3),-extrusion_size-main_lower_dist_z])
                fncylindera(h=mount_thickness*3,d=mount_thread_dia,align=[0,0,0], orient=[1,0,0]);
    }

/*#    cubea([motor_w+mount_thickness,motor_w+mount_thickness,zaxis_motor_offset_z], align=[0,0,-1]);*/

    // top plate
    difference()
    {
        cubea([motor_w, motor_w+mount_thickness*2, mount_thickness_h], align=[0,0,1], extrasize=[5,0,0], extrasize_align=[1,0,0]);

        translate([zmotor_mount_motor_offset,0,-1])
        linear_extrude(mount_thickness_h+2)
        stepper_motor_mount(17, slide_distance=0, mochup=false);
    }
}

module gantry_upper()
{
    for(i=[-1,1])
    translate([i*(main_width/2), 0])
    linear_extrusion(h=main_height, align=[-i,0,1], orient=[0,0,1]);

    translate([0, 0, main_height])
    linear_extrusion(h=main_upper_width, align=[0,0,1], orient=[1,0,0]);
}

module gantry_lower()
{
    for(i=[-1,1])
    translate([0,  i*(main_depth/2), 0])
    linear_extrusion(h=main_width, align=[0,i,-1], orient=[1,0,0]);

    for(i=[-1,1])
    translate([i*(main_width/2), 0, 0])
    linear_extrusion(h=main_depth, align=[-i,0,-1], orient=[0,1,0]);
}

include <thing_libutils/metric-thread.scad>;
include <thing_libutils/metric-hexnut.scad>;
use <thing_libutils/shapes.scad>;
use <thing_libutils/misc.scad>;
use <thing_libutils/attach.scad>;
use <thing_libutils/triangles.scad>
use <thing_libutils/linear-extrusion.scad>;

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

include <config.scad>
include <extruder-direct.scad>

axis_pos_x = -225/2*mm;
axis_range_z=[85,380];
axis_pos_z = axis_range_z[1]*mm/2;

main();

module main()
{
    color(extrusion_color)
    gantry_lower();

    color(extrusion_color)
    translate([0,0,-main_lower_dist_z])
    gantry_lower();

    color(extrusion_color)
    for(i=[-1,1])
    translate([0,i*(main_upper_dist_y/2),0])
    gantry_upper();

    // x axis
    translate([0,0,axis_pos_z])
    {
        translate([axis_pos_x,0,0])
        {
            xaxis_bearing_dist = 10;
            xcarriage_padding=10;
            xcarriage_width = xaxis_bearing[2]*2 + xaxis_bearing_dist + xcarriage_padding;
            // x carriage
            translate([0,xaxis_zaxis_distance_y,0])
            translate([0,-xaxis_bearing[1]/2,0])
            cubea([xcarriage_width, 5, xaxis_rod_distance+xaxis_bearing[1]+xcarriage_padding], align=[0,-1,0]);

            attach([[-motor_offset_x-motorWidth(extruder_motor)/2, 10, -17], [0,0,0]], extruder_conn_xcarriage)
            /*#cubea([10,10,10]);*/
                extruder();

            // x carriage bearings
            for(j=[-1,1])
            translate([j*(xaxis_bearing_dist+xaxis_bearing[2])/2,xaxis_zaxis_distance_y,(xaxis_rod_distance/2)])
            rotate([0,90,0])
                bearing(xaxis_bearing);

            translate([0, xaxis_zaxis_distance_y, -(xaxis_rod_distance/2)])
            rotate([0,90,0])
                bearing(xaxis_bearing);
        }

        // x smooth rods
        for(i=[-1,1])
            translate([0,xaxis_zaxis_distance_y,i*(xaxis_rod_distance/2)])
                fncylindera(h=xaxis_rod_l,d=xaxis_rod_d, orient=[1,0,0]);
    }

    // y smooth rods
    translate([0,0,yaxis_bearing[0]/2])
    {
        for(i=[-1,1])
        translate([i*(yaxis_rod_distance/2), 0, 0])
        {
            fncylindera(h=yaxis_rod_l,d=yaxis_rod_d, orient=[0,1,0]);

            for(j=[-1,1])
                translate([0,j*(yaxis_rod_distance/2),0])
                    rotate([90,0,0])
                    bearing(yaxis_bearing);
        }

        translate([0,0,yaxis_bearing[1]/2])
        {
            // y axis plate
            cubea(ycarriage_size, align=[0,0,1]);
        }
    }

    // z axis
    for(i=[-1,1])
    translate([i*(main_width/2 + lookup(NemaSideSize,zaxis_motor)/2), 0, 0])
    {
        translate([0,0,zaxis_motor_offset_z])
        mirror([i==-1?1:0,0,0])
        {
            zmotor_mount();

            attach([[0,0,0],[0,0,0]],zmotor_mount_conn_motor)
            {
                // z motor/leadscrews
                motor(zaxis_motor, NemaMedium, dualAxis=false, orientation=[0,180,0]);
            }
        }

        // z smooth rods
        translate([i*(zaxis_rod_screw_distance_x+zmotor_mount_motor_offset),0,0])
        {
            translate([0,0,zaxis_motor_offset_z])
                fncylindera(h=zaxis_rod_l,d=zaxis_rod_d,align=[0,0,1]);

            for(j=[-1,1])
                translate([0,0,axis_pos_z-j*xaxis_rod_distance/2])
                    bearing(zaxis_bearing);
        }

        translate([i*zmotor_mount_motor_offset, 0, axis_pos_z-xaxis_rod_distance/2-10])
        {
            difference()
            {
                fncylindera(h=10, d=zaxis_nut[1], align=[0,0,0]);
                union()
                {
                    fncylindera(h=10+1, d=zaxis_nut[0], center= false);
                    translate([0, 13.5, -1]) fncylindera(h=5, r=1.6);
                    translate([0, -13.5, -1]) fncylindera(h=5, r=1.6);
                }
            }
        }
    }
}

zmotor_mount_motor_offset=5;
zmotor_mount_conn_motor=[[-zmotor_mount_motor_offset, 0, 0],[0,1,0]];

module zmotor_mount()
{
    motor_w = lookup(NemaSideSize,zaxis_motor);

    mount_thickness = 5;
    mount_thickness_h = 8;
    mount_thread_dia = lookup(ThreadSize, extrusion_thread);
    mount_width = motor_w+mount_thickness*2 + mount_thread_dia*8;
    mount_h = main_lower_dist_z+extrusion_size+zaxis_motor_offset_z;

    // side triangles
    translate([-motor_w/2, 0, 0])
    for(i=[-1,1])
    {
        translate([mount_thickness, i*((motor_w/2)+mount_thickness/2), 0])
        rotate([90,90,0])
        Right_Angled_Triangle(a=motor_w-mount_thickness+zmotor_mount_motor_offset, b=main_lower_dist_z+extrusion_size+zaxis_motor_offset_z, height=mount_thickness, centerXYZ=[0,0,1]);

        translate([0, i*((motor_w/2)+mount_thickness/2), 0])
        cubea([mount_thickness, mount_thickness, mount_h], align=[1,0,-1]);
    }

    // top mount plate
    difference()
    {
        translate([-motor_w/2,0,-extrusion_size-zaxis_motor_offset_z])
            cubea([mount_thickness, mount_width, extrusion_size], align=[1,0,1]);

        for(i=[-1,1])
            translate([-motor_w/2,i*(motor_w/2+mount_thread_dia*3),-extrusion_size])
                fncylindera(h=mount_thickness*3,d=mount_thread_dia, orient=[1,0,0]);
    }

    // bottom mount plate
    difference()
    {
        translate([-motor_w/2,0,-main_lower_dist_z-extrusion_size-zaxis_motor_offset_z])
            cubea([mount_thickness, mount_width, extrusion_size], align=[1,0,1]);

        for(i=[-1,1])
            translate([-motor_w/2,i*(motor_w/2+mount_thread_dia*3),-extrusion_size-main_lower_dist_z])
                fncylindera(h=mount_thickness*3,d=mount_thread_dia,align=[0,0,0], orient=[1,0,0]);
    }

/*#    cubea([motor_w+mount_thickness,motor_w+mount_thickness,zaxis_motor_offset_z], align=[0,0,-1]);*/

    // top plate
    difference()
    {
        cubea([motor_w, motor_w+mount_thickness*2, mount_thickness_h], align=[0,0,1], extrasize=[5,0,0], extrasize_align=[1,0,0]);

        translate([zmotor_mount_motor_offset,0,-1])
        linear_extrude(mount_thickness_h+2)
        stepper_motor_mount(17, slide_distance=0, mochup=false);
    }
}

module gantry_upper()
{
    for(i=[-1,1])
    translate([i*(main_width/2), 0])
    linear_extrusion(h=main_height, align=[-i,0,1], orient=[0,0,1]);

    translate([0, 0, main_height])
    linear_extrusion(h=main_upper_width, align=[0,0,1], orient=[1,0,0]);
}

module gantry_lower()
{
    for(i=[-1,1])
    translate([0,  i*(main_depth/2), 0])
    linear_extrusion(h=main_width, align=[0,i,-1], orient=[1,0,0]);

    for(i=[-1,1])
    translate([i*(main_width/2), 0, 0])
    linear_extrusion(h=main_depth, align=[-i,0,-1], orient=[0,1,0]);
}