x/ends: tweak motor screws inset (1mm)

x/ends: tweak motor screws inset (1mm)

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_switch = [10.3*mm, 20*mm, 6.3*mm];
xaxis_endstop_screw_offset_switch = [-2.45*mm, 0*mm, 0*mm];

xaxis_endstop_size_SN04 = [34.15*mm, 18.15*mm, 17.8*mm];
xaxis_endstop_screw_offset_SN04 = [-27.5*mm, 0*mm, 0*mm];
xaxis_endstop_offset_SN04 = [-3*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);

        /*if(xaxis_endstop_type == "SWITCH")*/
        {
            translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
            {
                rcubea(xaxis_endstop_size_switch, align=[-1,0,-1]);
            }
        }
        /*else if(xaxis_endstop_type == "SN04")*/
        {
            translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
            {
                translate(xaxis_endstop_offset_SN04)
                rcubea(xaxis_endstop_size_SN04, 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
        /*if(xaxis_endstop_type == "SWITCH")*/
        {
            translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
            translate(xaxis_endstop_screw_offset_switch)
            for(y=[-1,1])
            translate([-5*mm,y*9.5*mm/2,xaxis_endstop_size_switch[2]])
            {
                screw_cut(nut=NutHexM2_5, h=10*mm, embed_head=false, with_head=true, with_nut=false, orient=[0,0,-1], align=[0,0,-1]);
            }
        }
        /*else if(xaxis_endstop_type == "SN04")*/
        {
            translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
            translate(xaxis_endstop_screw_offset_SN04)
            for(y=[-1,1])
            translate([-0*mm,y*10.5*mm/2,xaxis_endstop_size_SN04[2]])
            {
                translate(xaxis_endstop_offset_SN04)
                screw_cut(nut=NutHexM3, h=20*mm, embed_head=false, with_head=true, with_nut=false, 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+3*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)
        {
            /*if(xaxis_endstop_type == "SWITCH")*/
            {
                translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
                {
                    difference()
                    {
                        rcubea(xaxis_endstop_size_switch, align=[-1,0,1]);

                        translate(xaxis_endstop_screw_offset_switch)
                        for(y=[-1,1])
                        translate([-5*mm,y*9.5*mm/2,xaxis_endstop_size_switch[2]])
                        screw(nut=NutHexM2_5, h=6*mm, orient=[0,0,-1], align=[0,0,-1]);
                    }
                }
            }
            /*else if(xaxis_endstop_type == "SN04")*/
            {
                translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
                {
                    translate(xaxis_endstop_offset_SN04)
                    translate([-36,-9,0])
                    rotate(ZAXIS*-90)
                    import("stl/SN04-N_Inductive_Proximity_Sensor_3528_0.stl");
                }
            }
        }

        // 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_switch = [10.3*mm, 20*mm, 6.3*mm];
xaxis_endstop_screw_offset_switch = [-2.45*mm, 0*mm, 0*mm];

xaxis_endstop_size_SN04 = [34.15*mm, 18.15*mm, 17.8*mm];
xaxis_endstop_screw_offset_SN04 = [-27.5*mm, 0*mm, 0*mm];
xaxis_endstop_offset_SN04 = [-3*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);

        /*if(xaxis_endstop_type == "SWITCH")*/
        {
            translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
            {
                rcubea(xaxis_endstop_size_switch, align=[-1,0,-1]);
            }
        }
        /*else if(xaxis_endstop_type == "SN04")*/
        {
            translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
            {
                translate(xaxis_endstop_offset_SN04)
                rcubea(xaxis_endstop_size_SN04, 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
        /*if(xaxis_endstop_type == "SWITCH")*/
        {
            translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
            translate(xaxis_endstop_screw_offset_switch)
            for(y=[-1,1])
            translate([-5*mm,y*9.5*mm/2,xaxis_endstop_size_switch[2]])
            {
                screw_cut(nut=NutHexM2_5, h=10*mm, embed_head=false, with_head=true, with_nut=false, orient=[0,0,-1], align=[0,0,-1]);
            }
        }
        /*else if(xaxis_endstop_type == "SN04")*/
        {
            translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
            translate(xaxis_endstop_screw_offset_SN04)
            for(y=[-1,1])
            translate([-0*mm,y*10.5*mm/2,xaxis_endstop_size_SN04[2]])
            {
                translate(xaxis_endstop_offset_SN04)
                screw_cut(nut=NutHexM3, h=20*mm, embed_head=false, with_head=true, with_nut=false, 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)
        {
            /*if(xaxis_endstop_type == "SWITCH")*/
            {
                translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
                {
                    difference()
                    {
                        rcubea(xaxis_endstop_size_switch, align=[-1,0,1]);

                        translate(xaxis_endstop_screw_offset_switch)
                        for(y=[-1,1])
                        translate([-5*mm,y*9.5*mm/2,xaxis_endstop_size_switch[2]])
                        screw(nut=NutHexM2_5, h=6*mm, orient=[0,0,-1], align=[0,0,-1]);
                    }
                }
            }
            /*else if(xaxis_endstop_type == "SN04")*/
            {
                translate([xaxis_end_width(with_motor),0,(xaxis_rod_distance/2)+xaxis_rod_d])
                {
                    translate(xaxis_endstop_offset_SN04)
                    translate([-36,-9,0])
                    rotate(ZAXIS*-90)
                    import("stl/SN04-N_Inductive_Proximity_Sensor_3528_0.stl");
                }
            }
        }

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


lcd2004: define part

lcd2004: define part

use <thing_libutils/linear-extrusion.scad>
use <thing_libutils/metric-screw.scad>
include <config.scad>

// wall thickness
lcd2004_wall_thick=4;
lcd2004_width=151+2*lcd2004_wall_thick;
lcd2004_depth=63.5+2*lcd2004_wall_thick;
mount_lcd2004_mount_angle=45;

module mount_lcd2004_parts(
    eps=[0.1,0.1,0], // gap for cavities
    part=undef,
    height=16*mm,
    align, orient, roll, extra_roll, extra_roll_orient
    )
{

    size_align(size=[lcd2004_width,lcd2004_depth,height], align=align, orient=orient, orient_ref=[0,0,1], roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    if(part==undef)
    {
        difference()
        {
            mount_lcd2004_parts(lcd2004_wall_thick=lcd2004_wall_thick, eps=eps, part="pos", height=height);
            mount_lcd2004_parts(lcd2004_wall_thick=lcd2004_wall_thick, eps=eps, part="neg", height=height);
        }
    }
    else if(part=="pos")
    {
        translate([-lcd2004_width/2,-lcd2004_depth/2,-height/2])
        rcubea(size=[lcd2004_width,lcd2004_depth,height], rounding_radius=3, align=[1,1,1]);
    }
    else if(part=="neg")
    mirror([0,1,0])
    translate([-lcd2004_width/2,-lcd2004_depth/2,-height/2])
    translate([lcd2004_wall_thick,lcd2004_wall_thick,0])
    {
        // LCD cutout
        translate([14.5,10,-2])
        cubea([97,40,19], align=[1,1,1]);

        // LCD PCB
        translate([14,-0.25,4])
        cubea([98.5,60.5,9+10], align=[1,1,1]);

        // Main PCB
        translate([0,7.75,8])
        cubea([151,56,5+22], align=[1,1,1]);

        // Switch axis
        translate([137,35,-2])
        cylindera(r=8/2, h=19, align=[0,0,1]);

        // buzzer
        translate([130+13.5/2,8+39+12.5/2,-2])
        cylindera(r=12.5/2, h=19, align=[0,0,1]);

        // reset switch
        translate([137,16,-2])
        cylindera(r=2, h=19, align=[0,0,1]);

        // buzzer/switch/reset
        translate([116,8,2])
        cubea([29,55.5,10], align=[1,1,1]);

        // buzzer/switch/reset
        translate([118,18,2])
        cubea([33,35.5,10], align=[1,1,1]);

        // buzzer/switch/reset
        translate([0,18,2])
        cubea([11,35.5,10], align=[1,1,1]);

        // SD slot
        translate([-lcd2004_wall_thick-1,17,9])
        cubea([lcd2004_wall_thick+2,27,10], align=[1,1,1]);

        // Screw hole for PCB
        translate([0+2.9,8+2.5,3])
        cylindera(r=2.5/2, h=6, align=[0,0,1]);

        // Screw hole for PCB
        translate([151-2.7,8+2.5,3])
        cylindera(r=2.5/2, h=6, align=[0,0,1]);

        // Screw hole for PCB
        translate([0+2.9,8+55.5-2.5,3])
        cylindera(r=2.5/2, h=6, align=[0,0,1]);

        // Screw hole for PCB
        translate([151-2.7,8+55.5-2.5,3])
        cylindera(r=2.5/2, h=6, align=[0,0,1]);

        // LCD pin header
        translate([14.5+8,60-5,2])
        cubea([42,8.5,9], align=[1,1,1]);

        // Anti-Warping
        translate([50,-lcd2004_wall_thick/2,2])
        cubea([2,55.5+8+lcd2004_wall_thick,11], align=[1,1,1]);

        // Anti-Warping
        translate([90,-lcd2004_wall_thick/2,2])
        cubea([2,55.5+8+lcd2004_wall_thick,11], align=[1,1,1]);
    }
}

module gantry_lower()
{
    color(color_extrusion)
    for(z=[-1,1])
    {
        translate([0,0,z*-main_lower_dist_z/2])
        {
            for(y=[-1,1])
            translate([0, y*(main_depth/2), 0])
            {
                if($preview_mode)
                {
                    cubea([main_width, extrusion_size, extrusion_size], align=[0,y,-1]);
                }
                else
                {
                    linear_extrusion(h=main_width, align=[0,y,-1], orient=[1,0,0]);
                }
            }

            for(x=[-1,1])
            translate([x*(main_width/2), 0, 0])
            {
                if($preview_mode)
                {
                    cubea([extrusion_size, main_depth, extrusion_size], align=[-x,0,-1]);
                }
                else
                {
                    linear_extrusion(h=main_depth, align=[-x,0,-1], orient=[0,1,0]);
                }
            }
        }
    }
}

lcd2004_mount_thickness = 5*mm;
module lcd2004_extrusion_conn(part, orient=[0,0,1], align=[0,0,0])
{
    s = [extrusion_size, extrusion_size, lcd2004_mount_thickness];
    size_align(size=s, orient=orient, orient_ref=[0,0,1], align=align)
    if(part==undef)
    {
        difference()
        {
            lcd2004_extrusion_conn(part="pos");
            lcd2004_extrusion_conn(part="neg");
        }
    }
    else if(part=="pos")
    {
        rcubea(size=s, rounding_radius=3);
    }
    else if(part=="neg")
    {
        translate([0,0,-lcd2004_mount_thickness/2])
        screw_cut(nut=extrusion_nut, with_nut=true, nut_cut_h=30, with_head=true, head_cut_h=30, length=12*mm, align=[0,0,1], orient=[0,0,1]);
    }
}

lcd2004_offset = [0, -34.2*mm,-5*mm];

module mount_lcd2004(part, show_gantry=false)
{
    if(part==undef)
    {
        difference()
        {
            mount_lcd2004(part="pos", show_gantry=show_gantry);
            mount_lcd2004(part="neg", show_gantry=show_gantry);
        }
    }
    else if(part == "pos")
    {
        hull()
        {
            for(x=[-1,1])
            translate([x*(lcd2004_width/2+extrusion_size),0,0])
            {
                translate([0,0,main_lower_dist_z/2-extrusion_size])
                lcd2004_extrusion_conn(part=part, orient=[0,0,1], align=[-x,0,-1]);
            }

            for(x=[-1,1])
            translate([x*(lcd2004_width/2+extrusion_size),0,0])
            {
                translate([0,0,-main_lower_dist_z/2])
                lcd2004_extrusion_conn(part=part, orient=[0,0,-1], align=[-x,0,1]);
            }

            translate(lcd2004_offset)
            rotate([-mount_lcd2004_mount_angle,0,0])
            mount_lcd2004_parts(part=part, orient=[0,1,0], align=[0,1,0]);

        }
        if(show_gantry)
        {
            translate([0,main_depth/2,0])
            translate([0,extrusion_size/2,0])
            gantry_lower();
        }

    }
    else if(part == "neg")
    {
        for(x=[-1,1])
        translate([x*(lcd2004_width/2+extrusion_size),0,0])
        {
            translate([0,0,main_lower_dist_z/2-extrusion_size])
            lcd2004_extrusion_conn(part=part, orient=[0,0,1], align=[-x,0,-1]);

            translate([0,0,-main_lower_dist_z/2])
            {
                lcd2004_extrusion_conn(part=part, orient=[0,0,-1], align=[-x,0,1]);
            }
        }

        translate([0,10*mm,-extrusion_size/2])
        /*#rcubea([lcd2004_width+30*mm, extrusion_size+.2, main_lower_dist_z-extrusion_size-lcd2004_mount_thickness*2]);*/
        rcubea([1000, extrusion_size+.2, main_lower_dist_z-extrusion_size-lcd2004_mount_thickness*2]);

        /*translate([0,0,-extrusion_size/2])*/
        /*rcubea([lcd2004_width, extrusion_size+.2, main_lower_dist_z-extrusion_size-lcd2004_mount_thickness*1]);*/

        translate(lcd2004_offset)
        rotate([-mount_lcd2004_mount_angle,0,0])
        {
            mount_lcd2004_parts(part=part, orient=[0,1,0], align=[0,1,0]);

            translate([0,1000+5,0])
            rotate([90,0,0])
            linear_extrude(height=1000)
            projection(cut=false)
            rotate([-90,0,0])
            mount_lcd2004_parts(part=part, orient=[0,1,0], align=[0,1,0]);
        }

        translate(lcd2004_offset)
        translate([0,27,-13])
        translate([0,1000,0])
        rotate([90,0,0])
        linear_extrude(height=1000)
        projection(cut=false)
        rotate([-90,0,0])
        mount_lcd2004_parts(part=part, orient=[0,1,0], align=[0,1,0]);

        translate([0,main_depth/2,0])
        translate([0,extrusion_size/2,0])
        gantry_lower();
    }

}

module part_lcd2004_mount()
{
    rotate([90+mount_lcd2004_mount_angle,0,0])
    translate(-lcd2004_offset)
    mount_lcd2004();
}

if(false)
{
    mount_lcd2004(show_gantry=true);
}

use <thing_libutils/linear-extrusion.scad>
use <thing_libutils/metric-screw.scad>
include <config.scad>

// wall thickness
lcd2004_wall_thick=4;
lcd2004_width=151+2*lcd2004_wall_thick;
lcd2004_depth=63.5+2*lcd2004_wall_thick;
mount_lcd2004_mount_angle=45;

module mount_lcd2004_parts(
    eps=[0.1,0.1,0], // gap for cavities
    part=undef,
    height=16*mm,
    align, orient, roll, extra_roll, extra_roll_orient
    )
{

    size_align(size=[lcd2004_width,lcd2004_depth,height], align=align, orient=orient, orient_ref=[0,0,1], roll=roll, extra_roll=extra_roll, extra_roll_orient=extra_roll_orient)
    if(part==undef)
    {
        difference()
        {
            mount_lcd2004_parts(lcd2004_wall_thick=lcd2004_wall_thick, eps=eps, part="pos", height=height);
            mount_lcd2004_parts(lcd2004_wall_thick=lcd2004_wall_thick, eps=eps, part="neg", height=height);
        }
    }
    else if(part=="pos")
    {
        translate([-lcd2004_width/2,-lcd2004_depth/2,-height/2])
        rcubea(size=[lcd2004_width,lcd2004_depth,height], rounding_radius=3, align=[1,1,1]);
    }
    else if(part=="neg")
    mirror([0,1,0])
    translate([-lcd2004_width/2,-lcd2004_depth/2,-height/2])
    translate([lcd2004_wall_thick,lcd2004_wall_thick,0])
    {
        // LCD cutout
        translate([14.5,10,-2])
        cubea([97,40,19], align=[1,1,1]);

        // LCD PCB
        translate([14,-0.25,4])
        cubea([98.5,60.5,9+10], align=[1,1,1]);

        // Main PCB
        translate([0,7.75,8])
        cubea([151,56,5+22], align=[1,1,1]);

        // Switch axis
        translate([137,35,-2])
        cylindera(r=8/2, h=19, align=[0,0,1]);

        // buzzer
        translate([130+13.5/2,8+39+12.5/2,-2])
        cylindera(r=12.5/2, h=19, align=[0,0,1]);

        // reset switch
        translate([137,16,-2])
        cylindera(r=2, h=19, align=[0,0,1]);

        // buzzer/switch/reset
        translate([116,8,2])
        cubea([29,55.5,10], align=[1,1,1]);

        // buzzer/switch/reset
        translate([118,18,2])
        cubea([33,35.5,10], align=[1,1,1]);

        // buzzer/switch/reset
        translate([0,18,2])
        cubea([11,35.5,10], align=[1,1,1]);

        // SD slot
        translate([-lcd2004_wall_thick-1,17,9])
        cubea([lcd2004_wall_thick+2,27,10], align=[1,1,1]);

        // Screw hole for PCB
        translate([0+2.9,8+2.5,3])
        cylindera(r=2.5/2, h=6, align=[0,0,1]);

        // Screw hole for PCB
        translate([151-2.7,8+2.5,3])
        cylindera(r=2.5/2, h=6, align=[0,0,1]);

        // Screw hole for PCB
        translate([0+2.9,8+55.5-2.5,3])
        cylindera(r=2.5/2, h=6, align=[0,0,1]);

        // Screw hole for PCB
        translate([151-2.7,8+55.5-2.5,3])
        cylindera(r=2.5/2, h=6, align=[0,0,1]);

        // LCD pin header
        translate([14.5+8,60-5,2])
        cubea([42,8.5,9], align=[1,1,1]);

        // Anti-Warping
        translate([50,-lcd2004_wall_thick/2,2])
        cubea([2,55.5+8+lcd2004_wall_thick,11], align=[1,1,1]);

        // Anti-Warping
        translate([90,-lcd2004_wall_thick/2,2])
        cubea([2,55.5+8+lcd2004_wall_thick,11], align=[1,1,1]);
    }
}

module gantry_lower()
{
    color(color_extrusion)
    for(z=[-1,1])
    {
        translate([0,0,z*-main_lower_dist_z/2])
        {
            for(y=[-1,1])
            translate([0, y*(main_depth/2), 0])
            {
                if($preview_mode)
                {
                    cubea([main_width, extrusion_size, extrusion_size], align=[0,y,-1]);
                }
                else
                {
                    linear_extrusion(h=main_width, align=[0,y,-1], orient=[1,0,0]);
                }
            }

            for(x=[-1,1])
            translate([x*(main_width/2), 0, 0])
            {
                if($preview_mode)
                {
                    cubea([extrusion_size, main_depth, extrusion_size], align=[-x,0,-1]);
                }
                else
                {
                    linear_extrusion(h=main_depth, align=[-x,0,-1], orient=[0,1,0]);
                }
            }
        }
    }
}

lcd2004_mount_thickness = 5*mm;
module lcd2004_extrusion_conn(part, orient=[0,0,1], align=[0,0,0])
{
    s = [extrusion_size, extrusion_size, lcd2004_mount_thickness];
    size_align(size=s, orient=orient, orient_ref=[0,0,1], align=align)
    if(part==undef)
    {
        difference()
        {
            lcd2004_extrusion_conn(part="pos");
            lcd2004_extrusion_conn(part="neg");
        }
    }
    else if(part=="pos")
    {
        rcubea(size=s, rounding_radius=3);
    }
    else if(part=="neg")
    {
        translate([0,0,-lcd2004_mount_thickness/2])
        screw_cut(nut=extrusion_nut, with_nut=true, nut_cut_h=30, with_head=true, head_cut_h=30, length=12*mm, align=[0,0,1], orient=[0,0,1]);
    }
}

lcd2004_offset = [0, -34.2*mm,-5*mm];

module mount_lcd2004(part, show_gantry=false)
{
    if(part==undef)
    {
        difference()
        {
            mount_lcd2004(part="pos", show_gantry=show_gantry);
            mount_lcd2004(part="neg", show_gantry=show_gantry);
        }
    }
    else if(part == "pos")
    {
        hull()
        {
            for(x=[-1,1])
            translate([x*(lcd2004_width/2+extrusion_size),0,0])
            {
                translate([0,0,main_lower_dist_z/2-extrusion_size])
                lcd2004_extrusion_conn(part=part, orient=[0,0,1], align=[-x,0,-1]);
            }

            for(x=[-1,1])
            translate([x*(lcd2004_width/2+extrusion_size),0,0])
            {
                translate([0,0,-main_lower_dist_z/2])
                lcd2004_extrusion_conn(part=part, orient=[0,0,-1], align=[-x,0,1]);
            }

            translate(lcd2004_offset)
            rotate([-mount_lcd2004_mount_angle,0,0])
            mount_lcd2004_parts(part=part, orient=[0,1,0], align=[0,1,0]);

        }
        if(show_gantry)
        {
            translate([0,main_depth/2,0])
            translate([0,extrusion_size/2,0])
            gantry_lower();
        }

    }
    else if(part == "neg")
    {
        for(x=[-1,1])
        translate([x*(lcd2004_width/2+extrusion_size),0,0])
        {
            translate([0,0,main_lower_dist_z/2-extrusion_size])
            lcd2004_extrusion_conn(part=part, orient=[0,0,1], align=[-x,0,-1]);

            translate([0,0,-main_lower_dist_z/2])
            {
                lcd2004_extrusion_conn(part=part, orient=[0,0,-1], align=[-x,0,1]);
            }
        }

        translate([0,10*mm,-extrusion_size/2])
        /*#rcubea([lcd2004_width+30*mm, extrusion_size+.2, main_lower_dist_z-extrusion_size-lcd2004_mount_thickness*2]);*/
        rcubea([1000, extrusion_size+.2, main_lower_dist_z-extrusion_size-lcd2004_mount_thickness*2]);

        /*translate([0,0,-extrusion_size/2])*/
        /*rcubea([lcd2004_width, extrusion_size+.2, main_lower_dist_z-extrusion_size-lcd2004_mount_thickness*1]);*/

        translate(lcd2004_offset)
        rotate([-mount_lcd2004_mount_angle,0,0])
        {
            mount_lcd2004_parts(part=part, orient=[0,1,0], align=[0,1,0]);

            translate([0,1000+5,0])
            rotate([90,0,0])
            linear_extrude(height=1000)
            projection(cut=false)
            rotate([-90,0,0])
            mount_lcd2004_parts(part=part, orient=[0,1,0], align=[0,1,0]);
        }

        translate(lcd2004_offset)
        translate([0,27,-13])
        translate([0,1000,0])
        rotate([90,0,0])
        linear_extrude(height=1000)
        projection(cut=false)
        rotate([-90,0,0])
        mount_lcd2004_parts(part=part, orient=[0,1,0], align=[0,1,0]);

        translate([0,main_depth/2,0])
        translate([0,extrusion_size/2,0])
        gantry_lower();
    }

}

/*$show_vit=true;*/

/*if(false)*/
{
    rotate([90+mount_lcd2004_mount_angle,0,0])
    translate(-lcd2004_offset)
    mount_lcd2004();
}

if(false)
{
    mount_lcd2004(show_gantry=true);
}


Temporairly hack into main model

Temporairly hack into main model

/*
	Assembly: LogoBotAssembly

	Master assembly for the LogoBot

	Local Frame:
		Centred on the origin, such that bottom edge of wheels sit on XY plane.
		Front of the robot faces towards y+

	Parameters:
		PenLift - Set to true to include a PenLift, defaults to false

	Returns:
		Complete LogoBot model

*/

// Connectors
// ----------
// These are used within this module to layout the various vitamins/sub-assemblies
// The same connectors are used to shape associated portions of the LogoBotBase_STL

LogoBot_Con_Breadboard          = [[-18, 40, 12],[0,0,-1], 0,0,0];

LogoBot_Con_LeftMotorDriver     = [[-45, 16, 8],[0,-1,0],0,0,0];
LogoBot_Con_RightMotorDriver    = [[45, 16, 8],[0,-1,0],0,0,0];

LogoBot_Con_Caster = [ [0, -BaseDiameter/2 + 10, 0], [0,0,1], 0, 0, 0];

LogoBot_Con_PenLift = [ [-20, -5, 10], [0,1,0], 0, 0, 0];

LogoBot_Con_SlideSwitch = [[-25, 0, 0], [0,0,-1], 90, 0,0];

// Assembly
// --------

function LogoBotAssembly_NumSteps() = 10 + (PenLift ? 1 : 0);

module LogoBotAssembly ( PenLift=false, Shell=true ) {

    assembly("assemblies/LogoBot.scad", "Final Assembly", str("LogoBotAssembly(PenLift=",PenLift,")")) {

        translate([0, 0, GroundClearance]) {

            // Default Design Elements
            // -----------------------

            // Base
            LogoBotBase_STL();

            step(1, "Connect the breadboard assembly to the underside of the base") {
                view(t=[0,17,12], r=[112,0,222], d=513);
                attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170), ExplodeSpacing=-20)
                    BreadboardAssembly();
            }

            // Bumper assemblies (x2)
            step(2, "Connect the two bumper assemblies" ) {
                view(t=[-6,7,19], r=[64,1,212], d=625);
				attach(DefConUp, DefConUp, ExplodeSpacing=20)
					for (x=[0,1], y=[0,1])
						mirror([0,y,0])
						mirror([x,0,0])
						translate([(BaseDiameter/2-10) * cos(45), (BaseDiameter/2-10) * sin(45), -17 ])
						rotate([0,0,-45])
						translate([9,8,5]) {
							rotate(a=180, v=[0,0,1]) {
							MicroSwitchHolder_STL();

							translate([12.7, 5.3, 3 + 3])
							mirror([0,1,0])
								MicroSwitch();
								}
						}

				for (i=[0,1])
				mirror([0,i,0])
				translate([0,0,-10])
					Bumper_STL();
            }

            step(3, "Push the two motor drivers onto the mounting posts") {
                view(t=[-6,7,19], r=[64,1,212], d=625);
                // Left Motor Driver
                attach(LogoBot_Con_LeftMotorDriver, ULN2003DriverBoard_Con_UpperLeft, ExplodeSpacing=20)
                    ULN2003DriverBoard();

                // Right Motor Driver
                attach(LogoBot_Con_RightMotorDriver, ULN2003DriverBoard_Con_UpperRight, ExplodeSpacing=20)
                    ULN2003DriverBoard();
            }

            // Motor + Wheel assemblies (x2)
            step(4, "Clip the two wheels assemblies onto the base and
                    connect the motor leads to the the motor drivers") {
                view(t=[-4,6,47], r=[66,0,190], d=740);

                for (i=[0:1])
                    mirror([i,0,0])
                    translate([BaseDiameter/2 + MotorOffsetX, 0, MotorOffsetZ])
                    translate([-15,0,0])
                    attach(DefConDown, DefConDown, ExplodeSpacing = 40)
                    translate([15,0,0])
                    rotate([-90, 0, 90]) {
                        assign($rightSide= i == 0? 1 : 0)
                            WheelAssembly();
                    }
            }

            // Connect jumper wires
            step(5,
                "Connect the jumper wires between the motor drivers and the Arduino") {
                view(t=[9,26,54], r=[38,0,161], d=766);
                view(title="plan", t=[0,32,16], r=[0,0,0], d=337);

                // Left
                JumperWire(
                    type = JumperWire_FM4,
                    con1 = attachedConnector(
                        LogoBot_Con_LeftMotorDriver,
                        ULN2003DriverBoard_Con_UpperLeft,
                        ULN2003DriverBoard_Con_Arduino,
                        ExplodeSpacing=20
                    ),
                    con2 = attachedConnector(
                        LogoBot_Con_Breadboard,
                        Breadboard_Con_BottomLeft(Breadboard_170),
                        Breadboard_Con_Pin(Breadboard_170, along=6, across=8),
                        ExplodeSpacing=20
                    ),
                    length = 100,
                    conVec1 = attachedDirection(
                        LogoBot_Con_LeftMotorDriver,
                        ULN2003DriverBoard_Con_UpperLeft,
                        [0,[1,0,0]]),
                    conVec2 = [-1,0,0],
                    midVec = [0.5,-1,0]
                );

                // Right
                JumperWire(
                    type = JumperWire_FM4,
                    con1 = attachedConnector(
                        LogoBot_Con_RightMotorDriver,
                        ULN2003DriverBoard_Con_UpperRight,
                        ULN2003DriverBoard_Con_Arduino,
                        ExplodeSpacing=20
                    ),
                    con2 = attachedConnector(
                        LogoBot_Con_Breadboard,
                        Breadboard_Con_BottomLeft(Breadboard_170),
                        Breadboard_Con_Pin(Breadboard_170, along=10, across=8),
                        ExplodeSpacing=20
                    ),
                    length = 100,
                    conVec1 = attachedDirection(
                        LogoBot_Con_RightMotorDriver,
                        ULN2003DriverBoard_Con_UpperRight,
                        [0,[1,0,0]]),
                    conVec2 = [-1,0,0],
                    midVec = [0.5,-1,0]
                );
            }
            // Battery assembly
            step(6, "Clip in the battery pack") {
                view(t=[-6,7,19], r=[64,1,212], d=625);

                attach(DefConDown, DefConDown, ExplodeSpacing=20)
                    translate([-25, -45, 12])
                        rotate([90, 0, 90]) {
                            BatteryPack(BatteryPack_AA);
                            battery_pack_double(BatteryPack_AA, 2, 4);
                        }
            }

            // Power Switch
            step(7, "Clip the power switch into place") {
                view(t=[-6,7,19], r=[64,1,212], d=625);

                attach(LogoBot_Con_SlideSwitch, DefConUp)
                    SlideSwitch();
            }

            // LED
            step(8, "Clip the LED into place") {
                view(t=[-6,7,19], r=[64,1,212], d=625);

                attach(DefConDown, DefConDown, ExplodeSpacing=20)
                    translate([0, -10, BaseDiameter/2])
                    LED();
            }

            // Piezo
            step(9, "Clip the piezo sounder into place") {
                view(t=[-6,7,19], r=[64,1,212], d=625);

                attach(DefConDown, DefConDown, ExplodeSpacing=20)
                    translate([-37, -32, 10])
                    murata_7BB_12_9();
            }

            // Caster
            step(10, "Push the caster assembly into the base so that it snaps into place") {
                view(t=[-6,7,19], r=[115,1,26], d=625);

                attach(LogoBot_Con_Caster, MarbleCaster_Con_Default, ExplodeSpacing=15)
                    MarbleCasterAssembly();
            }

            // Conditional Design Elements
            // ---------------------------

            // PenLift
            //   Placeholder of a micro servo to illustrate having conditional design elements
            if (PenLift) {
                // TODO: wrap into a PenLift sub-assembly
                step(11, "Fit the pen lift assembly") {
                    view(t=[-6,7,19], r=[64,1,212], d=625);

                    attach( LogoBot_Con_PenLift, MicroServo_Con_Horn)
                        MicroServo();
                }
            }

            // Shell + fixings
			if (Shell) {
				step(PenLift ? 12 : 11,
					"Push the shell down onto the base and twist to lock into place") {
					view(t=[11,-23,65], r=[66,0,217], d=1171);

					attach(DefConDown, DefConDown, ExplodeSpacing=BaseDiameter/2)
						BasicShell_STL();
				}
            }
        }

	}
}

/*
	Assembly: LogoBotAssembly

	Master assembly for the LogoBot

	Local Frame:
		Centred on the origin, such that bottom edge of wheels sit on XY plane.
		Front of the robot faces towards y+

	Parameters:
		PenLift - Set to true to include a PenLift, defaults to false

	Returns:
		Complete LogoBot model

*/

// Connectors
// ----------
// These are used within this module to layout the various vitamins/sub-assemblies
// The same connectors are used to shape associated portions of the LogoBotBase_STL

LogoBot_Con_Breadboard          = [[-18, 40, 12],[0,0,-1], 0,0,0];

LogoBot_Con_LeftMotorDriver     = [[-45, 16, 8],[0,-1,0],0,0,0];
LogoBot_Con_RightMotorDriver    = [[45, 16, 8],[0,-1,0],0,0,0];

LogoBot_Con_Caster = [ [0, -BaseDiameter/2 + 10, 0], [0,0,1], 0, 0, 0];

LogoBot_Con_PenLift = [ [-20, -5, 10], [0,1,0], 0, 0, 0];

LogoBot_Con_SlideSwitch = [[-25, 0, 0], [0,0,-1], 90, 0,0];

// Assembly
// --------

function LogoBotAssembly_NumSteps() = 10 + (PenLift ? 1 : 0);

module LogoBotAssembly ( PenLift=false, Shell=true ) {

    assembly("assemblies/LogoBot.scad", "Final Assembly", str("LogoBotAssembly(PenLift=",PenLift,")")) {

        translate([0, 0, GroundClearance]) {

            // Default Design Elements
            // -----------------------

            // Base
            LogoBotBase_STL();

            step(1, "Connect the breadboard assembly to the underside of the base") {
                view(t=[0,17,12], r=[112,0,222], d=513);
                attach(LogoBot_Con_Breadboard, Breadboard_Con_BottomLeft(Breadboard_170), ExplodeSpacing=-20)
                    BreadboardAssembly();
            }

            // Bumper assemblies (x2)
            step(2, "Connect the two bumper assemblies" ) {
                view(t=[-6,7,19], r=[64,1,212], d=625);
                attach(DefConUp, DefConUp, ExplodeSpacing=20)
                    for (x=[0,1], y=[0,1])
                    mirror([0,y,0])
                    mirror([x,0,0])
                    translate([(BaseDiameter/2-10) * cos(45), (BaseDiameter/2-10) * sin(45), -8 ])
                    rotate([0,0,-30])
                    MicroSwitch();
            }

            step(3, "Push the two motor drivers onto the mounting posts") {
                view(t=[-6,7,19], r=[64,1,212], d=625);
                // Left Motor Driver
                attach(LogoBot_Con_LeftMotorDriver, ULN2003DriverBoard_Con_UpperLeft, ExplodeSpacing=20)
                    ULN2003DriverBoard();

                // Right Motor Driver
                attach(LogoBot_Con_RightMotorDriver, ULN2003DriverBoard_Con_UpperRight, ExplodeSpacing=20)
                    ULN2003DriverBoard();
            }

            // Motor + Wheel assemblies (x2)
            step(4, "Clip the two wheels assemblies onto the base and
                    connect the motor leads to the the motor drivers") {
                view(t=[-4,6,47], r=[66,0,190], d=740);

                for (i=[0:1])
                    mirror([i,0,0])
                    translate([BaseDiameter/2 + MotorOffsetX, 0, MotorOffsetZ])
                    translate([-15,0,0])
                    attach(DefConDown, DefConDown, ExplodeSpacing = 40)
                    translate([15,0,0])
                    rotate([-90, 0, 90]) {
                        assign($rightSide= i == 0? 1 : 0)
                            WheelAssembly();
                    }
            }

            // Connect jumper wires
            step(5,
                "Connect the jumper wires between the motor drivers and the Arduino") {
                view(t=[9,26,54], r=[38,0,161], d=766);
                view(title="plan", t=[0,32,16], r=[0,0,0], d=337);

                // Left
                JumperWire(
                    type = JumperWire_FM4,
                    con1 = attachedConnector(
                        LogoBot_Con_LeftMotorDriver,
                        ULN2003DriverBoard_Con_UpperLeft,
                        ULN2003DriverBoard_Con_Arduino,
                        ExplodeSpacing=20
                    ),
                    con2 = attachedConnector(
                        LogoBot_Con_Breadboard,
                        Breadboard_Con_BottomLeft(Breadboard_170),
                        Breadboard_Con_Pin(Breadboard_170, along=6, across=8),
                        ExplodeSpacing=20
                    ),
                    length = 100,
                    conVec1 = attachedDirection(
                        LogoBot_Con_LeftMotorDriver,
                        ULN2003DriverBoard_Con_UpperLeft,
                        [0,[1,0,0]]),
                    conVec2 = [-1,0,0],
                    midVec = [0.5,-1,0]
                );

                // Right
                JumperWire(
                    type = JumperWire_FM4,
                    con1 = attachedConnector(
                        LogoBot_Con_RightMotorDriver,
                        ULN2003DriverBoard_Con_UpperRight,
                        ULN2003DriverBoard_Con_Arduino,
                        ExplodeSpacing=20
                    ),
                    con2 = attachedConnector(
                        LogoBot_Con_Breadboard,
                        Breadboard_Con_BottomLeft(Breadboard_170),
                        Breadboard_Con_Pin(Breadboard_170, along=10, across=8),
                        ExplodeSpacing=20
                    ),
                    length = 100,
                    conVec1 = attachedDirection(
                        LogoBot_Con_RightMotorDriver,
                        ULN2003DriverBoard_Con_UpperRight,
                        [0,[1,0,0]]),
                    conVec2 = [-1,0,0],
                    midVec = [0.5,-1,0]
                );
            }
            // Battery assembly
            step(6, "Clip in the battery pack") {
                view(t=[-6,7,19], r=[64,1,212], d=625);

                attach(DefConDown, DefConDown, ExplodeSpacing=20)
                    translate([-25, -45, 12])
                        rotate([90, 0, 90]) {
                            BatteryPack(BatteryPack_AA);
                            battery_pack_double(BatteryPack_AA, 2, 4);
                        }
            }

            // Power Switch
            step(7, "Clip the power switch into place") {
                view(t=[-6,7,19], r=[64,1,212], d=625);

                attach(LogoBot_Con_SlideSwitch, DefConUp)
                    SlideSwitch();
            }

            // LED
            step(8, "Clip the LED into place") {
                view(t=[-6,7,19], r=[64,1,212], d=625);

                attach(DefConDown, DefConDown, ExplodeSpacing=20)
                    translate([0, -10, BaseDiameter/2])
                    LED();
            }

            // Piezo
            step(9, "Clip the piezo sounder into place") {
                view(t=[-6,7,19], r=[64,1,212], d=625);

                attach(DefConDown, DefConDown, ExplodeSpacing=20)
                    translate([-37, -32, 10])
                    murata_7BB_12_9();
            }

            // Caster
            step(10, "Push the caster assembly into the base so that it snaps into place") {
                view(t=[-6,7,19], r=[115,1,26], d=625);

                attach(LogoBot_Con_Caster, MarbleCaster_Con_Default, ExplodeSpacing=15)
                    MarbleCasterAssembly();
            }

            // Conditional Design Elements
            // ---------------------------

            // PenLift
            //   Placeholder of a micro servo to illustrate having conditional design elements
            if (PenLift) {
                // TODO: wrap into a PenLift sub-assembly
                step(11, "Fit the pen lift assembly") {
                    view(t=[-6,7,19], r=[64,1,212], d=625);

                    attach( LogoBot_Con_PenLift, MicroServo_Con_Horn)
                        MicroServo();
                }
            }

            // Shell + fixings
			if (Shell) {
				step(PenLift ? 12 : 11,
					"Push the shell down onto the base and twist to lock into place") {
					view(t=[11,-23,65], r=[66,0,217], d=1171);

					attach(DefConDown, DefConDown, ExplodeSpacing=BaseDiameter/2)
						BasicShell_STL();
				}
            }
        }

	}
}


initial phi increased from 6 to 7 and step changed from 0.5 to 0.3

$fs=0.01;
h=7;
spacing=20;

module holderStop(phi)
{
  difference()
  {
    cylinder(r=phi/2+3, h=h);
    cylinder(r=phi/2,   h=h);
  }
}

// make different sizes
for(i = [0, 1, 2])
  // pack of 2 for each end of the holder
  for(j = [0, spacing])
    translate([j, i*spacing, 0])
      holderStop(6+1 + i*1/3);

$fs=0.01;
h=7;
spacing=20;

module holderStop(phi)
{
  difference()
  {
    cylinder(r=phi/2+3, h=h);
    cylinder(r=phi/2,   h=h);
  }
}

// make different sizes
for(i = [0, 1, 2])
  // pack of 2 for each end of the holder
  for(j = [0, spacing])
    translate([j, i*spacing, 0])
      holderStop(6 + i*0.5);