Updating lasercut.scad - adding twisted to gernerating file

UP = 0;
DOWN = 180;
LEFT = 90;
RIGHT = 270;
MID = 360;

generate = $generate == undef ? 0: $generate;

if (generate == 1)
{
    // openscad examples.scad -D generate=1 -o /tmp/d.csg 2>&1 >/dev/null  | sed 's/ECHO: \"\[LC\] //' | sed 's/"$//' | sed '$a;' >./generate.scad ; openscad ./generate.scad -o generate.dxf
    // (from http://forum.openscad.org/Sharing-Dump-echo-output-to-a-file-td12529.html)
//    echo("[LC] use <lasercut.scad>; \nprojection(cut = false) \n\t");
}

module lasercutoutSquare(thickness=3.1, x=0, y=0,
        simple_tabs=[], simple_tab_holes=[],
        captive_nuts=[], captive_nut_holes = [],
        finger_joints = [],
        bumpy_finger_joints = [],
        screw_tabs=[], screw_tab_holes=[],
        twist_holes=[], twist_connect=[],
        circles_add = [],
        circles_remove = [],
        slits = [],
        cutouts = [],
        flat_adjust = [],
)
{
    points = [[0,0], [x,0], [x,y], [0,y], [0,0]];
lasercutout(thickness=thickness,
        points = points,
        simple_tabs = simple_tabs,
        simple_tab_holes = simple_tab_holes,
        captive_nuts = captive_nuts,
        captive_nut_holes = captive_nut_holes,
        finger_joints = finger_joints,
        bumpy_finger_joints = bumpy_finger_joints,
        screw_tabs= screw_tabs, screw_tab_holes= screw_tab_holes,
        twist_holes=twist_holes, twist_connect=twist_connect,
        circles_add = circles_add,
        circles_remove = circles_remove,
        slits = slits,
        cutouts = cutouts,
        flat_adjust = flat_adjust
    );
}

module lasercutout(thickness=3.1,  points= [],
        simple_tabs=[], simple_tab_holes=[],
        captive_nuts=[], captive_nut_holes = [],
        finger_joints = [],
        bumpy_finger_joints = [],
        screw_tabs=[], screw_tab_holes=[],
        twist_holes=[], twist_connect=[],
        circles_add = [],
        circles_remove = [],
        slits = [],
        cutouts = [],
        flat_adjust = [],
)
{
    path_total = len(points);
    path = [ for (p = [0 : 1 : path_total]) p ];

    function max_y(points) = max([for (a = [0:1:len(points)-1])  points[a][1]]);
    function min_y(points) = min([for (a = [0:1:len(points)-1])  points[a][1]]);
    function max_x(points) = max([for (a = [0:1:len(points)-1])  points[a][0]]);
    function min_x(points) = min([for (a = [0:1:len(points)-1])  points[a][0]]);

    max_y = max_y(points);
    min_y = min_y(points);
    max_x = max_x(points);
    min_x = min_x(points);

    difference()
    {
        union()
        {
            linear_extrude(height = thickness , center = false)  polygon(points=points, path=path);
            for (t = [0:1:len(simple_tabs)-1])
            {
                simpleTab(simple_tabs[t][0], simple_tabs[t][1], simple_tabs[t][2], thickness);
            }
            for (t = [0:1:len(captive_nuts)-1])
            {
                captiveNutTab(captive_nuts[t][0], captive_nuts[t][1], captive_nuts[t][2], thickness);
            }
            for (t = [0:1:len(circles_add)-1])
            {
                circlesAdd(circles_add[t][0], circles_add[t][1], circles_add[t][2], thickness);
            }
            for (t = [0:1:len(finger_joints)-1])
            {
                fingerJoint(finger_joints[t][0], finger_joints[t][1], finger_joints[t][2], thickness, max_y, min_y, max_x, min_x);
            }
            for (t = [0:1:len(bumpy_finger_joints)-1])
            {
                fingerJoint(bumpy_finger_joints[t][0], bumpy_finger_joints[t][1], bumpy_finger_joints[t][2], thickness, max_y, min_y, max_x, min_x, bumps=true);
            }
            for (t = [0:1:len(screw_tabs)-1])
            {
                screwTab(screw_tabs[t][0], screw_tabs[t][1], screw_tabs[t][2], screw_tabs[t][3], thickness);
            }
        } // end union

        for (t = [0:1:len(simple_tab_holes)-1])
        {
               simpleTabHole(simple_tab_holes[t][0], simple_tab_holes[t][1], simple_tab_holes[t][2], thickness);
        }
        for (t = [0:1:len(captive_nuts)-1])
        {
            captiveNutBoltHole(captive_nuts[t][0], captive_nuts[t][1], captive_nuts[t][2], thickness);
        }
        for (t = [0:1:len(captive_nut_holes)-1])
        {
            captiveNutHole(captive_nut_holes[t][0], captive_nut_holes[t][1], captive_nut_holes[t][2], thickness);
        }
        for (t = [0:1:len(twist_holes)-1])
        {
            twistHole(twist_holes[t][0], twist_holes[t][1], twist_holes[t][2], twist_holes[t][3], thickness);
        }
        for (t = [0:1:len(twist_connect)-1])
        {
            twistConnect(twist_connect[t][0], twist_connect[t][1], twist_connect[t][2], thickness, max_y, min_y, max_x, min_x);
        }
        for (t = [0:1:len(circles_remove)-1])
        {
            circlesRemove(circles_remove[t][0], circles_remove[t][1], circles_remove[t][2], thickness);
        }
        for (t = [0:1:len(slits)-1])
        {
               simpleSlit(slits[t][0], slits[t][1], slits[t][2], slits[t][3], thickness);
        }
        for (t = [0:1:len(cutouts)-1])
        {
               simpleCutouts(cutouts[t][0], cutouts[t][1], cutouts[t][2], cutouts[t][3], thickness);
        }
        for (t = [0:1:len(screw_tabs)-1])
        {
               screwTabHoleForScrew(screw_tabs[t][0], screw_tabs[t][1], screw_tabs[t][2], screw_tabs[t][3], thickness);
        }
        for (t = [0:1:len(screw_tab_holes)-1])
        {
               screwTabHole(screw_tab_holes[t][0], screw_tab_holes[t][1], screw_tab_holes[t][2], screw_tab_holes[t][3], thickness);
        }
    }

    if (flat_adjust)
    {
        if ($children) translate([0 + flat_adjust[0], max_y(points) + thickness*3 + flat_adjust[1], 0])
                children();
    }
    else
    {
        if ($children) translate([0, max_y(points) + thickness*2.5, 0])
                children();
    }

    if (generate == 1)
    {
        echo(str("[LC] lasercutout(thickness = ",thickness,", \n          points = ",points));
        if(simple_tabs)
            echo(str("[LC]         , simple_tabs = ", simple_tabs));
        if(simple_tab_holes)
            echo(str("[LC]         , simple_tab_holes = ", simple_tab_holes));
        if(captive_nuts)
            echo(str("[LC]         , captive_nuts = ", captive_nuts));
        if(captive_nut_holes)
            echo(str("[LC]         , captive_nut_holes = ", captive_nut_holes));
        if(finger_joints)
            echo(str("[LC]         , finger_joints = ", finger_joints));
        if(bumpy_finger_joints)
            echo(str("[LC]         , bumpy_finger_joints = ", bumpy_finger_joints));
        if(screw_tabs)
            echo(str("[LC]         , screw_tabs = ", screw_tabs));
        if(screw_tab_holes)
            echo(str("[LC]         , screw_tab_holes = ", screw_tab_holes));
        if(twist_holes)
            echo(str("[LC]         , twist_holes = ", twist_holes));
        if(twist_connect)
            echo(str("[LC]         , twist_connect = ", twist_connect));
        if(circles_add)
            echo(str("[LC]         , circles_add = ", circles_add));
        if(circles_remove)
            echo(str("[LC]         , circles_remove = ", circles_remove));
        if(slits)
            echo(str("[LC]         , slits = ", slits));
        if(cutouts)
            echo(str("[LC]         , cutouts = ", cutouts));
        if(flat_adjust)
            echo(str("[LC]         , flat_adjust = ", flat_adjust));
        echo("[LC]         ) \n");
    }
}

module simpleTab(angle, x, y, thickness)
{
    translate([x,y,0]) rotate([0,0,angle]) translate([-thickness/2,0,0]) cube([thickness, thickness, thickness]);
}

module simpleTabHole(angle, x, y, thickness)
{
     // Special case does not go past edge - so make only 1 thickness y
     if (angle == 360)
     {
         translate([x,y,0]) rotate([0,0,0]) translate([0,0,-thickness]) cube([thickness, thickness, thickness*3]);
     }
     else
     {
         translate([x,y,0]) rotate([0,0,angle-180]) translate([-thickness/2,-thickness,-thickness]) cube([thickness, screw*2, thickness*3]);
     }
}

module captiveNutBoltHole(angle, x, y, thickness)
{
    translate([x,y,0]) rotate([0,0,angle-180]) union()
    {
        translate([-thickness/2,-thickness,-thickness]) cube([thickness, thickness*4, thickness*3]);
        translate([-thickness/2-thickness,thickness,-thickness]) cube([thickness*3, thickness, thickness*3]);
    }
}

module fingerJoint(angle, start_up, fingers, thickness, max_y, min_y, max_x, min_x, bumps = false)
{
    if ( angle == UP )
    {
        range_min = min_x;
        range_max = max_x;
        t_x = min_x;
        t_y = max_y;
        fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = bumps);
    }
    if ( angle == DOWN )
    {
        range_min = min_x;
        range_max = max_x;
        t_x = max_x;
        t_y = min_y;
        fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = bumps);
    }
    if ( angle == LEFT )
    {
        range_min = min_y;
        range_max = max_y;
        t_x = min_x;
        t_y = min_y;
        fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = bumps);
    }
    if ( angle == RIGHT )
    {
        range_min = min_y;
        range_max = max_y;
        t_x = max_x;
        t_y = max_y;
        fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = bumps);
    }

}

module fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = false)
{
    // The tweaks to y translate([0, -thickness,0]) ... thickness*2 rather than *1
    // Are to avoid edge cases and make the dxf export better.
    // All fun
    translate([t_x, t_y,0]) rotate([0,0,angle]) translate([0, -thickness,0])
    {
        for ( i = [range_min : (range_max-range_min)/fingers : range_max - (range_max-range_min)/fingers] )
        {
            if(start_up == 1)
            {
                translate([i,0,0])
                {
                    cube([ (range_max-range_min)/(fingers*2), thickness*2, thickness]);
                    if(bumps == true)
                    {
                        translate([(range_max-range_min)/(fingers*2), thickness*1.5, 0]) cylinder(h=thickness, r=thickness/10);
                    }
                }
            }
            else
            {
                translate([i+(range_max-range_min)/(fingers*2),0,0])
                {
                    cube([ (range_max-range_min)/(fingers*2), thickness*2, thickness]);
                    if(bumps == true)
                    {
                        if (i < (range_max - (range_max-range_min)/fingers ))
                        {
                            translate([(range_max-range_min)/(fingers*2), thickness*1.5, 0]) cylinder(h=thickness, r=thickness/10);
                        }
                    }
                }
            }
        }
    }

}

module screwTab(angle, x, y, screw, thickness)
{
    translate([x,y,0]) rotate([0,0,angle]) translate([-screw/2,0,0]) cube([screw*2, thickness+screw*2, thickness]);
}

module screwTabHoleForScrew(angle, x, y, screw, thickness)
{
    // not to be confused with screwTabHole
    translate([x,y,0]) rotate([0,0,angle]) translate([screw/2,thickness+screw,-thickness]) cylinder(h=thickness*3, r=screw/2);
}

module screwTabHole(angle, x, y, screw, thickness)
{
     // Special case does not go past edge - so make only 1 thickness y
     if (angle == 360)
     {
         translate([x,y,0]) rotate([0,0,0]) translate([0,0,-thickness]) cube([screw*2, thickness, thickness*3]);
     }
     else
     {
         translate([x,y,0]) rotate([0,0,angle-180]) translate([-thickness/2,-thickness,-thickness]) cube([thickness, thickness*2, thickness*3]);
     }
}

module captiveNutTab(angle, x, y, thickness)
{
    translate([x,y,0]) rotate([0,0,angle]) union()
    {
        translate([-thickness/2+thickness*2,-thickness,0]) cube([thickness*3, thickness*2, thickness]);
        translate([-thickness/2-thickness*4,-thickness,0]) cube([thickness*3, thickness*2, thickness]);
    }
}

module captiveNutHole(angle, x, y, thickness)
{
    translate([x,y,0]) rotate([0,0,angle]) union()
    {
        translate([-thickness/2,-thickness*2,-thickness]) cube([thickness, thickness, thickness*3]);
        translate([-thickness/2+thickness*2,-thickness*2,-thickness]) cube([thickness*3, thickness, thickness*3]);
        translate([-thickness/2-thickness*4,-thickness*2,-thickness]) cube([thickness*3, thickness, thickness*3]);
    }
}

module twistHole(angle, x, y, width, thickness)
{
// http://msraynsford.blogspot.co.uk/2012/06/panel-joinery-10.html
    translate([x,y,0]) rotate([0,0,angle-90]) union()
    {
        cube([width, thickness, thickness*3], center=true);
        difference()
        {
            // Need some trig,  radius is hypo of triangle
            // Other sides are thinkness/2 and thinkness*3
            //
            //
           radius = sqrt(  ((thickness/2)*(thickness/2)) + (thickness*1.5*thickness*1.5) );
           translate([0,0, -thickness]) cylinder(h=thickness*3, r=radius);
           translate([-thickness*3/2,thickness,0]) cube([thickness*2, thickness, thickness*3], center=true);
           translate([thickness*3/2,-thickness,0]) cube([thickness*2, thickness, thickness*3], center=true);
        }
    }
}

module twistConnect(angle, x, y, thickness, max_y, min_y, max_x, min_x)
{
    // Really should do trianometry for non-90 Angles, but hey ho
    if( (angle == LEFT) || (angle == RIGHT) )
    {
        translate([0,y,-thickness]) rotate([0,0,angle+90]) union()
        {
            gap = max_x - min_x;
            translate([-gap/2-(thickness*3/2),0,0]) cube([gap, thickness, thickness*3]);
            translate([+gap/2+(thickness*3/2),0,0]) cube([gap, thickness, thickness*3]);
        }
    }
    if( (angle == UP) || (angle == DOWN) )
    {
        translate([x,0,-thickness]) rotate([0,0,angle+90]) union()
        {
            gap = max_y - min_y;
            echo(gap);
            translate([-gap/2-(thickness*3/2),0,0]) cube([gap, thickness, thickness*3]);
            translate([+gap/2+(thickness*3/2),0,0]) cube([gap, thickness, thickness*3]);
        }
    }
}

module circlesAdd(radius, x, y, thickness)
{
    translate([x,y,0]) cylinder(h=thickness, r=radius);
}

module circlesRemove(radius, x, y, thickness)
{
    translate([x,y,-thickness]) cylinder(h=thickness*3, r=radius);
}

module simpleSlit(angle, x, y, length, thickness)
{
     translate([x,y,0]) rotate([0,0,angle-180]) translate([-thickness/2,-thickness,-thickness]) cube([thickness, length+thickness, thickness*3]);
}

module simpleCutouts(x, y, width, height, thickness)
{
     translate([x,y,0]) rotate([0,0,0]) translate([0,0,-thickness]) cube([width, height, thickness*3]);
}

module lasercutoutBox(thickness = 3.1, x=0, y=0, z=0, sides=6,
        simple_tab_holes_a=[],
        captive_nuts_a=[], captive_nut_holes_a=[],
        circles_add_a=[],
        circles_remove_a=[],
        slits_a = [],
        cutouts_a = [],
)
{
    if (sides==4)
    {
        fj = [
            [ [UP, 1, 4], [DOWN, 1, 4],    ],
            [ [UP, 0, 4], [DOWN, 1, 4],    ],
            [ [UP, 1, 4], [DOWN, 0, 4],    ],
            [ [UP, 1, 4], [DOWN, 1, 4],    ],
        ];
        translate([0,thickness,0]) lasercutoutBoxAdjustedFJ(thickness = thickness, x=x, y=y-thickness*2 , z=z-thickness*2, sides=sides, fj=fj,
            simple_tab_holes_a=simple_tab_holes_a,
            captive_nuts_a=captive_nuts_a, captive_nut_holes_a=captive_nut_holes_a,
            circles_add_a=circles_add_a,
            circles_remove_a=circles_remove_a,
            slits_a=slits_a,
            cutouts_a=cutouts_a);
    }
    if (sides==5)
    {
        st = [
        [[DOWN, -thickness/2, 0], [DOWN, -thickness/2, y-thickness] ],
        [[DOWN, -thickness/2, 0], ],
        [[UP, -thickness/2, z-thickness*2], ],
        ];
        fj = [
            [ [UP, 1, 4], [DOWN, 1, 4], [LEFT, 1, 4],   ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 1, 4],   ],
            [ [UP, 1, 4], [DOWN, 0, 4], [LEFT, 1, 4],   ],
            [ [UP, 1, 4], [DOWN, 1, 4], [LEFT, 1, 4],   ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 0, 4], [RIGHT, 1, 4]  ],
        ];

        translate([thickness,thickness,0]) lasercutoutBoxAdjustedFJ(thickness = thickness, x=x-thickness, y=y-thickness*2 , z=z-thickness*2, sides=sides, fj=fj, st=st,
            simple_tab_holes_a=simple_tab_holes_a,
            captive_nuts_a=captive_nuts_a, captive_nut_holes_a=captive_nut_holes_a,
            circles_add_a=circles_add_a,
            circles_remove_a=circles_remove_a,
            slits_a=slits_a,
            cutouts_a=cutouts_a);
        }
    if (sides==6)
    {
        st = [
        [[DOWN, -thickness/2, 0], [DOWN, -thickness/2, y-thickness] , [RIGHT, x-thickness*2, -thickness/2]],
        [[DOWN, -thickness/2, 0], [RIGHT, x-thickness*2, -thickness/2]],
        [[UP, -thickness/2, z-thickness*2], [DOWN, x-thickness*1.5, 0], ],
        [[UP, z-thickness*1.5, y-thickness*2], ],
        ];
        fj = [
            [ [UP, 1, 4], [DOWN, 1, 4], [LEFT, 1, 4], [RIGHT, 0, 4]  ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 1, 4], [RIGHT, 0, 4]  ],
            [ [UP, 1, 4], [DOWN, 0, 4], [LEFT, 1, 4], [RIGHT, 0, 4]  ],
            [ [UP, 1, 4], [DOWN, 1, 4], [LEFT, 1, 4], [RIGHT, 0, 4]  ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 0, 4], [RIGHT, 1, 4]  ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 0, 4], [RIGHT, 1, 4]  ],
        ];
        translate([thickness,thickness,0]) lasercutoutBoxAdjustedFJ(thickness = thickness, x=x-thickness*2, y=y-thickness*2 , z=z-thickness*2, sides=sides, fj=fj, st=st,
            simple_tab_holes_a=simple_tab_holes_a,
            captive_nuts_a=captive_nuts_a, captive_nut_holes_a=captive_nut_holes_a,
            circles_add_a=circles_add_a,
            circles_remove_a=circles_remove_a,
            slits_a=slits_a,
            cutouts_a=cutouts_a);
    }
}

module lasercutoutBoxAdjustedFJ(thickness = 3.1, x=0, y=0, z=0, sides=6, fj=[], st=[],
        simple_tab_holes_a=[],
        captive_nuts_a=[], captive_nut_holes_a = [],
        circles_add_a = [],
        circles_remove_a = [],
        slits_a = [],
        cutouts_a = [])
{
    translate([0,0,0]) lasercutoutSquare(x=x, y=y, simple_tabs = st[0], finger_joints = fj[0],
                                simple_tab_holes=simple_tab_holes_a[0], captive_nuts=captive_nuts_a[0],
                                captive_nut_holes = captive_nut_holes_a[0],
                                circles_add = circles_add_a[0], circles_remove = circles_remove_a[0],
                                slits = slits_a[0],
                                cutouts = cutouts_a[0]);

    translate([0,0,z+thickness]) lasercutoutSquare(x=x, y=y, simple_tabs = st[1], finger_joints = fj[1],
                                simple_tab_holes=simple_tab_holes_a[1], captive_nuts=captive_nuts_a[1],
                                captive_nut_holes = captive_nut_holes_a[1],
                                circles_add = circles_add_a[1], circles_remove = circles_remove_a[1],
                                slits = slits_a[1],
                                cutouts = cutouts_a[1]);

    translate([0,0,thickness]) rotate([90,0,0]) lasercutoutSquare(x=x, y=z, finger_joints = fj[2],
                                simple_tab_holes=simple_tab_holes_a[2], captive_nuts=captive_nuts_a[2],
                                captive_nut_holes = captive_nut_holes_a[2],
                                circles_add = circles_add_a[2], circles_remove = circles_remove_a[2],
                                slits = slits_a[2],
                                cutouts = cutouts_a[2]);

    translate([0,y+thickness,thickness]) rotate([90,0,0]) lasercutoutSquare(x=x, y=z, simple_tabs = st[2],
                                finger_joints = fj[3],
                                simple_tab_holes=simple_tab_holes_a[3], captive_nuts=captive_nuts_a[3],
                                captive_nut_holes = captive_nut_holes_a[3],
                                circles_add = circles_add_a[3], circles_remove = circles_remove_a[3],
                                slits = slits_a[3],
                                cutouts = cutouts_a[3]);

    if (sides>4)
    {
        translate([0,0,thickness]) rotate([0,-90,0]) lasercutoutSquare(x=z, y=y, finger_joints = fj[4],
                                simple_tab_holes=simple_tab_holes_a[4], captive_nuts=captive_nuts_a[4],
                                captive_nut_holes = captive_nut_holes_a[4],
                                circles_add = circles_add_a[4], circles_remove = circles_remove_a[4],
                                slits = slits_a[4],
                                cutouts = cutouts_a[4]);
        }

    if (sides>5)
    {
        translate([x+thickness,0,thickness]) rotate([0,-90,0]) lasercutoutSquare(x=z, y=y,
                                simple_tabs = st[3], finger_joints = fj[5],
                                simple_tab_holes=simple_tab_holes_a[5], captive_nuts=captive_nuts_a[5],
                                captive_nut_holes = captive_nut_holes_a[5],
                                circles_add = circles_add_a[5], circles_remove = circles_remove_a[5],
                                slits = slits_a[5],
                                cutouts = cutouts_a[5]);
    }
}

UP = 0;
DOWN = 180;
LEFT = 90;
RIGHT = 270;
MID = 360;

generate = $generate == undef ? 0: $generate;

if (generate == 1)
{
    // openscad examples.scad -D generate=1 -o /tmp/d.csg 2>&1 >/dev/null  | sed 's/ECHO: \"\[LC\] //' | sed 's/"$//' | sed '$a;' >./generate.scad ; openscad ./generate.scad -o generate.dxf
    // (from http://forum.openscad.org/Sharing-Dump-echo-output-to-a-file-td12529.html)
//    echo("[LC] use <lasercut.scad>; \nprojection(cut = false) \n\t");
}

module lasercutoutSquare(thickness=3.1, x=0, y=0,
        simple_tabs=[], simple_tab_holes=[],
        captive_nuts=[], captive_nut_holes = [],
        finger_joints = [],
        bumpy_finger_joints = [],
        screw_tabs=[], screw_tab_holes=[],
        twist_holes=[], twist_connect=[],
        circles_add = [],
        circles_remove = [],
        slits = [],
        cutouts = [],
        flat_adjust = [],
)
{
    points = [[0,0], [x,0], [x,y], [0,y], [0,0]];
lasercutout(thickness=thickness,
        points = points,
        simple_tabs = simple_tabs,
        simple_tab_holes = simple_tab_holes,
        captive_nuts = captive_nuts,
        captive_nut_holes = captive_nut_holes,
        finger_joints = finger_joints,
        bumpy_finger_joints = bumpy_finger_joints,
        screw_tabs= screw_tabs, screw_tab_holes= screw_tab_holes,
        twist_holes=twist_holes, twist_connect=twist_connect,
        circles_add = circles_add,
        circles_remove = circles_remove,
        slits = slits,
        cutouts = cutouts,
        flat_adjust = flat_adjust
    );
}

module lasercutout(thickness=3.1,  points= [],
        simple_tabs=[], simple_tab_holes=[],
        captive_nuts=[], captive_nut_holes = [],
        finger_joints = [],
        bumpy_finger_joints = [],
        screw_tabs=[], screw_tab_holes=[],
        twist_holes=[], twist_connect=[],
        circles_add = [],
        circles_remove = [],
        slits = [],
        cutouts = [],
        flat_adjust = [],
)
{
    path_total = len(points);
    path = [ for (p = [0 : 1 : path_total]) p ];

    function max_y(points) = max([for (a = [0:1:len(points)-1])  points[a][1]]);
    function min_y(points) = min([for (a = [0:1:len(points)-1])  points[a][1]]);
    function max_x(points) = max([for (a = [0:1:len(points)-1])  points[a][0]]);
    function min_x(points) = min([for (a = [0:1:len(points)-1])  points[a][0]]);

    max_y = max_y(points);
    min_y = min_y(points);
    max_x = max_x(points);
    min_x = min_x(points);

    difference()
    {
        union()
        {
            linear_extrude(height = thickness , center = false)  polygon(points=points, path=path);
            for (t = [0:1:len(simple_tabs)-1])
            {
                simpleTab(simple_tabs[t][0], simple_tabs[t][1], simple_tabs[t][2], thickness);
            }
            for (t = [0:1:len(captive_nuts)-1])
            {
                captiveNutTab(captive_nuts[t][0], captive_nuts[t][1], captive_nuts[t][2], thickness);
            }
            for (t = [0:1:len(circles_add)-1])
            {
                circlesAdd(circles_add[t][0], circles_add[t][1], circles_add[t][2], thickness);
            }
            for (t = [0:1:len(finger_joints)-1])
            {
                fingerJoint(finger_joints[t][0], finger_joints[t][1], finger_joints[t][2], thickness, max_y, min_y, max_x, min_x);
            }
            for (t = [0:1:len(bumpy_finger_joints)-1])
            {
                fingerJoint(bumpy_finger_joints[t][0], bumpy_finger_joints[t][1], bumpy_finger_joints[t][2], thickness, max_y, min_y, max_x, min_x, bumps=true);
            }
            for (t = [0:1:len(screw_tabs)-1])
            {
                screwTab(screw_tabs[t][0], screw_tabs[t][1], screw_tabs[t][2], screw_tabs[t][3], thickness);
            }
        } // end union

        for (t = [0:1:len(simple_tab_holes)-1])
        {
               simpleTabHole(simple_tab_holes[t][0], simple_tab_holes[t][1], simple_tab_holes[t][2], thickness);
        }
        for (t = [0:1:len(captive_nuts)-1])
        {
            captiveNutBoltHole(captive_nuts[t][0], captive_nuts[t][1], captive_nuts[t][2], thickness);
        }
        for (t = [0:1:len(captive_nut_holes)-1])
        {
            captiveNutHole(captive_nut_holes[t][0], captive_nut_holes[t][1], captive_nut_holes[t][2], thickness);
        }
        for (t = [0:1:len(twist_holes)-1])
        {
            twistHole(twist_holes[t][0], twist_holes[t][1], twist_holes[t][2], twist_holes[t][3], thickness);
        }
        for (t = [0:1:len(twist_connect)-1])
        {
            twistConnect(twist_connect[t][0], twist_connect[t][1], twist_connect[t][2], thickness, max_y, min_y, max_x, min_x);
        }
        for (t = [0:1:len(circles_remove)-1])
        {
            circlesRemove(circles_remove[t][0], circles_remove[t][1], circles_remove[t][2], thickness);
        }
        for (t = [0:1:len(slits)-1])
        {
               simpleSlit(slits[t][0], slits[t][1], slits[t][2], slits[t][3], thickness);
        }
        for (t = [0:1:len(cutouts)-1])
        {
               simpleCutouts(cutouts[t][0], cutouts[t][1], cutouts[t][2], cutouts[t][3], thickness);
        }
        for (t = [0:1:len(screw_tabs)-1])
        {
               screwTabHoleForScrew(screw_tabs[t][0], screw_tabs[t][1], screw_tabs[t][2], screw_tabs[t][3], thickness);
        }
        for (t = [0:1:len(screw_tab_holes)-1])
        {
               screwTabHole(screw_tab_holes[t][0], screw_tab_holes[t][1], screw_tab_holes[t][2], screw_tab_holes[t][3], thickness);
        }
    }

    if (flat_adjust)
    {
        if ($children) translate([0 + flat_adjust[0], max_y(points) + thickness*3 + flat_adjust[1], 0])
                children();
    }
    else
    {
        if ($children) translate([0, max_y(points) + thickness*2.5, 0])
                children();
    }

    if (generate == 1)
    {
        echo(str("[LC] lasercutout(thickness = ",thickness,", \n          points = ",points));
        if(simple_tabs)
            echo(str("[LC]         , simple_tabs = ", simple_tabs));
        if(simple_tab_holes)
            echo(str("[LC]         , simple_tab_holes = ", simple_tab_holes));
        if(captive_nuts)
            echo(str("[LC]         , captive_nuts = ", captive_nuts));
        if(captive_nut_holes)
            echo(str("[LC]         , captive_nut_holes = ", captive_nut_holes));
        if(finger_joints)
            echo(str("[LC]         , finger_joints = ", finger_joints));
        if(bumpy_finger_joints)
            echo(str("[LC]         , bumpy_finger_joints = ", bumpy_finger_joints));
        if(screw_tabs)
            echo(str("[LC]         , screw_tabs = ", screw_tabs));
        if(screw_tab_holes)
            echo(str("[LC]         , screw_tab_holes = ", screw_tab_holes));
        if(circles_add)
            echo(str("[LC]         , circles_add = ", circles_add));
        if(circles_remove)
            echo(str("[LC]         , circles_remove = ", circles_remove));
        if(slits)
            echo(str("[LC]         , slits = ", slits));
        if(cutouts)
            echo(str("[LC]         , cutouts = ", cutouts));
        if(flat_adjust)
            echo(str("[LC]         , flat_adjust = ", flat_adjust));
        echo("[LC]         ) \n");
    }
}

module simpleTab(angle, x, y, thickness)
{
    translate([x,y,0]) rotate([0,0,angle]) translate([-thickness/2,0,0]) cube([thickness, thickness, thickness]);
}

module simpleTabHole(angle, x, y, thickness)
{
     // Special case does not go past edge - so make only 1 thickness y
     if (angle == 360)
     {
         translate([x,y,0]) rotate([0,0,0]) translate([0,0,-thickness]) cube([thickness, thickness, thickness*3]);
     }
     else
     {
         translate([x,y,0]) rotate([0,0,angle-180]) translate([-thickness/2,-thickness,-thickness]) cube([thickness, screw*2, thickness*3]);
     }
}

module captiveNutBoltHole(angle, x, y, thickness)
{
    translate([x,y,0]) rotate([0,0,angle-180]) union()
    {
        translate([-thickness/2,-thickness,-thickness]) cube([thickness, thickness*4, thickness*3]);
        translate([-thickness/2-thickness,thickness,-thickness]) cube([thickness*3, thickness, thickness*3]);
    }
}

module fingerJoint(angle, start_up, fingers, thickness, max_y, min_y, max_x, min_x, bumps = false)
{
    if ( angle == UP )
    {
        range_min = min_x;
        range_max = max_x;
        t_x = min_x;
        t_y = max_y;
        fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = bumps);
    }
    if ( angle == DOWN )
    {
        range_min = min_x;
        range_max = max_x;
        t_x = max_x;
        t_y = min_y;
        fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = bumps);
    }
    if ( angle == LEFT )
    {
        range_min = min_y;
        range_max = max_y;
        t_x = min_x;
        t_y = min_y;
        fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = bumps);
    }
    if ( angle == RIGHT )
    {
        range_min = min_y;
        range_max = max_y;
        t_x = max_x;
        t_y = max_y;
        fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = bumps);
    }

}

module fingers(angle, start_up, fingers, thickness, range_min, range_max, t_x, t_y, bumps = false)
{
    // The tweaks to y translate([0, -thickness,0]) ... thickness*2 rather than *1
    // Are to avoid edge cases and make the dxf export better.
    // All fun
    translate([t_x, t_y,0]) rotate([0,0,angle]) translate([0, -thickness,0])
    {
        for ( i = [range_min : (range_max-range_min)/fingers : range_max - (range_max-range_min)/fingers] )
        {
            if(start_up == 1)
            {
                translate([i,0,0])
                {
                    cube([ (range_max-range_min)/(fingers*2), thickness*2, thickness]);
                    if(bumps == true)
                    {
                        translate([(range_max-range_min)/(fingers*2), thickness*1.5, 0]) cylinder(h=thickness, r=thickness/10);
                    }
                }
            }
            else
            {
                translate([i+(range_max-range_min)/(fingers*2),0,0])
                {
                    cube([ (range_max-range_min)/(fingers*2), thickness*2, thickness]);
                    if(bumps == true)
                    {
                        if (i < (range_max - (range_max-range_min)/fingers ))
                        {
                            translate([(range_max-range_min)/(fingers*2), thickness*1.5, 0]) cylinder(h=thickness, r=thickness/10);
                        }
                    }
                }
            }
        }
    }

}

module screwTab(angle, x, y, screw, thickness)
{
    translate([x,y,0]) rotate([0,0,angle]) translate([-screw/2,0,0]) cube([screw*2, thickness+screw*2, thickness]);
}

module screwTabHoleForScrew(angle, x, y, screw, thickness)
{
    // not to be confused with screwTabHole
    translate([x,y,0]) rotate([0,0,angle]) translate([screw/2,thickness+screw,-thickness]) cylinder(h=thickness*3, r=screw/2);
}

module screwTabHole(angle, x, y, screw, thickness)
{
     // Special case does not go past edge - so make only 1 thickness y
     if (angle == 360)
     {
         translate([x,y,0]) rotate([0,0,0]) translate([0,0,-thickness]) cube([screw*2, thickness, thickness*3]);
     }
     else
     {
         translate([x,y,0]) rotate([0,0,angle-180]) translate([-thickness/2,-thickness,-thickness]) cube([thickness, thickness*2, thickness*3]);
     }
}

module captiveNutTab(angle, x, y, thickness)
{
    translate([x,y,0]) rotate([0,0,angle]) union()
    {
        translate([-thickness/2+thickness*2,-thickness,0]) cube([thickness*3, thickness*2, thickness]);
        translate([-thickness/2-thickness*4,-thickness,0]) cube([thickness*3, thickness*2, thickness]);
    }
}

module captiveNutHole(angle, x, y, thickness)
{
    translate([x,y,0]) rotate([0,0,angle]) union()
    {
        translate([-thickness/2,-thickness*2,-thickness]) cube([thickness, thickness, thickness*3]);
        translate([-thickness/2+thickness*2,-thickness*2,-thickness]) cube([thickness*3, thickness, thickness*3]);
        translate([-thickness/2-thickness*4,-thickness*2,-thickness]) cube([thickness*3, thickness, thickness*3]);
    }
}

module twistHole(angle, x, y, width, thickness)
{
// http://msraynsford.blogspot.co.uk/2012/06/panel-joinery-10.html
    translate([x,y,0]) rotate([0,0,angle-90]) union()
    {
        cube([width, thickness, thickness*3], center=true);
        difference()
        {
            // Need some trig,  radius is hypo of triangle
            // Other sides are thinkness/2 and thinkness*3
            //
            //
           radius = sqrt(  ((thickness/2)*(thickness/2)) + (thickness*1.5*thickness*1.5) );
           translate([0,0, -thickness]) cylinder(h=thickness*3, r=radius);
           translate([-thickness*3/2,thickness,0]) cube([thickness*2, thickness, thickness*3], center=true);
           translate([thickness*3/2,-thickness,0]) cube([thickness*2, thickness, thickness*3], center=true);
        }
    }
}

module twistConnect(angle, x, y, thickness, max_y, min_y, max_x, min_x)
{
    // Really should do trianometry for non-90 Angles, but hey ho
    if( (angle == LEFT) || (angle == RIGHT) )
    {
        translate([0,y,-thickness]) rotate([0,0,angle+90]) union()
        {
            gap = max_x - min_x;
            translate([-gap/2-(thickness*3/2),0,0]) cube([gap, thickness, thickness*3]);
            translate([+gap/2+(thickness*3/2),0,0]) cube([gap, thickness, thickness*3]);
        }
    }
    if( (angle == UP) || (angle == DOWN) )
    {
        translate([x,0,-thickness]) rotate([0,0,angle+90]) union()
        {
            gap = max_y - min_y;
            echo(gap);
            translate([-gap/2-(thickness*3/2),0,0]) cube([gap, thickness, thickness*3]);
            translate([+gap/2+(thickness*3/2),0,0]) cube([gap, thickness, thickness*3]);
        }
    }
}

module circlesAdd(radius, x, y, thickness)
{
    translate([x,y,0]) cylinder(h=thickness, r=radius);
}

module circlesRemove(radius, x, y, thickness)
{
    translate([x,y,-thickness]) cylinder(h=thickness*3, r=radius);
}

module simpleSlit(angle, x, y, length, thickness)
{
     translate([x,y,0]) rotate([0,0,angle-180]) translate([-thickness/2,-thickness,-thickness]) cube([thickness, length+thickness, thickness*3]);
}

module simpleCutouts(x, y, width, height, thickness)
{
     translate([x,y,0]) rotate([0,0,0]) translate([0,0,-thickness]) cube([width, height, thickness*3]);
}

module lasercutoutBox(thickness = 3.1, x=0, y=0, z=0, sides=6,
        simple_tab_holes_a=[],
        captive_nuts_a=[], captive_nut_holes_a=[],
        circles_add_a=[],
        circles_remove_a=[],
        slits_a = [],
        cutouts_a = [],
)
{
    if (sides==4)
    {
        fj = [
            [ [UP, 1, 4], [DOWN, 1, 4],    ],
            [ [UP, 0, 4], [DOWN, 1, 4],    ],
            [ [UP, 1, 4], [DOWN, 0, 4],    ],
            [ [UP, 1, 4], [DOWN, 1, 4],    ],
        ];
        translate([0,thickness,0]) lasercutoutBoxAdjustedFJ(thickness = thickness, x=x, y=y-thickness*2 , z=z-thickness*2, sides=sides, fj=fj,
            simple_tab_holes_a=simple_tab_holes_a,
            captive_nuts_a=captive_nuts_a, captive_nut_holes_a=captive_nut_holes_a,
            circles_add_a=circles_add_a,
            circles_remove_a=circles_remove_a,
            slits_a=slits_a,
            cutouts_a=cutouts_a);
    }
    if (sides==5)
    {
        st = [
        [[DOWN, -thickness/2, 0], [DOWN, -thickness/2, y-thickness] ],
        [[DOWN, -thickness/2, 0], ],
        [[UP, -thickness/2, z-thickness*2], ],
        ];
        fj = [
            [ [UP, 1, 4], [DOWN, 1, 4], [LEFT, 1, 4],   ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 1, 4],   ],
            [ [UP, 1, 4], [DOWN, 0, 4], [LEFT, 1, 4],   ],
            [ [UP, 1, 4], [DOWN, 1, 4], [LEFT, 1, 4],   ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 0, 4], [RIGHT, 1, 4]  ],
        ];

        translate([thickness,thickness,0]) lasercutoutBoxAdjustedFJ(thickness = thickness, x=x-thickness, y=y-thickness*2 , z=z-thickness*2, sides=sides, fj=fj, st=st,
            simple_tab_holes_a=simple_tab_holes_a,
            captive_nuts_a=captive_nuts_a, captive_nut_holes_a=captive_nut_holes_a,
            circles_add_a=circles_add_a,
            circles_remove_a=circles_remove_a,
            slits_a=slits_a,
            cutouts_a=cutouts_a);
        }
    if (sides==6)
    {
        st = [
        [[DOWN, -thickness/2, 0], [DOWN, -thickness/2, y-thickness] , [RIGHT, x-thickness*2, -thickness/2]],
        [[DOWN, -thickness/2, 0], [RIGHT, x-thickness*2, -thickness/2]],
        [[UP, -thickness/2, z-thickness*2], [DOWN, x-thickness*1.5, 0], ],
        [[UP, z-thickness*1.5, y-thickness*2], ],
        ];
        fj = [
            [ [UP, 1, 4], [DOWN, 1, 4], [LEFT, 1, 4], [RIGHT, 0, 4]  ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 1, 4], [RIGHT, 0, 4]  ],
            [ [UP, 1, 4], [DOWN, 0, 4], [LEFT, 1, 4], [RIGHT, 0, 4]  ],
            [ [UP, 1, 4], [DOWN, 1, 4], [LEFT, 1, 4], [RIGHT, 0, 4]  ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 0, 4], [RIGHT, 1, 4]  ],
            [ [UP, 0, 4], [DOWN, 1, 4], [LEFT, 0, 4], [RIGHT, 1, 4]  ],
        ];
        translate([thickness,thickness,0]) lasercutoutBoxAdjustedFJ(thickness = thickness, x=x-thickness*2, y=y-thickness*2 , z=z-thickness*2, sides=sides, fj=fj, st=st,
            simple_tab_holes_a=simple_tab_holes_a,
            captive_nuts_a=captive_nuts_a, captive_nut_holes_a=captive_nut_holes_a,
            circles_add_a=circles_add_a,
            circles_remove_a=circles_remove_a,
            slits_a=slits_a,
            cutouts_a=cutouts_a);
    }
}

module lasercutoutBoxAdjustedFJ(thickness = 3.1, x=0, y=0, z=0, sides=6, fj=[], st=[],
        simple_tab_holes_a=[],
        captive_nuts_a=[], captive_nut_holes_a = [],
        circles_add_a = [],
        circles_remove_a = [],
        slits_a = [],
        cutouts_a = [])
{
    translate([0,0,0]) lasercutoutSquare(x=x, y=y, simple_tabs = st[0], finger_joints = fj[0],
                                simple_tab_holes=simple_tab_holes_a[0], captive_nuts=captive_nuts_a[0],
                                captive_nut_holes = captive_nut_holes_a[0],
                                circles_add = circles_add_a[0], circles_remove = circles_remove_a[0],
                                slits = slits_a[0],
                                cutouts = cutouts_a[0]);

    translate([0,0,z+thickness]) lasercutoutSquare(x=x, y=y, simple_tabs = st[1], finger_joints = fj[1],
                                simple_tab_holes=simple_tab_holes_a[1], captive_nuts=captive_nuts_a[1],
                                captive_nut_holes = captive_nut_holes_a[1],
                                circles_add = circles_add_a[1], circles_remove = circles_remove_a[1],
                                slits = slits_a[1],
                                cutouts = cutouts_a[1]);

    translate([0,0,thickness]) rotate([90,0,0]) lasercutoutSquare(x=x, y=z, finger_joints = fj[2],
                                simple_tab_holes=simple_tab_holes_a[2], captive_nuts=captive_nuts_a[2],
                                captive_nut_holes = captive_nut_holes_a[2],
                                circles_add = circles_add_a[2], circles_remove = circles_remove_a[2],
                                slits = slits_a[2],
                                cutouts = cutouts_a[2]);

    translate([0,y+thickness,thickness]) rotate([90,0,0]) lasercutoutSquare(x=x, y=z, simple_tabs = st[2],
                                finger_joints = fj[3],
                                simple_tab_holes=simple_tab_holes_a[3], captive_nuts=captive_nuts_a[3],
                                captive_nut_holes = captive_nut_holes_a[3],
                                circles_add = circles_add_a[3], circles_remove = circles_remove_a[3],
                                slits = slits_a[3],
                                cutouts = cutouts_a[3]);

    if (sides>4)
    {
        translate([0,0,thickness]) rotate([0,-90,0]) lasercutoutSquare(x=z, y=y, finger_joints = fj[4],
                                simple_tab_holes=simple_tab_holes_a[4], captive_nuts=captive_nuts_a[4],
                                captive_nut_holes = captive_nut_holes_a[4],
                                circles_add = circles_add_a[4], circles_remove = circles_remove_a[4],
                                slits = slits_a[4],
                                cutouts = cutouts_a[4]);
        }

    if (sides>5)
    {
        translate([x+thickness,0,thickness]) rotate([0,-90,0]) lasercutoutSquare(x=z, y=y,
                                simple_tabs = st[3], finger_joints = fj[5],
                                simple_tab_holes=simple_tab_holes_a[5], captive_nuts=captive_nuts_a[5],
                                captive_nut_holes = captive_nut_holes_a[5],
                                circles_add = circles_add_a[5], circles_remove = circles_remove_a[5],
                                slits = slits_a[5],
                                cutouts = cutouts_a[5]);
    }
}


cable guide support element

cable guide support element

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


Corn gets a tail

Corn gets a tail


Tweak hardboard thickness/tolerance; tweak mask opening/design; add finger hole to tank support acrylic.

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

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

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

// 1/8" Hardboard:
hardboard_thickness = 3.2;
    // 2.8mm = 0.110"
    //   * looked good based on cut test piece for hardboard slotting into hardboard
    //   * 2015-03-06: Hanging supports didn't fit into cutouts (or did only with lots of force)
    // 3.1mm for some breathing room
    //   * 2016-02-26: Still tighter than is ideal for sliding parts together.
    // 3.2mm for just a smidge more...

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

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

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

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

// tank base plate
base_height = overhang/2;

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

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

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

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

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

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

// for slight offsets/tweaks
epsilon = 1;

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

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

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

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

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

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

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

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

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

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

// rounded corners in x,z directions
module rounded_rect(x,y,z, center, radius) {
    difference() {
        cube([x,y,z], center);

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

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

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

module tank_support() {
    translate([thickness/2, thickness/2, base_height+thickness])
    union() {
        tank_support_layer1();
        color("Grey", alpha=0.5)
            tank_support_layer2();
/*
        color("Grey", alpha=0.5)
            tank_support_layer3();
*/
    }
}
// Two strips of hardboard on either side of LED strip
module tank_support_layer1() {
    // front bottom strip, w/ space for wires
    translate([0, 0, 0])
        cube([tank_width/3, depth-thickness, thickness]);
    // back bottom strip, full-depth
    translate([2*tank_width/3, 0, 0])
        cube([tank_width/3, depth-thickness-20, thickness]);
}
// Clear acrylic over LED strip
module tank_support_layer2() {
    translate([0, 0, thickness])
    difference() {
        cube([tank_width, depth-thickness, thickness]);
        translate([tank_width/2, depth-tank_width/2, 0])
            cylinder(d=25, h=100, center=true);
    }
}
// Clear acrylic end blocks to prevent tank shifting / seat it in correct location
// [Currently unused -- 2016-02-26]
module tank_support_layer3() {
    translate([0, 0, thickness*2])
        cube([tank_width, tank_foot_offset_right, thickness]);
    translate([0, depth-thickness-tank_foot_offset_left, thickness*2])
        cube([tank_width, tank_foot_offset_left, thickness]);
}

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

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

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

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

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

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

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

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

// 1/8" Hardboard:
hardboard_thickness = 3.1;
    // 2.8mm = 0.110"
    //   * looked good based on cut test piece for hardboard slotting into hardboard
    //   * 2015-03-06: Hanging supports didn't fit into cutouts (or did only with lots of force)
    // 3.1mm for some breathing room

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

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

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

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

// tank base plate
base_height = overhang/2;

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

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

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

// x-position of mask piece inside box
mask_loc = tank_width + thickness;  // center-to-center
// dimensions for mask view opening
view_opening_left = 75;
view_opening_right = 85;
view_opening_width = depth-thickness - view_opening_left - view_opening_right;
view_opening_bottom = 29;
view_opening_height = 90;

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

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

// for slight offsets/tweaks
epsilon = 1;

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

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

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

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

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

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

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

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

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

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

// rounded corners in x,z directions
module rounded_rect(x,y,z, center, radius) {
    difference() {
        cube([x,y,z], center);

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

module top_cover() {
    // width1 = main cover ; width2 = tank section cover
    width1 = width - mask_loc - thickness;
    width2 = mask_loc - thickness;
    color("Grey", alpha=0.5)
    union() {
        translate([0,-outset,height-thickness])
        difference() {
            translate([mask_loc+thickness/2,0,0])
                cube([width1+thickness+outset,depth+outset*2,thickness]);
            cutouts(4,width1,outset,rot=0,trans=[width1/2+mask_loc,0,0]);
            cutouts(4,width1,outset,rot=180,trans=[width1/2+mask_loc,depth+outset*2,0]);
            cutouts(5,depth-thickness,outset,rot=90,trans=[width+outset,depth/2+outset,0]);
        }
        translate([-outset-thickness/2,-overhang,height-thickness])
        difference() {
            cube([width2+thickness+outset,depth+overhang*2,thickness]);
            cutouts(5,depth-thickness,outset,rot=90,trans=[outset+thickness/2,depth/2+overhang,0]);
        }
    }
}

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

module tank_support() {
    translate([thickness/2, thickness/2, base_height+thickness])
    union() {
        tank_support_layer1();
        color("Grey", alpha=0.5)
            tank_support_layer2();
        color("Grey", alpha=0.5)
            tank_support_layer3();
    }
}
// Two strips of hardboard on either side of LED strip
module tank_support_layer1() {
    // front bottom strip, w/ space for wires
    translate([0, 0, 0])
        cube([tank_width/3, depth-thickness, thickness]);
    // back bottom strip, full-depth
    translate([2*tank_width/3, 0, 0])
        cube([tank_width/3, depth-thickness-20, thickness]);
}
// Clear acrylic over LED strip
module tank_support_layer2() {
    translate([0, 0, thickness])
        cube([tank_width, depth-thickness, thickness]);
}
// Clear acrylic end blocks to prevent tank shifting / seat it in correct location
module tank_support_layer3() {
    translate([0, 0, thickness*2])
        cube([tank_width, tank_foot_offset_right, thickness]);
    translate([0, depth-thickness-tank_foot_offset_left, thickness*2])
        cube([tank_width, tank_foot_offset_left, thickness]);
}

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

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

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

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

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


belt_fastener: increase/fix belt path cuts

include <config.scad>;
include <thing_libutils/system.scad>;
include <thing_libutils/thread-data.scad>;
include <thing_libutils/nut-data.scad>;
include <thing_libutils/pulley.scad>;
include <thing_libutils/timing-belts-data.scad>;
use <thing_libutils/screws.scad>
use <thing_libutils/timing-belts.scad>

module belt_fastener(part, belt=TimingBelt_GT2_2, belt_width=6*mm, belt_dist=pulley_2GT_20T[2], width=55*mm, with_tensioner=true, align=U, orient=X)
{
    belt_t2 = belt_t2_thickness(belt) + .2*mm;
    tension_screw_nut = NutHexM3;
    tension_screw_thread = ThreadM3;
    tension_screw_dia = lookup(ThreadSize, tension_screw_thread);
    angle_screw_thread = ThreadM4;
    angle_screw_dia = 3.25*mm;
    height = belt_dist + belt_t2 + angle_screw_dia + 3*mm;
    slide_width=20*mm;
    depth = belt_width + 10*mm;

    s= [width, depth, height];
    size_align(size=s, orient=orient, align=align, orient_ref=-X)
    {
        if(part==undef)
        {
            difference()
            {
                belt_fastener("pos", belt=belt, belt_width=belt_width, belt_dist=belt_dist, width=width, with_tensioner=with_tensioner, align=align, orient=orient);
                belt_fastener("neg", belt=belt, belt_width=belt_width, belt_dist=belt_dist, width=width, with_tensioner=with_tensioner, align=align, orient=orient);
            }
            %belt_fastener("vit", belt=belt, belt_width=belt_width, belt_dist=belt_dist, width=width, with_tensioner=with_tensioner, align=align, orient=orient);
        }
        else if(part=="pos")
        {
            hull()
            {
                translate(-belt_dist/2*Z)
                {
                    // support for belt path
                    rcubea([width, belt_width, belt_t2*2 + angle_screw_dia]);

                    translate(-belt_width*Y)
                    translate(-angle_screw_dia/2*Y)
                    rcubea([width, angle_screw_dia+2*mm, belt_t2*2 + angle_screw_dia]);
                }
            }
        }
        else if(part=="neg")
        {
            translate(-belt_dist/2*Z)
            if(with_tensioner)
            {
                translate([0,0,belt_t2/4])
                cubea(size=[1000, belt_width+.1, belt_t2], extrasize=1000*Y, extrasize_align=Y);

                /*translate([0,0,angle_screw_dia/2])*/
                {
                    hull()
                    {
                        for(i=[-1,1])
                        translate(i*11*mm*X)
                        cylindera(d=angle_screw_dia+belt_t2, h=belt_width+.1, extra_h=1000, extra_align=Y, orient=Y);
                    }

                    hull()
                    {
                        for(i=[-1,1])
                        translate(i*slide_width/2*X)
                        translate(-belt_width*Y)
                        translate(-angle_screw_dia*Y)
                        translate(-.1*Y)
                        cylindera(d=angle_screw_dia+.3*mm, h=1000, orient=Y, align=Y);
                    }

                    translate(-belt_width*Y)
                    {
                        // cut for angle screw
                        translate(-1*2*mm*Y)
                        translate(-1*11*mm*X)
                        cylindera(d=angle_screw_dia+.3*mm, h=1000, orient=X, align=X);

                        translate(-1*11*mm*X)
                        nut_trap_cut(nut=tension_screw_nut, screw_offset=3*mm, cut_screw=true, orient=-X, trap_axis=Y);
                    }
                }

            }
            else
            {
                cubea([1000, belt_width+3*mm, belt_t2*1.8], extrasize=[0,1000,1*mm], extrasize_align=Y+Z);
            }

            translate(belt_dist/2*Z)
            cubea([1000, belt_width+3*mm, belt_t2*2], extrasize=1000*Y, extrasize_align=Y);
        }
        else if(part=="vit")
        {
            if(with_tensioner)
            {
                /*translate(-belt_dist/2*Z)*/
                /*translate(-belt_width*Y)*/
                /*translate(screw_offset_x*X)*/
                /*screw(nut=tension_screw_nut, h=25*mm, with_head=true, with_nut=false, head_embed=true, with_nut=false, orient=X, align=X);*/

                // 90 angle metal screw
                /*translate(screw_offset_x*X)*/
                translate(-belt_dist/2*Z)
                translate(-belt_width*Y)
                translate(-angle_screw_dia/2*Y)
                {
                    cylindera(d=angle_screw_dia+.2*mm, h=20*mm, orient=X, align=X);
                    cylindera(d=angle_screw_dia+.2*mm, h=11*mm, orient=Y, align=Y+X);
                }
            }
        }
    }
}

if(false)
{
    for(z=[-1,1])
    for(z=xaxis_beltpath_z_offsets)
    {
        translate([0, 0, z])
        {
            translate([0, xaxis_carriage_beltpath_offset_y, 0])
            translate([-main_width/2, 0, 0])
            rotate([90,0,0])
            belt_path(main_width, xaxis_belt_width, xaxis_pulley_inner_d, orient=X, align=X);

            proj_extrude_axis(axis=-Y)
            translate([0, xaxis_carriage_beltpath_offset_y, 0])
            mirror([0,0,sign(z)>1?1:0])
            /*mirror(X)*/
            belt_fastener();
        }
    }
}

include <config.scad>;
include <thing_libutils/system.scad>;
include <thing_libutils/thread-data.scad>;
include <thing_libutils/nut-data.scad>;
include <thing_libutils/pulley.scad>;
include <thing_libutils/timing-belts-data.scad>;
use <thing_libutils/screws.scad>
use <thing_libutils/timing-belts.scad>

module belt_fastener(part, belt=TimingBelt_GT2_2, belt_width=6*mm, belt_dist=pulley_2GT_20T[2], width=55*mm, with_tensioner=true, align=U, orient=X)
{
    belt_t2 = belt_t2_thickness(belt) + .2*mm;
    tension_screw_nut = NutHexM3;
    tension_screw_thread = ThreadM3;
    tension_screw_dia = lookup(ThreadSize, tension_screw_thread);
    angle_screw_thread = ThreadM4;
    angle_screw_dia = 3.25*mm;
    height = belt_dist + belt_t2 + angle_screw_dia + 3*mm;
    slide_width=20*mm;
    depth = belt_width + 10*mm;

    s= [width, depth, height];
    size_align(size=s, orient=orient, align=align, orient_ref=-X)
    {
        if(part==undef)
        {
            difference()
            {
                belt_fastener("pos", belt=belt, belt_width=belt_width, belt_dist=belt_dist, width=width, with_tensioner=with_tensioner, align=align, orient=orient);
                belt_fastener("neg", belt=belt, belt_width=belt_width, belt_dist=belt_dist, width=width, with_tensioner=with_tensioner, align=align, orient=orient);
            }
            %belt_fastener("vit", belt=belt, belt_width=belt_width, belt_dist=belt_dist, width=width, with_tensioner=with_tensioner, align=align, orient=orient);
        }
        else if(part=="pos")
        {
            hull()
            {
                translate(-belt_dist/2*Z)
                {
                    // support for belt path
                    rcubea([width, belt_width, belt_t2*2 + angle_screw_dia]);

                    translate(-belt_width*Y)
                    translate(-angle_screw_dia/2*Y)
                    rcubea([width, angle_screw_dia+2*mm, belt_t2*2 + angle_screw_dia]);
                }
            }
        }
        else if(part=="neg")
        {
            translate(-belt_dist/2*Z)
            if(with_tensioner)
            {
                translate([0,0,belt_t2/4])
                cubea(size=[1000, belt_width+.1, belt_t2], extrasize=1000*Y, extrasize_align=Y);

                /*translate([0,0,angle_screw_dia/2])*/
                {
                    hull()
                    {
                        for(i=[-1,1])
                        translate(i*11*mm*X)
                        cylindera(d=angle_screw_dia+belt_t2, h=belt_width+.1, extra_h=1000, extra_align=Y, orient=Y);
                    }

                    hull()
                    {
                        for(i=[-1,1])
                        translate(i*slide_width/2*X)
                        translate(-belt_width*Y)
                        translate(-angle_screw_dia*Y)
                        translate(-.1*Y)
                        cylindera(d=angle_screw_dia+.3*mm, h=1000, orient=Y, align=Y);
                    }

                    translate(-belt_width*Y)
                    {
                        // cut for angle screw
                        translate(-1*2*mm*Y)
                        translate(-1*11*mm*X)
                        cylindera(d=angle_screw_dia+.3*mm, h=1000, orient=X, align=X);

                        translate(-1*11*mm*X)
                        nut_trap_cut(nut=tension_screw_nut, screw_offset=3*mm, cut_screw=true, orient=-X, trap_axis=Y);
                    }
                }

            }
            else
            {
                cubea([1000, belt_width+3*mm, belt_t2*1.8], extrasize=[0,0,1*mm], extrasize_align=Z);
            }

            translate(belt_dist/2*Z)
            cubea([1000, belt_width+3*mm, belt_t2*2]);
        }
        else if(part=="vit")
        {
            if(with_tensioner)
            {
                /*translate(-belt_dist/2*Z)*/
                /*translate(-belt_width*Y)*/
                /*translate(screw_offset_x*X)*/
                /*screw(nut=tension_screw_nut, h=25*mm, with_head=true, with_nut=false, head_embed=true, with_nut=false, orient=X, align=X);*/

                // 90 angle metal screw
                /*translate(screw_offset_x*X)*/
                translate(-belt_dist/2*Z)
                translate(-belt_width*Y)
                translate(-angle_screw_dia/2*Y)
                {
                    cylindera(d=angle_screw_dia+.2*mm, h=20*mm, orient=X, align=X);
                    cylindera(d=angle_screw_dia+.2*mm, h=11*mm, orient=Y, align=Y+X);
                }
            }
        }
    }
}

if(false)
{
    for(z=[-1,1])
    for(z=xaxis_beltpath_z_offsets)
    {
        translate([0, 0, z])
        {
            translate([0, xaxis_carriage_beltpath_offset_y, 0])
            translate([-main_width/2, 0, 0])
            rotate([90,0,0])
            belt_path(main_width, xaxis_belt_width, xaxis_pulley_inner_d, orient=X, align=X);

            proj_extrude_axis(axis=-Y)
            translate([0, xaxis_carriage_beltpath_offset_y, 0])
            mirror([0,0,sign(z)>1?1:0])
            /*mirror(X)*/
            belt_fastener();
        }
    }
}


Added files via upload

Added files via upload

Zdim = 45;
Xdim = 60;
Ydim = 67;
thickness = 2;
supportDiam = 5;
buttonDiam = 16;
XT60X = 15.54;
XT60Y = 8.16 + 0.5;
XT60Z = 16.10 + 0.5;

textHV = "HVDC";
textLV = "5V";
$fn=80;

//--------------------------------
module box(Xdim, Ydim, Zdim, thickness, application)
    //Creates a box, dimensions are internal
    {
        difference() {

            cube([Xdim + thickness, Ydim + thickness, Zdim], center = true);
            translate([0, 0, thickness / 2]) cube([Xdim, Ydim, Zdim], center = true);

            if (application == "servo") {
                translate([-Xdim / 2, 0, -0.5 * Zdim]) cube([5, Ydim + 2 * thickness, Zdim / 2 + thickness], center = true);
                translate([+Xdim / 2, 0, -0.5 * Zdim]) cube([5, Ydim + 2 * thickness, Zdim / 2 + thickness], center = true);
                translate([+Xdim / 2, 0, 0]) cube([thickness / 2, 0.3 * Ydim, Zdim], center = true);
                translate([-Xdim / 2, 0, 0]) cube([thickness / 2, 0.3 * Ydim, Zdim], center = true);
            }

        }
    }

module support(shape, D1, H1, D1H, D2, H2, D2H, offsetval, tolerance) {
    difference() {
        union() {

                if (shape == "square") {
                    // translate([0,0,H1/2])
                    cube([D1, D1, H1], center = true);
                    translate([offsetval, 0, (H2 / 2 + (H2 + H1) / 3)]) cube([D2, D2, H2], center = true);

                } else {
                    cylinder(r = (D1 / 2), h = H1, center = false);
                    translate([offsetval, 0, H1]) cylinder(r = (D2 / 2), h = H2, center = false);
                }

                if (offsetval != 0) {
                    translate([0, 0, H1 - (H1 + H2) / 6])
                    hull() { //Connecition
                        if (shape == "square") {
                            translate([0, 0, (H1 + H2) / 9]) cube([D1, D1, (H1 + H2) / 3], center = true);
                            translate([offsetval, 0, (H1 + H2) / 9]) cube([D2, D2, (H1 + H2) / 3], center = true);

                        } else {
                            cylinder(r = (D1 / 2), h = (H1 + H2) / 3, center = false);
                            translate([offsetval, 0, 0]) cylinder(r = D2 / 2, h = (H1 + H2) / 3, center = false);
                        }
                    }
                }
            }
            //Holes
        cylinder(r = D1H / 2 + tolerance, h = 3 * H1, center = false);
        translate([offsetval, 0, H1 - (H1 + H2) / 2]) cylinder(r = D2H / 2 + tolerance, h = 3 * H2, center = false);
    }

}

module SUB_pillars() {
    translate([-Xdim / 2 + supportDiam / 2, -Ydim / 2 + supportDiam / 2, 0]) support("square", supportDiam, Zdim, 2.5, 0, 0, 0, 0, 0.2);
    translate([(Xdim / 2 - supportDiam / 2), (Ydim / 2 - supportDiam / 2), 0]) support("square", supportDiam, Zdim, 2.5, 0, 0, 0, 0, 0.2);
    translate([Xdim / 2 - supportDiam / 2, -Ydim / 2 + supportDiam / 2, 0]) support("square", supportDiam, Zdim, 2.5, 0, 0, 0, 0, 0.2);
    translate([-Xdim / 2 + supportDiam / 2, Ydim / 2 - supportDiam / 2, 0]) support("square", supportDiam, Zdim, 2.5, 0, 0, 0, 0, 0.2);
}

module SUB_regulatorSupport() {
    translate([0, 0, 0]) support("round", supportDiam, 8, 2, 0, 0, 0, 0, 0.2);
    translate([16, 33, 0]) support("round", supportDiam, 8, 2, 0, 0, 0, 0, 0.2);

}

module powerPlant() {
    difference() {
        box(Xdim, Ydim, Zdim, thickness, "box");
        //Button Holes
        translate([Xdim / 4, Ydim / 2, -buttonDiam/2-3]) rotate([90, 0, 0]) {

            cylinder(r = buttonDiam / 2, h = 10, center = true);
            translate([0, buttonDiam + 5, 0]) cylinder(r = buttonDiam / 2, h = 10, center = true);
        }
    }
    SUB_pillars();

    translate([-Xdim / 3, -Ydim / 5+2, -Zdim / 2]) SUB_regulatorSupport();

    //XT60 holder
    translate([0, -Ydim / 2 + XT60Y / 2, 0]) box(XT60X, XT60Y, Zdim, thickness, "servo");

}

module labelVoltage(textL) {

    translate([0, 0, 0]) rotate([0, 0, -90]) linear_extrude(height = thickness / 2) text(textL, font = "Liberation Sans", size = 4);
    translate([4, 3, 0]) rotate([0, 0, -90]) linear_extrude(height = thickness / 2) text("+", font = "Liberation Sans", size = 2);
    translate([0, 3, 0]) rotate([0, 0, -90]) linear_extrude(height = thickness / 2) text("-", font = "Liberation Sans", size = 2);
}

module topLid() {

    translate([0, 0, 0])
    difference() {
        cube([Xdim + thickness, Ydim + thickness, thickness / 2], center = true);
        SUB_lidHoles(0.5);
    }
    translate([-Xdim / 4, -Ydim / 4, 0]) labelVoltage(textHV);
    translate([-Xdim / 4 - 12, -Ydim / 4, 0]) labelVoltage(textLV);

}

module SUB_lidHoles(T) {
    translate([-Xdim / 2 + supportDiam / 2, -Ydim / 2 + supportDiam / 2, -5]) cylinder(r = (2.5 + T) / 2, h = 10);
    translate([(Xdim / 2 - supportDiam / 2), (Ydim / 2 - supportDiam / 2), -5]) cylinder(r = (2.5 + T) / 2, h = 10);
    translate([Xdim / 2 - supportDiam / 2, -Ydim / 2 + supportDiam / 2, -5]) cylinder(r = (2.5 + T) / 2, h = 10);
    translate([-Xdim / 2 + supportDiam / 2, Ydim / 2 - supportDiam / 2, -5]) cylinder(r = (2.5 + T) / 2, h = 10);

    //Holes to power the lines, need to check the geomety. For now visually adjusted
     translate([-Xdim / 4 + 3, -Ydim / 6, -5]) cylinder(r = 4 / 2, h = 10);
     translate([-Xdim / 4 - 9, -Ydim / 6, -5]) cylinder(r = 4 / 2, h = 10);

    //XT60 hole
    translate([0, -Ydim / 2 + XT60Y / 2, 0]) cube([1 * XT60X, 1 * XT60Y, Zdim], center = true);

}

powerPlant();

//translate([0, 0, 0]) topLid();

Zdim = 40;
Xdim = 60;
Ydim = 67;
thickness = 2;
supportDiam = 5;
buttonDiam = 16;
XT60X = 15.54;
XT60Y = 8.16;
XT60Z = 16.10;

textHV = "HVDC";
textLV = "5V";
$fn=80;

//--------------------------------
module box(Xdim, Ydim, Zdim, thickness, application)
    //Creates a box, dimensions are internal
    {
        difference() {

            cube([Xdim + thickness, Ydim + thickness, Zdim], center = true);
            translate([0, 0, thickness / 2]) cube([Xdim, Ydim, Zdim], center = true);

            if (application == "servo") {
                translate([-Xdim / 2, 0, -0.5 * Zdim]) cube([5, Ydim + 2 * thickness, Zdim / 2 + thickness], center = true);
                translate([+Xdim / 2, 0, -0.5 * Zdim]) cube([5, Ydim + 2 * thickness, Zdim / 2 + thickness], center = true);
                translate([+Xdim / 2, 0, 0]) cube([thickness / 2, 0.3 * Ydim, Zdim], center = true);
                translate([-Xdim / 2, 0, 0]) cube([thickness / 2, 0.3 * Ydim, Zdim], center = true);
            }

        }
    }

module support(shape, D1, H1, D1H, D2, H2, D2H, offsetval, tolerance) {
    difference() {
        union() {

                if (shape == "square") {
                    // translate([0,0,H1/2])
                    cube([D1, D1, H1], center = true);
                    translate([offsetval, 0, (H2 / 2 + (H2 + H1) / 3)]) cube([D2, D2, H2], center = true);

                } else {
                    cylinder(r = (D1 / 2), h = H1, center = false);
                    translate([offsetval, 0, H1]) cylinder(r = (D2 / 2), h = H2, center = false);
                }

                if (offsetval != 0) {
                    translate([0, 0, H1 - (H1 + H2) / 6])
                    hull() { //Connecition
                        if (shape == "square") {
                            translate([0, 0, (H1 + H2) / 9]) cube([D1, D1, (H1 + H2) / 3], center = true);
                            translate([offsetval, 0, (H1 + H2) / 9]) cube([D2, D2, (H1 + H2) / 3], center = true);

                        } else {
                            cylinder(r = (D1 / 2), h = (H1 + H2) / 3, center = false);
                            translate([offsetval, 0, 0]) cylinder(r = D2 / 2, h = (H1 + H2) / 3, center = false);
                        }
                    }
                }
            }
            //Holes
        cylinder(r = D1H / 2 + tolerance, h = 3 * H1, center = false);
        translate([offsetval, 0, H1 - (H1 + H2) / 2]) cylinder(r = D2H / 2 + tolerance, h = 3 * H2, center = false);
    }

}

module SUB_pillars() {
    translate([-Xdim / 2 + supportDiam / 2, -Ydim / 2 + supportDiam / 2, 0]) support("square", supportDiam, Zdim, 2.5, 0, 0, 0, 0, 0.2);
    translate([(Xdim / 2 - supportDiam / 2), (Ydim / 2 - supportDiam / 2), 0]) support("square", supportDiam, Zdim, 2.5, 0, 0, 0, 0, 0.2);
    translate([Xdim / 2 - supportDiam / 2, -Ydim / 2 + supportDiam / 2, 0]) support("square", supportDiam, Zdim, 2.5, 0, 0, 0, 0, 0.2);
    translate([-Xdim / 2 + supportDiam / 2, Ydim / 2 - supportDiam / 2, 0]) support("square", supportDiam, Zdim, 2.5, 0, 0, 0, 0, 0.2);
}

module SUB_regulatorSupport() {
    translate([0, 0, 0]) support("round", supportDiam, 8, 2, 0, 0, 0, 0, 0.2);
    translate([11, 33, 0]) support("round", supportDiam, 8, 2, 0, 0, 0, 0, 0.2);

}

module powerPlant() {
    difference() {
        box(Xdim, Ydim, Zdim, thickness, "box");
        //Button Holes
        translate([Xdim / 4, Ydim / 2, -buttonDiam / 2]) rotate([90, 0, 0]) {

            cylinder(r = buttonDiam / 2, h = 10, center = true);
            translate([0, buttonDiam + 3, 0]) cylinder(r = buttonDiam / 2, h = 10, center = true);
        }
    }
    SUB_pillars();

    translate([-Xdim / 4, -Ydim / 5, -Zdim / 2]) SUB_regulatorSupport();

    //XT60 holder
    translate([0, -Ydim / 2 + XT60Y / 2, 0]) box(XT60X, XT60Y, Zdim, thickness, "servo");

}

module labelVoltage(textL) {

    translate([0, 0, 0]) rotate([0, 0, -90]) linear_extrude(height = thickness / 2) text(textL, font = "Liberation Sans", size = 4);
    translate([4, 3, 0]) rotate([0, 0, -90]) linear_extrude(height = thickness / 2) text("+", font = "Liberation Sans", size = 2);
    translate([0, 3, 0]) rotate([0, 0, -90]) linear_extrude(height = thickness / 2) text("-", font = "Liberation Sans", size = 2);
}

module topLid() {

    translate([0, 0, 0])
    difference() {
        cube([Xdim + thickness, Ydim + thickness, thickness / 2], center = true);
        SUB_lidHoles();
    }
    translate([-Xdim / 4, -Ydim / 4, 0]) labelVoltage(textHV);
    translate([-Xdim / 4 - 12, -Ydim / 4, 0]) labelVoltage(textLV);

}

module SUB_lidHoles() {
    translate([-Xdim / 2 + supportDiam / 2, -Ydim / 2 + supportDiam / 2, -5]) cylinder(r = 2.5 / 2, h = 10);
    translate([(Xdim / 2 - supportDiam / 2), (Ydim / 2 - supportDiam / 2), -5]) cylinder(r = 2.5 / 2, h = 10);
    translate([Xdim / 2 - supportDiam / 2, -Ydim / 2 + supportDiam / 2, -5]) cylinder(r = 2.5 / 2, h = 10);
    translate([-Xdim / 2 + supportDiam / 2, Ydim / 2 - supportDiam / 2, -5]) cylinder(r = 2.5 / 2, h = 10);

    //Holes to power the lines, need to check the geomety. Fro now visually adjuste
     translate([-Xdim / 4 + 3, -Ydim / 6, -5]) cylinder(r = 4 / 2, h = 10);
     translate([-Xdim / 4 - 9, -Ydim / 6, -5]) cylinder(r = 4 / 2, h = 10);

    //XT60 hole
    translate([0, -Ydim / 2 + XT60Y / 2, 0]) cube([1 * XT60X, 1 * XT60Y, Zdim], center = true);

}

powerPlant();

//translate([0, 0, 0]) topLid();


Customizer parameters

Customizer parameters

INCHES = 25.4;

// Number of colors
NUMBER = 5; // [2:5]

// Diameter of the base shape
DIAMETER = 60; // [10:200]

// Height of the object
HEIGHT = 120; // [20:300]

// Twist in degress
TWIST = 180; // [0:720]

// Relative displacement of base shapes
DISTANCE = 0.7; // [0:1]

// Scale of the tip relative to the base
SCALE = 5/8; // [0:2]

// Should the object be hollow?
HOLLOW = false; // [true, false]

// If not hollow, What color should the inside have?
INSIDE_COLOR = 1; // [0:5]

module baseshape(size) {
 circle(size/2);
* rotate([0,0,45]) square(size, center=true);
* rotate([0,0,45]) pentagon(size/2);
}

COLORS = ["White", "Red", "Green", "Blue", "Yellow", "Black", "Pink"];
NUM_COLORS = 8;
ITEM = -1;
use <MCAD/regular_shapes.scad>

module base(neg = false) {
  if (neg) {
    difference() {
      translate([DIAMETER/2*DISTANCE,0]) baseshape(DIAMETER);
      translate([-DIAMETER/2*DISTANCE,0]) baseshape(DIAMETER);
    }
  }
  else {
    translate([-DIAMETER/2*DISTANCE,0]) baseshape(DIAMETER);
  }
}

module baseitem(n) {
  rotate([0,0,n*360/NUMBER]) translate([DIAMETER/2*DISTANCE,0]) baseshape(DIAMETER);
}

module newbase(n) {
  difference() {
    baseitem(n);
    for (i=[0:NUMBER-1]) {
      if (i!=n) baseitem(i);
    }
  }
}

module smoother() {
  hull() {
    sphere(r=10);
    translate([0,0,54.5]) sphere(r=4);
  }
}

module extrudepart() {
    linear_extrude(height=HEIGHT, twist=TWIST, scale=[SCALE, SCALE], convexity=3) child();
}

module basepart(item) {
    extrudepart() baseitem(item);
    translate([0,0,HEIGHT]) rotate([0,0,TWIST])
     scale(SCALE) rotate([0,0,item*360/NUMBER])
      translate([DIAMETER/2*DISTANCE,0])
       sphere(DIAMETER/2);
}

module part(item) {
    color(COLORS[item%NUM_COLORS]) {
        render() difference() {
            basepart(item);
            for (i=[0:NUMBER-1]) if (i!=item) basepart(i);
        }
        if (!HOLLOW && INSIDE_COLOR == item) inside();
    }
}

module inside() {
    extrudepart() for (i=[1:NUMBER-1], j=[0:i-1]) {
        intersection() {baseitem(i); baseitem(j);}
    }
}

module horn(item) {
  if (item == -1) {
      for (x=[0:NUMBER-1]) part(x);
      if (!HOLLOW) color(COLORS[INSIDE_COLOR]) inside();
  }
  else {
      part(item);
  }
}

horn(ITEM);

INCHES = 25.4;

NUMBER = 5;
DIAMETER = 60;
HEIGHT = 120;
TWIST = 0.5;
DISTANCE = 0.7;
SCALE = 5/8;

HOLLOW = false;
INSIDE_COLOR = 1;

module baseshape(size) {
 circle(size/2);
* rotate([0,0,45]) square(size, center=true);
* rotate([0,0,45]) pentagon(size/2);
}

COLORS = ["White", "Red", "Green", "Blue", "Yellow", "Black", "Pink"];
NUM_COLORS = 8;
ITEM = -1;
use <MCAD/regular_shapes.scad>

module base(neg = false) {
  if (neg) {
    difference() {
      translate([DIAMETER/2*DISTANCE,0]) baseshape(DIAMETER);
      translate([-DIAMETER/2*DISTANCE,0]) baseshape(DIAMETER);
    }
  }
  else {
    translate([-DIAMETER/2*DISTANCE,0]) baseshape(DIAMETER);
  }
}

module baseitem(n) {
  rotate([0,0,n*360/NUMBER]) translate([DIAMETER/2*DISTANCE,0]) baseshape(DIAMETER);
}

module newbase(n) {
  difference() {
    baseitem(n);
    for (i=[0:NUMBER-1]) {
      if (i!=n) baseitem(i);
    }
  }
}

module smoother() {
  hull() {
    sphere(r=10);
    translate([0,0,54.5]) sphere(r=4);
  }
}

module extrudepart() {
    linear_extrude(height=HEIGHT, twist=TWIST*360, scale=[SCALE, SCALE], convexity=3) child();
}

module basepart(item) {
    extrudepart() baseitem(item);
    translate([0,0,HEIGHT]) rotate([0,0,TWIST*360])
     scale(SCALE) rotate([0,0,item*360/NUMBER])
      translate([DIAMETER/2*DISTANCE,0])
       sphere(DIAMETER/2);
}

module part(item) {
    color(COLORS[item%NUM_COLORS]) {
        render() difference() {
            basepart(item);
            for (i=[0:NUMBER-1]) if (i!=item) basepart(i);
        }
        if (!HOLLOW && INSIDE_COLOR == item) inside();
    }
}

module inside() {
    extrudepart() for (i=[1:NUMBER-1], j=[0:i-1]) {
        intersection() {baseitem(i); baseitem(j);}
    }
}

module horn(item) {
  if (item == -1) {
      for (x=[0:NUMBER-1]) part(x);
      if (!HOLLOW) color(COLORS[INSIDE_COLOR]) inside();
  }
  else {
      part(item);
  }
}

horn(ITEM);