main: Only render PSU's in detail mode

main: Only render PSU's in detail mode

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

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

include <config.scad>
include <misc.scad>
include <extruder-direct.scad>
include <x-carriage.scad>
include <y-axis-motor-mount.scad>
include <y-axis-carriage-bearing-mount.scad>
include <y-axis-carriage-belt-clamp.scad>
include <y-axis-idler.scad>
include <z-axis-motor-mount.scad>
include <psu.scad>
include <rod-clamps.scad>

use <scad-utils/trajectory.scad>
use <scad-utils/trajectory_path.scad>
use <scad-utils/transformations.scad>
use <scad-utils/shapes.scad>
use <list-comprehension-demos/skin.scad>

axis_pos_x = 0*mm;
axis_pos_y = 0*mm;
axis_range_z=[85,380];
axis_pos_z = axis_range_z[1]*mm/2;

module x_axis()
{
    // x axis
    translate([0,0,axis_pos_z])
    {
        if(!preview_mode)
        {
            translate([0, xaxis_zaxis_distance_y, 0])
                belt_path(500, 6, xaxis_pulley_d, orient=[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(show_bearings=true);
            }

            if(!preview_mode)
            {
                attach([[35, -0, -0], [1,0,0]], extruder_conn_xcarriage)
                {
                    extruder();
                }
            }
        }

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

module y_axis()
{
    // y axis
    attach([[yaxis_belt_path_offset_x,main_depth/2,yaxis_motor_offset_z], [0,-1,0]], yaxis_motor_mount_conn)
    {
        yaxis_motor_mount(show_motor=true);
    }

    extrusion_idler_conn = [[yaxis_belt_path_offset_x, -main_depth/2-extrusion_size, -extrusion_size+yaxis_idler_offset_z], [0,-1,0]];
    attach(extrusion_idler_conn, yaxis_idler_conn)
    {
        yaxis_idler();
        attach(yaxis_idler_conn_pulleyblock, yaxis_idler_pulleyblock_conn)
        {
            yaxis_idler_pulleyblock(show_bearing=true);
        }
    }

    translate([yaxis_belt_path_offset_x,0,yaxis_belt_path_offset_z])
    {
        if(!preview_mode)
        {
            translate([0,main_depth/2-yaxis_motor_offset_x,0])
            {
                /*cubea([10,main_depth-yaxis_motor_offset_x,10], align=[0,-1,0]);*/
                belt_path(main_depth-yaxis_motor_offset_x-yaxis_idler_pulley_offset_y, 6, yaxis_pulley_d, align=[0,-1,0], orient=[0,1,0]);
            }
        }
    }

    translate([0,-axis_pos_y,yaxis_bearing[0]/2])
    attach(yaxis_carriage_bearing_mount_conn_bearing, yaxis_belt_mount_conn)
    {
        yaxis_belt_holder();
    }

    translate([0,0,yaxis_bearing[0]/2])
    {
        for(i=[-1,1])
        for(j=[-1,1])
        {
            attach([[i*(yaxis_rod_distance/2),j*(main_depth/2+extrusion_size/2),0],[0,0,1]],mount_rod_clamp_conn_rod)
            {
                mount_rod_clamp_full(rod_d=zaxis_rod_d, thick=4, width=extrusion_size, thread=zmotor_mount_clamp_thread, orient=[0,1,0]);
            }
        }

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

            for(j=[-1,1])
            {
                attach([[0,j*(yaxis_bearing_distance_y/2)-axis_pos_y,0],[0,0,-1]], yaxis_carriage_bearing_mount_conn_bearing)
                {
                    yaxis_carriage_bearing_mount(show_bearing=true, show_zips=true);
                }
            }
        }

        attach(yaxis_carriage_bearing_mount_conn_bearing, [[0,axis_pos_y,0],[0,0,1]])
        {
            /*translate([x*yaxis_carriage_size[0]/2, y*(yaxis_carriage_size[1]-16*mm)/2, 0])*/
            // y axis plate
            for(x=[-1,1])
            for(y=[-1,1])
            hull()
            {
                translate([x*yaxis_carriage_size[0]/2, y*(yaxis_carriage_size[1])/2, 0])
                {
                    fncylindera(d=10*mm, h=yaxis_carriage_size[2], align=[-x,-y,1]);
                }
                translate([x*(yaxis_carriage_size[0]/2-16*mm), y*(yaxis_carriage_size[1]/2-16*mm), 0])
                {
                    fncylindera(d=10*mm, h=yaxis_carriage_size[2], align=[-x,-y,1]);
                }
            }

            cubea([yaxis_carriage_size[0]-16*mm*2,yaxis_carriage_size[1]-16*mm*2, yaxis_carriage_size[2]], align=[0,0,1]);

            translate([0,0,10*mm])
                cubea(yaxis_carriage_size, align=[0,0,1]);
        }
    }

}

module z_axis()
{
    // z axis
    for(i=[-1,1])
    {
        translate([i*(main_width/2),0,main_height])
        {
            mirror([i==-1?1:0,0,0])
            {
                upper_gantry_zrod_connector();

                translate([zmotor_mount_rod_offset_x, 0, extrusion_size/2])
                    mount_rod_clamp_half(
                            rod_d=zaxis_rod_d,
                            screw_dist=zmotor_mount_clamp_dist,
                            thick=5,
                            base_thick=5,
                            width=zmotor_mount_thickness_h,
                            thread=zmotor_mount_clamp_thread);
            }
        }

        translate([i*(main_width/2), 0, 0])
        {
            translate([0,0,zaxis_motor_offset_z])
                mirror([i==-1?1:0,0,0])
                {
                    zaxis_motor_mount(show_motor=true);

                    translate([zmotor_mount_rod_offset_x, 0, zmotor_mount_thickness_h/2])
                    {
                        mount_rod_clamp_half(
                                rod_d=zaxis_rod_d,
                                screw_dist=zmotor_mount_clamp_dist,
                                thick=5,
                                base_thick=5,
                                width=zmotor_mount_thickness_h,
                                thread=zmotor_mount_clamp_thread);
                    }
                }

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

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

            }

            translate([i*zmotor_mount_rod_offset_x, 0, axis_pos_z])
                #fncylindera(h=100, d=zaxis_nut[1], align=[0,0,0]);

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

}

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

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

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

    x_axis();
    y_axis();
    z_axis();

    if(!preview_mode)
    {
        for(x=[-1,1])
        translate([x*(main_width/2-extrusion_size),main_depth/2,-main_lower_dist_z-extrusion_size])
        {
            translate([-x*psu_w/2, -psu_d/2, psu_h/2])
            {
                psu();

                mirror([x==-1?1:0,0,0])
                    translate([0, -psu_screw_dist_y/2, 0])
                    psu_extrusion_bracket_side();

                translate([0, psu_screw_dist_y/2, 0])
                    psu_extrusion_bracket_back();
            }
        }

        translate([0,-main_depth/2,-main_lower_dist_z-extrusion_size])
            translate([-100,100,0])
            rotate([0,0,270])
            import("stl/RAMPS1_4.STL");
    }
}

module upper_gantry_zrod_connector()
{
    upper_width_extra = main_upper_width-main_width;
    difference()
    {
        union()
        {
            hull()
            {
                // attach to z rod
                translate([lookup(NemaSideSize,zaxis_motor)/2, 0, 0])
                translate([(zaxis_rod_screw_distance_x+zmotor_mount_motor_offset),0,0])
                {
                    cubea([gantry_connector_thickness, zmotor_mount_clamp_width, extrusion_size+gantry_connector_thickness], align=[-1,0,1]);
                }

                // connect upper gantry
                translate([upper_width_extra/2, 0, 0])
                    translate([0,0,extrusion_size/2])
                    cubea([gantry_connector_thickness,main_upper_dist_y+extrusion_size,extrusion_size], align=[1,0,0], extrasize=[0,0,gantry_connector_thickness], extrasize_align=[0,0,1]);
            }

            translate([upper_width_extra/2, 0, 0])
                translate([0,0,extrusion_size])
                cubea([extrusion_size*2,main_upper_dist_y+extrusion_size,gantry_connector_thickness], align=[-1,0,1]);

        }

        // cut out z rod mounting clamp nut traps and screw holes
        translate([0,0,extrusion_size/2])
        translate([upper_width_extra/2, 0, 0])
        for(i=[-1,1])
        {
            translate([-1, i*zmotor_mount_clamp_dist/2, 0])
            {
                fncylindera(fn=6, d=zmotor_mount_clamp_nut_dia, h=zmotor_mount_clamp_nut_thick, orient=[1,0,0], align=[1,0,0]);

                fncylindera(d=zmotor_mount_clamp_thread_dia, h=100, orient=[1,0,0], align=[1,0,0]);
            }
        }

        translate([0,0,extrusion_size/2])
        translate([upper_width_extra/4, 0, 0])
        for(x=[-1,1])
        for(y=[-1,1])
        {
            translate([x*extrusion_thread_dia*2, y*main_upper_dist_y/2, -1])
            {
                fncylindera(d=extrusion_thread_dia, h=100, orient=[0,0,1], align=[1,0,1]);
            }
        }

        // cutout for z rod
        translate([lookup(NemaSideSize,zaxis_motor)/2, 0, 0])
        translate([(zaxis_rod_screw_distance_x+zmotor_mount_motor_offset),0,extrusion_size])
        fncylindera(d=zaxis_rod_d*1.01, h=extrusion_size*3, align=[0,0,0], orient=[0,0,1]);
    }
}

module gantry_upper()
{
    for(i=[-1,1])
    translate([i*(main_width/2), 0, 0])
    {
        if(preview_mode)
        {
            cubea(size=[extrusion_size, extrusion_size, main_height], align=[-i,0,1]);
        }
        else
        {
            linear_extrusion(h=main_height, align=[-i,0,1], orient=[0,0,1]);
        }
    }

    translate([0, 0, main_height])
    {
        if(preview_mode)
        {
            cubea(size=[main_upper_width, extrusion_size, extrusion_size], align=[0,0,1]);
        }
        else
        {
            linear_extrusion(h=main_upper_width, align=[0,0,1], orient=[1,0,0]);
        }
    }
}

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

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

module enclosure()
{
    w=60*cm;
    h=60*cm;
    d=60*cm;
    wallthick=2*cm;
    backthick=0.5*cm;

    translate([0, 0, h/2])
    {
        // left/right walls
        for(i=[-1,1])
            translate([i*w/2,0,0])
                cubea([wallthick,d,h], align=[-i, 0, 0]);

        // back plate
        translate([0,d/2,0])
            cubea([w,backthick,h], align=[0,-1, 0]);

        // top/bottom plate
        for(i=[-1,1])
        translate([0,0,i*h/2])
            cubea([w-wallthick*2,d,wallthick], align=[0,0, -i]);
    }
}

translate([0, 0, main_lower_dist_z+extrusion_size*2])
main();

/*psu();*/

/*%enclosure();*/

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

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

include <config.scad>
include <misc.scad>
include <extruder-direct.scad>
include <x-carriage.scad>
include <y-axis-motor-mount.scad>
include <y-axis-carriage-bearing-mount.scad>
include <y-axis-carriage-belt-clamp.scad>
include <y-axis-idler.scad>
include <z-axis-motor-mount.scad>
include <psu.scad>
include <rod-clamps.scad>

use <scad-utils/trajectory.scad>
use <scad-utils/trajectory_path.scad>
use <scad-utils/transformations.scad>
use <scad-utils/shapes.scad>
use <list-comprehension-demos/skin.scad>

axis_pos_x = 0*mm;
axis_pos_y = 0*mm;
axis_range_z=[85,380];
axis_pos_z = axis_range_z[1]*mm/2;

module x_axis()
{
    // x axis
    translate([0,0,axis_pos_z])
    {
        if(!preview_mode)
        {
            translate([0, xaxis_zaxis_distance_y, 0])
                belt_path(500, 6, xaxis_pulley_d, orient=[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(show_bearings=true);
            }

            if(!preview_mode)
            {
                attach([[35, -0, -0], [1,0,0]], extruder_conn_xcarriage)
                {
                    extruder();
                }
            }
        }

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

module y_axis()
{
    // y axis
    attach([[yaxis_belt_path_offset_x,main_depth/2,yaxis_motor_offset_z], [0,-1,0]], yaxis_motor_mount_conn)
    {
        yaxis_motor_mount(show_motor=true);
    }

    extrusion_idler_conn = [[yaxis_belt_path_offset_x, -main_depth/2-extrusion_size, -extrusion_size+yaxis_idler_offset_z], [0,-1,0]];
    attach(extrusion_idler_conn, yaxis_idler_conn)
    {
        yaxis_idler();
        attach(yaxis_idler_conn_pulleyblock, yaxis_idler_pulleyblock_conn)
        {
            yaxis_idler_pulleyblock(show_bearing=true);
        }
    }

    translate([yaxis_belt_path_offset_x,0,yaxis_belt_path_offset_z])
    {
        if(!preview_mode)
        {
            translate([0,main_depth/2-yaxis_motor_offset_x,0])
            {
                /*cubea([10,main_depth-yaxis_motor_offset_x,10], align=[0,-1,0]);*/
                belt_path(main_depth-yaxis_motor_offset_x-yaxis_idler_pulley_offset_y, 6, yaxis_pulley_d, align=[0,-1,0], orient=[0,1,0]);
            }
        }
    }

    translate([0,-axis_pos_y,yaxis_bearing[0]/2])
    attach(yaxis_carriage_bearing_mount_conn_bearing, yaxis_belt_mount_conn)
    {
        yaxis_belt_holder();
    }

    translate([0,0,yaxis_bearing[0]/2])
    {
        for(i=[-1,1])
        for(j=[-1,1])
        {
            attach([[i*(yaxis_rod_distance/2),j*(main_depth/2+extrusion_size/2),0],[0,0,1]],mount_rod_clamp_conn_rod)
            {
                mount_rod_clamp_full(rod_d=zaxis_rod_d, thick=4, width=extrusion_size, thread=zmotor_mount_clamp_thread, orient=[0,1,0]);
            }
        }

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

            for(j=[-1,1])
            {
                attach([[0,j*(yaxis_bearing_distance_y/2)-axis_pos_y,0],[0,0,-1]], yaxis_carriage_bearing_mount_conn_bearing)
                {
                    yaxis_carriage_bearing_mount(show_bearing=true, show_zips=true);
                }
            }
        }

        attach(yaxis_carriage_bearing_mount_conn_bearing, [[0,axis_pos_y,0],[0,0,1]])
        {
            /*translate([x*yaxis_carriage_size[0]/2, y*(yaxis_carriage_size[1]-16*mm)/2, 0])*/
            // y axis plate
            for(x=[-1,1])
            for(y=[-1,1])
            hull()
            {
                translate([x*yaxis_carriage_size[0]/2, y*(yaxis_carriage_size[1])/2, 0])
                {
                    fncylindera(d=10*mm, h=yaxis_carriage_size[2], align=[-x,-y,1]);
                }
                translate([x*(yaxis_carriage_size[0]/2-16*mm), y*(yaxis_carriage_size[1]/2-16*mm), 0])
                {
                    fncylindera(d=10*mm, h=yaxis_carriage_size[2], align=[-x,-y,1]);
                }
            }

            cubea([yaxis_carriage_size[0]-16*mm*2,yaxis_carriage_size[1]-16*mm*2, yaxis_carriage_size[2]], align=[0,0,1]);

            translate([0,0,10*mm])
                cubea(yaxis_carriage_size, align=[0,0,1]);
        }
    }

}

module z_axis()
{
    // z axis
    for(i=[-1,1])
    {
        translate([i*(main_width/2),0,main_height])
        {
            mirror([i==-1?1:0,0,0])
            {
                upper_gantry_zrod_connector();

                translate([zmotor_mount_rod_offset_x, 0, extrusion_size/2])
                    mount_rod_clamp_half(
                            rod_d=zaxis_rod_d,
                            screw_dist=zmotor_mount_clamp_dist,
                            thick=5,
                            base_thick=5,
                            width=zmotor_mount_thickness_h,
                            thread=zmotor_mount_clamp_thread);
            }
        }

        translate([i*(main_width/2), 0, 0])
        {
            translate([0,0,zaxis_motor_offset_z])
                mirror([i==-1?1:0,0,0])
                {
                    zaxis_motor_mount(show_motor=true);

                    translate([zmotor_mount_rod_offset_x, 0, zmotor_mount_thickness_h/2])
                    {
                        mount_rod_clamp_half(
                                rod_d=zaxis_rod_d,
                                screw_dist=zmotor_mount_clamp_dist,
                                thick=5,
                                base_thick=5,
                                width=zmotor_mount_thickness_h,
                                thread=zmotor_mount_clamp_thread);
                    }
                }

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

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

            }

            translate([i*zmotor_mount_rod_offset_x, 0, axis_pos_z])
                #fncylindera(h=100, d=zaxis_nut[1], align=[0,0,0]);

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

}

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

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

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

    x_axis();
    y_axis();
    z_axis();

    for(x=[-1,1])
    translate([x*(main_width/2-extrusion_size),main_depth/2,-main_lower_dist_z-extrusion_size])
    {
        translate([-x*psu_w/2, -psu_d/2, psu_h/2])
        {
            psu();

            mirror([x==-1?1:0,0,0])
            translate([0, -psu_screw_dist_y/2, 0])
                psu_extrusion_bracket_side();

            translate([0, psu_screw_dist_y/2, 0])
                psu_extrusion_bracket_back();
        }
    }

    if(!preview_mode)
    {
        translate([0,-main_depth/2,-main_lower_dist_z-extrusion_size])
            translate([-100,100,0])
            rotate([0,0,270])
            import("stl/RAMPS1_4.STL");
    }
}

module upper_gantry_zrod_connector()
{
    upper_width_extra = main_upper_width-main_width;
    difference()
    {
        union()
        {
            hull()
            {
                // attach to z rod
                translate([lookup(NemaSideSize,zaxis_motor)/2, 0, 0])
                translate([(zaxis_rod_screw_distance_x+zmotor_mount_motor_offset),0,0])
                {
                    cubea([gantry_connector_thickness, zmotor_mount_clamp_width, extrusion_size+gantry_connector_thickness], align=[-1,0,1]);
                }

                // connect upper gantry
                translate([upper_width_extra/2, 0, 0])
                    translate([0,0,extrusion_size/2])
                    cubea([gantry_connector_thickness,main_upper_dist_y+extrusion_size,extrusion_size], align=[1,0,0], extrasize=[0,0,gantry_connector_thickness], extrasize_align=[0,0,1]);
            }

            translate([upper_width_extra/2, 0, 0])
                translate([0,0,extrusion_size])
                cubea([extrusion_size*2,main_upper_dist_y+extrusion_size,gantry_connector_thickness], align=[-1,0,1]);

        }

        // cut out z rod mounting clamp nut traps and screw holes
        translate([0,0,extrusion_size/2])
        translate([upper_width_extra/2, 0, 0])
        for(i=[-1,1])
        {
            translate([-1, i*zmotor_mount_clamp_dist/2, 0])
            {
                fncylindera(fn=6, d=zmotor_mount_clamp_nut_dia, h=zmotor_mount_clamp_nut_thick, orient=[1,0,0], align=[1,0,0]);

                fncylindera(d=zmotor_mount_clamp_thread_dia, h=100, orient=[1,0,0], align=[1,0,0]);
            }
        }

        translate([0,0,extrusion_size/2])
        translate([upper_width_extra/4, 0, 0])
        for(x=[-1,1])
        for(y=[-1,1])
        {
            translate([x*extrusion_thread_dia*2, y*main_upper_dist_y/2, -1])
            {
                fncylindera(d=extrusion_thread_dia, h=100, orient=[0,0,1], align=[1,0,1]);
            }
        }

        // cutout for z rod
        translate([lookup(NemaSideSize,zaxis_motor)/2, 0, 0])
        translate([(zaxis_rod_screw_distance_x+zmotor_mount_motor_offset),0,extrusion_size])
        fncylindera(d=zaxis_rod_d*1.01, h=extrusion_size*3, align=[0,0,0], orient=[0,0,1]);
    }
}

module gantry_upper()
{
    for(i=[-1,1])
    translate([i*(main_width/2), 0, 0])
    {
        if(preview_mode)
        {
            cubea(size=[extrusion_size, extrusion_size, main_height], align=[-i,0,1]);
        }
        else
        {
            linear_extrusion(h=main_height, align=[-i,0,1], orient=[0,0,1]);
        }
    }

    translate([0, 0, main_height])
    {
        if(preview_mode)
        {
            cubea(size=[main_upper_width, extrusion_size, extrusion_size], align=[0,0,1]);
        }
        else
        {
            linear_extrusion(h=main_upper_width, align=[0,0,1], orient=[1,0,0]);
        }
    }
}

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

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

module enclosure()
{
    w=60*cm;
    h=60*cm;
    d=60*cm;
    wallthick=2*cm;
    backthick=0.5*cm;

    translate([0, 0, h/2])
    {
        // left/right walls
        for(i=[-1,1])
            translate([i*w/2,0,0])
                cubea([wallthick,d,h], align=[-i, 0, 0]);

        // back plate
        translate([0,d/2,0])
            cubea([w,backthick,h], align=[0,-1, 0]);

        // top/bottom plate
        for(i=[-1,1])
        translate([0,0,i*h/2])
            cubea([w-wallthick*2,d,wallthick], align=[0,0, -i]);
    }
}

translate([0, 0, main_lower_dist_z+extrusion_size*2])
main();

/*psu();*/

/*%enclosure();*/


font.scad is now a module as well

font.scad is now a module as well

use <lcd.scad>
use <menu_knob.scad>
use <menu_knob_manipulator.scad>
use <reset_button.scad>
use <sdcard_reader.scad>
use <cable_guide.scad>
include <positions.scad>

module front()
{
  difference()
  {
    union()
    {
      // main mounting legs
      for(offset = [ [boxSize[0]-16, 0, boxSize[2]-7], [boxSize[0]-70, 0, boxSize[2]-7] ])
        translate(offset)
          cube([16, 40+13, 7]);
      // side mounting wing
      translate([15, 0, 6])
        cube([18, 20, 7]);

      difference()
      {
        // main working area
        cube(boxSize);
        // prusa's framework  corner
        translate([15, 0, 0])
          cube([143-15, 53, boxSize[2]]);
        // LCD
        translate(lcdOffset)
        {
          // LCD itself
          translate([1/2-2/2, 10-1, 2])
            cube([97+2, 40+2, 10]);
          // place for PCB
          translate([-2/2, -1/2, -lcdOffset[2]-7])
            cube([98+2, 60+1, 10]);
          // place for cables
          translate([9, 60-5, -2])
            cube([39,10,4]);
          lcdBoard();
        }
        // encoder
        translate(encoderOffset)
        {
          translate([-2, -1/2, -8])
            cube([14+2*2, 12+1, 8+6]);
          menuKnob();
        }
        // SD card mount
        translate(sdCardOffset)
          rotate([0,180,0])
            sdCardReader();
        // reset button
        translate(resetButtonOffset)
        {
          translate([0, 0, -resetButtonOffset[2]])
            cylinder(r=(16+1)/2, h=boxSize[2])
          resetButton();
        }
        // cable guide drill
        translate([boxSize[0]-98-5, 40+16+6, 0])
          cableGuide();
        // side mounting wing cut-in, so that screw will hold tight
        cube([15, 40, 6]);
      }
    }

    // SD card mounting drills
    translate(sdCardOffset)
      rotate([0,180,0])
      {
        for(offset = [
                       [3/2+1, 3/2+1, -20/2],
                       [3/2+1, 31-3/2-1, -20/2]
                     ])
        {
          translate(offset)
            cylinder(r=3/2, h=20, $fs=1);
        }
      }
    // SD card mounting place
    translate(sdCardOffset + [+2/2, -2/2, 0])
      rotate([0,180,0])
        cube([45+2,31+2,2+8]);

    // LCD mounting drills
    translate(lcdOffset)
      for(offset = [
                     [3/2+1, 3/2+1, -1],
                     [98-3/2-1, 3/2+1, -1],
                     [3/2+1, 60-3/2-1, -1],
                     [98-3/2-1, 60-3/2-1, -1]
                   ])
      {
        translate(offset)
          cylinder(r=3/2, h=20, $fs=1);
      }

    // frame-mounting drills
    for(offsetOY = [ 13/2+5/2, 40+13+5])
      for(offsetOX = [15/2, boxSize[0]-70+16/2, boxSize[0]-16/2])
        translate([offsetOX, offsetOY, -1])
          cylinder(r=5/2, h=6+7+2, $fs=1);
  }

  // LCD stub
  %translate(lcdOffset)
    lcdBoard();
  // encoder stub
  %translate(encoderOffset)
  {
    menuKnob();
    translate([7, 6, 23+4])
      rotate([180,0,0])
        menuKnobManipulator();
  }
  // SD card mount stub
  %translate(sdCardOffset)
    rotate([0,180,0])
      sdCardReader();
  // reset button stub
  %translate(resetButtonOffset)
    resetButton();
}

//rotate([180, 0, 0])
  front();

use <lcd.scad>
use <menu_knob.scad>
use <menu_knob_manipulator.scad>
use <reset_button.scad>
use <sdcard_reader.scad>
use <cable_guide.scad>
include <positions.scad>

//rotate([180, 0, 0])
{
  difference()
  {
    union()
    {
      // main mounting legs
      for(offset = [ [boxSize[0]-16, 0, boxSize[2]-7], [boxSize[0]-70, 0, boxSize[2]-7] ])
        translate(offset)
          cube([16, 40+13, 7]);
      // side mounting wing
      translate([15, 0, 6])
        cube([18, 20, 7]);

      difference()
      {
        // main working area
        cube(boxSize);
        // prusa's framework  corner
        translate([15, 0, 0])
          cube([143-15, 53, boxSize[2]]);
        // LCD
        translate(lcdOffset)
        {
          // LCD itself
          translate([1/2-2/2, 10-1, 2])
            cube([97+2, 40+2, 10]);
          // place for PCB
          translate([-2/2, -1/2, -lcdOffset[2]-7])
            cube([98+2, 60+1, 10]);
          // place for cables
          translate([9, 60-5, -2])
            cube([39,10,4]);
          lcdBoard();
        }
        // encoder
        translate(encoderOffset)
        {
          translate([-2, -1/2, -8])
            cube([14+2*2, 12+1, 8+6]);
          menuKnob();
        }
        // SD card mount
        translate(sdCardOffset)
          rotate([0,180,0])
            sdCardReader();
        // reset button
        translate(resetButtonOffset)
        {
          translate([0, 0, -resetButtonOffset[2]])
            cylinder(r=(16+1)/2, h=boxSize[2])
          resetButton();
        }
        // cable guide drill
        translate([boxSize[0]-98-5, 40+16+6, 0])
          cableGuide();
        // side mounting wing cut-in, so that screw will hold tight
        cube([15, 40, 6]);
      }
    }

    // SD card mounting drills
    translate(sdCardOffset)
      rotate([0,180,0])
      {
        for(offset = [
                       [3/2+1, 3/2+1, -20/2],
                       [3/2+1, 31-3/2-1, -20/2]
                     ])
        {
          translate(offset)
            cylinder(r=3/2, h=20, $fs=1);
        }
      }
    // SD card mounting place
    translate(sdCardOffset + [+2/2, -2/2, 0])
      rotate([0,180,0])
        cube([45+2,31+2,2+8]);

    // LCD mounting drills
    translate(lcdOffset)
      for(offset = [
                     [3/2+1, 3/2+1, -1],
                     [98-3/2-1, 3/2+1, -1],
                     [3/2+1, 60-3/2-1, -1],
                     [98-3/2-1, 60-3/2-1, -1]
                   ])
      {
        translate(offset)
          cylinder(r=3/2, h=20, $fs=1);
      }

    // frame-mounting drills
    for(offsetOY = [ 13/2+5/2, 40+13+5])
      for(offsetOX = [15/2, boxSize[0]-70+16/2, boxSize[0]-16/2])
        translate([offsetOX, offsetOY, -1])
          cylinder(r=5/2, h=6+7+2, $fs=1);
  }

  // LCD stub
  %translate(lcdOffset)
    lcdBoard();
  // encoder stub
  %translate(encoderOffset)
  {
    menuKnob();
    translate([7, 6, 23+4])
      rotate([180,0,0])
        menuKnobManipulator();
  }
  // SD card mount stub
  %translate(sdCardOffset)
    rotate([0,180,0])
      sdCardReader();
  // reset button stub
  %translate(resetButtonOffset)
    resetButton();
}


misc: add fallback and v_fallback

misc: add fallback and v_fallback

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 vec_add(vec,v) = [for(vv=vec) vv+v];

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_= e==undef ? len(v)-1 : e)
(e==start ? v[e] : v[e_] + v_sum(v,e_-1,start));

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) = [for(i=[start:1:len(v)-1]) 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);


Add documentation

Add documentation

// Module containing code for revised Travel Bracket for Wintermute Delta Printer
module TravelBracket()
{
	difference()
	{
		union()
		{
			// Import base HIMAWIRI STL from approved directory
			import("..//..//approved//TRV_BRKT.stl");

			// Fill in unneeded holes
			// These aspects of the original HIMAWRI base printer are not used on
			// the Wintermute fork
			translate([16,17,3])rotate([90,0,0])cylinder(h=10,r=1.8,$fn=40);
			translate([16,17,11])rotate([90,0,0])cylinder(h=10,r=1.8,$fn=40);
			translate([22,9,0])cube([6,8,14]);
			translate([-22,9,0])mirror([1,0,0])cube([6,8,14]);
			translate([22,-9,0])mirror([0,1,0])cube([6,6,14]);
			translate([-22,-9,0])mirror([1,1,0])cube([6,6,14]);
			translate([13,11,0])cube([6,3,14]);

			// Linear bearing holes
			difference()
			{
				// Cylinder defining the outer shell
				union()
				{
					translate([25,0,0])cylinder(h=16,r=10,$fn=40);	// Left
					translate([-25,0,0])cylinder(h=16,r=10,$fn=40);	// Right
				}

				// Cylider defining the inner section to be removed
				// (Where the linear bearings will fit)
				union()
				{
					translate([25,0,-1])cylinder(h=18,r=8,$fn=40);	// Left
					translate([-25,0,-1])cylinder(h=18,r=8,$fn=40);	// Right
				}
			}
		}
		// Holes for set screws to add further support for linear bearings
		translate([-25,0,7])rotate([90,0,0])cylinder(h=40,r=1.8,$fn=40,center=true);
		translate([25,0,7])rotate([90,0,0])cylinder(h=40,r=1.8,$fn=40,center=true);
	}
}

// Module defining the shape of a M3 Nut for screw mounts
module NutSocket(width,depth,height)
{
	side_length=sqrt(pow(width,2)/2);
	translate([0,0,height/2])cube([width,depth,height],center=true);
	rotate([0,45,0])cube([side_length,depth,side_length],center=true);
}

difference()
{
	TravelBracket();

	union()
{
		translate([-25,12.5,5.5])NutSocket(6,3,14);
		translate([25,12.5,5.5])NutSocket(6,3,14);
		mirror([0,1,0])translate([-25,11,5.5])NutSocket(6,3,14);
		mirror([0,1,0])translate([25,11,5.5])NutSocket(6,3,14);
	}
}

module TravelBracket(){
difference(){
	union(){
		import("..//..//approved//TRV_BRKT.stl");

		// Fill in unneeded holes
		color([255,0,0])translate([16,17,3])rotate([90,0,0])cylinder(h=10,r=1.8,$fn=40);
		color([255,0,0])translate([16,17,11])rotate([90,0,0])cylinder(h=10,r=1.8,$fn=40);

		translate([22,9,0])cube([6,8,14]);
		translate([-22,9,0])mirror([1,0,0])cube([6,8,14]);
		translate([22,-9,0])mirror([0,1,0])cube([6,6,14]);
		translate([-22,-9,0])mirror([1,1,0])cube([6,6,14]);

		translate([13,11,0])cube([6,3,14]);

		// Linear bearing holes
		difference(){
			union(){
				translate([25,0,0])cylinder(h=16,r=10,$fn=40);
				translate([-25,0,0])cylinder(h=16,r=10,$fn=40);
			}

			union(){
				translate([25,0,-1])cylinder(h=18,r=8,$fn=40);
				translate([-25,0,-1])cylinder(h=18,r=8,$fn=40);
			}
		}

	}

		// Mount screw Linear bearing
		translate([-25,0,7])rotate([90,0,0])cylinder(h=40,r=1.8,$fn=40,center=true);
		translate([25,0,7])rotate([90,0,0])cylinder(h=40,r=1.8,$fn=40,center=true);

}

}

module NutSocket(width,depth,height)
{
	side_length=sqrt(pow(width,2)/2);
	translate([0,0,height/2])cube([width,depth,height],center=true);
	rotate([0,45,0])cube([side_length,depth,side_length],center=true);
}

difference(){
	TravelBracket();

	union(){
		translate([-25,12.5,5.5])NutSocket(6,3,14);
		translate([25,12.5,5.5])NutSocket(6,3,14);
		mirror([0,1,0])translate([-25,11,5.5])NutSocket(6,3,14);
		mirror([0,1,0])translate([25,11,5.5])NutSocket(6,3,14);
	}
}


doc: fix typo

doc: fix typo

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2017-2022 Jean-Sebastien CONAN
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * Part of the camelSCAD library.
 *
 * Global constants to include.
 *
 * @package core/constants
 * @author jsconan
 */

/**
 * The name of the development rendering mode.
 * @type String
 */
MODE_DEV = "dev";

/**
 * The name of the production rendering mode.
 * @type String
 */
MODE_PROD = "prod";

/**
 * The name of the dirty rendering mode.
 * @type String
 */
MODE_DIRTY = "dirty";

/**
 * The name of the default rendering mode.
 * @type String
 */
DEFAULT_MODE = MODE_DEV;

/**
 * Degrees in a circle.
 * @type Number
 */
DEGREES = 360;

/**
 * Degrees of a right angle.
 * @type Number
 */
RIGHT = 90;

/**
 * Degrees of a straight angle.
 * @type Number
 */
STRAIGHT = 180;

/**
 * The end angle of the first quadrant in a circle.
 * @type Number
 */
QUADRANT_1 = RIGHT;

/**
 * The end angle of the second quadrant in a circle.
 * @type Number
 */
QUADRANT_2 = STRAIGHT;

/**
 * The end angle of the third quadrant in a circle.
 * @type Number
 */
QUADRANT_3 = DEGREES - RIGHT;

/**
 * The end angle of the fourth quadrant in a circle.
 * @type Number
 */
QUADRANT_4 = DEGREES;

/**
 * 2D-vector for the origin coordinates.
 * @type Number
 */
ORIGIN_2D = [0, 0];

/**
 * 3D-vector for the origin coordinates.
 * @type Number
 */
ORIGIN_3D = [0, 0, 0];

/**
 * Useful value for computations based on hexagons.
 * @type Number
 */
SQRT3 = sqrt(3);

/**
 * The distance tolerance that will apply to $fs to check if we still need to subdivide the curve.
 * @type Number
 */
BEZIER_TOLERANCE = .25;

/**
 * Minimum value for $fa and $fs.
 * @type Number
 */
MIN_ANGLE = 0.01;

/**
 * Minimum allowed size.
 * @type Number
 */
MIN_SIZE = 0.000001;

/**
 * A very very small value.
 * @type Number
 */
EPSILON = 0.0000001;

/**
 * The maximum number of decimals
 * @type Number
 */
MAX_DECIMALS = 7;

/**
 * The maximum recursion depth
 * @type Number
 */
MAX_RECURSE = 32;

/**
 * A value representing the Infinity.
 * @type Number
 */
INFINITY = 1e200 * 1e200;

/**
 * Not A Number.
 * @type Number
 */
NAN = 0/0;

/**
 * A value utilized to align wall and ensure proper cuts on one side.
 * @type Number
 */
ALIGN = 0.005;

/**
 * A value utilized to align wall and ensure proper cuts on both sides.
 * @type Number
 */
ALIGN2 = 2 * ALIGN;

/**
 * The default size of the build plate.
 * @type Number
 */
DEFAULT_BUILD_PLATE_SIZE = 200;

/**
 * The default size of the build volume.
 * @type Number
 */
DEFAULT_BUILD_VOLUME_SIZE = DEFAULT_BUILD_PLATE_SIZE;

/**
 * The default size of a cell on the build plate.
 * @type Number
 */
DEFAULT_BUILD_PLATE_CELL = 10;

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2017-2022 Jean-Sebastien CONAN
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * Part of the camelSCAD library.
 *
 * Global constants to include.
 *
 * @package core/constants
 * @author jsconan
 */

/**
 * The name of the development rendering mode.
 * @type String
 */
MODE_DEV = "dev";

/**
 * The name of the production rendering mode.
 * @type String
 */
MODE_PROD = "prod";

/**
 * The name of the dirty rendering mode.
 * @type String
 */
MODE_DIRTY = "dirty";

/**
 * The name of the default rendering mode.
 * @type String
 */
DEFAULT_MODE = MODE_DEV;

/**
 * Degrees in a circle.
 * @type Number
 */
DEGREES = 360;

/**
 * Degrees of a right angle.
 * @type Number
 */
RIGHT = 90;

/**
 * Degrees of a straight angle.
 * @type Number
 */
STRAIGHT = 180;

/**
 * The end angle of the first quadrant in a circle.
 * @type Number
 */
QUADRANT_1 = RIGHT;

/**
 * The end angle of the second quadrant in a circle.
 * @type Number
 */
QUADRANT_2 = STRAIGHT;

/**
 * The end angle of the third quadrant in a circle.
 * @type Number
 */
QUADRANT_3 = DEGREES - RIGHT;

/**
 * The end angle of the fourth quadrant in a circle.
 * @type Number
 */
QUADRANT_4 = DEGREES;

/**
 * 2D-vector for the orgigin coordinates.
 * @type Number
 */
ORIGIN_2D = [0, 0];

/**
 * 3D-vector for the orgigin coordinates.
 * @type Number
 */
ORIGIN_3D = [0, 0, 0];

/**
 * Useful value for computations based on hexagons.
 * @type Number
 */
SQRT3 = sqrt(3);

/**
 * The distance tolerance that will apply to $fs to check if we still need to subdivide the curve.
 * @type Number
 */
BEZIER_TOLERANCE = .25;

/**
 * Minimum value for $fa and $fs.
 * @type Number
 */
MIN_ANGLE = 0.01;

/**
 * Minimum allowed size.
 * @type Number
 */
MIN_SIZE = 0.000001;

/**
 * A very very small value.
 * @type Number
 */
EPSILON = 0.0000001;

/**
 * The maximum number of decimals
 * @type Number
 */
MAX_DECIMALS = 7;

/**
 * The maximum recursion depth
 * @type Number
 */
MAX_RECURSE = 32;

/**
 * A value representing the Infinity.
 * @type Number
 */
INFINITY = 1e200 * 1e200;

/**
 * Not A Number.
 * @type Number
 */
NAN = 0/0;

/**
 * A value utilized to align wall and ensure proper cuts on one side.
 * @type Number
 */
ALIGN = 0.005;

/**
 * A value utilized to align wall and ensure proper cuts on both sides.
 * @type Number
 */
ALIGN2 = 2 * ALIGN;

/**
 * The default size of the build plate.
 * @type Number
 */
DEFAULT_BUILD_PLATE_SIZE = 200;

/**
 * The default size of the build volume.
 * @type Number
 */
DEFAULT_BUILD_VOLUME_SIZE = DEFAULT_BUILD_PLATE_SIZE;

/**
 * The default size of a cell on the build plate.
 * @type Number
 */
DEFAULT_BUILD_PLATE_CELL = 10;


Tri at my age

Tri at my age

shed_length = 5184;
shed_width = 1829+305/2;
shed_front_height = 2400;
shed_front_back = 2135;
base_timber = 47;
base_sheet_t = 18;

module cubeI(size,center=false)
{
    echo("Dim: ",size);
    cube(size, center=center);
}

module shedBaseSection(x,y,timber_width=75,timber_length=47, braces=0)
{
    echo("shedBaseSection: x,y,timber_width,timber_length, braces ", x,y,timber_width,timber_length, braces);
    echo("http://www.wickes.co.uk/Wickes-Sawn-Kiln-Dried-47X75-x-3000mm-Single/p/107186#tab-DynamicDeliveryWebTab_content");
    cubeI([x,timber_width,timber_length]);
    translate([0,y-timber_width,0]) cubeI([x,timber_width,timber_length]);
    translate([0,timber_width,0]) cubeI([timber_width,y-timber_width*2,timber_length]);
    translate([x-timber_width,timber_width,0]) cubeI([timber_width,y-timber_width*2,timber_length]);
    if(braces > 0)
    {
        middle_y = y - timber_width*2;
        part_y = middle_y/(braces+1);
        for(loop=[1 : braces ])
        {
                translate([timber_width,timber_width/(braces+1) + (loop*part_y),0]) cubeI([x-timber_width*2, timber_width,timber_length]);
        }
    }
}

module shedBase(x,y,number_x,number_y, braces=1)
{
    for(base_y = [0 : number_y-1])
    {
        for(base_x = [0 : number_x-1])
            {
                translate([base_x*(x/number_x),base_y*(y/number_y),0]) shedBaseSection(x/number_x,y/number_y, braces = braces);
            }
    }
}

module floorMembrane(x,y,thickness,color="Black", overlap=10)
{
    echo("x,y,thickness=1,color, overlap=1",x,y,thickness,color, overlap);
    echo("http://www.screwfix.com/p/dmp-damp-proof-membrane-black-1000ga-3-x-4m/50464#product_additional_details_container");
    translate([-overlap,-overlap,0]) color(color, 1) cube([x+overlap*2,y+overlap*2, thickness]);
}

//module shedLowerFloor(x,y, panel_slf_w = 607, panel_slf_l =   1829, panel_slf_t =   5.5, panel_min_dimentions = 100)
module shedLowerFloor(x,y, panel_slf_w = 1220, panel_slf_l = 2440, panel_slf_t = base_sheet_t, panel_min_dimentions = 100)
{
//    echo("http://www.wickes.co.uk/Wickes-Non-Structural-Hardwood-Plywood-5-5x607x1829mm/p/111196");
    echo("http://www.wickes.co.uk/Wickes-General-Purpose-OSB3-Board-18x1220x2440mm/p/110517");
    x_panels = round(-0.5+x/panel_slf_w);
    y_panels = round(-0.5+y/panel_slf_l);
    echo(y_panels);
    if(y_panels>0) // Panel is Not longer than width
    {
        for(base_y = [0 : y_panels-1]) //Truncate not round
        {
            for(base_x = [0 : x_panels-1])
            {
                translate([base_x*panel_slf_w,base_y*panel_slf_l,0]) cubeI([panel_slf_w, panel_slf_l,panel_slf_t]);
                if(y-(panel_slf_l*y_panels) > panel_min_dimentions)
                translate([base_x*panel_slf_w,y_panels*panel_slf_l,0]) cubeI([panel_slf_w, y-(panel_slf_l*y_panels),panel_slf_t]);
                }
            if(x-(panel_slf_w*x_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,(y_panels-1)*panel_slf_l,0]) cubeI([x-(panel_slf_w*x_panels), panel_slf_l,panel_slf_t]);
            if(y-(panel_slf_l*y_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,y_panels*panel_slf_l,0]) cubeI([x-(panel_slf_w*x_panels), y-(panel_slf_l*y_panels),panel_slf_t]);
        }
    }
    else // Panel is longer than width
    {
     for(base_x = [0 : x_panels-1])
     {
        translate([base_x*panel_slf_w,0,0]) cubeI([panel_slf_w, y,panel_slf_t]);
     }
     if(x-(panel_slf_w*x_panels) > panel_min_dimentions)
                translate([(x_panels)*panel_slf_w,0,0]) cubeI([x-(panel_slf_w*x_panels), y,panel_slf_t]);
    }
}

module verticalStruts(x,z,timber_width=47,timber_length=47, braces=0, beams=0)
{
//    echo("shedBaseSection(x,z,timber_width,timber_length, braces",x,z,timber_width,timber_length, braces);
//    echo("Walls - http://www.wickes.co.uk/Wickes-Sawn-Kiln-Dried-47-x-47-x-2400mm-Pack-6/p/107114");
    cubeI([x,timber_width,timber_length]);
    translate([0,0,z-timber_width]) cubeI([x,timber_width,timber_length]);
    translate([0,0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
    translate([x-timber_width,0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
    if(braces > 0)
    {
        middle_z = z - timber_width*2;
        part_z = middle_z/(braces+1);
        for(loop=[1 : braces ])
        {
                translate([timber_width,0,timber_width/(braces+1) + (loop*part_z)]) cubeI([x-timber_width*2, timber_width,timber_length]);
        }
    }
    if(beams> 0)
    {
        middle_x = x - timber_length*2;
        part_x = middle_x/(beams+1);
        for(loop=[1 : beams ])
        {
                translate([timber_length/(beams+1) + (loop*part_x),0,timber_width]) cubeI([timber_length, timber_width,z-timber_length*2]);
        }
    }
}

module backWallStruts(x,z,number_x, braces=1)
{
    echo("backWallStruts(x,z,number_x, braces)",x,z,number_x, braces);
        for(base_x = [0 : number_x-1])
            {
                translate([base_x*(x/number_x),0,0]) verticalStruts(x/number_x,z, braces = braces);
            }
}

module sideWallStruts(x,z,number_x, braces=1)
{
    echo("sideWallStruts(x,z,number_x, braces)",x,z,number_x, braces);
    rotate([0,0,90]) backWallStruts(x,z,number_x, braces);
}

module frontWallStruts(x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h, braces)
{
    echo("frontWallStruts(x,z,space_to_window_w, space_to_window_h, window_w, window_h, door_w, braces)", x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h, braces);
    translate([0,0,0]) verticalStruts(space_to_window_w,space_to_window_h+window_h, braces = braces);
    translate([space_to_window_w,0,0]) verticalStruts(window_w,space_to_window_h, braces = braces, beams=1);
    translate([0,0,space_to_window_h+window_h]) verticalStruts(space_to_window_w/2,z-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w/2,0,space_to_window_h+window_h]) verticalStruts(window_w+space_to_window_w,z-(space_to_window_h+window_h), braces = braces, beams=2);
    translate([space_to_window_w+window_w,0,0]) verticalStruts(space_to_window_w,space_to_window_h+window_h, braces = braces);
    if(door_h-(space_to_window_h+window_h>0))  translate([space_to_window_w/2+window_w+space_to_window_w,0,space_to_window_h+window_h]) verticalStruts(space_to_window_w/2,door_h-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w/2+window_w+space_to_window_w,0,door_h]) verticalStruts(x-(space_to_window_w/2+window_w+space_to_window_w),z-door_h, braces = braces, beams=2);
    translate([space_to_window_w+window_w+space_to_window_w+door_w,0,0]) verticalStruts(x-(space_to_window_w+window_w+space_to_window_w+door_w),door_h, braces = braces);
}

module roof(x,y,z_front,z_back, panel_slf_w = 1220, panel_slf_l = 2440, panel_slf_t = base_sheet_t)
{
    opp = z_front-z_back;
    adj = y - base_timber;
    hyp = sqrt(opp*opp+adj*adj);
    theta = atan(opp/adj);
    overhang = (panel_slf_l-hyp)/2;
    // Want it to line up with front of timber truct not back
    height_adjust = base_timber * tan(theta);
    echo("overhang", overhang, opp, hyp, theta, height_adjust);
    translate([0,0,z_front+base_timber+panel_slf_t+height_adjust])
    {
        rotate([-theta,0,0]) cubeI([panel_slf_w,panel_slf_l, panel_slf_t]);
    }
}

shedBase(shed_length, shed_width, number_x=5,number_y=1, braces=1);
translate([0,0,base_timber]) floorMembrane(shed_length, shed_width, thickness = 1);
translate([0,0,base_timber+1]) shedLowerFloor(shed_length, shed_width);
translate([0,shed_width-base_timber,base_timber+1+base_sheet_t])  backWallStruts(shed_length, shed_front_back, 5);
translate([base_timber,base_timber,base_timber+1+base_sheet_t])  sideWallStruts(shed_width-base_timber*2,shed_front_back,2, braces=1);
translate([shed_length,base_timber,base_timber+1+base_sheet_t])  sideWallStruts(shed_width-base_timber*2,shed_front_back,2, braces=1);

translate([0,0,base_timber+1+base_sheet_t])  frontWallStruts(shed_length,shed_front_height, space_to_window_w=1830/2, space_to_window_h=2030-610, window_w=1830, window_h=610, door_w=870, , door_h=2030, braces=0);

roof(shed_length, shed_width,shed_front_height,shed_front_back);

//shedBaseSection(shed_length/6,shed_width, braces = 1);

shed_length = 5184;
shed_width = 1829+305/2;
shed_front_height = 2400;
shed_front_back = 2135;
base_timber = 47;

module cubeI(size,center=false)
{
    echo("Dim: ",size);
    cube(size, center=center);
}

module shedBaseSection(x,y,timber_width=75,timber_length=47, braces=0)
{
    echo("shedBaseSection: x,y,timber_width,timber_length, braces ", x,y,timber_width,timber_length, braces);
    echo("http://www.wickes.co.uk/Wickes-Sawn-Kiln-Dried-47X75-x-3000mm-Single/p/107186#tab-DynamicDeliveryWebTab_content");
    cubeI([x,timber_width,timber_length]);
    translate([0,y-timber_width,0]) cubeI([x,timber_width,timber_length]);
    translate([0,timber_width,0]) cubeI([timber_width,y-timber_width*2,timber_length]);
    translate([x-timber_width,timber_width,0]) cubeI([timber_width,y-timber_width*2,timber_length]);
    if(braces > 0)
    {
        middle_y = y - timber_width*2;
        part_y = middle_y/(braces+1);
        for(loop=[1 : braces ])
        {
                translate([timber_width,timber_width/(braces+1) + (loop*part_y),0]) cubeI([x-timber_width*2, timber_width,timber_length]);
        }
    }
}

module shedBase(x,y,number_x,number_y, braces=1)
{
    for(base_y = [0 : number_y-1])
    {
        for(base_x = [0 : number_x-1])
            {
                translate([base_x*(x/number_x),base_y*(y/number_y),0]) shedBaseSection(x/number_x,y/number_y, braces = braces);
            }
    }
}

module floorMembrane(x,y,thickness,color="Black", overlap=1)
{
    echo("x,y,thickness=1,color, overlap=1",x,y,thickness,color, overlap);
    echo("http://www.screwfix.com/p/dmp-damp-proof-membrane-black-1000ga-3-x-4m/50464#product_additional_details_container");
    translate([-overlap,-overlap,0]) color(color, 1) cube([x+overlap*2,y+overlap*2, thickness]);
}

module shedLowerFloor(x,y, panel_slf_w = 607, panel_slf_l =   1829, panel_slf_t =   5.5, panel_min_dimentions = 100)
{
    echo("http://www.wickes.co.uk/Wickes-Non-Structural-Hardwood-Plywood-5-5x607x1829mm/p/111196");
    x_panels = round(-0.5+x/panel_slf_w);
    y_panels = round(-0.5+y/panel_slf_l);
    for(base_y = [0 : y_panels-1]) //Truncate not round
    {
        for(base_x = [0 : x_panels-1])
        {
            translate([base_x*panel_slf_w,base_y*panel_slf_l,0]) cubeI([panel_slf_w, panel_slf_l,panel_slf_t]);
            if(y-(panel_slf_l*y_panels) > panel_min_dimentions)
            translate([base_x*panel_slf_w,y_panels*panel_slf_l,0]) cubeI([panel_slf_w, y-(panel_slf_l*y_panels),panel_slf_t]);
            }
        if(x-(panel_slf_w*x_panels) > panel_min_dimentions)
            translate([(x_panels)*panel_slf_w,(y_panels-1)*panel_slf_l,0]) cubeI([x-(panel_slf_w*x_panels), panel_slf_l,panel_slf_t]);
        if(y-(panel_slf_l*y_panels) > panel_min_dimentions)
            translate([(x_panels)*panel_slf_w,y_panels*panel_slf_l,0]) cubeI([x-(panel_slf_w*x_panels), y-(panel_slf_l*y_panels),panel_slf_t]);
    }

}

module verticalStruts(x,z,timber_width=47,timber_length=47, braces=0)
{
//    echo("shedBaseSection(x,z,timber_width,timber_length, braces",x,z,timber_width,timber_length, braces);
//    echo("Walls - http://www.wickes.co.uk/Wickes-Sawn-Kiln-Dried-47-x-47-x-2400mm-Pack-6/p/107114");
    cubeI([x,timber_width,timber_length]);
    translate([0,0,z-timber_width]) cubeI([x,timber_width,timber_length]);
    translate([0,0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
    translate([x-timber_width,0,timber_width]) cubeI([timber_width,timber_length,z-timber_width*2]);
    if(braces > 0)
    {
        middle_z = z - timber_width*2;
        part_z = middle_z/(braces+1);
        for(loop=[1 : braces ])
        {
                translate([timber_width,0,timber_width/(braces+1) + (loop*part_z)]) cubeI([x-timber_width*2, timber_width,timber_length]);
        }
    }
}

module backWallStruts(x,z,number_x, braces=1)
{
    echo("backWallStruts(x,z,number_x, braces)",x,z,number_x, braces);
        for(base_x = [0 : number_x-1])
            {
                translate([base_x*(x/number_x),0,0]) verticalStruts(x/number_x,z, braces = braces);
            }
}

module frontWallStruts(x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h)
{
    echo("frontWallStruts(x,z,space_to_window_w, space_to_window_h, window_w, window_h, door_w)", x,z, space_to_window_w, space_to_window_h, window_w, window_h, door_w, door_h);
    translate([0,0,0]) verticalStruts(space_to_window_w,space_to_window_h+window_h, braces = braces);
    for(support_windows = [0 : 1])
    {
        translate([space_to_window_w+(support_windows*window_w/2),0,0]) verticalStruts(window_w/2,space_to_window_h, braces = braces);
    }
    translate([0,0,space_to_window_h+window_h]) verticalStruts(space_to_window_w/2,z-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w/2,0,space_to_window_h+window_h]) verticalStruts(window_w+space_to_window_w,z-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w+window_w,0,0]) verticalStruts(space_to_window_w,space_to_window_h+window_h, braces = braces);
    translate([space_to_window_w/2+window_w+space_to_window_w,0,space_to_window_h+window_h]) verticalStruts(space_to_window_w/2,door_h-(space_to_window_h+window_h), braces = braces);
    translate([space_to_window_w/2+window_w+space_to_window_w,0,door_h]) verticalStruts(x-(space_to_window_w/2+window_w+space_to_window_w),z-door_h, braces = braces);
    translate([space_to_window_w+window_w+space_to_window_w+door_w,0,0]) verticalStruts(x-(space_to_window_w+window_w+space_to_window_w+door_w),door_h, braces = braces);
}

shedBase(shed_length, shed_width, number_x=4,number_y=1, braces=1);
translate([0,0,base_timber]) floorMembrane(shed_length, shed_width, thickness = 1);
translate([0,0,base_timber+1]) shedLowerFloor(shed_length, shed_width);
translate([0,shed_width-base_timber,base_timber+1+5.5])  backWallStruts(shed_length, shed_front_back, 5);
translate([0,0,base_timber+1+5.5])  frontWallStruts(shed_length,shed_front_height, space_to_window_w=1830/2, space_to_window_h=shed_front_height-610-610, window_w=1830, window_h=610, door_w=870, , door_h=2030);

//shedBaseSection(shed_length/6,shed_width, braces = 1);


Strip clearance (use thickness), add light bar, fix width/length/depth, simplify code.

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

width = 20;   // x
depth = 20;   // y
height = 10;  // z

// width and height of tank base plate
base_width = width/5;
base_height = 1;
light_bar_width = 1;
light_height = height-1;

// height of side supports
//side_support_height = height/2; // full side walls
side_support_height = 2;

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

// components to include (comment out unwanted)
vert_faces();
side_supports();
tank_base();
light_bar();

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

module vert_faces() {
	difference() {
		union() {
			vert_face(x=0);
			vert_face(x=width);
		}
		side_support_base(height_scale=0.5);
		side_support_top(height_scale=0.5);
	}
}

module vert_face(x) {
	translate([x-thickness/2,0,0])
		cube([thickness,depth,height]);
}

module tank_base() {
	translate([thickness,0,base_height])
	difference() {
		cube([base_width, depth, thickness]);
		cutouts(3,base_width);
		translate([0,depth-overhang-thickness/2,0])
			cutouts(3,base_width);
	}
}

module light_bar() {
	translate([thickness+base_width-light_bar_width,0,light_height])
	difference() {
		cube([light_bar_width, depth, thickness]);
		cutouts(2,light_bar_width);
		translate([0,depth-overhang-thickness/2,0])
			cutouts(2,light_bar_width);
	}
}

module cutouts(num, width) {
    w = width / ((num-1) * 2);
    for (i = [0 : num-1]) {
        translate([-w/2 + i*2*w,0,0])
            cube([w, overhang+thickness/2, thickness]);
    }
}

module side_supports() {
	difference() {
		union() {
			side_support_base();
			side_support_top();
		}
		vert_faces();
		tank_base();
        light_bar();
	}
}

module side_support_top(height_scale=1) {
	translate([0,0,height])
	side_support_base(height_scale=-height_scale);
}

module side_support_base(height_scale=1) {
	scale([1,1,height_scale])
		union() {
			side_support(y=overhang);
			side_support(y=depth-overhang);
		}
}

module side_support(y) {
	translate([-overhang,y-thickness/2,0])
		cube([width+overhang*2, thickness, side_support_height]);
}

thickness = 0.25;

width = 20;
length = 20;
height = 10;

// width and height of tank base plate
base_width = width/4;
base_height = 1;

// height of side supports
//side_support_height = height/2; // full side walls
side_support_height = 2;

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

// for minor corrections
epsilon = 0.01;

vert_faces();
side_supports();
tank_base();

module vert_faces() {
	difference() {
		union() {
			vert_face(x=0);
			vert_face(x=length);
		}
		side_support_base(clearance=0.1, height_scale=0.5);
		side_support_top(clearance=0.1, height_scale=0.5);
	}
}

module vert_face(x) {
	translate([x-thickness/2,0,0])
		cube([thickness,width,height]);
}

module tank_base(clearance=0) {
	translate([thickness,0,base_height])
	difference() {
		cube([base_width, length, thickness + clearance]);
		tank_base_cutouts(clearance);
		translate([0,length-overhang-thickness/2,0])
			tank_base_cutouts(clearance);
	}
}

module tank_base_cutouts(clearance=0) {
	cube([base_width/8, overhang+thickness/2, thickness+clearance]);
	translate([3*base_width/8,0,0])
		cube([base_width/4, overhang+thickness/2, thickness+clearance]);
	translate([7*base_width/8,0,0])
		cube([base_width/8+epsilon, overhang+thickness/2, thickness+clearance]);
}

module side_supports() {
	difference() {
		union() {
			side_support_base();
			side_support_top();
		}
		vert_faces();
		tank_base(clearance=0.1);
	}
}

module side_support_top(clearance=0, height_scale=1) {
	translate([0,0,height])
	side_support_base(clearance=clearance, height_scale=-height_scale);
}

module side_support_base(clearance=0, height_scale=1) {
	scale([1,1,height_scale])
		union() {
			side_support(y=overhang, clearance=clearance);
			side_support(y=width-overhang, clearance=clearance);
		}
}

module side_support(y, clearance) {
	translate([-overhang,y-thickness/2-clearance/2,0])
		cube([length+overhang*2, thickness+clearance, side_support_height+clearance]);
}


feat: add a module for simple test elements

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2022 Jean-Sebastien CONAN
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * Part of the camelSCAD library.
 *
 * Testing helpers for visual tests.
 *
 * @package shape/context
 * @author jsconan
 */

/**
 * Places the children at the proper test area.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param Number [index] - The index number of the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not the children are centered and need to be moved.
 */
module placeVisualTest(length, width, index=0, margin=0, cols=0, center=false) {
    margin = vector2D(margin);
    x = cols ? index % cols : index;
    y = cols ? floor(index / cols) : 0;
    offset = center ? [length, width, 0] / 2 : ORIGIN_3D;

    translate(offset + [(length + margin.x) * x, (width + margin.y) * y, 0]) {
        children();
    }
}

/**
 * Renders a test element.
 * @param String|Vector c - The color of the element.
 * @param Number|Vector size - The size of the element.
 */
module testElement(c, size=1) {
    color(c) {
        cube(size);
    }
}

/**
 * Colorizes a test element.
 * @param Number alpha - The alpha level for the testbed color.
 */
module testbedColor(alpha) {
    %color(TESTBED_COLOR, alpha) {
        children();
    }
}

/**
 * Extrudes and colorizes a test element.
 * @param Number alpha - The alpha level for the testbed color.
 */
module testbedExtrude(alpha) {
    testbedColor(alpha) {
        linear_extrude(height=TESTBED_THICKNESS, convexity=10, center=true) {
            children();
        }
    }
}

/**
 * Renders a test area, moving the tested shapes in it.
 * @param Number index - The index number of the test.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not the children are centered and need to be moved.
 */
module visualTest(index, length, width, title="test", margin=1, cols=0, center=false) {
    title = str(title, " ", index);

    placeVisualTest(
        length = length,
        width = width,
        index = index,
        margin = margin,
        cols = cols,
        center = center
    ) {
        // test area
        testbedExtrude(.1) {
            square([length, width], center=center);
        }
        translate(center ? ORIGIN_3D : [length, width, 0] / 2) {
            testbedExtrude(1) {
                text(title, size=min(length, width) / len(title), font="Liberation Sans", valign="center", halign="center");
            }
        }

        // tested shapes
        children();
    }
}

/**
 * Renders a test area for each child module, moving the tested shapes in it.
 * A single test can be selected by its index thanks to the global variable `selectTest`.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not center the whole suite.
 * @param Boolean [centerEach] - Whether or not center each test area.
 */
module visualTestSuite(length, width, title="test", margin=1, cols=0, center=false, centerEach=false) {
    margin = vector2D(margin);
    count = $children;
    selectTest = is_undef(selectTest) ? undef : min(max(0, selectTest), count - 1);
    start = is_undef(selectTest) ? 0 : selectTest;
    end = is_undef(selectTest) ? count - 1 : selectTest;
    lines = cols ? ceil(count / cols) : 1;
    offset = center ? -([margin.x * (cols - 1), margin.y * (lines - 1), 0] + [length * cols, width * lines, 0]) / 2 : ORIGIN_3D;

    translate(offset) {
        for (i = [start : end]) {
            visualTest(
                index = i,
                length = length,
                width = width,
                title = title,
                margin = margin,
                cols = cols,
                center = centerEach
            ) {
                children(i);
            }
        }
    }
}

// Select a single test to show
selectTest = undef;

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2022 Jean-Sebastien CONAN
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * Part of the camelSCAD library.
 *
 * Testing helpers for visual tests.
 *
 * @package shape/context
 * @author jsconan
 */

/**
 * Places the children at the proper test area.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param Number [index] - The index number of the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not the children are centered and need to be moved.
 */
module placeVisualTest(length, width, index=0, margin=0, cols=0, center=false) {
    margin = vector2D(margin);
    x = cols ? index % cols : index;
    y = cols ? floor(index / cols) : 0;
    offset = center ? [length, width, 0] / 2 : ORIGIN_3D;

    translate(offset + [(length + margin.x) * x, (width + margin.y) * y, 0]) {
        children();
    }
}

/**
 * Colorizes a test element.
 * @param Number alpha - The alpha level for the testbed color.
 */
module testbedColor(alpha) {
    %color(TESTBED_COLOR, alpha) {
        children();
    }
}

/**
 * Extrudes and colorizes a test element.
 * @param Number alpha - The alpha level for the testbed color.
 */
module testbedExtrude(alpha) {
    testbedColor(alpha) {
        linear_extrude(height=TESTBED_THICKNESS, convexity=10, center=true) {
            children();
        }
    }
}

/**
 * Renders a test area, moving the tested shapes in it.
 * @param Number index - The index number of the test.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not the children are centered and need to be moved.
 */
module visualTest(index, length, width, title="test", margin=1, cols=0, center=false) {
    title = str(title, " ", index);

    placeVisualTest(
        length = length,
        width = width,
        index = index,
        margin = margin,
        cols = cols,
        center = center
    ) {
        // test area
        testbedExtrude(.1) {
            square([length, width], center=center);
        }
        translate(center ? ORIGIN_3D : [length, width, 0] / 2) {
            testbedExtrude(1) {
                text(title, size=min(length, width) / len(title), font="Liberation Sans", valign="center", halign="center");
            }
        }

        // tested shapes
        children();
    }
}

/**
 * Renders a test area for each child module, moving the tested shapes in it.
 * A single test can be selected by its index thanks to the global variable `selectTest`.
 * @param Number length - The length of the test area.
 * @param Number width - The width of the test area.
 * @param String [title] - The title for the test.
 * @param Number|Vector [margin] - A margin between each test.
 * @param Number [cols] - The number of columns per lines.
 * @param Boolean [center] - Whether or not center the whole suite.
 * @param Boolean [centerEach] - Whether or not center each test area.
 */
module visualTestSuite(length, width, title="test", margin=1, cols=0, center=false, centerEach=false) {
    margin = vector2D(margin);
    count = $children;
    selectTest = is_undef(selectTest) ? undef : min(max(0, selectTest), count - 1);
    start = is_undef(selectTest) ? 0 : selectTest;
    end = is_undef(selectTest) ? count - 1 : selectTest;
    lines = cols ? ceil(count / cols) : 1;
    offset = center ? -([margin.x * (cols - 1), margin.y * (lines - 1), 0] + [length * cols, width * lines, 0]) / 2 : ORIGIN_3D;

    translate(offset) {
        for (i = [start : end]) {
            visualTest(
                index = i,
                length = length,
                width = width,
                title = title,
                margin = margin,
                cols = cols,
                center = centerEach
            ) {
                children(i);
            }
        }
    }
}

// Select a single test to show
selectTest = undef;


added connector and TODO for connector slot on the other end

length = 144.5;
height = 12;
span   = 40;

module main_block()
{
  cube([length, span, height]);
}

module connector()
{
  translate([-6.5, -6.5/2, 0])
    cube([6.5, 6.5, height]);
  translate([-6.5-11.5/2+1, 0, 0])
    cylinder(r=11.5/2, h=height);
}

module connector_slot()
{
  // TODO
}

module main_track()
{
  difference()
  {
    union()
    {
      main_block();
      translate([0, 40/2, 0])
        connector();
    }
    #connector_slot();
  }
}

main_track();

length = 144.5;

module main_track()
{
  cube([length, 40, 12]);
}

main_track();