Add positions for power and status LEDs

Add positions for power and status LEDs

/*
    Vitamin: ArduinoPro
    Model of an Arduino Pro Mini/Micro

    Derived from: https://github.com/sparkfun/Pro_Micro/
             and: https://github.com/sparkfun/Arduino_Pro_Mini_328/

    Authors:
        Jamie Osborne (@jmeosbn)

    Local Frame:
        Point of origin is the centre of the bottom left pin (9)

    Parameters:
        type: can be "mini", or "micro";
              otherwise returns a blank board with header pin holes

    Returns:
        Model of an Arduino Pro Mini/Micro with header holes
*/

// ArduinoPro Model Variants
ArduinoPro_Mini             = 1;            // default
ArduinoPro_Micro            = 2;

// ArduinoPro Without Programming Port
ArduinoPro_No_Port          = 0;

// Show Header Pins
ArduinoPro_Pins_Normal      = 1;
ArduinoPro_Pins_Opposite    = 2;

// ArduinoPro Without Header Pins
ArduinoPro_No_Pins          = 0;            // default

// ArduinoPro PCB Variables
ArduinoPro_PCB_Pitch    = 2.54;                         // spacing of the holes
ArduinoPro_PCB_Inset    = ArduinoPro_PCB_Pitch /2;      // inset from board edge
ArduinoPro_PCB_Type     = 0.047 * 25.4; // .047 .063    // standard PCB thickness
ArduinoPro_PCB_Layers   = 0.035 * 2;                    // total copper layer thickness
ArduinoPro_PCB_Height   = ArduinoPro_PCB_Type +
                          ArduinoPro_PCB_Layers;        // height of the PCB (along z)
ArduinoPro_PCB_Length   = 1.3 * 25.4;                   // length of the PCB (along y)
ArduinoPro_PCB_Width    = 0.7 * 25.4;                   //  width of the PCB (along x)
ArduinoPro_PCB_Colour   = [26/255, 90/255, 160/255];    // colour of solder mask

module ArduinoPro_MicroUSB()
{
    include <MicroUSB.scad>
    // Subtracting `ArduinoPro_PCB_Inset` compansates for board origin
    //-: Not sure if board origin would be better set after creation
    move_x  = ArduinoPro_PCB_Width / 2 - ArduinoPro_PCB_Inset;
    move_y  = ArduinoPro_PCB_Length - ArduinoPro_PCB_Inset;
    move_z  = ArduinoPro_PCB_Height;

    // Add USB header and move to top of board (along x)
    translate([move_x, move_y, move_z])
        MicroUSB_Receptacle();
}

module ArduinoPro_PCB()
{
    // Bare PCB, origin through location for bottom left hole
    translate([-ArduinoPro_PCB_Inset, -ArduinoPro_PCB_Inset, 0])
        square(size = [ArduinoPro_PCB_Width, ArduinoPro_PCB_Length]);
}

module ArduinoPro_Header_Hole()
{
    // Standard PCB hole
    holediameter        = 1.25;         // diameter of PCB hole
    circle(d = holediameter);
}

module ArduinoPro_Header_Pin(rotation = 0)
{
    // Standard PCB header pin
    pcbholepitch        = 2.54;         // spacing of PCB holes
    holediameter        = 1.25;         // diameter of PCB hole
    pinheight           =   11;         // length of PCB pins
    pinoffset           =    3;         // offset of PCB pins
    pinwidth            = 0.63;         // width/gauge of PCB pins
    spacerwidth         = 2.25;         // height of breakaway pin frame
    spacerheight        = 2.25;         // height of breakaway pin frame

    // Header Pins
    color("white")
    translate([0, 0, -pinoffset])
    linear_extrude(pinheight)
        square(pinwidth, center = true);

    // Pin Spacers
    color("black")
    linear_extrude(spacerheight)
        square(spacerwidth, center = true);

    // Break-Away Material
    color("black")
    // FIXME: material needs to be rotated based on vector argument
    rotate(rotation, 0, 0)
    translate([0, pcbholepitch/2, 0])
    linear_extrude(spacerheight)
        square([spacerwidth * 0.85, pcbholepitch - spacerwidth], center = true);
}

module ArduinoPro_Headers_Layout()
{
    // distance between the two header rows
    // FIXME: improve this to be a multiple of 0.1 inch
    rowpitch  = ArduinoPro_PCB_Width - ArduinoPro_PCB_Inset*2;
    // length for holes, leaving room for end header and insets
    rowlength = ArduinoPro_PCB_Length - ArduinoPro_PCB_Pitch - ArduinoPro_PCB_Inset*2;

    // Add headers to either side (along y)
    for (x = [0, rowpitch], y = [0 : ArduinoPro_PCB_Pitch : rowlength]) {
        translate([x, y, 0]) children();
    }
}

module ArduinoPro_Serial_Header_Layout()
{
    // y position for header
    header_y = ArduinoPro_PCB_Length - ArduinoPro_PCB_Inset*2;
    // width for holes, leaving room for insets
    rowwidth = ArduinoPro_PCB_Width - ArduinoPro_PCB_Inset*2;

    // Add serial header along top of board (along x)
    for (x = [ArduinoPro_PCB_Inset : ArduinoPro_PCB_Pitch : rowwidth]) {
        translate([x, header_y, 0]) children();
    }
}

module ArduinoPro_SMT_Components(type = ArduinoPro_Mini)
{
    // Distance from origin to middle along y
    moveX = ArduinoPro_PCB_Width/2 - ArduinoPro_PCB_Inset;
    moveZ = ArduinoPro_PCB_Height;

    // Controller IC
    color(Grey20)
    translate([moveX, 0.5 * 25.4, moveZ])
    rotate([0,0,45])
    linear_extrude(height=1)
      square(sqrt(pow(0.4 * 25.4, 2)/2), center=true);

    // Parts for Pro Mini
    if (type == ArduinoPro_Mini) {
        // Reset switch
        translate([moveX, 0.15 * 25.4, moveZ])
        {
            color("silver")
            linear_extrude(height=1.7)
                square([4.5, 4.5], center=true);
            color("gold")
            translate([0, 0, 1.7])
            linear_extrude(height=1.0)
                circle(d=2.5);
        }

        // RX and TX LEDs
        color("white")
        translate([0.5 * 25.4, 0.05 * 25.4, moveZ])
        linear_extrude(height=1)
        {
            // Green - Status
            square([0.7,1.2], center=true);
            // Red - Power
            translate([-0.2 * 25.4, 0.75 * 25.4, 0])
                square([1.2,0.7], center=true);
        }
    }

    // Parts for Pro Micro
    if (type == ArduinoPro_Micro) {
        // Crystal
        color("silver")
        translate([moveX, 0.15 * 25.4, moveZ])
        linear_extrude(height=1)
            square([5, 3], center=true);

        // RX and TX LEDs
        color("white")
        translate([moveX/2, 0.2 * 25.4, moveZ])
        linear_extrude(height=1)
        {
            // RX - Yellow
            square([0.7,1.2], center=true);
            // Power - Red
            translate([0, 0.75 * 25.4, 0])
                square([1.2,0.7], center=true);
            // TX - Green
            translate([0.3 * 25.4, 0, 0])
                square([0.7,1.2], center=true);
        }
    }

    // Capacitors etc.
    moveY = (type == ArduinoPro_Mini ? 0.9 : 0.8) * 25.4;
    color("silver")
    translate([moveX/2, moveY, moveZ])
    linear_extrude(height=2)
    {
        square([1.5, 3.5], center=true);
        translate([7.5, 0, 0])
          square([1.5, 3.5], center=true);
    }

    // Other components
    color("silver")
    translate([moveX, moveY, moveZ])
    linear_extrude(height=1)
    {
        square([1, 2.5], center=true);
        translate([0, 3.25, 0])
            square([2.5, 1.5], center=true);
    }
}

module ArduinoPro(type = ArduinoPro_Mini, headerpins = 0, serialpins = 0)
{
    color(ArduinoPro_PCB_Colour)
    linear_extrude(height=ArduinoPro_PCB_Height)
    difference() {
        // Base PCB
        ArduinoPro_PCB();

        // Common Headers for Pro Mini/Micro
        ArduinoPro_Headers_Layout()
            ArduinoPro_Header_Hole();

        // Pro Mini Serial Header
        if (type == ArduinoPro_Mini) {
            ArduinoPro_Serial_Header_Layout()
                ArduinoPro_Header_Hole();
        }
    }

    // Pro Mini Serial Header Pins
    serialontop = (serialpins != ArduinoPro_Pins_Opposite);
    if (serialpins > 0 && type == ArduinoPro_Mini) {
        // Offset for board height if located on top
        translate([0, 0, serialontop ? ArduinoPro_PCB_Height : 0])
            mirror([0, 0, serialontop ? 0 : 1 ])
            ArduinoPro_Serial_Header_Layout()
                ArduinoPro_Header_Pin(rotation = 90);
    }

    // Common Header Pins
    headerontop = (headerpins == ArduinoPro_Pins_Opposite);
    if (headerpins > 0) {
        // Offset for board height located on top
        translate([0, 0, headerontop ? ArduinoPro_PCB_Height : 0])
            mirror([0, 0, headerontop ? 0 : 1 ])
            ArduinoPro_Headers_Layout()
                ArduinoPro_Header_Pin();
    }

    // Pro Micro USB port
    if (type == ArduinoPro_Micro) ArduinoPro_MicroUSB();

    // Surface mount components
    ArduinoPro_SMT_Components(type);
}

// Example Usage
// ArduinoPro(ArduinoPro_Micro, ArduinoPro_Pins_Normal);
// ArduinoPro(ArduinoPro_Mini,  ArduinoPro_Pins_Normal, ArduinoPro_Pins_Normal);

/*
    Vitamin: ArduinoPro
    Model of an Arduino Pro Mini/Micro

    Derived from: https://github.com/sparkfun/Pro_Micro/
             and: https://github.com/sparkfun/Arduino_Pro_Mini_328/

    Authors:
        Jamie Osborne (@jmeosbn)

    Local Frame:
        Point of origin is the centre of the bottom left pin (9)

    Parameters:
        type: can be "mini", or "micro";
              otherwise returns a blank board with header pin holes

    Returns:
        Model of an Arduino Pro Mini/Micro with header holes
*/

// ArduinoPro Model Variants
ArduinoPro_Mini             = 1;            // default
ArduinoPro_Micro            = 2;

// ArduinoPro Without Programming Port
ArduinoPro_No_Port          = 0;

// Show Header Pins
ArduinoPro_Pins_Normal      = 1;
ArduinoPro_Pins_Opposite    = 2;

// ArduinoPro Without Header Pins
ArduinoPro_No_Pins          = 0;            // default

// ArduinoPro PCB Variables
ArduinoPro_PCB_Pitch    = 2.54;                         // spacing of the holes
ArduinoPro_PCB_Inset    = ArduinoPro_PCB_Pitch /2;      // inset from board edge
ArduinoPro_PCB_Type     = 0.047 * 25.4; // .047 .063    // standard PCB thickness
ArduinoPro_PCB_Layers   = 0.035 * 2;                    // total copper layer thickness
ArduinoPro_PCB_Height   = ArduinoPro_PCB_Type +
                          ArduinoPro_PCB_Layers;        // height of the PCB (along z)
ArduinoPro_PCB_Length   = 1.3 * 25.4;                   // length of the PCB (along y)
ArduinoPro_PCB_Width    = 0.7 * 25.4;                   //  width of the PCB (along x)
ArduinoPro_PCB_Colour   = [26/255, 90/255, 160/255];    // colour of solder mask

module ArduinoPro_MicroUSB()
{
    include <MicroUSB.scad>
    // Subtracting `ArduinoPro_PCB_Inset` compansates for board origin
    //-: Not sure if board origin would be better set after creation
    move_x  = ArduinoPro_PCB_Width / 2 - ArduinoPro_PCB_Inset;
    move_y  = ArduinoPro_PCB_Length - ArduinoPro_PCB_Inset;
    move_z  = ArduinoPro_PCB_Height;

    // Add USB header and move to top of board (along x)
    translate([move_x, move_y, move_z])
        MicroUSB_Receptacle();
}

module ArduinoPro_PCB()
{
    // Bare PCB, origin through location for bottom left hole
    translate([-ArduinoPro_PCB_Inset, -ArduinoPro_PCB_Inset, 0])
        square(size = [ArduinoPro_PCB_Width, ArduinoPro_PCB_Length]);
}

module ArduinoPro_Header_Hole()
{
    // Standard PCB hole
    holediameter        = 1.25;         // diameter of PCB hole
    circle(d = holediameter);
}

module ArduinoPro_Header_Pin(rotation = 0)
{
    // Standard PCB header pin
    pcbholepitch        = 2.54;         // spacing of PCB holes
    holediameter        = 1.25;         // diameter of PCB hole
    pinheight           =   11;         // length of PCB pins
    pinoffset           =    3;         // offset of PCB pins
    pinwidth            = 0.63;         // width/gauge of PCB pins
    spacerwidth         = 2.25;         // height of breakaway pin frame
    spacerheight        = 2.25;         // height of breakaway pin frame

    // Header Pins
    color("white")
    translate([0, 0, -pinoffset])
    linear_extrude(pinheight)
        square(pinwidth, center = true);

    // Pin Spacers
    color("black")
    linear_extrude(spacerheight)
        square(spacerwidth, center = true);

    // Break-Away Material
    color("black")
    // FIXME: material needs to be rotated based on vector argument
    rotate(rotation, 0, 0)
    translate([0, pcbholepitch/2, 0])
    linear_extrude(spacerheight)
        square([spacerwidth * 0.85, pcbholepitch - spacerwidth], center = true);
}

module ArduinoPro_Headers_Layout()
{
    // distance between the two header rows
    // FIXME: improve this to be a multiple of 0.1 inch
    rowpitch  = ArduinoPro_PCB_Width - ArduinoPro_PCB_Inset*2;
    // length for holes, leaving room for end header and insets
    rowlength = ArduinoPro_PCB_Length - ArduinoPro_PCB_Pitch - ArduinoPro_PCB_Inset*2;

    // Add headers to either side (along y)
    for (x = [0, rowpitch], y = [0 : ArduinoPro_PCB_Pitch : rowlength]) {
        translate([x, y, 0]) children();
    }
}

module ArduinoPro_Serial_Header_Layout()
{
    // y position for header
    header_y = ArduinoPro_PCB_Length - ArduinoPro_PCB_Inset*2;
    // width for holes, leaving room for insets
    rowwidth = ArduinoPro_PCB_Width - ArduinoPro_PCB_Inset*2;

    // Add serial header along top of board (along x)
    for (x = [ArduinoPro_PCB_Inset : ArduinoPro_PCB_Pitch : rowwidth]) {
        translate([x, header_y, 0]) children();
    }
}

module ArduinoPro_SMT_Components(type = ArduinoPro_Mini)
{
    // Distance from origin to middle along y
    moveX = ArduinoPro_PCB_Width/2 - ArduinoPro_PCB_Inset;

    // Controller IC
    color(Grey20)
    translate([moveX, 0.5 * 25.4, ArduinoPro_PCB_Height])
    rotate([0,0,45])
    linear_extrude(height=1)
      square(sqrt(pow(0.4 * 25.4, 2)/2), center=true);

    // Parts for Pro Micro
    if (type == ArduinoPro_Mini) {
        // Reset switch
        translate([moveX, 0.15 * 25.4, ArduinoPro_PCB_Height])
        union() {
            color("silver")
            linear_extrude(height=1.7)
                square([6, 3.5], center=true);
            color("darkorange")
            translate([0, 0, 1.7])
            linear_extrude(height=1.0)
                square([3, 2], center=true);
        }
    }

    // Parts for Pro Micro
    if (type == ArduinoPro_Micro) {
        // Crystal
        color("silver")
        translate([moveX, 0.15 * 25.4, ArduinoPro_PCB_Height])
        linear_extrude(height=1)
            square([6, 3.5], center=true);
    }

    // Capacitors etc.
    moveY = (type == ArduinoPro_Mini ? 0.9 : 0.8) * 25.4;
    color("silver")
    translate([moveX/2, moveY, ArduinoPro_PCB_Height])
    linear_extrude(height=2)
    union() {
        square([1.5, 3.5], center=true);
        translate([7.5, 0, 0])
          square([1.5, 3.5], center=true);
    }

    // Other components
    color("silver")
    translate([moveX, moveY, ArduinoPro_PCB_Height])
    linear_extrude(height=1)
    union() {
        square([1, 2.5], center=true);
        translate([0, 3, 0])
            square([2.5, 1.5], center=true);
    }
}

module ArduinoPro(type = ArduinoPro_Mini, headerpins = 0, serialpins = 0)
{
    color(ArduinoPro_PCB_Colour)
    linear_extrude(height=ArduinoPro_PCB_Height)
    difference() {
        // Base PCB
        ArduinoPro_PCB();

        // Common Headers for Pro Mini/Micro
        ArduinoPro_Headers_Layout()
            ArduinoPro_Header_Hole();

        // Pro Mini Serial Header
        if (type == ArduinoPro_Mini) {
            ArduinoPro_Serial_Header_Layout()
                ArduinoPro_Header_Hole();
        }
    }

    // Pro Mini Serial Header Pins
    serialontop = (serialpins != ArduinoPro_Pins_Opposite);
    if (serialpins > 0 && type == ArduinoPro_Mini) {
        // Offset for board height if located on top
        translate([0, 0, serialontop ? ArduinoPro_PCB_Height : 0])
            mirror([0, 0, serialontop ? 0 : 1 ])
            ArduinoPro_Serial_Header_Layout()
                ArduinoPro_Header_Pin(rotation = 90);
    }

    // Common Header Pins
    headerontop = (headerpins == ArduinoPro_Pins_Opposite);
    if (headerpins > 0) {
        // Offset for board height located on top
        translate([0, 0, headerontop ? ArduinoPro_PCB_Height : 0])
            mirror([0, 0, headerontop ? 0 : 1 ])
            ArduinoPro_Headers_Layout()
                ArduinoPro_Header_Pin();
    }

    // Pro Micro USB port
    if (type == ArduinoPro_Micro) ArduinoPro_MicroUSB();

    // Surface mount components
    ArduinoPro_SMT_Components(type);
}

// Example Usage
// ArduinoPro(ArduinoPro_Micro, ArduinoPro_Pins_Normal);
// ArduinoPro(ArduinoPro_Mini,  ArduinoPro_Pins_Normal, ArduinoPro_Pins_Normal);


misc: Remove assert (now built-in)

misc: Remove assert (now built-in)

use <system.scad>
use <scad-utils/linalg.scad>
use <scad-utils/lists.scad>

function posvec(path_vec) = [for(v=path_vec) translation_part(v)];
function posvec_x(path_vec) = [for(v=path_vec) translation_part(v)[0]];
function posvec_y(path_vec) = [for(v=path_vec) translation_part(v)[1]];
function posvec_z(path_vec) = [for(v=path_vec) translation_part(v)[2]];

function vec_i(v3,i) = [for(v=v3) v[i]];
function vec_0(v3) = vec_i(v3,0);
function vec_1(v3) = vec_i(v3,1);
function vec_2(v3) = vec_i(v3,2);

function v_itrlen(vec) = [0:1:len(vec)-1];

function range1(v1) = [min(v1), max(v1)];
function range3(v3) = [range1(vec_0(v3)), range1(vec_1(v3)), range1(vec_2(v3))];
function bbox(r) = [ [r[0][0],r[1][0],r[2][0]], [r[0][1],r[1][1],r[2][1]] ];
function transform_pp(vec_m, t_pre, t_post) = [for(m=vec_m) (t_pre*m)*t_post];
function transform_pre(vec_m, t) = [for(m=vec_m) t*m];
function transform_post(vec_m, t) = [for(m=vec_m) m*t];

function clamp(v, v1, v2) = min(max(v,v1),v2);

// from the start (or s'th element) to the e'th element - remember elements are zero based
function v_sum(v,e=undef,start=0) =
let(e_= fallback(e, len(v)-1))
(e==start ? v[e] : v[e_] + v_sum(v,e_-1,start));

function v_i(vec,i) = [for(vv=vec) vv[i]];
function v_get(vec,key) = [for(vv=vec) get(key, vv)];
function v_add(vec,v) = [for(vv=vec) vv+v];
function v_sub(vec,v) = [for(vv=vec) vv+v];
function v_avg(v,e=undef,start=0) = v_sum(v,e,start) / (len(v));
function v_abs(v, start=0) = [for(i=[start:1:len(v)-1]) abs(v[i])];
function v_sign(v, start=0) = [for(i=[start:1:len(v)-1]) sign(v[i])];
function v_max(v, m, start=0) = [for(i=[start:1:len(v)-1]) max(v[i],m)];
function v_min(v, m, start=0) = [for(i=[start:1:len(v)-1]) min(v[i],m)];
function v_clamp(v, v1, v2, start=0) = [for(i=[start:1:len(v)-1]) clamp(v[i],v1,v2)];

// cumulative sum of vector [1,2,3] = [1,3,6]
function v_cumsum(v, start=0, end) = [for(i=[start:1:end==undef?len(v)-1:end]) v_sum(v,i)];

// filter/remove a val from a vec
function filter(vec,val=undef) = [for(v=vec) if(v!=val) v];

function vec_pair_double_transform_post(vec,t1,t2)=
    flatten(
        [for(v=vec)
            [transform_post(v,t1),transform_post(v,t2)]
        ]);

function vec_pair_double(vec)=flatten([for(v=vec) [v,v] ]);

/** Calculates length of hypotenuse according to pythagoras */
function pythag_hyp(a, b)=sqrt(a*a+b*b);
function pythag_leg(b, c)=sqrt(c*c-b*b);

//-- Calculate the module of a vector
function v_mod(v) = (sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]));

//-- Calculate the cros product of two vectors
function v_cross(u,v) = [
  u[1]*v[2] - v[1]*u[2],
  -(u[0]*v[2] - v[0]*u[2]) ,
  u[0]*v[1] - v[0]*u[1]];

//-- Calculate the dot product of two vectors
function v_dot(u,v) = u[0]*v[0]+u[1]*v[1]+u[2]*v[2];

//-- Return the unit vector of a vector
function v_unitv(v) = v/v_mod(v);

//-- Return the angle between two vectores
function v_anglev(u,v) = acos( v_dot(u,v) / (v_mod(u)*v_mod(v)) );

function _orient_angles(zaxis)=
[-asin(zaxis.y / norm(zaxis)),
    atan2(zaxis.x, zaxis.z),
    0];

//matrix rotation functions
function _rotate_x_matrix(a)=
[[1,0,0,0],
    [0,cos(a),-sin(a),0],
    [0,sin(a),cos(a),0],
    [0,0,0,1]];

function _rotate_y_matrix(a)=
[[cos(a),0,sin(a),0],
    [0,1,0,0],
    [-sin(a),0,cos(a),0],
    [0,0,0,1]];

function _rotate_z_matrix(a)=
[[cos(a),-sin(a),0,0],
    [sin(a),cos(a),0,0],
    [0,0,1,0],
    [0,0,0,1]];

function _rotate_matrix(a)=_rotate_z_matrix(a.z)*_rotate_y_matrix(a.y)*_rotate_x_matrix(a.x);

//hadamard product (aka "component-wise" product) for vectors
function hadamard(v1,v2) = [v1.x*v2.x, v1.y*v2.y, v1.z*v2.z];

// search in a dictionary (vector w/key-value pairs) and replace the value for a given key
function dict_replace(dict, key, newvalue)=[
        for(kv=dict)
        kv[0] == key ?
            [key, newvalue]
            :
            kv
        ];

function dict_replace_multiple(dict, newvaluesdict)=[
    for(kv_old=dict)
    let(r = search(kv_old[0], newvaluesdict, num_returns_per_match=0, index_col_num=0))
    r==[]?kv_old:newvaluesdict[r[0]]
    ];

function sinh(x) = (1 - pow(e, -2 * x)) / (2 * pow(e, -x));
function cosh(x) = (1 + pow(e, -2 * x)) / (2 * pow(e, -x));
function tanh(x) = sinh(x) / cosh(x);
function cot(x) = 1 / tan(x);

function factorial(n) = n == 0 ? 1 : factorial(n - 1) * n;

if(false)
{
    vec=[ 10, 20, 30, 40 ];
    echo("v_sum=", v_sum(vec,2,1)); // is 20+30=50
    echo("v_sum=", v_sum(vec));
    echo("v_cumsum=", v_cumsum(vec));
}

// FUNCTION: is_String(x)
//   Returns true if x is a string, false otherwise.
function is_string(x) =
	x == undef || len(x) == undef
		? false // if undef, a boolean or a number
		: len(str(x,x)) == len(x)*2; // if an array, this is false

// FUNCTION: is_array(x)
//   Returns true if x is an array, false otherwise.
function is_array(x) = is_string(x) ? false : len(x) != undef;

function identity(d)        = d == 0 ? 1 : [for(y=[1:d]) [for(x=[1:d]) x == y ? 1 : 0] ];
function unit_vector(v)     = let(x=v[0], y=v[1], z=v[2]) [x/norm(v), y/norm(v), z/norm(v)];
function skew_symmetric(v)  = let(x=v[0], y=v[1], z=v[2]) [[0, -z, y], [z, 0, -x], [-y, x, 0]];
function tensor_product1(u) = let(x=u[0], y=u[1], z=u[2]) [[x*x, x*y, x*z], [x*y, y*y, y*z], [x*z, y*z, z*z]];
function v_rotate(a, v)
    = is_array(a)
? let(rx=a[0], ry=a[1], rz=a[2])
    [[1, 0, 0],              [0, cos(rx), -sin(rx)], [0, sin(rx), cos(rx)]]
    * [[cos(ry), 0, sin(ry)],  [0, 1, 0],              [-sin(ry), 0, cos(ry)]]
    * [[cos(rz), -sin(rz), 0], [sin(rz), cos(rz), 0],  [0, 0, 1]]
    : let(uv=unit_vector(v))
    cos(a)*identity(3) + sin(a)*skew_symmetric(uv) + (1 - cos(a))*tensor_product1(uv);

 /*echo(rotate(90, [1,0,0]) * [1, 0, 0]);*/
 /*echo(rotate(90, [1,0,0]) * [0, 1, 0]);*/
 /*echo(rotate(90, [1,0,0]) * [0, 0, 1]);*/

function lerp(v0, v1, t) =  (1-t)*v0 + t*v1;

// echo(fallback(undef,1));
function fallback(a, b) = a==undef?b:a;

// echo(fallback(undef,[undef, 1]));
function v_fallback(a,v,i=0) = (a!=undef || i > len(v)-1) ? a : v_fallback(v[i],v,i+1);

function fn_from_r(r) =
                    $fn > 0.0 ?
                    ($fn >= 3 ? $fn : 3)
                    :
                    ceil(max(min(360.0 / $fa, r*2*PI / $fs), 5));

function fn_from_d(d) =
                    $fn > 0.0 ?
                    ($fn >= 3 ? $fn : 3)
                    :
                    ceil(max(min(360.0 / $fa, d*PI / $fs), 5));

use <system.scad>
use <scad-utils/linalg.scad>
use <scad-utils/lists.scad>

function posvec(path_vec) = [for(v=path_vec) translation_part(v)];
function posvec_x(path_vec) = [for(v=path_vec) translation_part(v)[0]];
function posvec_y(path_vec) = [for(v=path_vec) translation_part(v)[1]];
function posvec_z(path_vec) = [for(v=path_vec) translation_part(v)[2]];

function vec_i(v3,i) = [for(v=v3) v[i]];
function vec_0(v3) = vec_i(v3,0);
function vec_1(v3) = vec_i(v3,1);
function vec_2(v3) = vec_i(v3,2);

function v_itrlen(vec) = [0:1:len(vec)-1];

function range1(v1) = [min(v1), max(v1)];
function range3(v3) = [range1(vec_0(v3)), range1(vec_1(v3)), range1(vec_2(v3))];
function bbox(r) = [ [r[0][0],r[1][0],r[2][0]], [r[0][1],r[1][1],r[2][1]] ];
function transform_pp(vec_m, t_pre, t_post) = [for(m=vec_m) (t_pre*m)*t_post];
function transform_pre(vec_m, t) = [for(m=vec_m) t*m];
function transform_post(vec_m, t) = [for(m=vec_m) m*t];

function clamp(v, v1, v2) = min(max(v,v1),v2);

// from the start (or s'th element) to the e'th element - remember elements are zero based
function v_sum(v,e=undef,start=0) =
let(e_= fallback(e, len(v)-1))
(e==start ? v[e] : v[e_] + v_sum(v,e_-1,start));

function v_i(vec,i) = [for(vv=vec) vv[i]];
function v_get(vec,key) = [for(vv=vec) get(key, vv)];
function v_add(vec,v) = [for(vv=vec) vv+v];
function v_sub(vec,v) = [for(vv=vec) vv+v];
function v_avg(v,e=undef,start=0) = v_sum(v,e,start) / (len(v));
function v_abs(v, start=0) = [for(i=[start:1:len(v)-1]) abs(v[i])];
function v_sign(v, start=0) = [for(i=[start:1:len(v)-1]) sign(v[i])];
function v_max(v, m, start=0) = [for(i=[start:1:len(v)-1]) max(v[i],m)];
function v_min(v, m, start=0) = [for(i=[start:1:len(v)-1]) min(v[i],m)];
function v_clamp(v, v1, v2, start=0) = [for(i=[start:1:len(v)-1]) clamp(v[i],v1,v2)];

// cumulative sum of vector [1,2,3] = [1,3,6]
function v_cumsum(v, start=0, end) = [for(i=[start:1:end==undef?len(v)-1:end]) v_sum(v,i)];

// filter/remove a val from a vec
function filter(vec,val=undef) = [for(v=vec) if(v!=val) v];

function vec_pair_double_transform_post(vec,t1,t2)=
    flatten(
        [for(v=vec)
            [transform_post(v,t1),transform_post(v,t2)]
        ]);

function vec_pair_double(vec)=flatten([for(v=vec) [v,v] ]);

/** Calculates length of hypotenuse according to pythagoras */
function pythag_hyp(a, b)=sqrt(a*a+b*b);
function pythag_leg(b, c)=sqrt(c*c-b*b);

//-- Calculate the module of a vector
function v_mod(v) = (sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]));

//-- Calculate the cros product of two vectors
function v_cross(u,v) = [
  u[1]*v[2] - v[1]*u[2],
  -(u[0]*v[2] - v[0]*u[2]) ,
  u[0]*v[1] - v[0]*u[1]];

//-- Calculate the dot product of two vectors
function v_dot(u,v) = u[0]*v[0]+u[1]*v[1]+u[2]*v[2];

//-- Return the unit vector of a vector
function v_unitv(v) = v/v_mod(v);

//-- Return the angle between two vectores
function v_anglev(u,v) = acos( v_dot(u,v) / (v_mod(u)*v_mod(v)) );

function _orient_angles(zaxis)=
[-asin(zaxis.y / norm(zaxis)),
    atan2(zaxis.x, zaxis.z),
    0];

//matrix rotation functions
function _rotate_x_matrix(a)=
[[1,0,0,0],
    [0,cos(a),-sin(a),0],
    [0,sin(a),cos(a),0],
    [0,0,0,1]];

function _rotate_y_matrix(a)=
[[cos(a),0,sin(a),0],
    [0,1,0,0],
    [-sin(a),0,cos(a),0],
    [0,0,0,1]];

function _rotate_z_matrix(a)=
[[cos(a),-sin(a),0,0],
    [sin(a),cos(a),0,0],
    [0,0,1,0],
    [0,0,0,1]];

function _rotate_matrix(a)=_rotate_z_matrix(a.z)*_rotate_y_matrix(a.y)*_rotate_x_matrix(a.x);

//hadamard product (aka "component-wise" product) for vectors
function hadamard(v1,v2) = [v1.x*v2.x, v1.y*v2.y, v1.z*v2.z];

// search in a dictionary (vector w/key-value pairs) and replace the value for a given key
function dict_replace(dict, key, newvalue)=[
        for(kv=dict)
        kv[0] == key ?
            [key, newvalue]
            :
            kv
        ];

function dict_replace_multiple(dict, newvaluesdict)=[
    for(kv_old=dict)
    let(r = search(kv_old[0], newvaluesdict, num_returns_per_match=0, index_col_num=0))
    r==[]?kv_old:newvaluesdict[r[0]]
    ];

function sinh(x) = (1 - pow(e, -2 * x)) / (2 * pow(e, -x));
function cosh(x) = (1 + pow(e, -2 * x)) / (2 * pow(e, -x));
function tanh(x) = sinh(x) / cosh(x);
function cot(x) = 1 / tan(x);

function factorial(n) = n == 0 ? 1 : factorial(n - 1) * n;

if(false)
{
    vec=[ 10, 20, 30, 40 ];
    echo("v_sum=", v_sum(vec,2,1)); // is 20+30=50
    echo("v_sum=", v_sum(vec));
    echo("v_cumsum=", v_cumsum(vec));
}

// FUNCTION: is_String(x)
//   Returns true if x is a string, false otherwise.
function is_string(x) =
	x == undef || len(x) == undef
		? false // if undef, a boolean or a number
		: len(str(x,x)) == len(x)*2; // if an array, this is false

// FUNCTION: is_array(x)
//   Returns true if x is an array, false otherwise.
function is_array(x) = is_string(x) ? false : len(x) != undef;

function identity(d)        = d == 0 ? 1 : [for(y=[1:d]) [for(x=[1:d]) x == y ? 1 : 0] ];
function unit_vector(v)     = let(x=v[0], y=v[1], z=v[2]) [x/norm(v), y/norm(v), z/norm(v)];
function skew_symmetric(v)  = let(x=v[0], y=v[1], z=v[2]) [[0, -z, y], [z, 0, -x], [-y, x, 0]];
function tensor_product1(u) = let(x=u[0], y=u[1], z=u[2]) [[x*x, x*y, x*z], [x*y, y*y, y*z], [x*z, y*z, z*z]];
function v_rotate(a, v)
    = is_array(a)
? let(rx=a[0], ry=a[1], rz=a[2])
    [[1, 0, 0],              [0, cos(rx), -sin(rx)], [0, sin(rx), cos(rx)]]
    * [[cos(ry), 0, sin(ry)],  [0, 1, 0],              [-sin(ry), 0, cos(ry)]]
    * [[cos(rz), -sin(rz), 0], [sin(rz), cos(rz), 0],  [0, 0, 1]]
    : let(uv=unit_vector(v))
    cos(a)*identity(3) + sin(a)*skew_symmetric(uv) + (1 - cos(a))*tensor_product1(uv);

 /*echo(rotate(90, [1,0,0]) * [1, 0, 0]);*/
 /*echo(rotate(90, [1,0,0]) * [0, 1, 0]);*/
 /*echo(rotate(90, [1,0,0]) * [0, 0, 1]);*/

function lerp(v0, v1, t) =  (1-t)*v0 + t*v1;

// echo(fallback(undef,1));
function fallback(a, b) = a==undef?b:a;

// echo(fallback(undef,[undef, 1]));
function v_fallback(a,v,i=0) = (a!=undef || i > len(v)-1) ? a : v_fallback(v[i],v,i+1);

// Called when an assertion fails (invokes infinite recursion).
function assertion_failed() = (assertion_failed());

// Assert that `bool` is true.
//
// If `bool` is false, emit the error `msg`,
//  and terminate compilation (via infinite recursion).
module assert(bool, msg = "")
{
    if(bool == false)
    {
        echo("Assertion Failed: ", msg);
        echo("", assertion_failed());
    }
}

function fn_from_r(r) =
                    $fn > 0.0 ?
                    ($fn >= 3 ? $fn : 3)
                    :
                    ceil(max(min(360.0 / $fa, r*2*PI / $fs), 5));

function fn_from_d(d) =
                    $fn > 0.0 ?
                    ($fn >= 3 ? $fn : 3)
                    :
                    ceil(max(min(360.0 / $fa, d*PI / $fs), 5));


side-walls with sensors

side-walls with sensors

// stackable trays bolt together
// topper narrows to exact card size for camera positioning
// IR sensor positions allow mcu to adjust card height
// screw drive allows stepper disable to reduce power/heat
// 100lb test thread x 4 for lifting

use <bearing.scad>;

// * bearing(pos=[0,0,0], angle=[0,0,0], model=SkateBearing, outline=false, material=Steel, sideMaterial=Brass);

use <motors.scad>;

// * stepper_motor_mount(nema_standard, slide_distance=0, mochup=true, tolerance=0);

use <hardware.scad>;

// * screw(length, nutpos, washer, bearingpos = -1);

use <involute_gears.scad>;

end_x = 2.5*25.4 + 22 + 20;

//end_plate();
//end_mold();
sideplate();
//screwin();
//opb6xx();

module screwin(ang=0, tail=10, nutpos=9, shaft=12){
    rotate([0,0,ang])
    screw(shaft, nutpos, 0, $fn=24);
    translate([0,-5.8/2,-nutpos-2.5])
       cube([tail,5.8,2.5], center=false);
}

module end_plate(x = end_x, y = 70, thk = 7, boltlen=12){
    difference(){
        minkowski(){
        cylinder(1,1.5,center=false, $fn=20);
        cube([x,y,thk], center=false);
        }
        translate([x/2, 42.3/2, 22]) rotate([180,0,0])
            #steppercut();

        for (i=[1,-1]){
            translate([x/2 + i*2.5/2*25.4+i*9,13,0])
                cylinder(r=22.2/2, h=20, center=true, $fn=64);
            translate([x/2 + i*2.5/2*25.4+i*9,y-13,0])
                cylinder(r=22.2/2, h=20, center=true, $fn=64);
            translate([x/2 + i*2.5/2*25.4+i*boltlen/2+i*20,y/2-10,thk/2]) rotate([0,90,90-90*i])
                #screwin(ang=0, tail=10, nutpos=8, shaft=12);
            translate([x/2 + i*2.5/2*25.4+i*boltlen/2+i*20,y/2+10,thk/2]) rotate([0,90,90-90*i])
                #screwin(ang=0, tail=10, nutpos=8, shaft=12);
            translate([x/2 + i*2.5/4*25.4,y+4,thk/2]) rotate([0,-270,90])
                #screwin(ang=0, tail=10, nutpos=8, shaft=12);

        }
    }
}

module end_mold(x = end_x, y = 70, thk = 7, boltlen=12){
    // a positive for cutting into side-panels
    translate([thk+1, 0, 4]) rotate([0,-90,0]){
    cube([thk,y,thk], center=false);
        for (i=[-1]){
            translate([x/2 + i*2.5/2*25.4+i*boltlen/2+i*20,y/2+10,thk/2]) rotate([0,90,90-90*i])
                    screwin(ang=0, tail=10, nutpos=8, shaft=12);
            translate([x/2 + i*2.5/2*25.4+i*boltlen/2+i*20,y/2-10,thk/2]) rotate([0,90,90-90*i])
                    screwin(ang=0, tail=10, nutpos=8, shaft=12);
        }
    }
}

module opb6xx(
    sensor_bottom_x = 5,
    sensor_bottom_y = 4.6,
    sensor_bottom_z = 1.7,
    sensor_top = [6.35,4.6,3.3]){

    sensor_bot = [sensor_bottom_x,sensor_bottom_y,sensor_bottom_z];
    totalsensor_ht = sensor_top[2] + sensor_bottom_z;

    for (j=[-1,1]){
        translate([0, j*(y/2 - sensor_bottom_y/2), -8.25]){
            translate([0,0,sensor_bottom_z/2])
                #cube(sensor_bot, center=true);
            translate([0,0,totalsensor_ht/2 + sensor_bottom_z/2- 0.1])
                #cube(sensor_top, center=true);
        cube([sensor_bottom_x, sensor_bottom_y, 16], center=true);
        cylinder(r=sensor_bottom_x/2-0.25, h=17, center=false);
        }
    }
}

module sideplate(x = 4.1*25.4, y = 70, z = 5){
    // structure
    difference(){
        cube([x,y,z], center=false);
        end_mold();
        translate([x, y, 0]) rotate([0,0,180])
            end_mold();
        for (i=[2/7, 5/7]){
            translate([x/2, y*i, 0]) scale([3.6,1.18,1]){
                #cylinder(r=y/6, h=z+.1, center=false, $fn=128);
            }
            translate([x*i, 2.2, 5.7]) rotate([180,0,0])
                #opb6xx();
        }
    }
}

module steppercut(xy=42.3, curve=5.7, tall=20){
    linear_extrude(height=tall)
        minkowski(){
            square([xy-curve, xy-curve], center=true);
            circle(r = curve/2, center=true, $fn=16);
        }
    linear_extrude(height=tall+12)
        stepper_motor_mount(17, slide_distance=0, mochup=true, tolerance=0);
}

// stackable trays bolt together
// topper narrows to exact card size for camera positioning
// IR sensor positions allow mcu to adjust card height
// screw drive allows stepper disable to reduce power/heat
// 100lb test thread x 4 for lifting

use <bearing.scad>;

// * bearing(pos=[0,0,0], angle=[0,0,0], model=SkateBearing, outline=false, material=Steel, sideMaterial=Brass);

use <motors.scad>;

// * stepper_motor_mount(nema_standard, slide_distance=0, mochup=true, tolerance=0);

use <hardware.scad>;

// * screw(length, nutpos, washer, bearingpos = -1);

use <involute_gears.scad>;

end_x = 2.5*25.4 + 22 + 20;

end_plate();
//screwin();

module screwin(ang=0, tail=10, nutpos=9, shaft=12){
    rotate([0,0,ang])
    screw(shaft, nutpos, 0, $fn=24);
    translate([0,-5.8/2,-nutpos-2.5])
       cube([tail,5.8,2.5], center=false);
}

module end_plate(x = end_x, y = 70, thk = 7, boltlen=12){
    difference(){
        minkowski(){
        cylinder(1,1.5,center=false);
        cube([x,y,thk], center=false, $fn=20);
        }
        translate([x/2, 42.3/2, 22]) rotate([180,0,0])
            #steppercut();

        for (i=[1,-1]){
            translate([x/2 + i*2.5/2*25.4+i*9,13,0])
                cylinder(r=22.2/2, h=20, center=true, $fn=64);
            translate([x/2 + i*2.5/2*25.4+i*9,y-13,0])
                cylinder(r=22.2/2, h=20, center=true, $fn=64);
            translate([x/2 + i*2.5/2*25.4+i*boltlen/2+i*20,y/2-10,thk/2]) rotate([0,90,90-90*i])
                #screwin(ang=0, tail=10, nutpos=8, shaft=12);
            translate([x/2 + i*2.5/2*25.4+i*boltlen/2+i*20,y/2+10,thk/2]) rotate([0,90,90-90*i])
                #screwin(ang=0, tail=10, nutpos=8, shaft=12);
            translate([x/2 + i*2.5/4*25.4,y+4,thk/2]) rotate([0,-270,90])
                #screwin(ang=0, tail=10, nutpos=8, shaft=12);

        }
    }
}

module steppercut(xy=42.3, curve=5.7, tall=20){
    linear_extrude(height=tall)
        minkowski(){
            square([xy-curve, xy-curve], center=true);
            circle(r = curve/2, center=true, $fn=16);
        }
    linear_extrude(height=tall+12)
        stepper_motor_mount(17, slide_distance=0, mochup=true, tolerance=0);
}


Update to "pillar" holes.

Update to "pillar" holes.

// OpenSCAD for a "mezzanine" extension for a Turtlebot3 burger
//           It includes supports for a breadboard and
//           rivet holes for a battery. The breadboard is
//           centered on the origin. y is positive toward the RPi.
thickness = 4;

wide_x  = 110;    // 1/2 width of the "wings"
start_cutout_x  = 40;   // 1/2 width of cutout around the helper board
end_cutout_x    = 65;   // 1/2 width of cutout around the helper board
narrow_x= 80;    // 1/2 width of the battery holder

min_y    = 18;    // beginning of "wing
cutout_y = 0;     // close edge of the cutout
wide_y  = -26;    // The near edge the wide rectangular portion
fillet_side= 16;  // End of flare to narrow
full_y  = -86;    // Greatest extent of the plate

// NOTE: Added 1mm of "slop" to breadboard dimensions
breadboard_width = 165;     //
breadboard_height = 27;     // Chopped
breadboard_thickness = 10;
breadboard_z = 10;          // Base to bottom of breadboard
post_width   = 8;          // Support post for breadboard

pillar_x     = 24;    // Center-line to center of pillar
pillar_y     = -20;   // Baseline to center of pillar
rim          = 3;     // Width of rim

arduino_width       = 54; // y direction
battery_rivet_x     = 28; // On centerline
battery_rivet_y     = 8;  // On centerline, 16mm from rim.
main_rivet_x        = 48; // Connect to main board
main_rivet_y        = 0;

rivet_radius = 2.5;   // Hole radius for rivet attachment
round_radius = 2;     // Radius of the corner arcs

// Make 4 holders for the breadboard corners
module breadboard_posts() {
     z = breadboard_z+thickness;

     for(i=[[breadboard_width/2,breadboard_height/2,z/2],
            [-breadboard_width/2,breadboard_height/2,z/2],
            [-breadboard_width/2,-breadboard_height/2,z/2],
            [breadboard_width/2,-breadboard_height/2,z/2]]) {
                translate(i)
                cube(size=[post_width,post_width,z],center=true);
       }
}
// Offset holders for the breadboard corners to make notches at the top
module breadboard_notches() {
     z = 2*breadboard_z+thickness;
     w = post_width;
     for(i=[[breadboard_width/2-w/2,breadboard_height/2-w/2,z/2],
            [-breadboard_width/2+w/2,breadboard_height/2-w/2,z/2],
            [-breadboard_width/2+w/2,-breadboard_height/2+w/2,z/2],
            [breadboard_width/2-w/2,-breadboard_height/2+w/2,z/2]]) {
                translate(i)
                cube(size=[w,w,breadboard_thickness],center=true);
       }
}
// Subtract this from the base platform
// These are the posts.
// Cylinder arguments: height,r1,r2,center
module post_holes(z) {
    for(i=[[pillar_x,pillar_y],[-pillar_x,pillar_y]]) {
        translate(i)
        rotate(22.5,0,0)
        cylinder(z,4,4,false,$fn=6);
    }
}

// These are for connecting the main board
// z - thickness
module main_holes(z) {
    for(i=[[main_rivet_x,main_rivet_y],
           [-main_rivet_x,main_rivet_y]] ) {
        translate(i)
        cylinder(r=rivet_radius,h=z);
    }
}
// These are the various holes for battery rivets.
// We've offset by 35mm
module rivet_holes(z) {
    for(i=[[battery_rivet_x-35,0],[-battery_rivet_x-35,0],
           [-35,battery_rivet_y],[-35,-battery_rivet_y]]) {
        translate(i)
        cylinder(r=rivet_radius,h=z);
    }
}
// These are the various holes for arduino rivets.
// These are on the plus side
module arduino_holes(z) {
    for(i=[[11,17],[11,45],
           [62,0],[64,48]]) {
        translate(i)
        cylinder(r=rivet_radius,h=z);
    }
}
// These are for connecting the "ears" near the
// end of the wings.
module ear_holes(z) {
    for(i=[[wide_x-14,cutout_y-2+10],
           [wide_x-14,cutout_y-2-10],
           [-wide_x+14,cutout_y-2+10],
           [-wide_x+14,cutout_y-2-10]]) {
        translate(i)
        cylinder(r=rivet_radius,h=z);
    }
}
// 0,0 is upper left corner
module fillet(radius,angle) {
    rotate(angle,[0,0,1])
    difference() {
        square(radius,false);
        translate([radius,radius,0])
        circle(r=radius);
    }

}
// This is the basic plate. The baseline is y=0 along the edge of the helper
// board next to the RPi GPIO header. This object cannot be translated once created.
// y is positive towards the RPi. Couldn't make a single hull work.
module base(z) {
    $fn=100;
    linear_extrude(height=z,center=false,convexity=10) {
        // place circles in the corners, account for the rounding radius
        union() {
         // Breadboard rectangular area.
        hull() {
            translate([-wide_x+round_radius/2, wide_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2, wide_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([-wide_x+round_radius/2, cutout_y-round_radius/2, 0])
            circle(r=round_radius);
            }
        };
        // Battery area
        hull() {
            translate([narrow_x-round_radius/2, wide_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([narrow_x-round_radius/2, full_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([-narrow_x+round_radius/2, full_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([-narrow_x+round_radius/2, wide_y-round_radius/2, 0])
            circle(r=round_radius);
        }
        // Wingtip left
        hull() {
            translate([-wide_x+round_radius/2, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([-wide_x+round_radius/2, min_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([-end_cutout_x+round_radius/2, min_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([-start_cutout_x+round_radius/2, cutout_y+round_radius/2, 0])
            circle(r=round_radius);
        }
        // Wingtip right
        hull() {
            translate([wide_x-round_radius/2, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2, min_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([end_cutout_x-round_radius/2, min_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([start_cutout_x-round_radius/2, cutout_y+round_radius/2, 0])
            circle(r=round_radius);
        }
        translate([narrow_x,wide_y,0])
        fillet(fillet_side,270);
        translate([-narrow_x,wide_y,0])
        fillet(fillet_side,180);
    }
}

// This is the basic plate less a rim width all around. This is meant to
// be subtracted from the standard plate. There should be no rim where hulls join.
module base_cutout(z,rwidth) {
    $fn=100;
    linear_extrude(height=z,center=false,convexity=10) {
        // place circles in the corners, account for the rounding radius and rim
        // There is no rim on joint.
        union() {
            // Breadboard rectangular area.
            hull() {
            translate([-wide_x+round_radius/2+rwidth, wide_y+round_radius/2+rwidth, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2-rwidth, wide_y+round_radius/2+rwidth, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2-rwidth, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([-wide_x+round_radius/2+rwidth, cutout_y-round_radius/2, 0])
            circle(r=round_radius);
            }
        };
        // Battery area
        hull() {
            translate([narrow_x-round_radius/2-rwidth, wide_y+round_radius, 0])
            circle(r=round_radius);

            translate([narrow_x-round_radius/2-rwidth, full_y+round_radius/2+rwidth, 0])
            circle(r=round_radius);

            translate([-narrow_x+round_radius/2+rwidth, full_y+round_radius/2+rwidth, 0])
            circle(r=round_radius);

            translate([-narrow_x+round_radius/2+rwidth, wide_y+round_radius, 0])
            circle(r=round_radius);
        }
        // Wingtip left
        hull() {
            translate([-wide_x+round_radius/2+rwidth, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([-wide_x+round_radius/2+rwidth, min_y+round_radius/2-rwidth, 0])
            circle(r=round_radius);

            translate([-end_cutout_x+round_radius/2-rwidth, min_y+round_radius/2-rwidth, 0])
            circle(r=round_radius);

            translate([-start_cutout_x+round_radius/2, cutout_y-round_radius/2-rwidth, 0])
            circle(r=round_radius);
        }
        // Wingtip right
        hull() {
            translate([wide_x-round_radius/2-rwidth, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2-rwidth, min_y+round_radius/2-rwidth, 0])
            circle(r=round_radius);

            translate([end_cutout_x-round_radius/2+rwidth, min_y+round_radius/2-rwidth, 0])
            circle(r=round_radius);

            translate([start_cutout_x-round_radius/2, cutout_y-round_radius/2-rwidth, 0])
            circle(r=round_radius);
        }
        translate([narrow_x-rwidth,-wide_y+rwidth,0])
        fillet(fillet_side,270);
        translate([-narrow_x+rwidth,-wide_y+rwidth,0])
        fillet(fillet_side,180);
    }
}

// --------------------- Final Assembly ----------------------
// Baseplate
translate([0,0,-thickness/2])
difference() {
    base(thickness);
    post_holes(thickness);
    main_holes(thickness);
    ear_holes(thickness);
    translate([0,full_y+32,0]) {
        rivet_holes(thickness);
    }
    translate([0,full_y+thickness,0]) {
        arduino_holes(thickness);
    }
}
translate([0,0,thickness/2])
difference() {
     color("red")
     base(thickness);
     base_cutout(thickness,rim);
}
 translate([0,0,thickness/2])
difference() {
    breadboard_posts();
    breadboard_notches();
}

// OpenSCAD for a "mezzanine" extension for a Turtlebot3 burger
//           It includes supports for a breadboard and
//           rivet holes for a battery. The breadboard is
//           centered on the origin. y is positive toward the RPi.
thickness = 4;

wide_x  = 110;    // 1/2 width of the "wings"
start_cutout_x  = 40;   // 1/2 width of cutout around the helper board
end_cutout_x    = 65;   // 1/2 width of cutout around the helper board
narrow_x= 80;    // 1/2 width of the battery holder

min_y    = 18;    // beginning of "wing
cutout_y = 0;     // close edge of the cutout
wide_y  = -26;    // The near edge the wide rectangular portion
fillet_side= 16;  // End of flare to narrow
full_y  = -86;    // Greatest extent of the plate

// NOTE: Added 1mm of "slop" to breadboard dimensions
breadboard_width = 165;     //
breadboard_height = 27;     // Chopped
breadboard_thickness = 10;
breadboard_z = 10;          // Base to bottom of breadboard
post_width   = 8;          // Support post for breadboard

pillar_x     = 24;    // Center-line to center of pillar
pillar_y     = -20;   // Baseline to center of pillar
rim          = 3;     // Width of rim

arduino_width       = 54; // y direction
battery_rivet_x     = 28; // On centerline
battery_rivet_y     = 8;  // On centerline, 16mm from rim.
main_rivet_x        = 48; // Connect to main board
main_rivet_y        = 0;

rivet_radius = 2.5;   // Hole radius for rivet attachment
round_radius = 2;     // Radius of the corner arcs

// Make 4 holders for the breadboard corners
module breadboard_posts() {
     z = breadboard_z+thickness;

     for(i=[[breadboard_width/2,breadboard_height/2,z/2],
            [-breadboard_width/2,breadboard_height/2,z/2],
            [-breadboard_width/2,-breadboard_height/2,z/2],
            [breadboard_width/2,-breadboard_height/2,z/2]]) {
                translate(i)
                cube(size=[post_width,post_width,z],center=true);
       }
}
// Offset holders for the breadboard corners to make notches at the top
module breadboard_notches() {
     z = 2*breadboard_z+thickness;
     w = post_width;
     for(i=[[breadboard_width/2-w/2,breadboard_height/2-w/2,z/2],
            [-breadboard_width/2+w/2,breadboard_height/2-w/2,z/2],
            [-breadboard_width/2+w/2,-breadboard_height/2+w/2,z/2],
            [breadboard_width/2-w/2,-breadboard_height/2+w/2,z/2]]) {
                translate(i)
                cube(size=[w,w,breadboard_thickness],center=true);
       }
}
// Subtract this from the base platform
// These are the posts
module post_holes(z) {
    for(i=[[pillar_x,pillar_y],[-pillar_x,pillar_y]]) {
        translate(i)
        rotate(22.5,0,0)
        cylinder(r=4,h=z,$fn=6);
    }
}

// These are for connecting the main board
// z - thickness
module main_holes(z) {
    for(i=[[main_rivet_x,main_rivet_y],
           [-main_rivet_x,main_rivet_y]] ) {
        translate(i)
        cylinder(r=rivet_radius,h=z);
    }
}
// These are the various holes for battery rivets.
// We've offset by 35mm
module rivet_holes(z) {
    for(i=[[battery_rivet_x-35,0],[-battery_rivet_x-35,0],
           [-35,battery_rivet_y],[-35,-battery_rivet_y]]) {
        translate(i)
        cylinder(r=rivet_radius,h=z);
    }
}
// These are the various holes for arduino rivets.
// These are on the plus side
module arduino_holes(z) {
    for(i=[[11,17],[11,45],
           [62,0],[64,48]]) {
        translate(i)
        cylinder(r=rivet_radius,h=z);
    }
}
// These are for connecting the "ears" near the
// end of the wings.
module ear_holes(z) {
    for(i=[[wide_x-14,cutout_y-2+10],
           [wide_x-14,cutout_y-2-10],
           [-wide_x+14,cutout_y-2+10],
           [-wide_x+14,cutout_y-2-10]]) {
        translate(i)
        cylinder(r=rivet_radius,h=z);
    }
}
// 0,0 is upper left corner
module fillet(radius,angle) {
    rotate(angle,[0,0,1])
    difference() {
        square(radius,false);
        translate([radius,radius,0])
        circle(r=radius);
    }

}
// This is the basic plate. The baseline is y=0 along the edge of the helper
// board next to the RPi GPIO header. This object cannot be translated once created.
// y is positive towards the RPi. Couldn't make a single hull work.
module base(z) {
    $fn=100;
    linear_extrude(height=z,center=false,convexity=10) {
        // place circles in the corners, account for the rounding radius
        union() {
         // Breadboard rectangular area.
        hull() {
            translate([-wide_x+round_radius/2, wide_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2, wide_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([-wide_x+round_radius/2, cutout_y-round_radius/2, 0])
            circle(r=round_radius);
            }
        };
        // Battery area
        hull() {
            translate([narrow_x-round_radius/2, wide_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([narrow_x-round_radius/2, full_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([-narrow_x+round_radius/2, full_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([-narrow_x+round_radius/2, wide_y-round_radius/2, 0])
            circle(r=round_radius);
        }
        // Wingtip left
        hull() {
            translate([-wide_x+round_radius/2, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([-wide_x+round_radius/2, min_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([-end_cutout_x+round_radius/2, min_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([-start_cutout_x+round_radius/2, cutout_y+round_radius/2, 0])
            circle(r=round_radius);
        }
        // Wingtip right
        hull() {
            translate([wide_x-round_radius/2, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2, min_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([end_cutout_x-round_radius/2, min_y+round_radius/2, 0])
            circle(r=round_radius);

            translate([start_cutout_x-round_radius/2, cutout_y+round_radius/2, 0])
            circle(r=round_radius);
        }
        translate([narrow_x,wide_y,0])
        fillet(fillet_side,270);
        translate([-narrow_x,wide_y,0])
        fillet(fillet_side,180);
    }
}

// This is the basic plate less a rim width all around. This is meant to
// be subtracted from the standard plate. There should be no rim where hulls join.
module base_cutout(z,rwidth) {
    $fn=100;
    linear_extrude(height=z,center=false,convexity=10) {
        // place circles in the corners, account for the rounding radius and rim
        // There is no rim on joint.
        union() {
            // Breadboard rectangular area.
            hull() {
            translate([-wide_x+round_radius/2+rwidth, wide_y+round_radius/2+rwidth, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2-rwidth, wide_y+round_radius/2+rwidth, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2-rwidth, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([-wide_x+round_radius/2+rwidth, cutout_y-round_radius/2, 0])
            circle(r=round_radius);
            }
        };
        // Battery area
        hull() {
            translate([narrow_x-round_radius/2-rwidth, wide_y+round_radius, 0])
            circle(r=round_radius);

            translate([narrow_x-round_radius/2-rwidth, full_y+round_radius/2+rwidth, 0])
            circle(r=round_radius);

            translate([-narrow_x+round_radius/2+rwidth, full_y+round_radius/2+rwidth, 0])
            circle(r=round_radius);

            translate([-narrow_x+round_radius/2+rwidth, wide_y+round_radius, 0])
            circle(r=round_radius);
        }
        // Wingtip left
        hull() {
            translate([-wide_x+round_radius/2+rwidth, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([-wide_x+round_radius/2+rwidth, min_y+round_radius/2-rwidth, 0])
            circle(r=round_radius);

            translate([-end_cutout_x+round_radius/2-rwidth, min_y+round_radius/2-rwidth, 0])
            circle(r=round_radius);

            translate([-start_cutout_x+round_radius/2, cutout_y-round_radius/2-rwidth, 0])
            circle(r=round_radius);
        }
        // Wingtip right
        hull() {
            translate([wide_x-round_radius/2-rwidth, cutout_y-round_radius/2, 0])
            circle(r=round_radius);

            translate([wide_x-round_radius/2-rwidth, min_y+round_radius/2-rwidth, 0])
            circle(r=round_radius);

            translate([end_cutout_x-round_radius/2+rwidth, min_y+round_radius/2-rwidth, 0])
            circle(r=round_radius);

            translate([start_cutout_x-round_radius/2, cutout_y-round_radius/2-rwidth, 0])
            circle(r=round_radius);
        }
        translate([narrow_x-rwidth,-wide_y+rwidth,0])
        fillet(fillet_side,270);
        translate([-narrow_x+rwidth,-wide_y+rwidth,0])
        fillet(fillet_side,180);
    }
}

// --------------------- Final Assembly ----------------------
// Baseplate
translate([0,0,-thickness/2])
difference() {
    base(thickness);
    post_holes(thickness);
    main_holes(thickness);
    ear_holes(thickness);
    translate([0,full_y+32,0]) {
        rivet_holes(thickness);
    }
    translate([0,full_y+thickness,0]) {
        arduino_holes(thickness);
    }
}
translate([0,0,thickness/2])
difference() {
     color("red")
     base(thickness);
     base_cutout(thickness,rim);
}
 translate([0,0,thickness/2])
difference() {
    breadboard_posts();
    breadboard_notches();
}


using exterial diameter for con distancing

include <funcions.scad>;

int_dia=110;
ext_dia=int_dia+5+2*(3+10);

translate([0, ext_dia/2+15, 0])
  con();
lid(internal_diameter=int_dia, external_diameter=ext_dia);

include <funcions.scad>;

int_dia=110;
ext_dia=int_dia+5+2*(3+10);

translate([0, int_dia/2+30, 0])
  con();
lid(internal_diameter=int_dia, external_diameter=ext_dia);


pulley: bugfix alignment of idlers

pulley: bugfix alignment of idlers

include <thing_libutils/units.scad>
include <thing_libutils/shapes.scad>

// h, full_h, inner_d, outer_d, walls, bore
pulley_2GT_20T_idler = [8.65*mm, undef, 12*mm, 18*mm, 1*mm, 5*mm];
pulley_2GT_20T       = [8.65*mm, 16*mm, 12*mm, 16*mm, 1.15*mm, 5*mm];

module pulley(pulley=pulley_2GT_20T, flip=false, align=[0,0,0], orient = [0,0,1])
{
    is_idler = pulley[1] == undef;
    pulley_full(
            is_idler=is_idler,
            h=pulley[0],
            full_h=is_idler?undef:pulley[1],
            inner_d=pulley[2],
            outer_d=pulley[3],
            walls=pulley[4],
            bore=pulley[5],
            flip=flip,
            align=align,
            orient=orient
            );
}

module pulley_full(h, inner_d, outer_d, bore, walls, is_idler=false, full_h, flip=false, align=[0,0,0], orient = [0,0,1])
{
    size_align(size=[outer_d, outer_d, full_h==undef?h:full_h], align=align, orient=orient)
    {
        //for flipping
        mirror([0,0,flip?-1:0])
        //center
        translate([0,0,full_h==undef?-h/2:-full_h/2])
        difference()
        {
            union()
            {
                fncylindera(d = outer_d, h = walls, align=[0,0,1], orient=[0,0,1]);

                translate([0,0,0])
                fncylindera(d = inner_d, h = h, align=[0,0,1], orient=[0,0,1]);

                translate([0,0,h-walls])
                fncylindera(d = outer_d, h = walls, align=[0,0,1], orient=[0,0,1]);

                translate([0,0,h])
                if(!is_idler)
                {
                    fncylindera(d = outer_d, h = full_h-h, align=[0,0,1], orient=[0,0,1]);
                }
            }
            translate([0,0,-.1])
            fncylindera(d = bore, h = full_h+.2, align=[0,0,1], orient=[0,0,1]);
        }
    }
}

/*debug = true;*/
/*if(debug)*/
/*{*/
    /*pulley(pulley_2GT_20T_idler, align=[0,0,0], orient=[0,0,1]);*/
    /*pulley(pulley_2GT_20T, align=[0,0,1], orient=[0,0,1], flip=false);*/
    /*pulley(pulley_2GT_20T, align=[0,0,-1], orient=[0,0,1], flip=false);*/
/*}*/

include <thing_libutils/units.scad>
include <thing_libutils/shapes.scad>

// h, full_h, inner_d, outer_d, walls, bore
pulley_2GT_20T_idler = [8.65*mm, undef, 12*mm, 18*mm, 1*mm, 5*mm];
pulley_2GT_20T       = [8.65*mm, 16*mm, 12*mm, 16*mm, 1.15*mm, 5*mm];

module pulley(pulley=pulley_2GT_20T, flip=false, align=[0,0,0], orient = [0,0,1])
{
    is_idler = pulley[1] == undef;
    pulley_full(
            is_idler=is_idler,
            h=pulley[0],
            full_h=is_idler?0:pulley[1],
            inner_d=pulley[2],
            outer_d=pulley[3],
            walls=pulley[4],
            bore=pulley[5],
            flip=flip,
            align=align,
            orient=orient
            );
}

module pulley_full(h, inner_d, outer_d, bore, walls, is_idler=false, full_h, flip=false, align=[0,0,0], orient = [0,0,1])
{
    size_align(size=[outer_d, outer_d, full_h==undef?h:full_h], align=align, orient=orient)
    {
        //for flipping
        mirror([0,0,flip?-1:0])
        //center
        translate([0,0,full_h==undef?-h/2:-full_h/2])
        difference()
        {
            union()
            {
                fncylindera(d = outer_d, h = walls, align=[0,0,1], orient=[0,0,1]);

                translate([0,0,0])
                fncylindera(d = inner_d, h = h, align=[0,0,1], orient=[0,0,1]);

                translate([0,0,h-walls])
                fncylindera(d = outer_d, h = walls, align=[0,0,1], orient=[0,0,1]);

                translate([0,0,h])
                if(!is_idler)
                {
                    fncylindera(d = outer_d, h = full_h-h, align=[0,0,1], orient=[0,0,1]);
                }
            }
            translate([0,0,-.1])
            fncylindera(d = bore, h = full_h+.2, align=[0,0,1], orient=[0,0,1]);
        }
    }
}

/*debug = true;*/
/*if(debug)*/
/*{*/
    /*[>pulley(pulley_2GT_20T_idler, align=[0,0,1], orient=[0,0,1]);<]*/
    /*[>pulley(pulley_2GT_20T, align=[0,0,1], orient=[0,0,1], flip=false);<]*/
    /*pulley(pulley_2GT_20T, align=[0,0,-1], orient=[0,0,1], flip=false);*/
/*}*/


Create deksiriboza_1.scad

Create deksiriboza_1.scad

error: pathspec '3D-models/SCAD/deksiriboza_1.scad' did not match any file(s) known to git


x/ends: change preview setup slightly

x/ends: change preview setup slightly

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

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

motor_mount_wall_thick = xaxis_pulley[1] - xaxis_pulley[0]/2 + 4*mm;
xaxis_end_pulley_offset = 39*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=MHexNutM4, 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=MHexNutM5, 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=MHexNutM2_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=MHexNutM2_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);

                // 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=MHexNutM2_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=MHexNutM5, 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/metric-screw.scad>
use <thing_libutils/timing-belts.scad>

motor_mount_wall_thick = xaxis_pulley[1] - xaxis_pulley[0]/2 + 4*mm;
xaxis_end_pulley_offset = 39*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=MHexNutM4, 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=MHexNutM5, 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=MHexNutM2_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=MHexNutM2_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);

                // 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=MHexNutM2_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=MHexNutM5, 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,0,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);
}