x/ends: fix endstop screw offsets

x/ends: fix endstop screw offsets

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

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

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

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

function xaxis_end_width(with_motor) = with_motor? xaxis_end_motorsize+xaxis_end_motor_offset[0] - xaxis_end_motorsize/2 : zaxis_bearing[1]/2+zaxis_nut[1];

module xaxis_end_body(part, with_motor, beltpath_index=0, nut_top=false, with_xrod_adjustment=false)
{
    nut_h = zaxis_nut[4];
    bearing_sizey = zaxis_bearing[1] + 5*mm;

    xaxis_end_xrod_offset_z = xaxis_rod_l/2 - (main_width/2 + zmotor_mount_rod_offset_x);
    xaxis_rod_d_support = xaxis_rod_d+5*mm;
    xaxis_rod_l_support = xaxis_end_xrod_offset_z + 8*mm + (with_xrod_adjustment?8*mm:0);

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

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

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

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

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

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

        // support around z axis bearings
        translate([0, -xaxis_zaxis_distance_y, 0])
        cylindera(h=xaxis_end_wz,d=bearing_sizey, orient=[0,0,1], align=[0,0,0], round_radius=2);

        // belt idler screw cut support
        for(z=[-1,1])
        for(y=[-1,1])
        translate([xaxis_end_pulley_offset,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            translate([0,y*-xaxis_beltpath_width/2,0])
            cylindera(orient=YAXIS, d=15*mm, h=7*mm, align=-y*YAXIS);
        }
    }
    else if(part=="neg")
    {
        // cut support around z axis bearings
        translate([0, -xaxis_zaxis_distance_y, 0])
        translate([0,0,.1])
        cubea([bearing_sizey+.1, bearing_sizey+.2, xaxis_end_wz+.4], orient=[0,0,1], align=[0,-1,0]);

        // x smooth rods
        for(z=[-1,1])
        translate([0,0*mm,z*(xaxis_rod_distance/2)])
        {
            xoff = - xaxis_end_xrod_offset_z -(with_xrod_adjustment?6:0)*mm;
            translate([xoff,0,0])
            cylindera(h=abs(xoff)+xaxis_end_width(with_motor)+1,d=xaxis_rod_d+.5*mm, orient=[1,0,0], align=[1,0,0]);

            if(with_xrod_adjustment)
            {
                translate([-xaxis_rod_l_support+5*mm,0,0])
                nut_trap_cut(nut=NutHexM4, trap_axis=YAXIS, orient=-XAXIS, align=XAXIS);

                translate([-xaxis_rod_l_support-7*mm,0,0])
                cylindera(d=10, h=10, orient=XAXIS);
            }
        }
    }
    else if(part=="vit")
    {
    }
}

module xaxis_end(part, with_motor=false, stop_x_rods=true, beltpath_index=0, show_motor=false, nut_top=false, show_nut=false, show_rods=false, show_bearings=false, with_xrod_adjustment=false)
{
    nut_h = zaxis_nut[4];
    extrasize = with_motor?0*mm:0*mm;
    extrasize_align = 1;

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

            // projection against z, ensure easy print
            translate([0,0,-xaxis_end_wz/2])
                linear_extrude(1)
                projection(cut=false)
                xaxis_end_body(part=part, with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top, with_xrod_adjustment=with_xrod_adjustment);
        }
    }
    else if(part=="neg")
    {
        xaxis_end_body(part=part, with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top, with_xrod_adjustment=with_xrod_adjustment);

        // belt idler screw cut
        for(z=[-1,1])
        translate([xaxis_end_pulley_offset,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            translate([0, -xaxis_beltpath_width/2 - 7*mm, 0])
            {
                screw_cut(nut=NutHexM5, h=xaxis_beltpath_width+13*mm, with_nut=true, orient=[0,1,0], align=[0,1,0]);
            }
        }

        //endstop mount screw cuts
        translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
        translate(xaxis_endstop_screw_offset)
        for(y=[-1,1])
        translate([-5*mm,y*9.5*mm/2,xaxis_endstop_size[2]])
        {
            screw_cut(nut=NutHexM2_5, screw_l=6*mm, screw_l_extra=0*mm, embed_head=false, with_head=true, with_nut=false, orient=[0,0,-1], align=[0,0,-1]);
            /*nut_trap_cut(nut=NutHexM2_5, screw_l=6*mm, screw_l_extra=0*mm, trap_axis=[-1,0,0], orient=[0,0,1], align=[0,0,-1]);*/
        }

        xaxis_end_beltpath(height=xaxis_beltpath_height_body, width=xaxis_beltpath_width);

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

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

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

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

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

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

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

        }

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

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

                    for(i=[-1,1])
                    translate([i*zaxis_nut[5], 0, -zaxis_nut[3]])
                    screw_cut(thread=zaxis_nut[6], h=20*mm, with_nut=false, orient=ZAXIS, align=ZAXIS);
                }
            }
        }
    }
    else if(part=="vit")
    {
        xaxis_end_body(part=part, with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top, with_xrod_adjustment=with_xrod_adjustment);

        if(with_motor)
        {
            translate([0,0,xaxis_beltpath_z_offsets[beltpath_index]])
            translate(xaxis_end_motor_offset)
            {
                // 1mm due to motor offset
                translate([0,-1*mm,0])
                {
                    // 1mm between pulley and motor
                    translate([0,-1*mm,0])
                    pulley(xaxis_pulley, flip=false, orient=[0,1,0], align=[0,-1,0]);

                    if(show_motor)
                    {
                        motor(xaxis_motor, NemaMedium, dualAxis=false, orientation=[-90,0,0]);
                    }
                }
            }
        }

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

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

        // belt idler pulley screw
        for(z=[-1,1])
        translate([xaxis_end_pulley_offset,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            translate([0, -xaxis_beltpath_width/2 - 7*mm, 0])
            {
                screw(nut=NutHexM5, h=25*mm, with_nut=true, orient=[0,1,0], align=[0,1,0]);
            }
        }

        for(z=[-1,1])
        translate([xaxis_end_pulley_offset,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            pulley(xaxis_idler_pulley, orient=[0,1,0]);
        }

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

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

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

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

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

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

if(false)
{
    // x axis
    /*translate([0,0,axis_pos_z])*/
    {
        /*if(!$preview_mode)*/
        {
            zrod_offset = zmotor_mount_rod_offset_x;
            for(x=[-1,1])
            {
                z=xaxis_beltpath_z_offsets[x<0?0:x];
                xo = x>0?xaxis_end_pulley_offset:xaxis_end_motor_offset[0];
                xw = x<0?xaxis_end_pulley_offset:xaxis_end_motor_offset[0];
                translate([-main_width/2-xo/2, xaxis_zaxis_distance_y, z])
                rotate([90,0,0])
                color("black")
                belt_path(main_width+xw, 6, xaxis_pulley_inner_d, orient=[1,0,0], align=[1,0,0]);
            }
        }

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

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

        for(x=[-1,1])
        translate([x*(main_width/2), 0, 0])
        translate([x*(zmotor_mount_rod_offset_x),0,0])
        cylindera(h=zaxis_rod_l,d=zaxis_rod_d, orient=ZAXIS, align=[0,0,0]);

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

}

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

module part_x_end_right()
{
    x=1;
    translate([0,0,xaxis_end_wz/2])
    mirror([max(0,x),0,0])
    xaxis_end(with_motor=true, beltpath_index=max(0,x), with_xrod_adjustment=true);
}

module part_x_end_left()
{
    x=-1;
    translate([0,0,xaxis_end_wz/2])
    mirror([max(0,x),0,0])
    xaxis_end(with_motor=true, beltpath_index=max(0,x), with_xrod_adjustment=true);
}

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

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

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

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

function xaxis_end_width(with_motor) = with_motor? xaxis_end_motorsize+xaxis_end_motor_offset[0] - xaxis_end_motorsize/2 : zaxis_bearing[1]/2+zaxis_nut[1];

module xaxis_end_body(part, with_motor, beltpath_index=0, nut_top=false, with_xrod_adjustment=false)
{
    nut_h = zaxis_nut[4];
    bearing_sizey = zaxis_bearing[1] + 5*mm;

    xaxis_end_xrod_offset_z = xaxis_rod_l/2 - (main_width/2 + zmotor_mount_rod_offset_x);
    xaxis_rod_d_support = xaxis_rod_d+5*mm;
    xaxis_rod_l_support = xaxis_end_xrod_offset_z + 8*mm + (with_xrod_adjustment?8*mm:0);

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

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

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

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

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

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

        // support around z axis bearings
        translate([0, -xaxis_zaxis_distance_y, 0])
        cylindera(h=xaxis_end_wz,d=bearing_sizey, orient=[0,0,1], align=[0,0,0], round_radius=2);

        // belt idler screw cut support
        for(z=[-1,1])
        for(y=[-1,1])
        translate([xaxis_end_pulley_offset,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            translate([0,y*-xaxis_beltpath_width/2,0])
            cylindera(orient=YAXIS, d=15*mm, h=7*mm, align=-y*YAXIS);
        }
    }
    else if(part=="neg")
    {
        // cut support around z axis bearings
        translate([0, -xaxis_zaxis_distance_y, 0])
        translate([0,0,.1])
        cubea([bearing_sizey+.1, bearing_sizey+.2, xaxis_end_wz+.4], orient=[0,0,1], align=[0,-1,0]);

        // x smooth rods
        for(z=[-1,1])
        translate([0,0*mm,z*(xaxis_rod_distance/2)])
        {
            xoff = - xaxis_end_xrod_offset_z -(with_xrod_adjustment?6:0)*mm;
            translate([xoff,0,0])
            cylindera(h=abs(xoff)+xaxis_end_width(with_motor)+1,d=xaxis_rod_d+.5*mm, orient=[1,0,0], align=[1,0,0]);

            if(with_xrod_adjustment)
            {
                translate([-xaxis_rod_l_support+5*mm,0,0])
                nut_trap_cut(nut=NutHexM4, trap_axis=YAXIS, orient=-XAXIS, align=XAXIS);

                translate([-xaxis_rod_l_support-7*mm,0,0])
                cylindera(d=10, h=10, orient=XAXIS);
            }
        }
    }
    else if(part=="vit")
    {
    }
}

module xaxis_end(part, with_motor=false, stop_x_rods=true, beltpath_index=0, show_motor=false, nut_top=false, show_nut=false, show_rods=false, show_bearings=false, with_xrod_adjustment=false)
{
    nut_h = zaxis_nut[4];
    extrasize = with_motor?0*mm:0*mm;
    extrasize_align = 1;

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

            // projection against z, ensure easy print
            translate([0,0,-xaxis_end_wz/2])
                linear_extrude(1)
                projection(cut=false)
                xaxis_end_body(part=part, with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top, with_xrod_adjustment=with_xrod_adjustment);
        }
    }
    else if(part=="neg")
    {
        xaxis_end_body(part=part, with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top, with_xrod_adjustment=with_xrod_adjustment);

        // belt idler screw cut
        for(z=[-1,1])
        translate([xaxis_end_pulley_offset,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            translate([0, -xaxis_beltpath_width/2 - 7*mm, 0])
            {
                screw_cut(nut=NutHexM5, h=xaxis_beltpath_width+13*mm, with_nut=true, orient=[0,1,0], align=[0,1,0]);
            }
        }

        //endstop mount screw cuts
        translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
        translate(xaxis_endstop_screw_offset)
        for(y=[-1,1])
        translate([-5*mm,y*9.5*mm/2,xaxis_endstop_size[2]])
        {
            screw_cut(nut=NutHexM2_5, screw_l=6*mm, screw_l_extra=0*mm, embed_head=false, with_head=true, with_nut=false, orient=[0,0,-1], align=[0,0,-1]);
            /*nut_trap_cut(nut=NutHexM2_5, screw_l=6*mm, screw_l_extra=0*mm, trap_axis=[-1,0,0], orient=[0,0,1], align=[0,0,-1]);*/
        }

        xaxis_end_beltpath(height=xaxis_beltpath_height_body, width=xaxis_beltpath_width);

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

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

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

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

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

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

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

        }

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

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

                    for(i=[-1,1])
                    translate([i*zaxis_nut[5], 0, -zaxis_nut[3]])
                    screw_cut(thread=zaxis_nut[6], h=20*mm, with_nut=false, orient=ZAXIS, align=ZAXIS);
                }
            }
        }
    }
    else if(part=="vit")
    {
        xaxis_end_body(part=part, with_motor=with_motor, beltpath_index=beltpath_index, nut_top=nut_top, with_xrod_adjustment=with_xrod_adjustment);

        if(with_motor)
        {
            translate([0,0,xaxis_beltpath_z_offsets[beltpath_index]])
            translate(xaxis_end_motor_offset)
            {
                // 1mm due to motor offset
                translate([0,-1*mm,0])
                {
                    // 1mm between pulley and motor
                    translate([0,-1*mm,0])
                    pulley(xaxis_pulley, flip=false, orient=[0,1,0], align=[0,-1,0]);

                    if(show_motor)
                    {
                        motor(xaxis_motor, NemaMedium, dualAxis=false, orientation=[-90,0,0]);
                    }
                }
            }
        }

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

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

        // belt idler pulley screw
        for(z=[-1,1])
        translate([xaxis_end_pulley_offset,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            translate([0, -xaxis_beltpath_width/2 - 7*mm, 0])
            {
                screw(nut=NutHexM5, h=25*mm, with_nut=true, orient=[0,1,0], align=[0,1,0]);
            }
        }

        for(z=[-1,1])
        translate([xaxis_end_pulley_offset,0,-xaxis_beltpath_z_offsets[max(0,beltpath_index)]])
        {
            pulley(xaxis_idler_pulley, orient=[0,1,0]);
        }

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

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

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

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

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

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

if(false)
{
    // x axis
    /*translate([0,0,axis_pos_z])*/
    {
        /*if(!$preview_mode)*/
        {
            zrod_offset = zmotor_mount_rod_offset_x;
            for(x=[-1,1])
            {
                z=xaxis_beltpath_z_offsets[x<0?0:x];
                xo = x>0?xaxis_end_pulley_offset:xaxis_end_motor_offset[0];
                xw = x<0?xaxis_end_pulley_offset:xaxis_end_motor_offset[0];
                translate([-main_width/2-xo/2, xaxis_zaxis_distance_y, z])
                rotate([90,0,0])
                color("black")
                belt_path(main_width+xw, 6, xaxis_pulley_inner_d, orient=[1,0,0], align=[1,0,0]);
            }
        }

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

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

        for(x=[-1,1])
        translate([x*(main_width/2), 0, 0])
        translate([x*(zmotor_mount_rod_offset_x),0,0])
        cylindera(h=zaxis_rod_l,d=zaxis_rod_d, orient=ZAXIS, align=[0,0,0]);

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

}

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

module part_x_end_right()
{
    x=1;
    translate([0,0,xaxis_end_wz/2])
    mirror([max(0,x),0,0])
    xaxis_end(with_motor=true, beltpath_index=max(0,x), with_xrod_adjustment=true);
}

module part_x_end_left()
{
    x=-1;
    translate([0,0,xaxis_end_wz/2])
    mirror([max(0,x),0,0])
    xaxis_end(with_motor=true, beltpath_index=max(0,x), with_xrod_adjustment=true);
}


The rainmaker code was refactored to parameterize more arguments.


both elements on one drawing

both elements on one drawing

use <leftWall.scad>
use <rightWall.scad>

leftWall();
translate([0, -85, 0])
  rightWall();

error: pathspec 'navibot_sr8980_docking_station_walls/all.scad' did not match any file(s) known to git


Tweaks

Tweaks

//
// This is the iotsa board itself, including the important connectors and
// holes. You can define here if you have shortened the board, which will
// not only change the board itself, but also the box we are going to create around it.
//
numberOfRowsRemoved = 0;
extraMMRemoved = 0; // 0 if nothing removed, 2.2 if any rows removed.
numberOfMMRemoved = extraMMRemoved + (numberOfRowsRemoved*2.54);

boardThickness = 1.8; // The thickness of the PCB board
regulatorAngle = 35; // 0, or higher if you've bent the regulator over backwards.

esp12AntennaStickout = 5; // How many mm the esp12 antenna sticks out of the iotsa board

module iotsaBoard() {
    module esp12() {
        union() {
            cube([24, 16, 2]); // the esp-12 board
            translate([8, 1.5, 2]) cube([15, 13, 2.5]); // the metal cap
        }
    }
    module regulator() {
        rotate([-regulatorAngle, 0, 0])
        translate([0, 0, 5])
        union() {
            translate([0, 4.5, 0]) cube([10.2, 1.3, 15]);
            translate([0, 0, 0]) cube([10.2, 4.5, 8.5]);
        }
    }
    module powerjack() {
        difference() {
            cube([9, 15, 11]);
            translate([4.5, -1, 7]) rotate([-90, 0, 0]) cylinder(14, d=6);
        }
    }
    translate([0, 0, -1.8])
    union() {
        // The board
        difference() {
            cube([63-numberOfMMRemoved,43,1.8]);
            translate([2, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([2, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([36.5, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([36.5, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([60.5, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([60.5, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
        }
        // The components
        translate([-esp12AntennaStickout, 13.5, 1.8]) esp12();
        translate([20, 15, 1.8]) regulator();
        translate([22, -3, 1.8]) powerjack();
    }
}

//
// Box parameters. We are going to compute the size from the parameters.
//
iotsaComponentHeight = 14;  // Make sure this is high enough
iotsaSolderingHeight = 3;   // How far the soldering extends below the board.
boxThickness = 1.5;
boxBottomThickness = boxThickness;
boxFrontThickness = boxThickness;   // This is where the power connector is
boxLeftThickness = boxThickness;
boxRightThickness = boxThickness;
boxBackThickness = boxThickness;
boxTopThickness = boxThickness;

leeWay = 0.3;   // gap we want between board and box (on all sides, individually)

module box() {
    innerXSize = (63-numberOfMMRemoved) + esp12AntennaStickout + 2*leeWay;
    innerYSize = 43 + 2*leeWay;
    innerZSize = boardThickness + iotsaComponentHeight + iotsaSolderingHeight + 2*leeWay;

    outerXSize = innerXSize + boxLeftThickness + boxRightThickness;
    outerYSize = innerYSize + boxFrontThickness + boxBackThickness;
    outerZSize = innerZSize + boxBottomThickness + boxTopThickness;

    strutThickness = 2;
    retainerWidth = 3;
    retainerLength = 6;

    module basicBox() {
         difference() {
            cube([outerXSize, outerYSize, outerZSize-boxTopThickness]);
            translate([boxLeftThickness, boxFrontThickness, boxBottomThickness])
                cube([innerXSize, innerYSize, innerZSize+1]);
        }
    }
    module frontHoles() {
        // Holes in the front. X and Z relative to iotsa frontleft corner, Y should be 2*frontThickness.
        translate([22-leeWay, 0, 0-leeWay]) cube([9+2*leeWay, 2*boxFrontThickness, 11+2*leeWay]);
    }
    module rightHoles() {
    }
    module bottomHoles() {
        // Holes in the bottom. X and Y are relative to inner box frontleft corner, Z should be 2*bottomThickness
        translate([3, 3+strutThickness, 0]) cylinder(2*boxBottomThickness, d=3, $fn=12);
        translate([innerXSize-3, 3+strutThickness, 0]) cylinder(2*boxBottomThickness, d=3, $fn=12);
        translate([3, innerYSize-(3+strutThickness), 0]) cylinder(2*boxBottomThickness, d=3, $fn=12);
        translate([innerXSize-3, innerYSize-(3+strutThickness), 0]) cylinder(2*boxBottomThickness, d=3, $fn=12);
    }

    union() {
        difference() {
            translate([-esp12AntennaStickout-boxLeftThickness-leeWay, -boxFrontThickness-leeWay, -(boardThickness+iotsaSolderingHeight+boxBottomThickness+leeWay)])
                union() {
                    // The box itself, with the cutouts for the lid
                    difference() {
                        basicBox();
                        // Front cutout
                        translate([boxLeftThickness, -0.5*boxFrontThickness, outerZSize-boxTopThickness-boxBottomThickness])
                            cube([innerXSize, 2*boxFrontThickness, 2*boxTopThickness]);
                        // back cutout
                        translate([boxLeftThickness, innerYSize+boxFrontThickness-0.5*leeWay, outerZSize-boxTopThickness-boxBottomThickness])
                            cube([innerXSize, 0.5*boxBackThickness+leeWay, 0.5*boxTopThickness]);
                        // left retainer hole
                        translate([boxLeftThickness,-0.5*boxFrontThickness, outerZSize-boxTopThickness-boxBottomThickness-retainerLength])
                            cube([retainerWidth, 2*boxFrontThickness, retainerWidth]);
                        // right retainer hole
                        translate([outerXSize-boxRightThickness-retainerWidth, -0.5*boxFrontThickness, outerZSize-boxTopThickness-boxBottomThickness-retainerLength])
                            cube([retainerWidth, 2*boxFrontThickness, retainerWidth]);
                    }
                    // The front support for the iotsa board
                    cube([outerXSize, boxFrontThickness+strutThickness, boxBottomThickness+iotsaSolderingHeight]);
                    // The back support for the outsa board
                    translate([0, innerYSize+boxFrontThickness-strutThickness, 0])
                        cube([outerXSize, boxBackThickness+strutThickness, boxBottomThickness+iotsaSolderingHeight]);
                }
                translate([0, -1.5*boxFrontThickness-leeWay, 0]) frontHoles();
                * translate() rightHoles();
                translate([-esp12AntennaStickout, -leeWay, -(boardThickness+iotsaSolderingHeight+1.5*boxBottomThickness+leeWay)]) bottomHoles();
            }
        % iotsaBoard();
    }
}
box();

//
// This is the iotsa board itself, including the important connectors and
// holes. You can define here if you have shortened the board, which will
// not only change the board itself, but also the box we are going to create around it.
//
numberOfRowsRemoved = 0;
extraMMRemoved = 0; // 0 if nothing removed, 2.2 if any rows removed.
numberOfMMRemoved = extraMMRemoved + (numberOfRowsRemoved*2.54);

boardThickness = 1.8; // The thickness of the PCB board
regulatorAngle = 35; // 0, or higher if you've bent the regulator over backwards.

module iotsaBoard() {
    module esp12() {
        union() {
            cube([24, 16, 2]); // the esp-12 board
            translate([8, 1.5, 2]) cube([15, 13, 2.5]); // the metal cap
        }
    }
    module regulator() {
        rotate([-regulatorAngle, 0, 0])
        translate([0, 0, 5])
        union() {
            translate([0, 4.5, 0]) cube([10.2, 1.3, 15]);
            translate([0, 0, 0]) cube([10.2, 4.5, 8.5]);
        }
    }
    module powerjack() {
        difference() {
            cube([9, 15, 11]);
            translate([4.5, -1, 7]) rotate([-90, 0, 0]) cylinder(14, d=6);
        }
    }
    translate([0, 0, -1.8])
    union() {
        // The board
        difference() {
            cube([63-numberOfMMRemoved,43,1.8]);
            translate([2, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([2, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([36.5, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([36.5, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([60.5, 2, -0.1]) cylinder(2, d=2, center=false, $fn=12);
            translate([60.5, 41, -0.1]) cylinder(2, d=2, center=false, $fn=12);
        }
        // The components
        translate([-5, 13.5, 1.8]) esp12();
        translate([20, 15, 1.8]) regulator();
        translate([22, -3, 1.8]) powerjack();
    }
}

//
// Box parameters. We are going to compute the size from the parameters.
//
iotsaComponentHeight = 20;  // Make sure this is high enough
iotsaSolderingHeight = 3;   // How far the soldering extends below the board.
boxThickness = 3;
boxBottomThickness = boxThickness;
boxFrontThickness = boxThickness;   // This is where the power connector is
boxLeftThickness = boxThickness;
boxRightThickness = boxThickness;
boxBackThickness = boxThickness;
boxTopThickness = boxThickness;

leeWay = 0.3;   // gap we want between board and box (on all sides, individually)

module box() {
    innerXSize = (63-numberOfMMRemoved) + 5 + 2*leeWay; // Add 5 for how far the esp12 antenna extends
    innerYSize = 43 + 2*leeWay;
    innerZSize = boardThickness + iotsaComponentHeight + iotsaSolderingHeight + 2*leeWay;

    outerXSize = innerXSize + boxLeftThickness + boxRightThickness;
    outerYSize = innerYSize + boxFrontThickness + boxBackThickness;
    outerZSize = innerZSize + boxBottomThickness + boxTopThickness;

    strutThickness = 2;
    retainerWidth = 3;
    retainerLength = 6;

    module basicBox() {
         difference() {
            cube([outerXSize, outerYSize, outerZSize-boxTopThickness]);
            translate([boxLeftThickness, boxFrontThickness, boxBottomThickness])
                cube([innerXSize, innerYSize, innerZSize+1]);
        }
    }
    module frontHoles() {
        // Holes in the front. X and Z relative to iotsa frontleft corner, Y should be 2*frontThickness.
        translate([22-leeWay, 0, 0-leeWay]) cube([9+2*leeWay, 2*boxFrontThickness, 11+2*leeWay]);
    }
    union() {
        difference() {
            translate([-5-boxLeftThickness-leeWay, -boxFrontThickness-leeWay, -(boardThickness+iotsaSolderingHeight+boxBottomThickness+leeWay)])
                union() {
                    // The box itself, with the cutouts for the lid
                    difference() {
                        basicBox();
                        // Front cutout
                        translate([boxLeftThickness, -boxFrontThickness, outerZSize-boxTopThickness-boxBottomThickness])
                            cube([innerXSize, 2*boxFrontThickness, 2*boxTopThickness]);
                        // back cutout
                        translate([boxLeftThickness, innerYSize+boxFrontThickness-0.5*leeWay, outerZSize-boxTopThickness-boxBottomThickness])
                            cube([innerXSize, 0.5*boxBackThickness+leeWay, 0.5*boxTopThickness]);
                        // left retainer hole
                        # translate([boxLeftThickness,-0.5*boxFrontThickness, outerZSize-boxTopThickness-boxBottomThickness-retainerLength])
                            cube([retainerWidth, 2*boxFrontThickness, retainerWidth]);
                        // right retainer hole
                        # translate([outerXSize-boxRightThickness-retainerWidth, -0.5*boxFrontThickness, outerZSize-boxTopThickness-boxBottomThickness-retainerLength])
                            cube([retainerWidth, 2*boxFrontThickness, retainerWidth]);
                    }
                    // The front support for the iotsa board
                    cube([outerXSize, boxFrontThickness+strutThickness, boxBottomThickness+iotsaSolderingHeight]);
                    // The back support for the outsa board
                    translate([0, innerYSize, 0])
                        cube([outerXSize, boxFrontThickness+strutThickness, boxBottomThickness+iotsaSolderingHeight]);
                }
                translate([0, -1.5*boxFrontThickness-leeWay, 0]) frontHoles();
            }
        % iotsaBoard();
    }
}
box();


Fix for new camera enclosure size.

Fix for new camera enclosure size.

// fishbox.scad
//  - Enclosure design for zebrafish Skinner box
//
//  Author: Mark Liffiton
//  Date: Oct, 2014 - Jan, 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:
window_thickness = 2.4;  // 2.4mm = 0.094" (looks good based on cut test piece for acrylic slotting into itself)

// 1/8" Hardboard:
thickness = 3.1;
    // 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

// 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 = 190 + thickness; // z = 19cm to accomodate 16cm tank height plus light holder, plus cover
                            // (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;

// light bar
light_bar_width = 30;
light_pos_x = 50;
light_height = height-thickness*2-8;

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

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

// for slight offsets/tweaks
epsilon = 1;

// vars to control projection for DXF export (see makefile)
if (DXF_TOP) {
    projection() top_cover();
} 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_LIGHT_BAR) {
    projection() light_bar();
} else if (DXF_CAMERA_SUPPORT) {
    projection() rotate(a=[90,0,0]) camera_supports(justone=true);
} else if (DXF_RPI_SUPPORT) {
    projection() rotate(a=[90,0,0]) rpi_support();
}
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.
    side(y=0);
    side(y=depth);
    tank_base();
    light_bar();
    camera_supports(x=width, y=depth/2, z=height/2);
    rpi_support(x=width, y=rpi_ypos-10, z=height/2);
    mock_rpi(x=width, y=rpi_ypos, z=height/2);
    top_cover();
}

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

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
        camera_supports(x=width+thickness*2, y=depth/2, z=height/2+support_drop, round=false);
        rpi_support(x=width+thickness*2, y=rpi_ypos-10, z=height/2+support_drop, round=false);
        wire_opening();
        // 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, 3/4*height])
                rounded_truncation(up=true);
            translate([0, 0, 3/4*height])
                rounded_truncation(up=true);
        }
    }
}

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

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

module camera_opening() {
    translate([width, depth/2, height/2])
        cube([thickness*2,10,10], center=true);
}

module camera_supports(x, y, z, justone=false, round=true) {
    spacing = 20;
    out = 11;
    up = 36;
    hanging_support(x, y + spacing/2, z, out, up, round=round);
    if (!justone) {
        hanging_support(x, y - spacing/2, z, out, up, round=round);
    }
}

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=inner_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) {
    difference() {
        cube([x,y,z], center);

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

module top_cover() {
    color("Grey", alpha=0.5)
    translate([-outset,-outset,height-thickness])
    difference() {
        cube([width+outset*2,depth+outset*2,thickness]);
        cutouts(5,width-thickness,outset,rot=0,trans=[width/2+outset,0,0]);
        cutouts(5,width-thickness,outset,rot=180,trans=[width/2+outset,depth+outset*2,0]);
        cutouts(5,depth-thickness,outset,rot=-90,trans=[0,depth/2+outset,0]);
        cutouts(5,depth-thickness,outset,rot=90,trans=[width+outset*2,depth/2+outset,0]);
    }
}

module tank_base() {
    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]);
    }
}

module light_bar_opening() {
    // A wider opening, so the bar can slide out of the way of placing or removing the fish tank
    translate([light_pos_x-light_bar_width/4, -outset, light_height])
        cube([light_bar_width*2, depth+outset*2, thickness]);
}

module light_bar() {
    translate([light_pos_x-light_bar_width/2, -outset, light_height])
    difference() {
        cube([light_bar_width, depth+outset*2, thickness]);
        cutouts(2,light_bar_width,outset,rot=0,trans=[light_bar_width/2,0,0]);
        cutouts(2,light_bar_width,outset,rot=180,trans=[light_bar_width/2,depth+outset*2,0]);
    }
}

module cutouts(num, width, outset, rot, trans) {
    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, -thickness/2, -thickness/2])
            cube([w, outset+thickness, thickness*2]);
    }
}

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

module side(y=0) {
    difference() {
        side_base(y);
        scale([1,1,0.5])
            vert_face_base(0, window_thickness);
        scale([1,1,0.5])
            vert_face_base(width);
        light_bar_opening();
        top_cover();
    }
}

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

module mock_rpi(x, y, z) {
    color("Blue", alpha=0.5)
    translate([x+thickness, 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 - Jan, 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:
window_thickness = 2.4;  // 2.4mm = 0.094" (looks good based on cut test piece for acrylic slotting into itself)

// 1/8" Hardboard:
thickness = 3.1;
    // 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

// 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 = 190 + thickness; // z = 19cm to accomodate 16cm tank height plus light holder, plus cover
                            // (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;

// light bar
light_bar_width = 30;
light_pos_x = 50;
light_height = height-thickness*2-8;

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

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

// for slight offsets/tweaks
epsilon = 1;

// vars to control projection for DXF export (see makefile)
if (DXF_TOP) {
    projection() top_cover();
} 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_LIGHT_BAR) {
    projection() light_bar();
} else if (DXF_CAMERA_SUPPORT) {
    projection() rotate(a=[90,0,0]) camera_supports(justone=true);
} else if (DXF_RPI_SUPPORT) {
    projection() rotate(a=[90,0,0]) rpi_support();
}
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.
    side(y=0);
    side(y=depth);
    tank_base();
    light_bar();
    camera_supports(x=width, y=depth/2, z=height/2);
    rpi_support(x=width, y=rpi_ypos-10, z=height/2);
    mock_rpi(x=width, y=rpi_ypos, z=height/2);
    top_cover();
}

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

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
        camera_supports(x=width+thickness*2, y=depth/2, z=height/2+support_drop, round=false);
        rpi_support(x=width+thickness*2, y=rpi_ypos-10, z=height/2+support_drop, round=false);
        wire_opening();
        // 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, 3/4*height])
                rounded_truncation(up=true);
            translate([0, 0, 3/4*height])
                rounded_truncation(up=true);
        }
    }
}

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

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

module camera_opening() {
    translate([width, depth/2, height/2])
        cube([thickness*2,10,10], center=true);
}

module camera_supports(x, y, z, justone=false, round=true) {
    spacing = 20;
    out = 10;
    up = 36;
    hanging_support(x, y + spacing/2, z, out, up, round=round);
    if (!justone) {
        hanging_support(x, y - spacing/2, z, out, up, round=round);
    }
}

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=inner_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) {
    difference() {
        cube([x,y,z], center);

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

module top_cover() {
    color("Grey", alpha=0.5)
    translate([-outset,-outset,height-thickness])
    difference() {
        cube([width+outset*2,depth+outset*2,thickness]);
        cutouts(5,width-thickness,outset,rot=0,trans=[width/2+outset,0,0]);
        cutouts(5,width-thickness,outset,rot=180,trans=[width/2+outset,depth+outset*2,0]);
        cutouts(5,depth-thickness,outset,rot=-90,trans=[0,depth/2+outset,0]);
        cutouts(5,depth-thickness,outset,rot=90,trans=[width+outset*2,depth/2+outset,0]);
    }
}

module tank_base() {
    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]);
    }
}

module light_bar_opening() {
    // A wider opening, so the bar can slide out of the way of placing or removing the fish tank
    translate([light_pos_x-light_bar_width/4, -outset, light_height])
        cube([light_bar_width*2, depth+outset*2, thickness]);
}

module light_bar() {
    translate([light_pos_x-light_bar_width/2, -outset, light_height])
    difference() {
        cube([light_bar_width, depth+outset*2, thickness]);
        cutouts(2,light_bar_width,outset,rot=0,trans=[light_bar_width/2,0,0]);
        cutouts(2,light_bar_width,outset,rot=180,trans=[light_bar_width/2,depth+outset*2,0]);
    }
}

module cutouts(num, width, outset, rot, trans) {
    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, -thickness/2, -thickness/2])
            cube([w, outset+thickness, thickness*2]);
    }
}

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

module side(y=0) {
    difference() {
        side_base(y);
        scale([1,1,0.5])
            vert_face_base(0, window_thickness);
        scale([1,1,0.5])
            vert_face_base(width);
        light_bar_opening();
        top_cover();
    }
}

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

module mock_rpi(x, y, z) {
    color("Blue", alpha=0.5)
    translate([x+thickness, 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]);
                }
}


added parameter for N elements

added parameter for N elements

eps=0.01;
N = 4;

module support()
{
  rotate([90, 0, 0])
  {
    // main block
    intersection()
    {
      difference()
      {
        cube([30, 30, 10]);
        // magnet hole
        translate([5, -eps, 10/2])
          rotate([-90, 0, 0])
            cylinder(d=5+0.5, h=5+0.5+eps, $fn=25);
        // main rounding
        translate([30, 30, -eps])
          cylinder(r=30, h=10+2*eps, $fn=200);
      }
      // truncating sharp edges
      cube([25, 25, 10+2*eps]);
    }
    // support prolonging
    cube([1.2, 35, 10]);
  }
}

for(i=[0:N-1])
  translate([0, i*(10+3), 0])
    support();

eps=0.01;

module support()
{
  rotate([90, 0, 0])
  {
    // main block
    intersection()
    {
      difference()
      {
        cube([30, 30, 10]);
        // magnet hole
        translate([5, -eps, 10/2])
          rotate([-90, 0, 0])
            cylinder(d=5+0.5, h=5+0.5+eps, $fn=25);
        // main rounding
        translate([30, 30, -eps])
          cylinder(r=30, h=10+2*eps, $fn=200);
      }
      // truncating sharp edges
      cube([25, 25, 10+2*eps]);
    }
    // support prolonging
    cube([1.2, 35, 10]);
  }
}

support();


add stl

add stl

use <lid.scad>;
use <fillet.scad>;

debug = false;
no_fillet = true;

print = false;

$fa = 48; // 12 for printing
$fs = 8; // 2 for printing
$fn = 0;

render_outer = false;
render_full = false;
render_all = true;

// thickness of the shell
wall = 3;

// handle dimensions
handle_max_width = 35;
handle_min_width = 20;
handle_length = 75;

// used to avoid phantom wall when cutting objects
space = 1;

// dimensions of the 4 switches
switch_r = 4;
switch_offset = [7,7]; // relative to top and bottom corner

// dimensions of the led
led_r = 3;
led_offset = 2; // relative to bottom of the gimbal

// dimensions of the module bay
module_bay = [60,45,18];
cutout = [13,4];
cutout_offset = [3,3];
module_bay_lip = 3;

// gimbal dimensions
gimbal = [58,58,30];
gimbal_r = 25;

// dimensions of 4 gimbal screws per gimbal
gimbal_screw_offset = [5,5];
gimbal_screw_r = 1.5;
gimbal_space = [0,0];

// power switch dimension
switch_cutout = [10,20];

// angle of left and right side of controller
side_angle = 10;

// inclination of the handles
tilt_angle = 25;

// screw dimensions
nut_r = 2.1;
screw_head_r = 2;
screw_body_r = 1;
holder_r = 3.5;
separator = 4;

// trim switches
trim_switch_r = 6.5/2;

dims = [gimbal[0]*2+gimbal_space[0]*2,
        max(gimbal[1]+2*gimbal_space[1], module_bay[1]+2*wall+2*module_bay_lip),
        gimbal[2]];

// needs to be bigger than model
box_size = (max(dims[0],dims[1],dims[2]) + handle_length) * 10;

module module_bay_cutout() {
    cube([15,2*wall,16], center=true);
}

module module_bay(solid=false) {
    translate([0,0,-wall])
    difference() {
        union() {
            translate([0,0,module_bay[2]/2+wall/2])
                cube([module_bay[0]+2*wall, module_bay[1]+2*wall, module_bay[2]+wall], center=true);
            difference() {
                translate([0,0,(module_bay_lip+wall)/2+module_bay[2]])
                    cube([module_bay[0]+2*wall+2*module_bay_lip,
                        module_bay[1]+2*wall+2*module_bay_lip,
                        module_bay_lip+wall], center=true);
                if (!solid)
                    translate([0,0,module_bay[2]/2+wall])
                        cube([module_bay[0]+2*wall,
                              module_bay[1]+2*wall,
                              module_bay[2]+wall], center=true);
            }
        }

        if (!solid) {
            translate([0,0,module_bay[2]+module_bay_lip/2+wall])
                cube([module_bay[0]+2*module_bay_lip,
                      module_bay[1]+2*module_bay_lip,
                      module_bay_lip], center=true);

            translate([0,0,wall+module_bay[2]/2])
                cube([module_bay[0], module_bay[1], module_bay[2]], center=true);

            translate([module_bay[0]/2-cutout[0]-cutout_offset[0],
                       module_bay[1]/2-cutout[1]-cutout_offset[1],
                       -space])
                cube([cutout[0], cutout[1], wall+2*space]);

            translate([0,module_bay[1]/2+wall,wall+8]) module_bay_cutout();
            translate([0,-module_bay[1]/2-wall,wall+8]) module_bay_cutout();
        }
    }
}

//!module_bay();

module core(d = [10,10,10], offset = 0, solid=false) {
    scale_cylinder = [1,0.1,1];

    addition = sin(side_angle)*d[1];

    minkowski() {
        hull() {
            translate([0,-d[1]/2,0]) scale(scale_cylinder)
                cylinder(1,d[0]/2+addition,d[0]/2,center=true);
            translate([0,d[1]/2,0]) scale(scale_cylinder)
                cylinder(1,r=d[0]/2,center=true);
        }
        scale([0.6,0.6,1]) sphere(d[2]/2 - offset);
    }
}

module core_with_bay(solid=false, debug=false) {
    if (!debug && !no_fillet) {
        fillet(r=25,steps=20) {
            core(dims, solid=true);
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                module_bay(solid=true);
        }
    } else {
        union() {
            core(dims, solid=true);
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                module_bay(solid=true);
        }
    }
}

module handle(debug=false) {
    addition = (handle_length+dims[1])*sin(side_angle);
    tilt_vector = tilt_angle*[cos(side_angle),sin(side_angle),0];
    sf = 0.75;
    scale_vector = [1,1,sf];

    hull() {
        difference() {
            core_with_bay(debug=debug);
            translate([-handle_max_width,0,0])
                rotate([0,0,side_angle+5])
                    cube(dims[0],center=true);
            translate([0,11,0])
                rotate([0,0,side_angle+5])
                    cube([2*dims[0],dims[0],dims[0]],center=true);
        }
        scale(scale_vector)
        rotate(tilt_vector)
        translate([dims[0]/2+addition-handle_min_width/2,
                   -dims[1]/2-handle_length,
                   dims[2]/2-sf*handle_min_width/2])
            sphere(handle_min_width);
    }
}

module core_with_bay_and_handles(debug=false) {
    union() {
        core_with_bay(debug=debug);
        handle(debug=debug);
        mirror() handle(debug=debug);
    }
}

module skin(debug=false, thickness=5, box=box_size) {
    if (!debug) intersection() {
        children();
        minkowski() {
            cylinder(thickness,thickness,0,$fn=3,center=true);
            difference() {
                cube(box,center=true);
                children();
            }
        }
    }

    if (debug) children();
}

module cutouts() {

    h = 2*wall+2*space;

    // gimbal cutout
    translate([gimbal[0]/2,0,dims[2]/2])
        cylinder(h, r=gimbal_r, center=true);

    // gimbal screw cutout
    translate([gimbal_screw_offset[0],gimbal[1]/2-gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal[0] - gimbal_screw_offset[0],gimbal[1]/2-gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal_screw_offset[0],-gimbal[1]/2+gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal[0] - gimbal_screw_offset[0],-gimbal[1]/2+gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    // switches
    translate([-gimbal[0]+switch_offset[0], gimbal[1]/2, dims[2]/2-switch_offset[1]])
        rotate ([90,0,0])
            cylinder(gimbal[1]/2, r=switch_r, center=true);

    translate([-gimbal[0]+switch_offset[0], gimbal[1]/2, -dims[2]/2+switch_offset[1]])
        rotate ([90,0,0])
            cylinder(gimbal[1]/2, r=switch_r, center=true);

     // power switch
     translate([0,dims[1]/2+dims[2]/2,0])
        rotate([0,90,90])
            cube([switch_cutout[0], switch_cutout[1], gimbal[1]/2], center=true);

     // led
     translate([0,-gimbal[1]/2-led_offset-led_r,gimbal[2]/2])
        cylinder(h, r=led_r, center=true);

     // trim switches
     translate([-dims[0]/2+handle_min_width+trim_switch_r-5, -dims[1]/2-16, 0])
        rotate([0,45,-25])
            translate([0,0,dims[2]/2])
                cylinder(h=dims[2], r=trim_switch_r, center=true);
}

module screw_cylinder(h=30, solid=false) {
    spacing = 2;
    l = (h-separator)/2;
    union() {
        translate([0,0,separator/2+l/2+spacing/2]) difference() {
            cylinder(l, r=holder_r, center=true);
            if (!solid) cylinder(l, r=nut_r, $fn=6, center=true);
        }
        translate([0,0,separator/2+spacing/2]) difference() {
            cylinder(separator/2, r=holder_r, center=true);
            if (!solid) cylinder(separator/2, r=screw_body_r, center=true);
        }
        translate([0,0,-separator/2-spacing/2]) difference() {
            cylinder(separator/2, r=holder_r, center=true);
            if (!solid) cylinder(separator/2, r=screw_body_r, center=true);
        }
        translate([0,0,-separator/2-l/2-spacing/2]) difference() {
            cylinder(l, r=holder_r, center=true);
            if (!solid) cylinder(l, r=screw_head_r, center=true);
        }
    }
}

module screw__(solid=false) {
    addition = (handle_length+dims[1])*sin(side_angle);
    sf = 0.75;

    translate([dims[0]/2+addition-handle_min_width/2,
               -dims[1]/2-handle_length-handle_min_width+4,
               dims[2]/2-sf*handle_min_width/2-3])
        rotate([tilt_angle,0,0])
            translate([0,0,-handle_max_width-7])
                mirror([0,0,1])
                    screw_cylinder(2*handle_max_width, solid=solid);
}

module screw___(solid=false) {
    screw_offset = -3;

    translate([-module_bay[0]/2+screw_offset-holder_r,gimbal[1]/2+holder_r+1,0])
        mirror([0,0,1])
            screw_cylinder(2*(gimbal[2]+module_bay[2]+wall), solid=solid);
}

module screw_(solid=false, debug=false) {
    intersection() {
        union() {
            screw__(solid=solid);
            screw___(solid=solid);
        }
        core_with_bay_and_handles(debug=debug);
    }
}

//screw_cylinder();
module screws(solid=false, debug=false) {
    screw_(solid=solid, debug=debug);
    mirror() screw_(solid=solid, debug=debug);
}

module body(solid = false, w = wall) {
    difference() {
        union() {
            difference() {
                skin(debug=debug,thickness=w, box=box_size) {
                    core_with_bay_and_handles(debug=debug);
                }
                translate([0,0,-dims[2]/2]) rotate([0,180,90])
                    module_bay(true);
                screws(solid=true, debug=debug);
            }
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                    module_bay(solid);
            screws(solid=false, debug=debug);
        }

        if (!solid) cutouts();
        if (!solid) mirror() cutouts();
    }
}

module full_skin() render() {
    if (render_full || render_all) {
        body(w=wall);
    } else {
        import("full_skin.stl");
    }
}

module inner_skin() render() {
    difference() {
        full_skin();
        outer_skin();
    }
}

module outer_skin() render() {
    if (render_outer || render_all) {
        body(w=wall/2);
    } else {
        import("outer_skin.stl");
    }
}

module bottom(box=box_size) {
    union () {
        difference() {
            outer_skin();
            translate([0,0,box/2]) cube(box, center=true);
            translate([0,-gimbal[1]/2,0])
                rotate([tilt_angle,0,0])
                    translate([0,0,box/2])
                        cube(box, center=true);
        }

        difference() {
            full_skin();
            translate([0,0,box/2-wall/2]) cube(box, center=true);
            translate([0,-gimbal[1]/2,0])
                rotate([tilt_angle,0,0])
                    translate([0,0,box/2-wall/2])
                        cube(box, center=true);
        }
    }
}

module cut_in_halves() {
    union() {
        translate([-100,0,gimbal[2]/2]) difference() {
            bottom(box=box_size);
            battery_compartments(solid=true);
        }

        translate([100,0,0]) difference() {
            full_skin();
            bottom(box=box_size);
        }
    }
}

module battery_compartments(solid=false) {
    union() {
        battery_compartment(solid);
        mirror() battery_compartment(solid);
    }
}

batt = [61,21,24];
batt_pos = [-dims[0]/2-2,-66,-16];
batt_rot = [90,26,-101];

module battery_compartment_(solid = false) {
    offset = solid ? wall : 0;
    union() {
        intersection() {
            outer_skin();
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                translate([0,0,offset/2])
                cube([batt[0]+wall,batt[1],batt[2]-offset],center=true);
        }

        intersection() {
            inner_skin();
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                translate([0,0,wall/4+offset/2])
                cube([batt[0],batt[1],batt[2]+wall/2-offset],center=true);
        }

        // cutout to open compartment
        if (solid) {
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                    union() {
                        translate([0,0,wall/2])
                            cube([batt[0],batt[1],batt[2]-wall],center=true);
                        translate([0,0,-batt[2]/2])
                            cube([batt[2]/2,batt[1],2*wall],center=true);
                    }
        }
    }
}

module battery_compartment(solid=false) {
    difference() {
        union() {
            difference() {
                battery_compartment_(solid);

                // cutout block to be able to unlatch
                if (!solid) {
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2+wall])
                            cube([batt[2],batt[1],wall],center=true);
                }
            }

            if (!solid) {
                scale([1.01,1.01,1.01])
                union() {
                    hull() {
                        intersection() {
                            battery_compartment_();
                            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                                rotate(batt_rot)
                                translate([0,0,-batt[2]/2+wall/4])
                                    cube([batt[2],batt[1],wall/2],center=true);
                        }

                        translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                            rotate(batt_rot)
                            translate([0,batt[1]/2-batt[1]/96,-batt[2]/2+wall/4+2*wall])
                                cube([batt[2],batt[1]/48,wall/2],center=true);
                    }

                    hull() {
                        intersection() {
                            battery_compartment_();
                            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                                rotate(batt_rot)
                                translate([0,0,-batt[2]/2+wall/4+2*wall])
                                    cube([batt[2],batt[1],wall/2],center=true);
                        }

                        translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                            rotate(batt_rot)
                            translate([0,batt[1]/2-batt[1]/96,-batt[2]/2+wall/4+2*wall])
                                cube([batt[2],batt[1]/48,wall/2],center=true);

                    }
                }
            }
        }

        if (!solid) {
            difference() {
                intersection() {
                    battery_compartment_();
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2])
                            cube([batt[2],batt[1],wall+2],center=true);
                }

                intersection() {
                    battery_compartment_();
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2])
                            cube([batt[2]/2,batt[1],wall+2],center=true);
                }
            }
        }
    }
}

if (render_outer) {
    outer_skin();
} else if (render_full) {
    full_skin();
} else {
    union() {
        cut_in_halves();
        translate([-100,dims[1]/2+2*module_bay[1],0]) rotate([0,180,90]) module_cover();
        translate([-100,-dims[1]/2-2*module_bay[1],0]) battery_compartments();
    }
}

use <lid.scad>;
use <fillet.scad>;

$fa = 12; //48
$fs = 2; //8
$fn = 0;

debug = false;

render_outer = false;
render_full = false;
render_all = true;

//$fa = 0.5; // default minimum facet angle is now 0.5
//$fs = 0.5; // default minimum facet size is now 0.5
//$fn = 50;

// thickness of the shell
wall = 3;

// handle dimensions
handle_max_width = 35;
handle_min_width = 20;
handle_length = 75;

// used to avoid phantom wall when cutting objects
space = 1;

// dimensions of the 4 switches
switch_r = 4;
switch_offset = [7,7]; // relative to top and bottom corner

// dimensions of the led
led_r = 3;
led_offset = 2; // relative to bottom of the gimbal

// dimensions of the module bay
module_bay = [60,45,18];
cutout = [13,4];
cutout_offset = [3,3];
module_bay_lip = 3;

// gimbal dimensions
gimbal = [58,58,30];
gimbal_r = 25;

// dimensions of 4 gimbal screws per gimbal
gimbal_screw_offset = [5,5];
gimbal_screw_r = 1.5;
gimbal_space = [0,0];

// power switch dimension
switch_cutout = [10,20];

// angle of left and right side of controller
side_angle = 10;

// inclination of the handles
tilt_angle = 25;

// screw dimensions
nut_r = 2.1;
screw_head_r = 2;
screw_body_r = 1;
holder_r = 3.5;
separator = 4;

// trim switches
trim_switch_r = 6.5/2;

dims = [gimbal[0]*2+gimbal_space[0]*2,
        max(gimbal[1]+2*gimbal_space[1], module_bay[1]+2*wall+2*module_bay_lip),
        gimbal[2]];

module module_bay_cutout() {
    cube([15,2*wall,16], center=true);
}

module module_bay(solid=false) {
    translate([0,0,-wall])
    difference() {
        union() {
            translate([0,0,module_bay[2]/2+wall/2])
                cube([module_bay[0]+2*wall, module_bay[1]+2*wall, module_bay[2]+wall], center=true);
            difference() {
                translate([0,0,(module_bay_lip+wall)/2+module_bay[2]])
                    cube([module_bay[0]+2*wall+2*module_bay_lip,
                        module_bay[1]+2*wall+2*module_bay_lip,
                        module_bay_lip+wall], center=true);
                if (!solid)
                    translate([0,0,module_bay[2]/2+wall])
                        cube([module_bay[0]+2*wall,
                              module_bay[1]+2*wall,
                              module_bay[2]+wall], center=true);
            }
        }

        if (!solid) {
            translate([0,0,module_bay[2]+module_bay_lip/2+wall])
                cube([module_bay[0]+2*module_bay_lip,
                      module_bay[1]+2*module_bay_lip,
                      module_bay_lip], center=true);

            translate([0,0,wall+module_bay[2]/2])
                cube([module_bay[0], module_bay[1], module_bay[2]], center=true);

            translate([module_bay[0]/2-cutout[0]-cutout_offset[0],
                       module_bay[1]/2-cutout[1]-cutout_offset[1],
                       -space])
                cube([cutout[0], cutout[1], wall+2*space]);

            translate([0,module_bay[1]/2+wall,wall+8]) module_bay_cutout();
            translate([0,-module_bay[1]/2-wall,wall+8]) module_bay_cutout();
        }
    }
}

//!module_bay();

module core(d = [10,10,10], offset = 0, solid=false) {
    scale_cylinder = [1,0.1,1];

    addition = sin(side_angle)*d[1];

    minkowski() {
        hull() {
            translate([0,-d[1]/2,0]) scale(scale_cylinder)
                cylinder(1,d[0]/2+addition,d[0]/2,center=true);
            translate([0,d[1]/2,0]) scale(scale_cylinder)
                cylinder(1,r=d[0]/2,center=true);
        }
        scale([0.6,0.6,1]) sphere(d[2]/2 - offset);
    }
}

module core_with_bay(solid=false, debug=false) {
    if (!debug) {
        fillet(r=25,steps=20) {
            core(dims, solid=true);
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                module_bay(solid=true);
        }
    } else {
        union() {
            core(dims, solid=true);
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                module_bay(solid=true);
        }
    }
}

module handle(debug=false) {
    addition = (handle_length+dims[1])*sin(side_angle);
    tilt_vector = tilt_angle*[cos(side_angle),sin(side_angle),0];
    sf = 0.75;
    scale_vector = [1,1,sf];

    hull() {
        difference() {
            core_with_bay(debug=debug);
            translate([-handle_max_width,0,0])
                rotate([0,0,side_angle+5])
                    cube(dims[0],center=true);
            translate([0,11,0])
                rotate([0,0,side_angle+5])
                    cube([2*dims[0],dims[0],dims[0]],center=true);
        }
        scale(scale_vector)
        rotate(tilt_vector)
        translate([dims[0]/2+addition-handle_min_width/2,
                   -dims[1]/2-handle_length,
                   dims[2]/2-sf*handle_min_width/2])
            sphere(handle_min_width);
    }
}

module core_with_bay_and_handles(debug=false) {
    union() {
        core_with_bay(debug=debug);
        handle(debug=debug);
        mirror() handle(debug=debug);
    }
}

module skin(debug=false, thickness=5, box=400) {
    if (!debug) intersection() {
        children();
        minkowski() {
            cylinder(thickness,thickness,0,$fn=3,center=true);
            difference() {
                cube(box,center=true);
                children();
            }
        }
    }

    if (debug) children();
}

module core_skin(debug=false, w=wall, box=400) {
    if (!debug) intersection() {
        core_with_bay_and_handles();
        minkowski() {
            cylinder(w,w,0,$fn=3,center=true);
            difference() {
                cube(box,center=true);
                core_with_bay_and_handles();
            }
        }
    }

    if (debug) core_with_bay_and_handles(debug=true);
}

module cutouts() {

    h = 2*wall+2*space;

    // gimbal cutout
    translate([gimbal[0]/2,0,dims[2]/2])
        cylinder(h, r=gimbal_r, center=true);

    // gimbal screw cutout
    translate([gimbal_screw_offset[0],gimbal[1]/2-gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal[0] - gimbal_screw_offset[0],gimbal[1]/2-gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal_screw_offset[0],-gimbal[1]/2+gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal[0] - gimbal_screw_offset[0],-gimbal[1]/2+gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    // switches
    translate([-gimbal[0]+switch_offset[0], gimbal[1]/2, dims[2]/2-switch_offset[1]])
        rotate ([90,0,0])
            cylinder(gimbal[1]/2, r=switch_r, center=true);

    translate([-gimbal[0]+switch_offset[0], gimbal[1]/2, -dims[2]/2+switch_offset[1]])
        rotate ([90,0,0])
            cylinder(gimbal[1]/2, r=switch_r, center=true);

     // power switch
     translate([0,dims[1]/2+dims[2]/2,0])
        rotate([0,90,90])
            cube([switch_cutout[0], switch_cutout[1], gimbal[1]/2], center=true);

     // led
     translate([0,-gimbal[1]/2-led_offset-led_r,gimbal[2]/2])
        cylinder(h, r=led_r, center=true);

     // trim switches
     translate([-dims[0]/2+handle_min_width+trim_switch_r-5, -dims[1]/2-16, 0])
        rotate([0,45,-25])
            translate([0,0,dims[2]/2])
                cylinder(h=dims[2], r=trim_switch_r, center=true);
}

module screw_cylinder(h=30, solid=false) {
    spacing = 2;
    l = (h-separator)/2;
    union() {
        translate([0,0,separator/2+l/2+spacing/2]) difference() {
            cylinder(l, r=holder_r, center=true);
            if (!solid) cylinder(l, r=nut_r, $fn=6, center=true);
        }
        translate([0,0,separator/2+spacing/2]) difference() {
            cylinder(separator/2, r=holder_r, center=true);
            if (!solid) cylinder(separator/2, r=screw_body_r, center=true);
        }
        translate([0,0,-separator/2-spacing/2]) difference() {
            cylinder(separator/2, r=holder_r, center=true);
            if (!solid) cylinder(separator/2, r=screw_body_r, center=true);
        }
        translate([0,0,-separator/2-l/2-spacing/2]) difference() {
            cylinder(l, r=holder_r, center=true);
            if (!solid) cylinder(l, r=screw_head_r, center=true);
        }
    }
}

module screw__(solid=false) {
    addition = (handle_length+dims[1])*sin(side_angle);
    sf = 0.75;

    translate([dims[0]/2+addition-handle_min_width/2,
               -dims[1]/2-handle_length-handle_min_width+4,
               dims[2]/2-sf*handle_min_width/2-3])
        rotate([tilt_angle,0,0])
            translate([0,0,-handle_max_width-7])
                mirror([0,0,1])
                    screw_cylinder(2*handle_max_width, solid=solid);
}

module screw___(solid=false) {
    screw_offset = -3;

    translate([-module_bay[0]/2+screw_offset-holder_r,gimbal[1]/2+holder_r+1,0])
        mirror([0,0,1])
            screw_cylinder(2*(gimbal[2]+module_bay[2]+wall), solid=solid);
}

module screw_(solid=false, debug=false) {
    intersection() {
        union() {
            screw__(solid=solid);
            screw___(solid=solid);
        }
        core_with_bay_and_handles(debug=debug);
    }
}

//screw_cylinder();
module screws(solid=false, debug=false) {
    screw_(solid=solid, debug=debug);
    mirror() screw_(solid=solid, debug=debug);
}

module body(solid = false, w = wall) {
    difference() {
        union() {
            difference() {
                skin(debug=debug,thickness=w, box=2*dims[0]) {
                    core_with_bay_and_handles(debug=debug);
                }
                translate([0,0,-dims[2]/2]) rotate([0,180,90])
                    module_bay(true);
                screws(solid=true, debug=debug);
            }
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                    module_bay(solid);
            screws(solid=false, debug=debug);
        }

        if (!solid) cutouts();
        if (!solid) mirror() cutouts();
    }
}

module full_skin() render() {
    if (render_full || render_all) {
        body(w=wall);
    } else {
        import("full_skin.stl");
    }
}

module inner_skin() render() {
    difference() {
        full_skin();
        outer_skin();
    }
}

module outer_skin() render() {
    if (render_outer || render_all) {
        body(w=wall/2);
    } else {
        import("outer_skin.stl");
    }
}

module bottom(box=400) {
    union () {
        difference() {
            outer_skin();
            translate([0,0,box/2]) cube(box, center=true);
            translate([0,-gimbal[1]/2,0])
                rotate([tilt_angle,0,0])
                    translate([0,0,box/2])
                        cube(box, center=true);
        }

        difference() {
            full_skin();
            translate([0,0,box/2-wall/2]) cube(box, center=true);
            translate([0,-gimbal[1]/2,0])
                rotate([tilt_angle,0,0])
                    translate([0,0,box/2-wall/2])
                        cube(box, center=true);
        }
    }
}

module cut_in_halves() {
    union() {
        translate([-100,0,gimbal[2]/2]) difference() {
            bottom(box=2*dims[0]);
            battery_compartments(solid=true);
        }

        translate([100,0,0]) difference() {
            full_skin();
            bottom(box=2*dims[0]);
        }
    }
}

module battery_compartments(solid=false) {
    union() {
        battery_compartment(solid);
        mirror() battery_compartment(solid);
    }
}

batt = [61,21,24];
batt_pos = [-dims[0]/2-2,-66,-16];
batt_rot = [90,26,-101];

module battery_compartment_(solid = false) {
    offset = solid ? wall : 0;
    union() {
        intersection() {
            outer_skin();
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                translate([0,0,offset/2])
                cube([batt[0]+wall,batt[1],batt[2]-offset],center=true);
        }

        intersection() {
            inner_skin();
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                translate([0,0,wall/4+offset/2])
                cube([batt[0],batt[1],batt[2]+wall/2-offset],center=true);
        }

        // cutout to open compartment
        if (solid) {
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                    union() {
                        translate([0,0,wall/2])
                            cube([batt[0],batt[1],batt[2]-wall],center=true);
                        translate([0,0,-batt[2]/2])
                            cube([batt[2]/2,batt[1],2*wall],center=true);
                    }
        }
    }
}

module battery_compartment(solid=false) {
    difference() {
        union() {
            difference() {
                battery_compartment_(solid);

                // cutout block to be able to unlatch
                if (!solid) {
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2+wall])
                            cube([batt[2],batt[1],wall],center=true);
                }
            }

            if (!solid) {
                scale([1.01,1.01,1.01])
                union() {
                    hull() {
                        intersection() {
                            battery_compartment_();
                            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                                rotate(batt_rot)
                                translate([0,0,-batt[2]/2+wall/4])
                                    cube([batt[2],batt[1],wall/2],center=true);
                        }

                        translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                            rotate(batt_rot)
                            translate([0,batt[1]/2-batt[1]/96,-batt[2]/2+wall/4+2*wall])
                                cube([batt[2],batt[1]/48,wall/2],center=true);
                    }

                    hull() {
                        intersection() {
                            battery_compartment_();
                            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                                rotate(batt_rot)
                                translate([0,0,-batt[2]/2+wall/4+2*wall])
                                    cube([batt[2],batt[1],wall/2],center=true);
                        }

                        translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                            rotate(batt_rot)
                            translate([0,batt[1]/2-batt[1]/96,-batt[2]/2+wall/4+2*wall])
                                cube([batt[2],batt[1]/48,wall/2],center=true);

                    }
                }
            }
        }

        if (!solid) {
            difference() {
                intersection() {
                    battery_compartment_();
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2])
                            cube([batt[2],batt[1],wall+2],center=true);
                }

                intersection() {
                    battery_compartment_();
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2])
                            cube([batt[2]/2,batt[1],wall+2],center=true);
                }
            }
        }
    }
}

if (render_outer) {
    outer_skin();
} else if (render_full) {
    full_skin();
} else {
    union() {
        cut_in_halves();
        translate([-100,dims[1]/2+2*module_bay[1],0]) rotate([0,180,90]) module_cover();
        translate([-100,-dims[1]/2-2*module_bay[1],0]) battery_compartments();
    }
}


CITOSINE

CITOSINE