Colors were added to the outer rings.

Colors were added to the outer rings.

//
// This OpenScad script generates coins, pendants, and ornaments.
//
// Author: Roberto Marquez - http://electronics.onebeartoe.org/
//
// This OpenSCAD script is a derivative of:
//
// Customizable Gimbal v1.0
// by TheNewHobbyist 2014 (http://thenewhobbyist.com)
// http://www.thingiverse.com/thing:340948
// https://github.com/thenewhobbyist/OpenSCAD/tree/master/Customizable%20Gimble
//
// "Customizable Gimbal" as well as this script are licensed under a
// Creative Commons Attribution-NonCommercial 3.0 Unported License.
//
// TheNewHobbyist's Inspiration and attribution:
//
// Printrbot Gimbal by DesignMakeTeach
// http://www.thingiverse.com/thing:317778
// Licensed under the Creative Commons - Attribution - Share Alike license.
//
// Stencil-o-Matic by Benjamin
// http://www.thingiverse.com/thing:55821
// Licensed under the Creative Commons - Attribution - Non-Commercial license.

use <spur/spurs-rotated/spurs-rotated.scad>;

$fn=100;

ornament();

module ornament(height = 3,
                includeGimbal = "yes" // [yes, no]
                )
{
    {
        union()
        {
            if(includeGimbal == "yes")
            {
                ring1(height);
                ring2(height);
                ring3(height);
            }

            difference()
            {
                color("blue")
                pattern(height);

                clipOversizedImages(center=true);
            }
        }
    }
}

module pattern(height)
{
    coinScale =  0.2;//0.199248

    union()
    {
    	scale([coinScale, -coinScale, 1])
    	{
    		union()
    		{
    		    coin(height=height);
			}
		}
	}
}

module clipOversizedImages()
{
	difference()
	{
		cylinder(r=200, h=10, center=true);
		cylinder(r=11, h=11, center=true);
	}
}

module ring1(height)
{
	difference()
	{
	    color("pink")
    	cylinder(r=19.35, h=height, center=true);

    	cylinder(r=17.35, h=56, center=true);
    	translate([17.45,0,0]) rotate([0,90,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
    	translate([-17.45,0,0]) rotate([0,270,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	}
}

module ring2(height)
{
	translate([16.95,0,0]) rotate([0,90,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	translate([-16.95,0,0]) rotate([0,270,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	difference()
	{
	    color("grey")
		cylinder(r=15.95, h=height, center=true);

		cylinder(r=13.95, h=56, center=true);
		translate([0,-14.2,0]) rotate([90,0,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
		translate([0,14.2,0]) rotate([270,0,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	}
}

module ring3(height)
{
	translate([0,13.75,0]) rotate([270,0,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	translate([0,-13.75,0]) rotate([90,0,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	difference()
	{
		cylinder(r=12.75, h=height, center=true);
		cylinder(r=10.75, h=56, center=true);
	}
}

//
// This OpenScad script generates coins, pendants, and ornaments.
//
// Author: Roberto Marquez - http://electronics.onebeartoe.org/
//
// This OpenSCAD script is a derivative of:
//
// Customizable Gimbal v1.0
// by TheNewHobbyist 2014 (http://thenewhobbyist.com)
// http://www.thingiverse.com/thing:340948
// https://github.com/thenewhobbyist/OpenSCAD/tree/master/Customizable%20Gimble
//
// "Customizable Gimbal" as well as this script are licensed under a
// Creative Commons Attribution-NonCommercial 3.0 Unported License.
//
// TheNewHobbyist's Inspiration and attribution:
//
// Printrbot Gimbal by DesignMakeTeach
// http://www.thingiverse.com/thing:317778
// Licensed under the Creative Commons - Attribution - Share Alike license.
//
// Stencil-o-Matic by Benjamin
// http://www.thingiverse.com/thing:55821
// Licensed under the Creative Commons - Attribution - Non-Commercial license.

use <spur/spurs-rotated/spurs-rotated.scad>;

$fn=100;

ornament();

module ornament(height = 3,
                includeGimbal = "yes" // [yes, no]
                )
{
    {
        union()
        {
            if(includeGimbal == "yes")
            {
                ring1(height);
                ring2(height);
                ring3(height);
            }

            difference()
            {
                color("blue")
                pattern(height);

                clipOversizedImages(center=true);
            }
        }
    }
}

module pattern(height)
{
    coinScale =  0.2;//0.199248

    union()
    {
    	scale([coinScale, -coinScale, 1])
    	{
    		union()
    		{
    		    coin(height=height);
			}
		}
	}
}

module clipOversizedImages()
{
	difference()
	{
		cylinder(r=200, h=10, center=true);
		cylinder(r=11, h=11, center=true);
	}
}

module ring1(height)
{
	difference()
	{
    	cylinder(r=19.35, h=height, center=true);

    	cylinder(r=17.35, h=56, center=true);
    	translate([17.45,0,0]) rotate([0,90,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
    	translate([-17.45,0,0]) rotate([0,270,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	}
}

module ring2(height)
{
	translate([16.95,0,0]) rotate([0,90,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	translate([-16.95,0,0]) rotate([0,270,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	difference()
	{
		cylinder(r=15.95, h=height, center=true);
		cylinder(r=13.95, h=56, center=true);
		translate([0,-14.2,0]) rotate([90,0,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
		translate([0,14.2,0]) rotate([270,0,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	}
}

module ring3(height)
{
	translate([0,13.75,0]) rotate([270,0,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	translate([0,-13.75,0]) rotate([90,0,0]) cylinder(r1=1.7, r2=0, h=2.1, center=true);
	difference()
	{
		cylinder(r=12.75, h=height, center=true);
		cylinder(r=10.75, h=56, center=true);
	}
}


Use donutSector instead of custom work

Use donutSector instead of custom work

/* Pins required -

pintack(side=true, h=7.8+2+2.5, bh=2);

*/

module Bumper_STL()
{
	printedPart("printedparts/Bumper.scad", "Bumper", "Bumper_STL()") {

	    //view(t=[0, -1, -1], r=[49, 0, 25], d=336);

		if (DebugCoordinateFrames) frame();
		if (DebugConnectors) connector(Wheel_Con_Default);

		color(Level2PlasticColor) {
			if (UseSTL) {
				import(str(STLPath, "Bumper.stl"));
			} else {
				BumperModel();
			}
		}
	}
}

module BumperModel()
{
	// Parameters
	thickness = dw;				// bumper thickness
	height = 15;				// bumper height
	offset = 5;					// distance from base to inside of bumper
	wrapAngle = 170;			// angle that bumper wraps around
	microSwitchAngle = 43.5;	// angle that microswitches are placed at

	bumpstopWidth = 8;			// width of little block that sticks out to hit the microswitch
	bumpstopHeight = 5;			// height of little block that sticks out to hit the microswitch

	// Calculated parameters
	outr = BaseDiameter/2 + offset + thickness;		// outside radius or the bumper

	// Bumper arc
	linear_extrude(height)
	rotate([0, 0, (180 - wrapAngle) / 2])
		donutSector(outr, outr - thickness, wrapAngle);

	// Connectors
	for(i=[0, 1])
	mirror([i, 0, 0])
	rotate([0, 0, microSwitchAngle]) {

		// Springy bits
		translate([15, BaseDiameter/2 - 11, 0])
		rotate([0, 0, -130])
		linear_extrude(5)
			donutSector(17, 17 - 2*perim, 175);

		// Bumpstops to hit microwitch arm
		translate([0, BaseDiameter/2 - 1 , 0])
			cube([bumpstopWidth, offset + 1 , bumpstopHeight]);

		// Microswitch plates
		translate([-10, BaseDiameter/2 - 23, 0])
			MicroSwitchPlate();
	}
}

module MicroSwitchPlate()
{
	// Parameters - todo: pickup from microswitch model?
	ms_length=12.9;
	ms_width=6.6;
	ms_height=5.8;

	base_length = ms_length + 2 * dw + 0.5;
	base_width = ms_width + 13.5;

	// Base
	translate([0, 6.5, 0])
		cube([base_length, base_width - 6.5, dw]);

	// Microswitch surround
	translate([0, 7, 0])
		cube([dw, base_width - 7, dw + 3]);
	translate([base_length - dw, 7, 0])
		cube([dw, base_width - 7, dw + 3]);
	translate([0, 13-dw, 0])
		cube([base_length, dw, dw + 2]);

	// Rear support lozenge with pin hole
	difference() {
		linear_extrude(dw + ms_height)
		hull() {
			translate([2.5, 7])
				circle(r=2.5);
			translate([base_length, 9.5])
				circle(r=eta);
			translate([13, 1])
				circle(r=2.5);
			translate([20, 1])
				circle(r=2.5);
		}

		// Actual hole
		translate([base_length/2 + 7, 2.65, dw + ms_height - eta])
		translate([0, 0, dw])
		mirror([0, 0, 1])
			pinhole(fixed=true);
	}
}

/* Pins required -

pintack(side=true, h=7.8+2+2.5, bh=2);

*/

module Bumper_STL()
{
	printedPart("printedparts/Bumper.scad", "Bumper", "Bumper_STL()") {

	    //view(t=[0, -1, -1], r=[49, 0, 25], d=336);

		if (DebugCoordinateFrames) frame();
		if (DebugConnectors) connector(Wheel_Con_Default);

		color(Level2PlasticColor) {
			if (UseSTL) {
				import(str(STLPath, "Bumper.stl"));
			} else {
				BumperModel();
			}
		}
	}
}

module BumperModel()
{
	// Parameters
	thickness = dw;				// bumper thickness
	height = 15;				// bumper height
	offset = 5;					// distance from base to inside of bumper
	wrapAngle = 170;			// angle that bumper wraps around
	microSwitchAngle = 43.5;	// angle that microswitches are placed at

	bumpstopWidth = 8;			// width of little block that sticks out to hit the microswitch
	bumpstopHeight = 5;			// height of little block that sticks out to hit the microswitch

	// Calculated parameters
	outr = BaseDiameter/2 + offset + thickness;		// outside radius or the bumper

	// Bumper arc
	linear_extrude(height)
	difference() {
		circle(r = outr);

		circle(r = outr - thickness);

		// Chop off below X-axis
		translate([-outr, -outr])
			square([outr*2, outr]);

		// Trim left side of arc
		rotate([0, 0, wrapAngle/2])
		translate([-outr, 0])
			square([outr, outr * 2]);

		// Trim right side of arc
		rotate([0, 0, -wrapAngle/2])
			square([outr, outr * 2]);
	}

	// Connectors
	for(i=[0, 1])
	mirror([i, 0, 0])
	rotate([0, 0, microSwitchAngle]) {

		// Springy bits
		translate([15, BaseDiameter/2 - 11, 0])
		rotate([0, 0, -130])
		linear_extrude(5)
			donutSector(17, 17 - 2*perim, 175);

		// Bumpstops to hit microwitch arm
		translate([0, BaseDiameter/2 - 1 , 0])
			cube([bumpstopWidth, offset + 1 , bumpstopHeight]);

		// Microswitch plates
		translate([-10, BaseDiameter/2 - 23, 0])
			MicroSwitchPlate();
	}
}

module MicroSwitchPlate()
{
	// Parameters - todo: pickup from microswitch model?
	ms_length=12.9;
	ms_width=6.6;
	ms_height=5.8;

	base_length = ms_length + 2 * dw + 0.5;
	base_width = ms_width + 13.5;

	// Base
	translate([0, 6.5, 0])
		cube([base_length, base_width - 6.5, dw]);

	// Microswitch surround
	translate([0, 7, 0])
		cube([dw, base_width - 7, dw + 3]);
	translate([base_length - dw, 7, 0])
		cube([dw, base_width - 7, dw + 3]);
	translate([0, 13-dw, 0])
		cube([base_length, dw, dw + 2]);

	// Rear support lozenge with pin hole
	difference() {
		linear_extrude(dw + ms_height)
		hull() {
			translate([2.5, 7])
				circle(r=2.5);
			translate([base_length, 9.5])
				circle(r=eta);
			translate([13, 1])
				circle(r=2.5);
			translate([20, 1])
				circle(r=2.5);
		}

		// Actual hole
		translate([base_length/2 + 7, 2.65, dw + ms_height - eta])
		translate([0, 0, dw])
		mirror([0, 0, 1])
			pinhole(fixed=true);
	}
}


Increased size of bumper bumpstop

Increased size of bumper bumpstop

To make bumper/microswitch easier and more tolerant to being bent out
of alignment…  might also avoid need for bumper stabiliser - subject to
testing

/* Pins required -

pintack(side=true, h=7.8+2+2.5+6*layers, bh=2);

*/

// TODO: The locations of these connectors are just horrible and too "magic"
Bumper_Con_LeftMicroSwitch =
[
	zr3Vect(   [(-10 + dw + 9.6 +.25), (BaseDiameter/2 - 16 + 4 + dw + 1.25 + .5), dw], 43.5    ),
	[0, 0, 1],
	43.5,
	0,
	0];

Bumper_Con_RightMicroSwitch =
[
	[ -zr3Vect([(-10 + dw + 9.6 +.25), (BaseDiameter/2 - 16 + 4 + dw + 1.25 + .5), dw], 43.5)[0],
	  zr3Vect([(-10 + dw + 9.6 +.25), (BaseDiameter/2 - 16 + 4 + dw + 1.25 + .5), dw], 43.5)[1],
	  zr3Vect([(-10 + dw + 9.6 +.25), (BaseDiameter/2 - 16 + 4 + dw + 1.25 + .5), dw], 43.5)[2] ],
	[0, 0, 1],
	-43.5,
	0,
	0];

Bumper_Con_LeftPin =
[
	zr3Vect(   [(-10 + 15.7 ), (BaseDiameter/2 - 16 + -7 + 2.65 ), dw + 5.8 + dw], 43.5    ),
	[0, 0, 1],
	43.5,
	0,
	0];

Bumper_Con_RightPin =
[
	[ -zr3Vect(   [(-10 + 15.7 ), (BaseDiameter/2 - 16 + -7 + 2.65 ), dw + 5.8 + dw], 43.5    )[0],
	  zr3Vect(   [(-10 + 15.7 ), (BaseDiameter/2 - 16 + -7 + 2.65 ), dw + 5.8 + dw], 43.5    )[1],
	  zr3Vect(   [(-10 + 15.7 ), (BaseDiameter/2 - 16 + -7 + 2.65 ), dw + 5.8 + dw], 43.5    )[2] ],
	[0, 0, 1],
	-43.5,
	0,
	0];

module Bumper_STL()
{
	printedPart("printedparts/Bumper.scad", "Bumper", "Bumper_STL()") {

	    view(t=[0,0,0], r=[58,0,225], d=681);

		if (DebugCoordinateFrames) frame();
		if (DebugConnectors) {
			connector(Bumper_Con_LeftMicroSwitch);
			connector(Bumper_Con_RightMicroSwitch);
			connector(Bumper_Con_LeftPin);
			connector(Bumper_Con_RightPin);
		}

		color(Level2PlasticColor) {
			if (UseSTL) {
				import(str(STLPath, "Bumper.stl"));
			} else {
				BumperModel();
			}
		}
	}
}

module BumperModel()
{
	// Parameters
	thickness = dw;				// bumper thickness
	height = 15;				// bumper height
	offset = 5;					// distance from base to inside of bumper
	wrapAngle = 170;			// angle that bumper wraps around
	microSwitchAngle = 43.5;	// angle that microswitches are placed at

	bumpstopWidth = 12;			// width of little block that sticks out to hit the microswitch
	bumpstopHeight = 7;			// height of little block that sticks out to hit the microswitch

	// Calculated parameters
	outr = BaseDiameter/2 + offset + thickness;		// outside radius of the bumper

    // Bumper flange
	rotate([0, 0, (180 - wrapAngle) / 2])
    difference() {
        linear_extrude(thickness*2)
            donutSector(outr, outr-thickness*2, wrapAngle, $fn=128);

        translate([0,0,thickness*2])
            rotate_extrude(convexity = 10, $fn=128)
                translate([outr-thickness*2,0,0])
                     circle(r=thickness, $fn=24);
    }

	// Bumper arc
	difference() {
		linear_extrude(height)
			rotate([0, 0, (180 - wrapAngle) / 2])
			donutSector(outr, outr - thickness, wrapAngle, $fn=128);

		// trim off the back of the bumper to save plastic and look cool
		translate([0,0,3])
			rotate([15,0,0])
			translate([-outr,0,0])
			cube([2*outr, 100,50]);
	}

	// text
	s = "LOGOBOT";
	lw = 10;  // letter width
	for (i=[0:len(s)-1])
		rotate([0,0,-21 + i*7 + (i==0 ? 2 : 0)])
		translate([5, outr-1.5, (height-10)/2 ])
		rotate([90,0,180])
		linear_extrude(2)
		text(s[i], size=10);

	// Connectors
	for(i=[0, 1])
	mirror([i, 0, 0])
	rotate([0, 0, microSwitchAngle])
	 {

		// Springy bits
		MicroSwitchSpring();

		// old Springy bits
		*translate([15, BaseDiameter/2 - 11, 0])
		rotate([0, 0, -130])
		linear_extrude(5)
			donutSector(17, 17 - 2*perim, 175);

		// Bumpstops to hit microwitch arm
		translate([-bumpstopWidth + 8, BaseDiameter/2 - 1 , 0])
			cube([bumpstopWidth, offset + 1 , bumpstopHeight]);

		// Microswitch plates
		translate([-10, BaseDiameter/2 - 16, 0])
			MicroSwitchPlate();
	}
}

module MicroSwitchSpring() {
	// generate a spring along a bezier curve :)

	// number of steps in curve
	steps = 50;

	// control points
	cp1 = [7, BaseDiameter/2];  // start
	cp4 = [3,  BaseDiameter/2 - 24];  // end
	cp2 = [ cp1[0] + 40,  cp1[1]-30 ];  // handle 1
	cp3 = [ cp4[0] + 20,  cp4[1]-20 ];  // handle 2

	// debug control points
	*color("red") {
		translate(cp1) cylinder(r=0.2, h=30);
		translate(cp2) cylinder(r=0.2, h=30);
		translate(cp3) cylinder(r=0.2, h=30);
		translate(cp4) cylinder(r=0.2, h=30);
	}

	// generate the actual curve
	linear_extrude(5)
		union() // union lots of little "pill" shapes for each segment of the curve
		for (i=[0:steps-2]) {
			// curve time parameters for this step and next
			u1 = i / (steps-1);
			u2 = (i+1) / (steps-1);

			// 2D points for this step and next
			p1 = PtOnBez2D(cp1, cp2, cp3, cp4, u1);
			p2 = PtOnBez2D(cp1, cp2, cp3, cp4, u2);

			// hull together circles at this the position of this step and next
			hull($fn=8) {
				translate(p1) circle(perim);
				translate(p2) circle(perim);
			}
		}
}

module MicroSwitchPlate()
{
	// Parameters - todo: pickup from microswitch model?
	ms_length=12.9;
	ms_width=6.6;
	ms_height=5.8;

	base_length = ms_length + 0.5 + 2*dw;
	base_width = ms_width + 6.5;

	// Base
	cube([base_length, base_width, dw]);
	// Microswitch surround
	cube([dw, base_width, dw + 3]);
	translate([base_length - dw, 0, 0])
		cube([dw, base_width, dw + 3]);
	translate([0, 4, 0])
		cube([base_length, dw, dw + 2]);

	// Rear support lozenge with pin hole
	lozenge_height = dw + ms_height;

	translate([0, -7, 0])
	difference() {
		linear_extrude(lozenge_height)
		hull() {
			translate([2.5, 7])
				circle(r=2.5);
			translate([base_length, 9.5])
				circle(r=eta);
			translate([13, 1])
				circle(r=2.5);
			translate([20, 1])
				circle(r=2.5);
		}

		// Actual hole
		translate([15.7, 2.65, lozenge_height + dw])
		mirror([0, 0, 1])
			pinhole(fixed=true);
	}
}

// Rotate a 3vector v = [x,y,z] about the origin by ang degrees
function zr3Vect(v, ang) =
[
	v[0] * cos(ang) - v[1] * sin(ang),
	v[0] * sin(ang) + v[1] * cos(ang),
	v[2]
];

/* Pins required -

pintack(side=true, h=7.8+2+2.5+6*layers, bh=2);

*/

// TODO: The locations of these connectors are just horrible and too "magic"
Bumper_Con_LeftMicroSwitch =
[
	zr3Vect(   [(-10 + dw + 9.6 +.25), (BaseDiameter/2 - 16 + 4 + dw + 1.25 + .5), dw], 43.5    ),
	[0, 0, 1],
	43.5,
	0,
	0];

Bumper_Con_RightMicroSwitch =
[
	[ -zr3Vect([(-10 + dw + 9.6 +.25), (BaseDiameter/2 - 16 + 4 + dw + 1.25 + .5), dw], 43.5)[0],
	  zr3Vect([(-10 + dw + 9.6 +.25), (BaseDiameter/2 - 16 + 4 + dw + 1.25 + .5), dw], 43.5)[1],
	  zr3Vect([(-10 + dw + 9.6 +.25), (BaseDiameter/2 - 16 + 4 + dw + 1.25 + .5), dw], 43.5)[2] ],
	[0, 0, 1],
	-43.5,
	0,
	0];

Bumper_Con_LeftPin =
[
	zr3Vect(   [(-10 + 15.7 ), (BaseDiameter/2 - 16 + -7 + 2.65 ), dw + 5.8 + dw], 43.5    ),
	[0, 0, 1],
	43.5,
	0,
	0];

Bumper_Con_RightPin =
[
	[ -zr3Vect(   [(-10 + 15.7 ), (BaseDiameter/2 - 16 + -7 + 2.65 ), dw + 5.8 + dw], 43.5    )[0],
	  zr3Vect(   [(-10 + 15.7 ), (BaseDiameter/2 - 16 + -7 + 2.65 ), dw + 5.8 + dw], 43.5    )[1],
	  zr3Vect(   [(-10 + 15.7 ), (BaseDiameter/2 - 16 + -7 + 2.65 ), dw + 5.8 + dw], 43.5    )[2] ],
	[0, 0, 1],
	-43.5,
	0,
	0];

module Bumper_STL()
{
	printedPart("printedparts/Bumper.scad", "Bumper", "Bumper_STL()") {

	    view(t=[0,0,0], r=[58,0,225], d=681);

		if (DebugCoordinateFrames) frame();
		if (DebugConnectors) {
			connector(Bumper_Con_LeftMicroSwitch);
			connector(Bumper_Con_RightMicroSwitch);
			connector(Bumper_Con_LeftPin);
			connector(Bumper_Con_RightPin);
		}

		color(Level2PlasticColor) {
			if (UseSTL) {
				import(str(STLPath, "Bumper.stl"));
			} else {
				BumperModel();
			}
		}
	}
}

module BumperModel()
{
	// Parameters
	thickness = dw;				// bumper thickness
	height = 15;				// bumper height
	offset = 5;					// distance from base to inside of bumper
	wrapAngle = 170;			// angle that bumper wraps around
	microSwitchAngle = 43.5;	// angle that microswitches are placed at

	bumpstopWidth = 8;			// width of little block that sticks out to hit the microswitch
	bumpstopHeight = 5;			// height of little block that sticks out to hit the microswitch

	// Calculated parameters
	outr = BaseDiameter/2 + offset + thickness;		// outside radius of the bumper

    // Bumper flange
	rotate([0, 0, (180 - wrapAngle) / 2])
    difference() {
        linear_extrude(thickness*2)
            donutSector(outr, outr-thickness*2, wrapAngle, $fn=128);

        translate([0,0,thickness*2])
            rotate_extrude(convexity = 10, $fn=128)
                translate([outr-thickness*2,0,0])
                     circle(r=thickness, $fn=24);
    }

	// Bumper arc
	difference() {
		linear_extrude(height)
			rotate([0, 0, (180 - wrapAngle) / 2])
			donutSector(outr, outr - thickness, wrapAngle, $fn=128);

		// trim off the back of the bumper to save plastic and look cool
		translate([0,0,3])
			rotate([15,0,0])
			translate([-outr,0,0])
			cube([2*outr, 100,50]);
	}

	// text
	s = "LOGOBOT";
	lw = 10;  // letter width
	for (i=[0:len(s)-1])
		rotate([0,0,-21 + i*7 + (i==0 ? 2 : 0)])
		translate([5, outr-1.5, (height-10)/2 ])
		rotate([90,0,180])
		linear_extrude(2)
		text(s[i], size=10);

	// Connectors
	for(i=[0, 1])
	mirror([i, 0, 0])
	rotate([0, 0, microSwitchAngle])
	 {

		// Springy bits
		MicroSwitchSpring();

		// old Springy bits
		*translate([15, BaseDiameter/2 - 11, 0])
		rotate([0, 0, -130])
		linear_extrude(5)
			donutSector(17, 17 - 2*perim, 175);

		// Bumpstops to hit microwitch arm
		translate([0, BaseDiameter/2 - 1 , 0])
			cube([bumpstopWidth, offset + 1 , bumpstopHeight]);

		// Microswitch plates
		translate([-10, BaseDiameter/2 - 16, 0])
			MicroSwitchPlate();
	}
}

module MicroSwitchSpring() {
	// generate a spring along a bezier curve :)

	// number of steps in curve
	steps = 50;

	// control points
	cp1 = [7, BaseDiameter/2];  // start
	cp4 = [3,  BaseDiameter/2 - 24];  // end
	cp2 = [ cp1[0] + 40,  cp1[1]-30 ];  // handle 1
	cp3 = [ cp4[0] + 20,  cp4[1]-20 ];  // handle 2

	// debug control points
	*color("red") {
		translate(cp1) cylinder(r=0.2, h=30);
		translate(cp2) cylinder(r=0.2, h=30);
		translate(cp3) cylinder(r=0.2, h=30);
		translate(cp4) cylinder(r=0.2, h=30);
	}

	// generate the actual curve
	linear_extrude(5)
		union() // union lots of little "pill" shapes for each segment of the curve
		for (i=[0:steps-2]) {
			// curve time parameters for this step and next
			u1 = i / (steps-1);
			u2 = (i+1) / (steps-1);

			// 2D points for this step and next
			p1 = PtOnBez2D(cp1, cp2, cp3, cp4, u1);
			p2 = PtOnBez2D(cp1, cp2, cp3, cp4, u2);

			// hull together circles at this the position of this step and next
			hull($fn=8) {
				translate(p1) circle(perim);
				translate(p2) circle(perim);
			}
		}
}

module MicroSwitchPlate()
{
	// Parameters - todo: pickup from microswitch model?
	ms_length=12.9;
	ms_width=6.6;
	ms_height=5.8;

	base_length = ms_length + 0.5 + 2*dw;
	base_width = ms_width + 6.5;

	// Base
	cube([base_length, base_width, dw]);
	// Microswitch surround
	cube([dw, base_width, dw + 3]);
	translate([base_length - dw, 0, 0])
		cube([dw, base_width, dw + 3]);
	translate([0, 4, 0])
		cube([base_length, dw, dw + 2]);

	// Rear support lozenge with pin hole
	lozenge_height = dw + ms_height;

	translate([0, -7, 0])
	difference() {
		linear_extrude(lozenge_height)
		hull() {
			translate([2.5, 7])
				circle(r=2.5);
			translate([base_length, 9.5])
				circle(r=eta);
			translate([13, 1])
				circle(r=2.5);
			translate([20, 1])
				circle(r=2.5);
		}

		// Actual hole
		translate([15.7, 2.65, lozenge_height + dw])
		mirror([0, 0, 1])
			pinhole(fixed=true);
	}
}

// Rotate a 3vector v = [x,y,z] about the origin by ang degrees
function zr3Vect(v, ang) =
[
	v[0] * cos(ang) - v[1] * sin(ang),
	v[0] * sin(ang) + v[1] * cos(ang),
	v[2]
];


Prepared the program for laser cutting

Prepared the program for laser cutting

/**
Program by Ankur Gupta
www.github.com/agupta231
Jan 2017

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/

init_square_size = 40;
connector_length = 15;
connector_thickness = 0.1;
iteration_multiplier = 0.5;
iterations = 6;

echo(version());

translation_vectors = [[1, 1], [-1, -1], [-1, 1], [1, -1]];
rotation_vectors = [[1, 1, -1], [1, 1, -1], [1, 1, 1], [1, 1, 1]];

function ev_mult(vec1, vec2) =
    len(vec1) != len(vec2) ? false : [for(i = [0:len(vec1) - 1]) vec1[i] * vec2[i]];

module translated_cube(translation_vector, starting_pos, size) {
    translate(translation_vector + starting_pos) {
      square(size, center = true);
    }
}

module connector(vector_keys, connector_size, square_size, starting_pos, current_iteration) {
    if(current_iteration < iterations) {
        for(i = vector_keys) {
            corner = ev_mult(translation_vectors[i], [square_size / 2, square_size / 2]) + starting_pos;
            connector_correction = ev_mult(translation_vectors[i], [connector_size / (2 * sqrt(2)), connector_size / (2 * sqrt(2))]);
            next_square_center = corner + ev_mult(translation_vectors[i], [connector_size / sqrt(2), connector_size / sqrt(2)]);

            translate(corner + connector_correction) {
                rotate(ev_mult(rotation_vectors[i], [0, 45, 45])) {
                    #square([connector_thickness, connector_size], center = true);
                }
            }

            if(i == 0) {
                pattern(next_square_center, "bottom_left", current_iteration + 1);
            }
            else if(i == 1) {
                pattern(next_square_center, "top_right", current_iteration + 1);
            }
            else if(i == 2) {
                pattern(next_square_center, "bottom_right", current_iteration + 1);
            }
            else if(i == 3){
                pattern(next_square_center, "top_left", current_iteration + 1);
            }
        }
    }
}

module pattern(starting_pos, seed_corner, current_iteration) {
    translation = false;
    translations = false;
    rotations = false;
    current_size = init_square_size * pow(iteration_multiplier, current_iteration - 1);
    current_connector_size = connector_length * pow(iteration_multiplier, current_iteration - 1);

    if(seed_corner == "center") {
        center_pos = [0,0] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "bottom_left") {
        center_pos = [current_size / 2, current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "bottom_right") {
        center_pos = [-current_size / 2, current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 2], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "top_left") {
        center_pos = [current_size / 2, -current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "top_right") {
        center_pos = [-current_size / 2, -current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([1, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
}

union() {
    pattern([0, 0], "center", 1);
}

/**
Program by Ankur Gupta
www.github.com/agupta231
Jan 2017

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/

init_square_size = 40;
connector_length = 20;
connector_thickness = 0.1;
iteration_multiplier = 0.5;
iterations = 8;

echo(version());

translation_vectors = [[1, 1], [-1, -1], [-1, 1], [1, -1]];
rotation_vectors = [[1, 1, -1], [1, 1, -1], [1, 1, 1], [1, 1, 1]];

function ev_mult(vec1, vec2) =
    len(vec1) != len(vec2) ? false : [for(i = [0:len(vec1) - 1]) vec1[i] * vec2[i]];

module translated_cube(translation_vector, starting_pos, size) {
    translate(translation_vector + starting_pos) {
      square(size, center = true);
    }
}

module connector(vector_keys, connector_size, square_size, starting_pos, current_iteration) {
    if(current_iteration < iterations) {
        for(i = vector_keys) {
            corner = ev_mult(translation_vectors[i], [square_size / 2, square_size / 2]) + starting_pos;
            connector_correction = ev_mult(translation_vectors[i], [connector_size / (2 * sqrt(2)), connector_size / (2 * sqrt(2))]);
            next_square_center = corner + ev_mult(translation_vectors[i], [connector_size / sqrt(2), connector_size / sqrt(2)]);

            translate(corner + connector_correction) {
                rotate(ev_mult(rotation_vectors[i], [0, 45, 45])) {
                    #square([connector_thickness, connector_size], center = true);
                }
            }

            if(i == 0) {
                pattern(next_square_center, "bottom_left", current_iteration + 1);
            }
            else if(i == 1) {
                pattern(next_square_center, "top_right", current_iteration + 1);
            }
            else if(i == 2) {
                pattern(next_square_center, "bottom_right", current_iteration + 1);
            }
            else if(i == 3){
                pattern(next_square_center, "top_left", current_iteration + 1);
            }
        }
    }
}

module pattern(starting_pos, seed_corner, current_iteration) {
    translation = false;
    translations = false;
    rotations = false;
    current_size = init_square_size * pow(iteration_multiplier, current_iteration - 1);
    current_connector_size = connector_length * pow(iteration_multiplier, current_iteration - 1);

    if(seed_corner == "center") {
        center_pos = [0,0] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "bottom_left") {
        center_pos = [current_size / 2, current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "bottom_right") {
        center_pos = [-current_size / 2, current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 2], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "top_left") {
        center_pos = [current_size / 2, -current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([0, 1, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
    else if(seed_corner == "top_right") {
        center_pos = [-current_size / 2, -current_size / 2] + starting_pos;
        translate(center_pos) square(current_size, center=true);

        connector([1, 2, 3], current_connector_size, current_size, center_pos, current_iteration);
    }
}

pattern([0, 0], "center", 1);


marker holder element

marker holder element

holderDiameter = 25 + 2*1.5;
cubeLen = 7+8/2 + 0 + holderDiameter/2;

difference()
{
  union()
  {
    // mounting rod
    cube([7,8,20]);
    translate([7,8/2,0])
      cylinder(r=8/2, h=20, $fs=0.1);

    // connecting bar
    translate([0, 0, 20])
      cube([cubeLen, 8, 4]);

    // marker holder - main part
    translate([cubeLen, -holderDiameter/2+8, 0])
      cylinder(r=(holderDiameter)/2, h=50);
  }
  // marker holder drill
  translate([cubeLen, -holderDiameter/2+8, 0])
    translate([0, 0, 1.5])
      cylinder(r=25/2, h=50);
}

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


progress on lid and oled display

progress on lid and oled display

$fn = 64;

// interference fit adjustment for 3D printer
iFitAdjust = .4;
iFitAdjust_d = .25;

// latch buttons
latchButtonDiameter = 13;
latchButtonHeight = 30;
module latch_button(color="green") {
    color(color)
        cylinder(h = latchButtonHeight, d = latchButtonDiameter, center = false);
}

// led momentary buttons
ledButtonDiameter = 25;
ledButtonHeight = 33;
module momentary_button(color="white") {
    color(color)
        cylinder(h = ledButtonHeight, d = ledButtonDiameter, center = false);
}

// encoders
encoderShaftDiameter = 8;
encoderShaftHeight = 18;
encoderShaftOffset = 9;
encoderWidth = 18;
encoderLength = 31;
encoderDepth = 8.2;
module encoder() {
    // encoder shaft
    cylinder(h = encoderShaftHeight, d = encoderShaftDiameter, center=false);
    // encoder base
    translate([-encoderWidth/2, -encoderShaftOffset, -encoderDepth/2])
        cube([encoderWidth, encoderLength, encoderDepth]);
}

// string
stringWallThickness = 1;
stringRecess = 5;
stringLength = 60;
photoresistorWidth = 5.5 + iFitAdjust_d;
photoresistorLength = photoresistorWidth;
photoresistorWireSep = 3.5;
ledLaserDiameter = 6.5 + iFitAdjust_d;
ledLaserLength = 10;
wireCasingDepth = 3;
stringWidth = ledLaserDiameter;
module photoresistor_holder() {
    difference() {
        cylinder(h = stringRecess + stringWallThickness * 2, d = 2 * stringWallThickness + photoresistorWidth + iFitAdjust_d, center = false);
        translate([0,0,-.1])
            cylinder(h = stringRecess + stringWallThickness * 2 + 1, d = photoresistorWidth + iFitAdjust_d, center = false);
        /*
        for (i = [-1,1])
            translate([i * photoresistorWireSep / 2, 0, -1])
                cylinder(h = 20, d = 1, center = false);
        */
    }
    rotate([0, 0, 180])
        translate([-(ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness)/2, -(ledLaserDiameter )/2 - 1, -(wireCasingDepth + stringWallThickness * 2)])
        difference() {
            cube([ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness, ledLaserDiameter + ledLaserDiameter / 2 + 1, wireCasingDepth + stringWallThickness * 2], center=false);
            translate([stringWallThickness, stringWallThickness * 2 + 1, stringWallThickness])
                cube([ledLaserDiameter + iFitAdjust_d , ledLaserDiameter + ledLaserDiameter / 2 - stringWallThickness, wireCasingDepth], center=false);
            translate([ledLaserDiameter / 2 + stringWallThickness, ledLaserDiameter / 2 + stringWallThickness, stringWallThickness])
                cylinder(h=wireCasingDepth + 2 * stringWallThickness, d = ledLaserDiameter + iFitAdjust, center=false);
        }
}
module led_holder() {
    difference() {
        cylinder(h = ledLaserLength + stringWallThickness * 2, d = 2 * stringWallThickness + ledLaserDiameter + iFitAdjust_d, center = false);
        translate([0,0,-.1])
            cylinder(h = ledLaserLength + stringWallThickness * 2 + 1, d = ledLaserDiameter + iFitAdjust_d, center = false);
        /*
        for (i = [-1,1])
            translate([i * photoresistorWireSep / 2, 0, -1])
                cylinder(h = 20, d = 1, center = false);
        */
    }
    translate([-(ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness)/2, -(ledLaserDiameter )/2 - 1, -(wireCasingDepth + stringWallThickness * 2)])
        difference() {
            cube([ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness, ledLaserDiameter + ledLaserDiameter / 2 + 1, wireCasingDepth + stringWallThickness * 2], center=false);
            translate([stringWallThickness, stringWallThickness * 2 + 1, stringWallThickness])
                cube([ledLaserDiameter + iFitAdjust_d , ledLaserDiameter + ledLaserDiameter / 2 - stringWallThickness, wireCasingDepth], center=false);
            translate([ledLaserDiameter / 2 + stringWallThickness, ledLaserDiameter / 2 + stringWallThickness, stringWallThickness])
                cylinder(h=wireCasingDepth + 2 * stringWallThickness, d = ledLaserDiameter + iFitAdjust, center=false);
        }
}
module string() {
 rotate([0, 0, 90])
  translate([0, -(wireCasingDepth + stringWallThickness * 2), ledLaserDiameter]) rotate([90, 0, 0])
   union() {
    photoresistor_holder();
    translate([0, 0, stringLength])
        rotate([180, 0, 0])
            led_holder();
    difference() {
        translate([-(ledLaserDiameter + 2 * stringWallThickness + iFitAdjust_d)/2, - ledLaserDiameter, 0])
            cube([ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness, ledLaserDiameter / 2, stringLength ], center = false);
        cylinder(h=100, d = ledLaserDiameter + iFitAdjust, center=false);
    }
   }
}

// display
OLEDWidth = 27.3;
OLEDDisplayHeight = 19;
OLEDScrewWidth = 20.7;
OLEDScrewHeight = 23.2;
OLEDScrewDiameter = 3;
OLEDDepth = 1;
OLEDDisplayDepth = 1;
module display() {
    difference() {
        union() {
            cube([OLEDWidth, OLEDWidth, OLEDDepth]);
            translate([0, (OLEDWidth - OLEDDisplayHeight) / 2, 1])
                cube([OLEDWidth, OLEDDisplayHeight, OLEDDisplayDepth]);
        }
        for (i = [0: 1])
            for (j = [0: 1])
                translate([(OLEDWidth - OLEDScrewWidth) / 2 + i * OLEDScrewWidth, (OLEDWidth - OLEDScrewHeight) / 2 + j * OLEDScrewHeight, -.1])
                    cylinder(h=boxThickness + 1, d=OLEDScrewDiameter);
    }
}

lidWidth = 100;
lidThickness = 2.5;
rowWidth = ledButtonDiameter + 10;
lidLength = rowWidth * 2 + ledButtonDiameter / 2;
controlOffset = 10;
lidScrewDiameter = 3.5;
lidScrewBoundaryOffset = 10;
module spoke_lid() {
    union() {
        difference () {
            cube([lidWidth, lidLength, lidThickness]);
            for (i = [0:1]) {
                translate([0, ledButtonDiameter + i * rowWidth, -lidThickness * 2]) {
            // led buttons
                    translate([ledButtonDiameter / 2 + controlOffset, 0, 0])
                        momentary_button();
            // encoders
                    translate([ledButtonDiameter / 2 + controlOffset + 2 * controlOffset + latchButtonDiameter / 2 + encoderLength / 4, 0, 0])
                    rotate([0, 0, 270])
                        encoder();
            // latch buttons
                    translate([ledButtonDiameter / 2 + controlOffset + 2 * controlOffset + latchButtonDiameter / 2 + encoderLength + 5, 0, 0])
                        latch_button();

                }
            }
            translate([lidScrewBoundaryOffset/2, lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidWidth - lidScrewBoundaryOffset/2, lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidScrewBoundaryOffset/2, lidLength - lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidWidth - lidScrewBoundaryOffset/2, lidLength - lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
        }
    }
}

module string_lid() {
    union() {
        difference () {
            union() {
                cube([lidWidth, lidLength, lidThickness]);
            }
            translate([lidScrewBoundaryOffset/2, lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidWidth - lidScrewBoundaryOffset/2, lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidScrewBoundaryOffset/2, lidLength - lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidWidth - lidScrewBoundaryOffset/2, lidLength - lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            for (i = [0:7])
                translate([15 + stringWidth * i * 1.5, 5, -.01])
                    rotate([0, 0, 90])
                        hull() string();
        }
        for (i = [0:7])
            translate([15 + stringWidth * i * 1.5, 5, 0])
                rotate([0, 0, 90])
                    string();
   }
}

boxHeight = 45;
screwTabDim = 10;
boxThickness = lidThickness;
boxWidth = lidWidth;
boxLength = lidLength;
boxScrewDiameter = lidScrewDiameter;
module spoke_box() {
    difference() {
        union() {
             difference () {
                union() {
                    cube([lidWidth, lidLength, lidThickness]);
                    for (i = [0,1])
                        for (j = [0,1]) {
                            translate([i * lidWidth - i * screwTabDim, j * lidLength - j * screwTabDim, 0])
                                cube([screwTabDim, screwTabDim, boxHeight]);
                    }
                    cube([boxWidth, boxThickness, boxHeight]);
                    translate([0, boxLength - boxThickness, 0])
                        cube([boxWidth, boxThickness, boxHeight]);
                    cube([boxThickness, boxLength, boxHeight]);
                    translate([boxWidth - screwTabDim, 0, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
                }
                for (i = [0,1])
                    for (j = [0,1]) {
                        translate([i * boxWidth - i * screwTabDim + screwTabDim / 2, j * boxLength - j * screwTabDim + screwTabDim / 2, boxHeight - 9])
                            cylinder(h=20, d = lidScrewDiameter);
                    }
                for (i = [1, 2])
                    translate([boxWidth - screwTabDim / 2, i * boxLength / 3, boxThickness])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
                translate([boxWidth - screwTabDim / 2, screwTabDim + .1, (boxHeight / 3) * 2])
                    rotate([90, 0, 0])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
                translate([boxWidth - screwTabDim / 2, boxLength - boxThickness, (boxHeight / 3) * 2])
                    rotate([90, 0, 0])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
            }
        }
    }
}

pentagonRadius = boxLength / (2.0 * sin(180. / 5.));
pentagonR = (1/10) * sqrt(25 + 10 * sqrt(5)) * boxLength;
module pentagon_core() {
    difference() {
        union() {
            cylinder(h=boxThickness, r=pentagonRadius, $fn=5);
            translate([-pentagonR, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            rotate([0, 0,  36])
                translate([pentagonR - screwTabDim, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            rotate([0, 0,  2 * 36])
                translate([-pentagonR, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            rotate([0, 0,  3 * 36])
                translate([pentagonR - screwTabDim, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            rotate([0, 0,  4 * 36])
                translate([-pentagonR, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            for (i = [0:4])
                rotate([0, 0, i * 72])
                    for (j = [-1, 1])
                        translate([-pentagonR + screwTabDim / 2, j * boxLength / 2 - j * screwTabDim / 2, 0])
                            translate([0, 0, boxHeight/2])
                                cube([screwTabDim, screwTabDim, boxHeight], center=true);
        }
        for (i = [0:4])
            rotate([0, 0, i * 72])
                translate([-pentagonR + screwTabDim / 2, 0, 0]) {
                    translate([0, -boxLength / 3 / 2, boxThickness])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
                    translate([0, boxLength / 3 / 2, boxThickness])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
                }
        for (i = [0:4])
            rotate([0, 0, i * 72])
                for (j = [-1, 1])
                    translate([-pentagonR + screwTabDim / 2, j * boxLength / 2 - (j == -1 ? -1 * screwTabDim : screwTabDim / 4), 0])
                        translate([0, 0, (boxHeight / 3) * 2])
                            rotate([90, 0, 0])
                                #cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
         for (i = [0:4])
            rotate([0, 0, i * 72])
                translate([pentagonRadius - (screwTabDim / 3) * 2, 0, boxHeight - 9.9])
                    cylinder(h=10, d=boxScrewDiameter);
   }
}

module pentagon_lid() {
    difference() {
        union() {
            cylinder(h=boxThickness, r=pentagonRadius, $fn=5);
        }
        for (i = [0:4])
            rotate([0, 0, i * 72])
                translate([pentagonRadius - (screwTabDim / 3) * 2, 0, -.1])
                    cylinder(h=10, d=boxScrewDiameter);
        translate([-OLEDWidth / 2, 0, 0]) rotate([0, 0, -18]) {
            translate([0, (OLEDWidth - OLEDDisplayHeight) / 2, -.1])
                cube([OLEDWidth, OLEDDisplayHeight, boxThickness + .2]);
                for (i = [0: 1])
                    for (j = [0: 1])
                        translate([(OLEDWidth - OLEDScrewWidth) / 2 + i * OLEDScrewWidth, (OLEDWidth - OLEDScrewHeight) / 2 + j * OLEDScrewHeight, -.1])
                            cylinder(h=boxThickness + 1, d=OLEDScrewDiameter);
        }
        for (i = [3:4])
            rotate([0, 0, i * 72])
                translate([pentagonRadius - (screwTabDim / 3) * 2, 0, -.1])
                    #cylinder(h=10, d=boxScrewDiameter);
    }
//    display();
}

module screw_tab() {
    difference() {
        cube([screwTabDim, 2 * screwTabDim, boxThickness]);
        for (i = [0,1])
            translate([screwTabDim / 2, screwTabDim / 2 + i * screwTabDim, -.1])
                cylinder(h=10, d=boxScrewDiameter);
    }
}

module case() {
    //translate([0, 0, boxHeight + boxThickness]) spoke_lid();
    //string_lid();
    //spoke_box();
    //screw_tab();
    //pentagon_core();
    //translate([0, 0, boxHeight + boxThickness])
        pentagon_lid();
    //display();
}

case();

$fn = 64;

// interference fit adjustment for 3D printer
iFitAdjust = .4;
iFitAdjust_d = .25;

// latch buttons
latchButtonDiameter = 13;
latchButtonHeight = 30;
module latch_button(color="green") {
    color(color)
        cylinder(h = latchButtonHeight, d = latchButtonDiameter, center = false);
}

// led momentary buttons
ledButtonDiameter = 25;
ledButtonHeight = 33;
module momentary_button(color="white") {
    color(color)
        cylinder(h = ledButtonHeight, d = ledButtonDiameter, center = false);
}

// encoders
encoderShaftDiameter = 8;
encoderShaftHeight = 18;
encoderShaftOffset = 9;
encoderWidth = 18;
encoderLength = 31;
encoderDepth = 8.2;
module encoder() {
    // encoder shaft
    cylinder(h = encoderShaftHeight, d = encoderShaftDiameter, center=false);
    // encoder base
    translate([-encoderWidth/2, -encoderShaftOffset, -encoderDepth/2])
        cube([encoderWidth, encoderLength, encoderDepth]);
}

// string
stringWallThickness = 1;
stringRecess = 5;
stringLength = 60;
photoresistorWidth = 5.5 + iFitAdjust_d;
photoresistorLength = photoresistorWidth;
photoresistorWireSep = 3.5;
ledLaserDiameter = 6.5 + iFitAdjust_d;
ledLaserLength = 10;
wireCasingDepth = 3;
stringWidth = ledLaserDiameter;
module photoresistor_holder() {
    difference() {
        cylinder(h = stringRecess + stringWallThickness * 2, d = 2 * stringWallThickness + photoresistorWidth + iFitAdjust_d, center = false);
        translate([0,0,-.1])
            cylinder(h = stringRecess + stringWallThickness * 2 + 1, d = photoresistorWidth + iFitAdjust_d, center = false);
        /*
        for (i = [-1,1])
            translate([i * photoresistorWireSep / 2, 0, -1])
                cylinder(h = 20, d = 1, center = false);
        */
    }
    rotate([0, 0, 180])
        translate([-(ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness)/2, -(ledLaserDiameter )/2 - 1, -(wireCasingDepth + stringWallThickness * 2)])
        difference() {
            cube([ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness, ledLaserDiameter + ledLaserDiameter / 2 + 1, wireCasingDepth + stringWallThickness * 2], center=false);
            translate([stringWallThickness, stringWallThickness * 2 + 1, stringWallThickness])
                cube([ledLaserDiameter + iFitAdjust_d , ledLaserDiameter + ledLaserDiameter / 2 - stringWallThickness, wireCasingDepth], center=false);
            translate([ledLaserDiameter / 2 + stringWallThickness, ledLaserDiameter / 2 + stringWallThickness, stringWallThickness])
                cylinder(h=wireCasingDepth + 2 * stringWallThickness, d = ledLaserDiameter + iFitAdjust, center=false);
        }
}
module led_holder() {
    difference() {
        cylinder(h = ledLaserLength + stringWallThickness * 2, d = 2 * stringWallThickness + ledLaserDiameter + iFitAdjust_d, center = false);
        translate([0,0,-.1])
            cylinder(h = ledLaserLength + stringWallThickness * 2 + 1, d = ledLaserDiameter + iFitAdjust_d, center = false);
        /*
        for (i = [-1,1])
            translate([i * photoresistorWireSep / 2, 0, -1])
                cylinder(h = 20, d = 1, center = false);
        */
    }
    translate([-(ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness)/2, -(ledLaserDiameter )/2 - 1, -(wireCasingDepth + stringWallThickness * 2)])
        difference() {
            cube([ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness, ledLaserDiameter + ledLaserDiameter / 2 + 1, wireCasingDepth + stringWallThickness * 2], center=false);
            translate([stringWallThickness, stringWallThickness * 2 + 1, stringWallThickness])
                cube([ledLaserDiameter + iFitAdjust_d , ledLaserDiameter + ledLaserDiameter / 2 - stringWallThickness, wireCasingDepth], center=false);
            translate([ledLaserDiameter / 2 + stringWallThickness, ledLaserDiameter / 2 + stringWallThickness, stringWallThickness])
                cylinder(h=wireCasingDepth + 2 * stringWallThickness, d = ledLaserDiameter + iFitAdjust, center=false);
        }
}
module string() {
 rotate([0, 0, 90])
  translate([0, -(wireCasingDepth + stringWallThickness * 2), ledLaserDiameter]) rotate([90, 0, 0])
   union() {
    photoresistor_holder();
    translate([0, 0, stringLength])
        rotate([180, 0, 0])
            led_holder();
    difference() {
        translate([-(ledLaserDiameter + 2 * stringWallThickness + iFitAdjust_d)/2, - ledLaserDiameter, 0])
            cube([ledLaserDiameter + iFitAdjust_d + 2 * stringWallThickness, ledLaserDiameter / 2, stringLength ], center = false);
        cylinder(h=100, d = ledLaserDiameter + iFitAdjust, center=false);
    }
   }
}

// display
    OLEDWidth = 27;
i2cOLEDHeight = 16;
i2cOLEDScrewWidth = 20;
i2cOLEDScrewHeight = 22;
module display() {
}

lidWidth = 100;
lidThickness = 2.5;
rowWidth = ledButtonDiameter + 10;
lidLength = rowWidth * 2 + ledButtonDiameter / 2;
controlOffset = 10;
lidScrewDiameter = 3.5;
lidScrewBoundaryOffset = 10;
module spoke_lid() {
    union() {
        difference () {
            cube([lidWidth, lidLength, lidThickness]);
            for (i = [0:1]) {
                translate([0, ledButtonDiameter + i * rowWidth, -lidThickness * 2]) {
            // led buttons
                    translate([ledButtonDiameter / 2 + controlOffset, 0, 0])
                        momentary_button();
            // encoders
                    translate([ledButtonDiameter / 2 + controlOffset + 2 * controlOffset + latchButtonDiameter / 2 + encoderLength / 4, 0, 0])
                    rotate([0, 0, 270])
                        encoder();
            // latch buttons
                    translate([ledButtonDiameter / 2 + controlOffset + 2 * controlOffset + latchButtonDiameter / 2 + encoderLength + 5, 0, 0])
                        latch_button();

                }
            }
            translate([lidScrewBoundaryOffset/2, lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidWidth - lidScrewBoundaryOffset/2, lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidScrewBoundaryOffset/2, lidLength - lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidWidth - lidScrewBoundaryOffset/2, lidLength - lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
        }
    }
}

module string_lid() {
    union() {
        difference () {
            union() {
                cube([lidWidth, lidLength, lidThickness]);
            }
            translate([lidScrewBoundaryOffset/2, lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidWidth - lidScrewBoundaryOffset/2, lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidScrewBoundaryOffset/2, lidLength - lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            translate([lidWidth - lidScrewBoundaryOffset/2, lidLength - lidScrewBoundaryOffset/2, -.1])
                cylinder(h=lidThickness * 2, d=lidScrewDiameter);
            for (i = [0:7])
                translate([15 + stringWidth * i * 1.5, 5, -.01])
                    rotate([0, 0, 90])
                        hull() string();
        }
        for (i = [0:7])
            translate([15 + stringWidth * i * 1.5, 5, 0])
                rotate([0, 0, 90])
                    string();
   }
}

boxHeight = 45;
screwTabDim = 10;
boxThickness = lidThickness;
boxWidth = lidWidth;
boxLength = lidLength;
boxScrewDiameter = lidScrewDiameter;
module spoke_box() {
    difference() {
        union() {
             difference () {
                union() {
                    cube([lidWidth, lidLength, lidThickness]);
                    for (i = [0,1])
                        for (j = [0,1]) {
                            translate([i * lidWidth - i * screwTabDim, j * lidLength - j * screwTabDim, 0])
                                cube([screwTabDim, screwTabDim, boxHeight]);
                    }
                    cube([boxWidth, boxThickness, boxHeight]);
                    translate([0, boxLength - boxThickness, 0])
                        cube([boxWidth, boxThickness, boxHeight]);
                    cube([boxThickness, boxLength, boxHeight]);
                    translate([boxWidth - screwTabDim, 0, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
                }
                for (i = [0,1])
                    for (j = [0,1]) {
                        translate([i * boxWidth - i * screwTabDim + screwTabDim / 2, j * boxLength - j * screwTabDim + screwTabDim / 2, boxHeight - 9])
                            cylinder(h=20, d = lidScrewDiameter);
                    }
                for (i = [1, 2])
                    translate([boxWidth - screwTabDim / 2, i * boxLength / 3, boxThickness])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
                translate([boxWidth - screwTabDim / 2, screwTabDim + .1, (boxHeight / 3) * 2])
                    rotate([90, 0, 0])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
                translate([boxWidth - screwTabDim / 2, boxLength - boxThickness, (boxHeight / 3) * 2])
                    rotate([90, 0, 0])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
            }
        }
    }
}

pentagonRadius = boxLength / (2.0 * sin(180. / 5.));
pentagonR = (1/10) * sqrt(25 + 10 * sqrt(5)) * boxLength;
module pentagon_core() {
    difference() {
        union() {
            cylinder(h=boxThickness, r=pentagonRadius, $fn=5);
            translate([-pentagonR, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            rotate([0, 0,  36])
                translate([pentagonR - screwTabDim, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            rotate([0, 0,  2 * 36])
                translate([-pentagonR, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            rotate([0, 0,  3 * 36])
                translate([pentagonR - screwTabDim, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            rotate([0, 0,  4 * 36])
                translate([-pentagonR, 0, 0])
                    translate([0, -boxLength / 2, 0])
                        cube([screwTabDim, boxLength, screwTabDim]);
            for (i = [0:4])
                rotate([0, 0, i * 72])
                    for (j = [-1, 1])
                        translate([-pentagonR + screwTabDim / 2, j * boxLength / 2 - j * screwTabDim / 2, 0])
                            translate([0, 0, boxHeight/2])
                                cube([screwTabDim, screwTabDim, boxHeight], center=true);
        }
        for (i = [0:4])
            rotate([0, 0, i * 72])
                translate([-pentagonR + screwTabDim / 2, 0, 0]) {
                    translate([0, -boxLength / 3 / 2, boxThickness])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
                    translate([0, boxLength / 3 / 2, boxThickness])
                        cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
                }
        for (i = [0:4])
            rotate([0, 0, i * 72])
                for (j = [-1, 1])
                    translate([-pentagonR + screwTabDim / 2, j * boxLength / 2 - (j == -1 ? -1 * screwTabDim : screwTabDim / 4), 0])
                        translate([0, 0, (boxHeight / 3) * 2])
                            rotate([90, 0, 0])
                                #cylinder(h=screwTabDim + .1 - boxThickness, d=boxScrewDiameter);
         for (i = [0:4])
            rotate([0, 0, i * 72])
                translate([pentagonRadius - (screwTabDim / 3) * 2, 0, boxHeight - 9.9])
                    cylinder(h=10, d=boxScrewDiameter);
   }
}

module screw_tab() {
    difference() {
        cube([screwTabDim, 2 * screwTabDim, boxThickness]);
        for (i = [0,1])
            translate([screwTabDim / 2, screwTabDim / 2 + i * screwTabDim, -.1])
                cylinder(h=10, d=boxScrewDiameter);
    }
}

module case() {
    //spoke_lid();
    //string_lid();
    //spoke_box();
    //screw_tab();
    pentagon_core();
}

case();


Updating lasercut.scad - change the way the 2d is generated (neater/less clutter)

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=[],
        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,
        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=[],
        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(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 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=[],
        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,
        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=[],
        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(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, 0])
                children();
    }

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

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