modify scad file

modify scad file

module round_cube(size_x, size_y, size_z, r) {
union() {
cube([size_x + 2*r, size_y, size_z], center=true);
cube([size_x , size_y + 2*r, size_z], center=true);
translate([-size_x/2, -size_y/2, 0]) cylinder(h=size_z, r=r, center=true);
translate([size_x/2, -size_y/2, 0]) cylinder(h=size_z, r=r, center=true);
translate([size_x/2, size_y/2, 0]) cylinder(h=size_z, r=r, center=true);
translate([-size_x/2, size_y/2, 0]) cylinder(h=size_z, r=r, center=true);
}

}

module gong_bang(all_x, side_x, mid_w, side_y, h) {
    cube([all_x, mid_w, h], center=true);
    translate([(all_x-side_x)/2, 0,0]) cube([side_x, side_y, h], center=true);
    translate([-(all_x-side_x)/2, 0,0]) cube([side_x, side_y, h], center=true);
}

gong_bang(80, 2, 2, 10, 40);

/*
color([0.5,0.5,0.99])
difference () {
round_cube(80+6,60+6,40,4);
translate([0,-2,3]) round_cube(80,58,40,4);
//translate([0,20,3]) round_cube(70,3,20,4);
translate([0,33.5,2]) cube([70,3,40],center=true);
}
*/

module round_cube(size_x, size_y, size_z, r) {
union() {
cube([size_x + 2*r, size_y, size_z], center=true);
cube([size_x , size_y + 2*r, size_z], center=true);
translate([-size_x/2, -size_y/2, 0]) cylinder(h=size_z, r=r, center=true);
translate([size_x/2, -size_y/2, 0]) cylinder(h=size_z, r=r, center=true);
translate([size_x/2, size_y/2, 0]) cylinder(h=size_z, r=r, center=true);
translate([-size_x/2, size_y/2, 0]) cylinder(h=size_z, r=r, center=true);
}

}

color([0.5,0.5,0.99])
difference () {
round_cube(80+6,60+6,40,4);
translate([0,-2,3]) round_cube(80,58,40,4);
//translate([0,20,3]) round_cube(70,3,20,4);
translate([0,33.5,2]) cube([70,3,40],center=true);
}


corrected ESC position

corrected ESC position

$fs = 0.25;
$fa = 8;

use <utils.scad>
use <parts.scad>
use <mount.scad>
use <cage.scad>
use <apex.scad>
use <platform.scad>

// whole car
module all()
{
    apex();
    platform();
    board();
    arrange(30) horizontal_sonar();
    color("white") cage(mode=2/*simple*/);
    esc();
    lcd();
}

all();

$fs = 0.25;
$fa = 8;

use <utils.scad>
use <parts.scad>
use <mount.scad>
use <cage.scad>
use <apex.scad>
use <platform.scad>

// whole car
module all()
{
    apex();
    platform();
    board();
    arrange(30) horizontal_sonar();
    color("white") cage(mode=2/*simple*/);
    translate ([33, -8, 79]) rotate([0, 0, 90]) esc();
    lcd();
}

all();


The # operator is used instead of the % operator.  The % operator causes the item to not show in the final render, while the # operator shows the item as transparent but still rendered with the final model.

use <../../shapes/open-cylinder/open-cylinder.scad>

module rainmaker(height = 140,
                 innerRadius = 30,
                 outerRadius = 34,
				 rungsPerLevel = 1,
				 shellDecoration = "none",
				 showShell = "yes",
			 	 stepRadius = 2,
				 zDistanceBetweenRungs = 5,
			 	 zRotateAngle = 25)
{
    union()
    {
        // the main shell
		if(showShell == "yes")
		{
    		#
	        openCylinder(height = height,
	                     innerRadius = innerRadius,
	                     outerRadius = outerRadius);
		}

        bottomZ = 5;

        // the steps
        zStart = bottomZ;
        step_xLength = (innerRadius * 2) + 1;
        rainmaker_steps(height = height,
						outerRadius = outerRadius,
						rungsPerLevel = rungsPerLevel,
						shellDecoration = shellDecoration,
						stepRadius = stepRadius,
						step_xLength = step_xLength,
						zDistanceBetweenRungs = zDistanceBetweenRungs,
						zRotateAngle = zRotateAngle,
                        zStart = zStart);

        // the bottom
        cylinder(r=outerRadius, h=bottomZ);
    }
}

// suport modules follow

module rainmaker_steps(height,
						outerRadius,
						rungsPerLevel,
						shellDecoration,
						stepRadius,
						step_xLength,
						zDistanceBetweenRungs,
						zRotateAngle,
						zStart)
{
	zEnd = height - zDistanceBetweenRungs - stepRadius;

    for(z = [zStart : zDistanceBetweenRungs : zEnd])
    {
		xTranslate = 0;
        zTranslate = zStart + z;

		rotate([0, 0, zRotateAngle * z])
		union()
		{
			for(s = [1 : 1 : rungsPerLevel])
			{
				union()
				{
					stepColor  = s == 1 ? "yellow" :
								 s == 2 ? "green" :
								 s == 3 ? "orange" :
								 s == 4 ? "blue" :
								          "pink";

					angleDivision = 360.0 / rungsPerLevel;
				    zRotate = (angleDivision * (s-1) ) / 2;

					color(stepColor)
			        translate([xTranslate, 0, zTranslate])
					rotate([0, 90, zRotate])
			        cylinder(r = stepRadius,
								h = step_xLength,
								center = true);

					if(shellDecoration == "bumps")
					{
//						bumpsRotate = (angleDivision * s) / s;
//						bumpsRotate = zRotate / s;
						bumpsRotate = zRotate;
//						bumpsRotate = s + 30;
//						bumpsRotate = 0;

						echo("bumps", s, bumpsRotate);

						color(stepColor)

						translate([outerRadius, 0, zTranslate])
//rotate([bumpsRotate, 0, 0])
//rotate([0, 90, bumpsRotate])
						sphere(r=3);

// this is the bump on the 'other' end
						color(stepColor)
						translate([-outerRadius, 0, zTranslate])
//						rotate([0, 90, bumpsRotate * s])
						sphere(r=3);
					}
				}
			}
		}
    }
}

use <../../shapes/open-cylinder/open-cylinder.scad>

module rainmaker(height = 140,
                 innerRadius = 30,
                 outerRadius = 34,
				 rungsPerLevel = 1,
				 shellDecoration = "none",
			 	 stepRadius = 2,
				 zDistanceBetweenRungs = 5,
			 	 zRotateAngle = 25)
{
    union()
    {
        // the main shell
    	%
        openCylinder(height = height,
                     innerRadius = innerRadius,
                     outerRadius = outerRadius);

        bottomZ = 5;

        // the steps
        zStart = bottomZ;
        step_xLength = (innerRadius * 2) + 1;
        rainmaker_steps(height = height,
						outerRadius = outerRadius,
						rungsPerLevel = rungsPerLevel,
						shellDecoration = shellDecoration,
						stepRadius = stepRadius,
						step_xLength = step_xLength,
						zDistanceBetweenRungs = zDistanceBetweenRungs,
						zRotateAngle = zRotateAngle,
                        zStart = zStart);

        // the bottom
        cylinder(r=outerRadius, h=bottomZ);
    }
}

// suport modules follow

module rainmaker_steps(height,
						outerRadius,
						rungsPerLevel,
						shellDecoration,
						stepRadius,
						step_xLength,
						zDistanceBetweenRungs,
						zRotateAngle,
						zStart)
{
	zEnd = height - zDistanceBetweenRungs - stepRadius;

    for(z = [zStart : zDistanceBetweenRungs : zEnd])
    {
		xTranslate = 0;
        zTranslate = zStart + z;

		rotate([0, 0, zRotateAngle * z])
		union()
		{
			for(s = [1 : 1 : rungsPerLevel])
			{
				stepColor  = s == 1 ? "yellow" :
							 s == 2 ? "green" :
							 s == 3 ? "orange" :
							 s == 4 ? "blue" :
							          "pink";

				angleDivision = 360.0 / rungsPerLevel;
			    zRotate = (angleDivision * (s-1) ) / 2;

				color(stepColor)
		        translate([xTranslate, 0, zTranslate])
				rotate([0, 90, zRotate])
		        cylinder(r = stepRadius,
							h = step_xLength,
							center = true);

				if(shellDecoration == "bumps")
				{
					color(stepColor)
					translate([outerRadius, 0, zTranslate])
					rotate([0, 90, zRotate])
					sphere(r=3);

					color(stepColor)
					translate([-outerRadius, 0, zTranslate])
					rotate([0, 90, zRotate])
					sphere(r=3);
				}
			}
		}
    }
}


frame

frame

// Copyright (c) 2015
// Author: Jeff Weisberg <jaw @ tcp4me.com>
// Created: 2015-Oct-04 22:32 (EDT)
// Function: frame for mini-copter

show_env   = 0;

pcb_x      = 25.0;
pcb_y      = 25.0;
pcb_z      =  1.6;

peg_pos    = 1.25;	// in from corner
prop_x     = 56.0;	// prop diam plus margin
mot_z      = 10.0; 	// ht of center of motor
motor_phi  = 5.0;	// slight dihedral
motor_diam = 7;

//################################################################

mot_pos = (prop_x - pcb_x) / 2;
mot_armang = atan( (mot_z + 2) / (mot_pos + peg_pos) / sqrt(2) );

$fn = 0;
$fa = .5;
$fs = 0.05;

//################################################################

module vcyl(x,y,z,diam1, diam2, len){
    translate([x,y,z])
        cylinder(r1=diam1/2, r2=diam2/2, h=len);
}

module hcyl(x,y,z,diam1, diam2, len){
    translate([x,y,z])
        rotate([0,90,0])
            cylinder(r1=diam1/2, r2=diam2/2, h=len);
}

module box(x,y,z, dx,dy,dz){
    translate([x, y, z]) cube([dx, dy, dz]);
}

module proparm(){

    // board support+attach
    translate([ (pcb_x/2-peg_pos)*sqrt(2), 0, 0]) rotate([0,0,45]){
        vcyl(0,0,-3, 3, 3,3);
        // slightly tapered pins
        vcyl(0,0,-.1, 1.0, 0.7, 2.1);
        box(-3.5,-.5,-1.5, 4,1,1.5);
        box( -.5,-.5,-1.5, 1,4,1.5);

    }

    // legs
    translate([ (pcb_x/2-peg_pos)*sqrt(2), 0, -3.0]) rotate([0,30,0]){
        intersection(){
            box(0,-1,0, 15,2,1.5);
            hcyl(0,0,1, 2, 1, 15);
        }
        translate([15,0,.5]) sphere(1.0);
    }

    difference(){
        union(){
            // motor arms
            intersection(){
                translate([ (pcb_x/2-peg_pos)*sqrt(2), 0, -2.0]) rotate([0,-mot_armang,0]) {
                    box(0, -1.4, 0, (mot_pos+peg_pos)*sqrt(2) / cos(mot_armang), 2.8,1 );
                    box(0,-.5,  -1.5, (mot_pos+peg_pos)*sqrt(2) / cos(mot_armang), 1,2.5 );
                }
                // clean up the bottom
                box(-5,-5,-3, 60,60,60);
            }
            // motor mounts
            translate([prop_x/2 * sqrt(2),0, mot_z]) rotate([0,-motor_phi,0]){
                vcyl(0,0,-3.25, motor_diam+2, motor_diam+2, 6.5 );

                // prop
                if( show_env ) color([.75,0,.75,1]) vcyl(0,0,5, 45, 45, 5);
            }

        }
        union(){
            // hole for motor
            translate([prop_x/2 * sqrt(2),0, mot_z]) rotate([0,-motor_phi,0]){
                vcyl(0,0,-5, motor_diam-.5, motor_diam-.5, 9 );
                rotate([0,0,90]) box(motor_diam/2-2, -.5,-5, 5,1,9);
            }
        }
    }
}

module cage(){
    // outer box
    box(-28/2-1.5, 28/2+.5, -1.25, 28+3, 1.0,2.5);
    box(-28/2-1.5, 28/2-.5, -.5,  28+3, 2.0,1.0);

    // rubber band attachment pegs
    hcyl(28/2+.5, -6, 0,  2,2,3);
    hcyl(28/2+.5,  6, 0,  2,2,3);
    hcyl(28/2+.5, -2, 0,  2,2,3);
    hcyl(28/2+.5,  2, 0,  2,2,3);
}

// ################################################################

if( show_env ) color([.75,0,.75, 1]) box(-pcb_x/2, -pcb_y/2, 0, pcb_x, pcb_y, pcb_z);

for( angle = [ 0 : 90 : 270 ] ){
    rotate([0,0,45+angle]) proparm();

    // outer cage
    rotate([0,0,angle]) translate([0,0,.60]) cage();

}

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


Add bottom fillet to yellow bin

Add bottom fillet to yellow bin

/* Note that the factory bins have draft which make them injection moldable; we
 * don't need that. */

/* The little circles that the legs fit into to keep the bins from moving around */
gLegCircleSize = 18;

/* Most of the bins have X in this orientation. */
gGridX = 40;
gGridY = 55;

/* So the bins don't fit *too* tightly together */
gTopSlop = 0.6;
// TODO This appears to have rounding issues with s3d; add 0.01 for it
gExtWidth = 0.4;
gWall = gExtWidth*3;

// TODO test print for HF, Stanley sizes
//gHeight=47.8; // for HF
gHeight=40.9; // for Stanley
gBaseThick=2;
gFilletRad=3;

gPegHole = 2.5;
gPegHead = 6;
gPegCircle = 18; // measure hole in base, and subtract 1mm for intentional slop
gPegDepth = 8;

module RoundRect(w,h,r=1) {
    hull()
    for(x_scale=[1,-1]) for(y_scale=[1,-1])
    scale([x_scale,y_scale])
    translate([w/2-r,h/2-r]) circle(r=r);
}
module Corner(r,e=1) {
    translate([-r,-r]) difference() {
        square([r+e,r+e]);
        circle(r=r,$fn=128);
    }
}
module Ess(w,h,r) {
    difference() {
        square([w,h]);
        // cut corner
        translate([w,h]) Corner(r);
    }
    // add corners
    translate([w,0]) rotate([0,0,180]) Corner(r,0);
    translate([0,h]) rotate([0,0,180]) Corner(r,0);
}

module Fillet(a, r) {
    intersection() {
        difference() {
            hull() {
                translate([-a,a]) circle(r=a);
                translate([a,-a]) circle(r=a);
            }
            hull() {
                translate([a,r]) circle(r=r);
                translate([r,a]) circle(r=r);
            }
        }
        square([a,a]);
    }
}
e=0.001;
module BaseOutline(x,y,wall,feet) {
    inside_wall_w = (x*gGridX)-(wall*2)-gTopSlop;
    inside_wall_h = (y*gGridY)-(wall*2)-gTopSlop;
    ///RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=32);
    difference() {
        RoundRect(inside_wall_w,inside_wall_h,3-wall,$fn=32);
        for(x_scale=[1,-1]) for(y_scale=[1,-1])
            scale([x_scale,y_scale])
            translate([-inside_wall_w/2-e,-inside_wall_h/2-e]) Fillet(11, 3, $fn=32);
    }
}

module BaseFillet(x,y,wall,feet) {
    translate([0,0,gFilletRad]) minkowski() {
        sphere(r=gFilletRad,$fn=32);
        linear_extrude(convexity=2,height=1) offset(delta=-gFilletRad) BaseOutline(x,y,wall,feet);
    }
}

module Bin(x=1,y=1,wall=gWall,feet=true,cutaway=false) {
    inside_wall_w = (x*gGridX)-(wall*2)-gTopSlop;
    inside_wall_h = (y*gGridY)-(wall*2)-gTopSlop;
    difference() {
        union() {
            linear_extrude(height=gHeight,convexity=4) difference() {
                RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=128);
                RoundRect(inside_wall_w,inside_wall_h,3-wall,$fn=32);
            }
            // Floor
            linear_extrude(height=gBaseThick+gFilletRad)
            RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=32);
            if(feet) {
                linear_extrude(height=gPegDepth+2,convexity=4)
                for(x_scale=[1,-1]) for(y_scale=[1,-1])
                    scale([x_scale,y_scale])
                    translate([-inside_wall_w/2,-inside_wall_h/2]) Fillet(11, 3, $fn=32);
                //Ess(6,6,3);
            }
        }
        if(feet) {
            for(x_scale=[1,-1]) for(y_scale=[1,-1])
                scale([x_scale,y_scale])
                translate([(x*gGridX-gTopSlop)/2,(y*gGridY-gTopSlop)/2,-1])
                rotate([0,0,180+45]) translate([(gPegCircle-gPegHead)/2,0])
                cylinder(d=gPegHole,h=gPegDepth+1,$fn=32);
            translate([0,0,gBaseThick]) BaseFillet(x,y,wall,true);
        }
        if(cutaway) {
            translate([-inside_wall_w/2,-inside_wall_h/2,-1])
            rotate([0,0,-45]) translate([0,-1,0]) cube([12,12,20]);
        }
    }
}

Bin(4,1);

/* Note that the factory bins have draft which make them injection moldable; we
 * don't need that. */

/* The little circles that the legs fit into to keep the bins from moving around */
gLegCircleSize = 18;

/* Most of the bins have X in this orientation. */
gGridX = 40;
gGridY = 55;

/* So the bins don't fit *too* tightly together */
gTopSlop = 0.6;
// TODO This appears to have rounding issues with s3d; add 0.01 for it
gExtWidth = 0.4;
gWall = gExtWidth*3;

// TODO test print for HF, Stanley sizes
//gHeight=47.8; // for HF
gHeight=40.9; // for Stanley

gPegHole = 2.5;
gPegHead = 6;
gPegCircle = 18; // measure hole in base, and subtract 1mm for intentional slop
gPegDepth = 8;

module RoundRect(w,h,r=1) {
    hull()
    for(x_scale=[1,-1]) for(y_scale=[1,-1])
    scale([x_scale,y_scale])
    translate([w/2-r,h/2-r]) circle(r=r);
}
module Corner(r,e=1) {
    translate([-r,-r]) difference() {
        square([r+e,r+e]);
        circle(r=r,$fn=128);
    }
}
module Ess(w,h,r) {
    difference() {
        square([w,h]);
        // cut corner
        translate([w,h]) Corner(r);
    }
    // add corners
    translate([w,0]) rotate([0,0,180]) Corner(r,0);
    translate([0,h]) rotate([0,0,180]) Corner(r,0);
}

module Fillet(a, r) {
    intersection() {
        difference() {
            hull() {
                translate([-a,a]) circle(r=a);
                translate([a,-a]) circle(r=a);
            }
            hull() {
                translate([a,r]) circle(r=r);
                translate([r,a]) circle(r=r);
            }
        }
        square([a,a]);
    }
}

module Bin(x=1,y=1,wall=gWall,feet=true,cutaway=false) {
    inside_wall_w = (x*gGridX)-(wall*2)-gTopSlop;
    inside_wall_h = (y*gGridY)-(wall*2)-gTopSlop;
    difference() {
        union() {
            linear_extrude(height=gHeight,convexity=4) difference() {
                RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=128);
                RoundRect(inside_wall_w,inside_wall_h,3-wall,$fn=32);
            }
            // Floor
            linear_extrude(height=2)
            RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=32);
            if(feet) {
                linear_extrude(height=gPegDepth+2,convexity=4)
                for(x_scale=[1,-1]) for(y_scale=[1,-1])
                    scale([x_scale,y_scale])
                    translate([-inside_wall_w/2,-inside_wall_h/2]) Fillet(11, 3, $fn=32);
                //Ess(6,6,3);
            }
        }
        if(feet) {
            for(x_scale=[1,-1]) for(y_scale=[1,-1])
                scale([x_scale,y_scale])
                translate([(x*gGridX-gTopSlop)/2,(y*gGridY-gTopSlop)/2,-1])
                rotate([0,0,180+45]) translate([(gPegCircle-gPegHead)/2,0])
                cylinder(d=gPegHole,h=gPegDepth+1,$fn=32);
        }
        if(cutaway) {
            translate([-inside_wall_w/2,-inside_wall_h/2,-1])
            rotate([0,0,-45]) translate([0,-1,0]) cube([12,12,20]);
        }
    }
}

Bin(4,1);


Discoverd that openscad DOENS'T HAVE ELEMENTWISE MUTIPLICATION.

I'm going to create my own functions to do this... Probably will end up
creating a seperate library to for other users as well.

/**
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 = 30;
connector_thickness = 0.1;
iteration_multiplier = 0.5;
iterations = 2;

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) =
    if(len(vec1) != len(vec2)) {
        echo("Vectors not the same dimensions");
        break;
    =
}

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

module connector(vector_keys, size, starting_pos, current_iteration) {
    if(current_iteration < iterations) {
        for(i = vector_keys) {
            echo(translation_vectors[i] );

            translate(translation_vectors[i] * [size / (2 * sqrt(2)), size / (2 * sqrt(2))] + starting_pos) {
                rotate(rotation_vectors[i] * [0, 45, 45]) {
                    #square([connector_thickness, size], center = true);
                }
            }
        }
    }
}

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") {
        translated_cube([0,0], starting_pos, current_size);
        connector([0, 1, 2, 3], current_connector_size, starting_pos, current_iteration);
    }
    else if(seed_corner == "bottom_left") {
        translated_cube([current_size / 2, current_size / 2], starting_pos, current_size);
        connector([0, 2, 3], current_connector_size, starting_pos, current_iteration);
    }
    else if(seed_corner == "bottom_right") {
        translated_cube([-current_size / 2, current_size / 2], starting_pos, current_size);
        connector([0, 1, 2], current_connector_size, starting_pos, current_iteration);
    }
    else if(seed_corner == "top_left") {
        translated_cube([current_size / 2, -current_size / 2], starting_pos, current_size);
        connector([0, 1, 3], current_connector_size, starting_pos, current_iteration);
    }
    else if(seed_corner == "top_right") {
        translated_cube([-current_size / 2, -current_size / 2], starting_pos, current_size);
        connector([1, 2, 3], current_connector_size, starting_pos, current_iteration);
    }
}

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 = 30;
connector_thickness = 0.1;
iteration_multiplier = 0.5;
iterations = 2;

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

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") {
        translation = [0,0];
        $dank = 1;
        translations = translation_vectors;
        rotations = rotation_vectors;
    }
    else if(seed_corner == "bottom_left") {
        translation = [current_size / 2, current_size / 2];
        translations = [translation_vectors[0] , translation_vectors[2], translation_vectors[3]];
        rotations = [rotation_vectors[0], rotation_vectors[2], rotation_vectors[3]];
    }
    else if(seed_corner == "bottom_right") {
        translation = [-current_size / 2, current_size / 2];
        translations = [translation_vectors[0], translation_vectors[1], translation_vectors[2]];
        rotations = [rotation_vectors[0], rotation_vectors[1], rotation_vectors[2]];
    }
    else if(seed_corner == "top_left") {
        translation = [current_size / 2, -current_size / 2];
        translations = [translation_vectors[0], translation_vectors[1], translation_vectors[3]];
        rotations = [rotation_vectors[0], rotation_vectors[1], rotation_vectors[3]];
    }
    else if(seed_corner == "top_right") {
        translation = [-current_size / 2, -current_size / 2];
        translations = [translation_vectors[1], translation_vectors[2], translation_vectors[3]];
        rotations = [rotation_vectors[1], rotation_vectors[2], rotation_vectors[3]];
    }

    translate(translation) {
        square(current_size, center = true);
    }

    if(current_iteration < iterations) {
        for(i = [0, len(translations) - 1]) {
            translate(translations[i] * [current_connector_size / (2 * sqrt(2)), current_connector_size / (2 * sqrt(2))]) {
                rotate(rotations[i] * [0, 45, 45]) {
                    square([connector_thickness, current_connector_size], center = true);
                }
            }

            if(translations[i] == [1, 1]) {
            }
            else if(translations[i] == [-1, -1]) {
            }
            else if(translations[i] == [-1, 1]) {
            }
            else if(translations[i] == [1, -1]) {
            }
        }
    }
}

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


A value for the increment step was added.

charmName = "balloon"; // [star, balloon, blue-moon, pacman, Spurs]

charmCount = 28; // [1:100]

charmXYScale = 0.9;   // [0.1:0.1:5]

cupHeight = 150; // [75:300]

/* [Hidden] */
maxRandom = 0;
charmIndcies = rands(0,maxRandom, charmCount);

charmXYScales = rands(charmXYScale, charmXYScale, charmCount);

depthScale = 40.2;
charmDepthScales = rands(depthScale, depthScale, charmCount);

difference()
{
	cup(cupHeight=cupHeight);

	// uses the default value for the charmStls parameter
	rotatedCutouts(charmCount = charmCount,
				   charmIndcies = charmIndcies,
				   charmXYScales = charmXYScales,
				   charmDepthScales = charmDepthScales,
				   charmStls = ["../../../../shapes/star/star.stl"]);
}

//use <../../../../shapes/cup/cup.scad>;
/**
  * Create a cylindrical cup.
  */
module cup(cupHeight=70, innerRadius=53)
{
	difference()
	{
		centered = false;

		// outer part of the cup
		translate([0,0,0])
		cylinder (h = cupHeight, r=55, center = centered, $fn=100);

		// subtracted inner cup
		translate([0,0,5])
		cylinder (h = cupHeight, r=innerRadius, center = centered, $fn=100);
	}
}

//use <../../../../cutouts/rotated-spiral-cutouts.scad>;
/**
  * This is a library for evenly spaced cutouts.
*/
module rotatedCutouts(charmCount = 16,
					  charmXYScales,
					  charmStls = ["../shapes/oshw/oshw.stl"],
					  charmIndcies = [],  // the vector of array index needs required
					  charmDepthScales,
					  yTranslateFactor = 5,
					  yTranslateMinimum = 0,
					  zRotationFactor = 30)
{
    for ( i = [0 : charmCount-1] )
    {

        single_rand = charmIndcies[i];

        charmIndex = round(single_rand);

		yTranslate = (i * yTranslateFactor) + yTranslateMinimum;

		zRotation = i * zRotationFactor;

        rotate([
                90,
                0,
                zRotation
        ])
        // normally x,y,z - but here y moves the little spurs up and down
        translate([15, yTranslate, 30])
        scale([
		       charmXYScales[charmIndex],
			   charmXYScales[charmIndex],
			   charmDepthScales[charmIndex] //20.2])
			 ])
//        import(charmStls[charmIndex])
//		star(1);

		if(charmName == "balloon")
		{
			echo("no sucka!");
			balloon();
		}
		else if(charmName == "blue-moon")
		{
			blueMoon();
		}
		else if(charmName == "pacman")
		{
			pacman();
		}
		else if(charmName == "Spurs")
		{
			spursa(5);
		}
		else
		{
			// star is the default
			star(1);
		}

    }
}

// from the OpenSCAD tutotials
module star_poly_Selection(h)
{
  scale([25.4/90, -25.4/90, 1]) union()
  {
    linear_extrude(height=h)
      polygon([[9.080000,-22.000000],[10.922500,-17.093750],[12.213125,-14.728281],[13.890000,-13.020000],[15.773906,-12.298125],[18.041250,-12.022500],[22.500000,-11.910000],[34.390000,-10.968750],[40.862812,-10.133906],[45.500000,-9.000000],[28.500000,4.610000],[21.270000,12.170000],[22.870000,22.000000],[27.500000,43.000000],[8.500000,31.580000],[-0.500000,27.200000],[-10.500000,32.320000],[-28.500000,43.000000],[-22.870000,21.000000],[-21.690000,11.090000],[-28.500000,4.130000],[-45.500000,-11.000000],[-11.500000,-13.000000],[-0.500000,-43.000000],[2.289219,-38.828594],[4.908750,-33.096250],[9.080000,-22.000000]]);
  }
}

module star(h)
{
	star_poly_Selection(h);
}

// ../../../../shapes/balloon/balloon.scad
module balloon()
{
	union ()
	{
		// balloon
		scale ([0.8, 1, 1])
		cylinder (h = 4, r=10, center = true, $fn=100);

		// middle knot piece
		knotPiece();

		// left knot piece
		rotate ([0, 0, -6.5])
		knotPiece();

		// right knot piece
		rotate ([0, 0, 6.5])
		knotPiece();
	}
}

module knotPiece()
{
	translate ([0, -10.5, 0])
	scale ([0.3, 0.8, 1])
	cylinder (h = 4, r=2, center = true, $fn=100);
}

// ../../../../shapes/blue-moon/blue-moon.scad
module blueMoon()
{
	difference()
	{
		// blue moon
		cylinder (h = 4, r=10, center = true, $fn=100);

		// top cutout
		translate([5, 5, 0])
		cylinder (h = 4.20, r=6, center = true, $fn=100);

		// bottom cutout
		translate([5, -5, 0])
		cylinder (h = 4.20, r=6, center = true, $fn=100);

		// remainder cutout
		translate([10, 0, 5])
		color("red")
		cylinder (h = 14.20, r=6, center = true, $fn=100);
	}
}

// http://www.thingiverse.com/thing:241081/
module pacman()
{
	//linear_extrude(height=50, center=true, convexity = 10, twist = 0)
	difference ()
	{
	  cylinder(r=20, center=true, h=5);

	  linear_extrude(height=10, center= true)
	//  linear_extrude(height=50, center=true, convexity = 10, twist = 0)
	//  {
		  polygon(points=[[0,0],[100,60],[100,-60]], paths=[[0,1,2]]);
	//  };
	}
}

//pacman();

// http://www.thingiverse.com/thing:603115
module poly_Selection(h)
{
  scale([25.4/90, -25.4/90, 1]) union()
  {
    linear_extrude(height=h)
      polygon([[13.500000,-56.000000],[29.500000,-55.000000],[54.500000,-55.000000],[58.503750,-55.048750],[60.522969,-55.341719],[62.290000,-56.020000],[64.325156,-57.853594],[66.241250,-60.541250],[69.240000,-66.000000],[80.500000,-87.000000],[106.280000,-135.000000],[118.870000,-159.000000],[122.780000,-198.000000],[124.023594,-204.882813],[125.843750,-209.652500],[128.457031,-214.095937],[132.080000,-220.000000],[148.490000,-248.000000],[153.376719,-256.487656],[155.041895,-259.143652],[156.518750,-260.956250],[158.055137,-262.086816],[159.898906,-262.696719],[165.500000,-263.000000],[214.500000,-263.000000],[214.500000,-213.000000],[213.360000,-196.000000],[213.360000,-154.000000],[213.495000,-150.462500],[213.360000,-147.000000],[196.920000,-117.000000],[156.350000,-45.000000],[125.920000,9.000000],[120.671250,18.592500],[117.385469,23.577187],[115.794199,25.366055],[114.320000,26.400000],[112.797344,26.797500],[110.981250,26.970000],[107.500000,27.000000],[79.500000,27.000000],[65.500000,28.000000],[31.500000,28.000000],[27.943750,28.030000],[24.500000,28.600000],[22.684180,29.597539],[20.548438,31.277812],[15.792500,35.922500],[7.670000,45.000000],[-36.500000,94.040000],[-25.500000,94.040000],[-15.500000,95.000000],[1.500000,95.000000],[22.500000,96.000000],[2.500000,114.080000],[-38.500000,149.000000],[-23.230000,165.000000],[-3.230000,188.000000],[14.500000,210.000000],[-66.500000,189.000000],[-91.500000,263.000000],[-92.970664,261.624141],[-94.426562,259.592500],[-97.197500,254.302500],[-101.500000,244.000000],[-121.500000,198.000000],[-200.500000,222.000000],[-174.840000,182.000000],[-154.500000,151.000000],[-187.500000,128.020000],[-214.500000,108.000000],[-166.500000,103.830000],[-135.500000,101.000000],[-137.410000,77.000000],[-139.590000,41.000000],[-140.500000,31.000000],[-138.108496,32.343340],[-135.722969,34.055469],[-131.038750,38.223750],[-122.500000,47.000000],[-111.500000,57.040000],[-94.500000,74.000000],[-77.500000,27.000000],[-107.500000,26.000000],[-117.196250,25.451250],[-122.812656,24.814531],[-126.090000,23.980000],[-127.513906,22.488438],[-128.773750,20.335000],[-130.730000,16.000000],[-138.200000,-1.000000],[-140.195000,-5.868750],[-140.649688,-8.406719],[-140.330000,-11.000000],[-134.780000,-21.000000],[-120.780000,-45.000000],[-74.650000,-124.000000],[-55.110000,-158.000000],[-46.570000,-206.000000],[-30.280000,-238.000000],[-21.920000,-254.000000],[-19.818750,-257.793750],[-18.535156,-259.646094],[-17.110000,-260.980000],[-15.251406,-261.684844],[-12.943750,-261.983750],[-8.500000,-261.990000],[5.500000,-261.000000],[19.500000,-261.000000],[31.500000,-260.040000],[43.500000,-260.040000],[43.500000,-217.000000],[44.500000,-200.000000],[44.500000,-162.000000],[43.800000,-154.000000],[28.100000,-127.000000],[-14.500000,-56.000000],[13.500000,-56.000000]]);
  }
}

module spursa(h)
{
	poly_Selection(h);
}

charmName = "blue-moon"; // [star, balloon, blue-moon, pacman, Spurs]

charmCount = 28; // [1:100]

charmXYScale = 0.9;   // [0.1:5]

cupHeight = 150; // [75:300]

/* [Hidden] */
maxRandom = 0;
charmIndcies = rands(0,maxRandom, charmCount);

charmXYScales = rands(charmXYScale, charmXYScale, charmCount);

depthScale = 40.2;
charmDepthScales = rands(depthScale, depthScale, charmCount);

difference()
{
	cup(cupHeight=cupHeight);

	// uses the default value for the charmStls parameter
	rotatedCutouts(charmCount = charmCount,
				   charmIndcies = charmIndcies,
				   charmXYScales = charmXYScales,
				   charmDepthScales = charmDepthScales,
				   charmStls = ["../../../../shapes/star/star.stl"]);
}

//use <../../../../shapes/cup/cup.scad>;
/**
  * Create a cylindrical cup.
  */
module cup(cupHeight=70, innerRadius=53)
{
	difference()
	{
		centered = false;

		// outer part of the cup
		translate([0,0,0])
		cylinder (h = cupHeight, r=55, center = centered, $fn=100);

		// subtracted inner cup
		translate([0,0,5])
		cylinder (h = cupHeight, r=innerRadius, center = centered, $fn=100);
	}
}

//use <../../../../cutouts/rotated-spiral-cutouts.scad>;
/**
  * This is a library for evenly spaced cutouts.
*/
module rotatedCutouts(charmCount = 16,
					  charmXYScales,
					  charmStls = ["../shapes/oshw/oshw.stl"],
					  charmIndcies = [],  // the vector of array index needs required
					  charmDepthScales,
					  yTranslateFactor = 5,
					  yTranslateMinimum = 0,
					  zRotationFactor = 30)
{
    for ( i = [0 : charmCount-1] )
    {

        single_rand = charmIndcies[i];

        charmIndex = round(single_rand);

		yTranslate = (i * yTranslateFactor) + yTranslateMinimum;

		zRotation = i * zRotationFactor;

        rotate([
                90,
                0,
                zRotation
        ])
        // normally x,y,z - but here y moves the little spurs up and down
        translate([15, yTranslate, 30])
        scale([
		       charmXYScales[charmIndex],
			   charmXYScales[charmIndex],
			   charmDepthScales[charmIndex] //20.2])
			 ])
//        import(charmStls[charmIndex])
//		star(1);

		if(charmName == "balloon")
		{
			echo("no sucka!");
			balloon();
		}
		else if(charmName == "blue-moon")
		{
			blueMoon();
		}
		else if(charmName == "pacman")
		{
			pacman();
		}
		else if(charmName == "Spurs")
		{
			spursa(5);
		}
		else
		{
			// star is the default
			star(1);
		}

    }
}

// from the OpenSCAD tutotials
module star_poly_Selection(h)
{
  scale([25.4/90, -25.4/90, 1]) union()
  {
    linear_extrude(height=h)
      polygon([[9.080000,-22.000000],[10.922500,-17.093750],[12.213125,-14.728281],[13.890000,-13.020000],[15.773906,-12.298125],[18.041250,-12.022500],[22.500000,-11.910000],[34.390000,-10.968750],[40.862812,-10.133906],[45.500000,-9.000000],[28.500000,4.610000],[21.270000,12.170000],[22.870000,22.000000],[27.500000,43.000000],[8.500000,31.580000],[-0.500000,27.200000],[-10.500000,32.320000],[-28.500000,43.000000],[-22.870000,21.000000],[-21.690000,11.090000],[-28.500000,4.130000],[-45.500000,-11.000000],[-11.500000,-13.000000],[-0.500000,-43.000000],[2.289219,-38.828594],[4.908750,-33.096250],[9.080000,-22.000000]]);
  }
}

module star(h)
{
	star_poly_Selection(h);
}

// ../../../../shapes/balloon/balloon.scad
module balloon()
{
	union ()
	{
		// balloon
		scale ([0.8, 1, 1])
		cylinder (h = 4, r=10, center = true, $fn=100);

		// middle knot piece
		knotPiece();

		// left knot piece
		rotate ([0, 0, -6.5])
		knotPiece();

		// right knot piece
		rotate ([0, 0, 6.5])
		knotPiece();
	}
}

module knotPiece()
{
	translate ([0, -10.5, 0])
	scale ([0.3, 0.8, 1])
	cylinder (h = 4, r=2, center = true, $fn=100);
}

// ../../../../shapes/blue-moon/blue-moon.scad
module blueMoon()
{
	difference()
	{
		// blue moon
		cylinder (h = 4, r=10, center = true, $fn=100);

		// top cutout
		translate([5, 5, 0])
		cylinder (h = 4.20, r=6, center = true, $fn=100);

		// bottom cutout
		translate([5, -5, 0])
		cylinder (h = 4.20, r=6, center = true, $fn=100);

		// remainder cutout
		translate([10, 0, 5])
		color("red")
		cylinder (h = 14.20, r=6, center = true, $fn=100);
	}
}

// http://www.thingiverse.com/thing:241081/
module pacman()
{
	//linear_extrude(height=50, center=true, convexity = 10, twist = 0)
	difference ()
	{
	  cylinder(r=20, center=true, h=5);

	  linear_extrude(height=10, center= true)
	//  linear_extrude(height=50, center=true, convexity = 10, twist = 0)
	//  {
		  polygon(points=[[0,0],[100,60],[100,-60]], paths=[[0,1,2]]);
	//  };
	}
}

//pacman();

// http://www.thingiverse.com/thing:603115
module poly_Selection(h)
{
  scale([25.4/90, -25.4/90, 1]) union()
  {
    linear_extrude(height=h)
      polygon([[13.500000,-56.000000],[29.500000,-55.000000],[54.500000,-55.000000],[58.503750,-55.048750],[60.522969,-55.341719],[62.290000,-56.020000],[64.325156,-57.853594],[66.241250,-60.541250],[69.240000,-66.000000],[80.500000,-87.000000],[106.280000,-135.000000],[118.870000,-159.000000],[122.780000,-198.000000],[124.023594,-204.882813],[125.843750,-209.652500],[128.457031,-214.095937],[132.080000,-220.000000],[148.490000,-248.000000],[153.376719,-256.487656],[155.041895,-259.143652],[156.518750,-260.956250],[158.055137,-262.086816],[159.898906,-262.696719],[165.500000,-263.000000],[214.500000,-263.000000],[214.500000,-213.000000],[213.360000,-196.000000],[213.360000,-154.000000],[213.495000,-150.462500],[213.360000,-147.000000],[196.920000,-117.000000],[156.350000,-45.000000],[125.920000,9.000000],[120.671250,18.592500],[117.385469,23.577187],[115.794199,25.366055],[114.320000,26.400000],[112.797344,26.797500],[110.981250,26.970000],[107.500000,27.000000],[79.500000,27.000000],[65.500000,28.000000],[31.500000,28.000000],[27.943750,28.030000],[24.500000,28.600000],[22.684180,29.597539],[20.548438,31.277812],[15.792500,35.922500],[7.670000,45.000000],[-36.500000,94.040000],[-25.500000,94.040000],[-15.500000,95.000000],[1.500000,95.000000],[22.500000,96.000000],[2.500000,114.080000],[-38.500000,149.000000],[-23.230000,165.000000],[-3.230000,188.000000],[14.500000,210.000000],[-66.500000,189.000000],[-91.500000,263.000000],[-92.970664,261.624141],[-94.426562,259.592500],[-97.197500,254.302500],[-101.500000,244.000000],[-121.500000,198.000000],[-200.500000,222.000000],[-174.840000,182.000000],[-154.500000,151.000000],[-187.500000,128.020000],[-214.500000,108.000000],[-166.500000,103.830000],[-135.500000,101.000000],[-137.410000,77.000000],[-139.590000,41.000000],[-140.500000,31.000000],[-138.108496,32.343340],[-135.722969,34.055469],[-131.038750,38.223750],[-122.500000,47.000000],[-111.500000,57.040000],[-94.500000,74.000000],[-77.500000,27.000000],[-107.500000,26.000000],[-117.196250,25.451250],[-122.812656,24.814531],[-126.090000,23.980000],[-127.513906,22.488438],[-128.773750,20.335000],[-130.730000,16.000000],[-138.200000,-1.000000],[-140.195000,-5.868750],[-140.649688,-8.406719],[-140.330000,-11.000000],[-134.780000,-21.000000],[-120.780000,-45.000000],[-74.650000,-124.000000],[-55.110000,-158.000000],[-46.570000,-206.000000],[-30.280000,-238.000000],[-21.920000,-254.000000],[-19.818750,-257.793750],[-18.535156,-259.646094],[-17.110000,-260.980000],[-15.251406,-261.684844],[-12.943750,-261.983750],[-8.500000,-261.990000],[5.500000,-261.000000],[19.500000,-261.000000],[31.500000,-260.040000],[43.500000,-260.040000],[43.500000,-217.000000],[44.500000,-200.000000],[44.500000,-162.000000],[43.800000,-154.000000],[28.100000,-127.000000],[-14.500000,-56.000000],[13.500000,-56.000000]]);
  }
}

module spursa(h)
{
	poly_Selection(h);
}


Simpler cut test, for use on hardboard.

Simpler cut test, for use on hardboard.

// Dimensions are in mm by default
height = 2.5;    // 0.1" = 2.54mm
mincut = 2;
maxcut = 3.6;      // 0.125" = 3.175mm, so get some cuts above that
cutstep = 0.2;
cutshift = maxcut * 2;
cutlength = 50;  // 5cm
length = cutlength + 2*maxcut;
numcuts = ((maxcut - mincut) / cutstep) + 1;
width = cutshift * (numcuts+1);

projection(cut=false) testobj();
//testobj();

module testobj() {
    difference () {
        cube([width, length, height]);

        // slot cuts
        for (i = [0 : numcuts-1]) {
            translate([cutshift * (i+0.5), 0, -50])
                cube([mincut + cutstep*i, cutlength, 100]);
        }
    }
}

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


[3d] Increase case front offset to 2.0mm

// preview[view:northeast, tilt:topdiagonal]

/* [Options] */
// Pit probe type
Control_Probe = "Thermocouple"; // [Thermocouple,Thermistor,None]
// Raspberry Pi Model
Pi_Model = "3B/2B/1B+"; // [3B/2B/1B+,Connectorless,1A+,Zero,3A+]
// Which case halves
Pieces = "Both"; // [Both,Top,Bottom]
// Include cutouts and mounts for LCD/Buttons
LCD = 1; // [0:None,1:2-line]
// Thickness of side walls (mm) - Set to trace width multiple
wall = 2.0;
/* [Advanced] */
// Thickness of top and bottom faces (mm)
wall_t = 1.45;
// Height of the body edge bead chamfer (mm)
body_chamfer_height = 1.5;
// Corner ear height (mm) - 0 to disable
MouseEarHeight = 0;
// Corner leg height (mm) - 0 to disable
MouseLegHeight = 0; //body_chamfer_height/2;
// Screw hardware
NutHardware = 0; // [0:Captive Nut,1:5mm Injection Threaded Insert,2:Threaded Insert]

/* [Hidden] */
// External corner radius on the body (mm)
body_corner_radius = wall*2-1/2;
w_off = 0.5; // offset the heatermeter origin from the left edge
d_off = 2.0; // offset the heatermeter origin from the front edge
w = inch(3.725)+0.7+w_off; // overall interior case width
d = inch(3.75)+1.0+d_off; // overall interior case depth
// 19.1+ headless Zero
// 22.4 headless Pi3 (limited by nuttrapps interfering with PCB to -6.7)
// 32 standard
h_b = [32.5-6.7, 32.5][LCD];  // overall interior case height

probe_centerline = 9.3; // case is split along probe centerline on the probe side
case_split = 12.4;  // and the case split on the other 3 sides

pic_ex = 1.7;
lcd_mount_t = 8.2 - (wall_t - 0.8);
pi_screw_t = 2.3;

body_chamfer_height_t = body_chamfer_height;
body_chamfer_height_b = body_chamfer_height;

led_dia = 2.9;
led_h = 4.6 - 1;
led_fudge = 0.3; // total extra diameter to expand the hole and head sphere
led_inset = 0.5; // amount to extend LED through case

e = 0.01;
is_jig = 0; // generate a jig for soldering LEDs

function inch(x) = x*25.4;

echo(str("Case dimensions (mm): ", w+2*wall, "x", d+2*wall, "x", h_b+2*wall_t));
main();

module main()
{
  if (is_jig) {
    intersection() {
      hm43_split();
      translate([wall+6,-d-wall+3,-e]) cube([w-12,d-8,15]);
    }

    difference() {
      cube([w-12,d-8,8 - wall_t]);
      translate([wall,wall,-e]) cube([w-12-2*wall, d-8-2*wall, 8 - wall_t +2*e]);
    }
  } /* is_jig */
  else
    hm43_split();
}

module cube_fillet_chamfer(size,f,c,$fn=32) {
  hull() {
    translate([f,f,c]) linear_extrude(height=size[2]-2*c) minkowski() {
      square([size[0]-2*f, size[1]-2*f]);
      circle(r=f, $fn=$fn);
    }
    translate([f+c,f+c,0]) linear_extrude(height=size[2]) minkowski() {
      square([size[0]-2*(f+c), size[1]-2*(f+c)]);
      circle(r=f, $fn=$fn);
    }
  }
}

module jhole(d, h) {
  translate([0,0,h/2]) cube([2*(wall+pic_ex),d,h], center=true);
}

module phole() {
  dia=4.5;
  // Hole for thermistor probe jack
  rotate([0,90,0]) cylinder(2*wall, d=dia, $fn=16);
  // because 3d print tends to curl up slightly between the probe holes,
  // do not go right to the edge on the final layer (stop 0.25mm short on each side)
  translate([0,-(dia+0.5)/2, -0.3]) cube([2*wall, dia+0.5, 0.6]);
}

module screwhole() {
  rotate(90) translate([0,0,-e]) cylinder(3.5, d=6, $fn=18);
}

module pic_ex_cube(interior) {
  translate([0,33.75+interior*(pic_ex+1.3),2])
    cube_fillet([pic_ex+e, 59.8-interior*(pic_ex*2+1.3+1.5), 20.8],
      vertical=[0, (1-interior)*pic_ex, (1-interior)*pic_ex, 0],
      top=[0,pic_ex,0,0],
      bottom=[0,pic_ex,0,0]);
}

module screw_pimount() {
  cylinder(wall_t+pi_screw_t, d=6.4, $fn=18);
  // alignment nubbies
  //cylinder(wall_t+pi_screw_t+1.4, d=2.4, $fn=12);
}

module screw_keyhole() {
  d=6.2;
  d2=3.5;
  off=4.15; // ideally would be D but runs into edge of case
  h=3;

  cylinder(wall_t+2*e, d=d, $fn=18);
  translate([-d2/2,0,0]) cube([d2,off,wall_t+2*e]);
  translate([0,off,0]) {
    cylinder(wall_t+2*e, d=d2, $fn=18);
    translate([0,0,wall_t+e]) cylinder(h+e, d=d, $fn=18);
  }
}

module screw_keyhole_p() {
  d=6.2+wall;
  h=2.3;

  difference() {
    cylinder(wall_t+h, d=d, $fn=18);
    translate([0,0,-e]) cylinder(wall_t+h+2*e, d=d-wall, $fn=18);
    translate([-d,0,-e]) cube([d*2, d/2, wall_t+h+2*e]);
  }
}

module btn_rnd() {
  dia = is_jig ? 6.0 : 7.2; // TPU button covers was 8.0
  cylinder(wall_t+2*e, d1=dia, d2=dia+1.5*wall_t, $fn=24);
  translate([-6.5,-6.5,0]) cube([13,13,0.5+e]);
}

module tc_plusminus() {
  T=0.75+e;
  H=2;
  W=6;
  // Minus
  translate([0,2,0])
    cube_fillet([T, W, H], top=[0,0,0,T]);
  translate([0,-(2+W),0]) {
    cube_fillet([T, W, H], top=[0,0,0,T]);
    translate([0,(W-H)/2,(H-W)/2])
      cube_fillet([T, H, W], top=[0,0,0,T]);
  }
}

module led() {
  translate([0, 0, -(led_h+1)]) {
    cylinder(1, d=led_dia+0.3+led_fudge, $fn=24);
    translate([0, 0, 1-e]) cylinder(led_h-led_dia/2+e, d=led_dia+led_fudge, $fn=24);
    translate([0, 0, 1+led_h-led_dia/2]) sphere(d=led_dia+led_fudge, $fn=24);
  }
}

module led_pillar() {
  translate([0,0,-led_h-e])
    cylinder(led_h-led_inset, d=led_dia+led_fudge+2*wall, $fn=24);
}

module nuttrap() {
  ww_w=3;
  ww_d=wall;
  nut_h=3.2;
  nut_ingress = 5.7;
  nut_d = nut_ingress / sin(60);
  nut_ingress_off = nut_ingress/sqrt(3)/2;
  oa_h=wall_t+0.4+nut_h+wall_t+[0,6.7][LCD];
  screw_l=[20,25][LCD];

  // bottom half for M3 socket cap screw (flush)
  difference() {
    translate([-5.5/2-ww_w, -nut_ingress/2-d_off-e, 0])
      cube_fillet([5.5+2*ww_w, nut_ingress+d_off+ww_d, 6], vertical=[3.4,3.4], $fn=20);
    // socket cap
    rotate(90) translate([0,0,-e]) cylinder(3.5, d=6, $fn=18);
    // screw shaft
    translate([0,0,3.5+0.3]) cylinder(oa_h-3, d=3.4, $fn=16);
    // rectangular hole to remove some of the solid layer material
    translate([-5.7/2, -(6*PI/18)/2, 0]) cube([5.7, 6*PI/18, 3.5+0.3+e]);
  }

  // top half M3 nut trap
  if (NutHardware == 0) { // Captive Nut Slots
    translate([0,0,h_b+wall_t-oa_h])
    difference() {
      translate([-(nut_d/2+ww_w), -nut_ingress/2-d_off, 0])
        cube_fillet([nut_d+2*ww_w, nut_ingress+d_off+ww_d, oa_h+e],
          vertical=[ww_d/2,3.4], $fn=20);
      // M3 screw
      translate([0,0,-e]) cylinder(wall_t, d1=4, d2=3.4, $fn=16);
      // nut hole / M3 extra
      translate([0,0,wall_t+0.3]) {
        // nut 2x for an elongated trap
        translate([-0.2,0,0]) cylinder(nut_h*1.5+e, d=nut_d, $fn=6);
        translate([+0.2,0,0]) cylinder(nut_h*1.5+e, d=nut_d, $fn=6);
        cylinder(oa_h-wall_t-0.3, d=4, $fn=16);  // M3 with plenty of clearance
        //translate([-50,-50,-100]) cube([100,100,100+e]); // cutaway top
      }
      // nut ingress (sideways trapezoid with wider side at ingress)
      translate([nut_ingress_off,-nut_ingress/2,wall_t+0.3])
         linear_extrude(nut_h+e) polygon([[0,0],
           [nut_d/2+ww_w-nut_ingress_off+0.4+e, -0.8],
           [nut_d/2+ww_w-nut_ingress_off+0.4+e, nut_ingress+0.8],
           [0,nut_ingress]]);
    }
  } else if (NutHardware == 1 || NutHardware == 2) { // Threaded inserts
    // 5mm injected molding threaded insert (M3 x 5.4 x 5.2 tall)
    // 5.0mm dia 5mm tall, with 5.6mm alignment area at top 1.2mm tall
    // Threaded insert (M3 x 4.6 x 5.7 tall)
    // 4.3mm dia 5.5mm tall, with 4.8mm alignment area at top 1.2mm tall
    nhd = [[5.0, 5.0, 5.6], [4.3, 5.5, 4.8]][NutHardware-1];
    translate([0,0,h_b+wall_t-(h_b-screw_l+2)])
    difference() {
      union() {
        cylinder((h_b-screw_l)+2, d=nhd[0]+2*wall, $fn=24);
        cylinder(1.2-e, d1=nhd[2]+2*wall, d2=nhd[0]+2*wall, $fn=24);
        translate([(nhd[0]+2*wall)/-2, -nut_ingress/2-d_off, 0])
          cube([nhd[0]+2*wall, nut_ingress/2+d_off, (h_b-screw_l)+2]);
      }
      cylinder(nhd[1]+1.2, d=nhd[0], $fn=24);
      translate([0,0,-e]) cylinder(1.2, d1=nhd[2], d2=nhd[0], $fn=24); // alignment helper
    }
  }
}

module locklip_p(l, l_offset=0,
  lip_insert_depth=2.0, // how deep V to insert
  lip_v_off=0.2, // extra height before starting insert
  lip_h_off=0.4, // back connector away from mating area
  lip_w=2.5,  // thickness of attachment beam
  insert_inset=[0,0] // inset the insert inside mating area
  ) {
  translate([l_offset,0,0]) rotate([90,0,90])
    difference() {
      linear_extrude(l-2*l_offset, convexity=11) polygon(points=[
        [0.1, -lip_w-lip_insert_depth-lip_h_off],  // 0.1 to add depth to keep extrusion manifold
        [-lip_w-lip_insert_depth-lip_h_off, 0],
        [-lip_w-lip_insert_depth-lip_h_off, lip_v_off+2*lip_insert_depth],
        [-lip_insert_depth-lip_h_off, lip_v_off+2*lip_insert_depth,],
        [-lip_h_off, lip_v_off+lip_insert_depth],
        [-lip_insert_depth-lip_h_off, lip_v_off],
        [-lip_insert_depth-lip_h_off, 0],
        [0.1, 0]
      ]);
      translate([-lip_w-lip_insert_depth-lip_h_off-e, 0, 0]) {
        if (insert_inset[0] > 0)
          translate([0,0,-e])
            cube([lip_w+lip_insert_depth+lip_h_off+0.1+2*e,
              lip_v_off+2*lip_insert_depth+e, insert_inset[0]+e]);
        if (insert_inset[1] > 0)
          translate([0, 0, l-2*l_offset-insert_inset[1]])
            cube([lip_w+lip_insert_depth+lip_h_off+0.1+2*e,
              lip_v_off+2*lip_insert_depth+e, insert_inset[1]+e]);
      }
    }
}

module locklip_n(l, l_offset=0,
  lip_insert_depth=2.2, // how deep V to insert
  lip_tip_clip=0.3, // how much to shave off the top tip
  ) {
  translate([l-l_offset,0,0]) rotate([90,0,0]) rotate([0,-90,0])
    linear_extrude(height=l-2*l_offset) polygon(points=[
      [0.1, -3*lip_insert_depth],  // 0.1 to add depth to keep extrusion manifold
      [0.1, -lip_tip_clip],
      [-lip_insert_depth+lip_tip_clip, -lip_tip_clip],
      [-e, -lip_insert_depth],
      [-lip_insert_depth, -2*lip_insert_depth]
    ]);
}

module lcd_screw() {
  rotate([180]) difference() {
    //cylinder(lcd_mount_t, d=2.5+3.2, $fn=16);
    cylinder(lcd_mount_t+e, d=2.5, $fn=16);
  }
}

module lcd_mount() {
  // Assuming starting at bottom left screw hole
  //lcd_screw();
  //translate([75,0,0]) lcd_screw(); // bottom right hole obscured by PCB
  translate([0,31,0]) lcd_screw();
  translate([75,31,0]) lcd_screw();
}

module lcd_neg() {
  // Assuming starting at bottom left screw hole
  // (Note LCD is not centered vertically on PCB, top is 5.15, bottom is 4.55mm)
  translate([1.0, 1.55, 0])
    cube([73.0, 27.3, lcd_mount_t+wall_t-0.8]); // black bezel inset
  translate([5.5, 7.5, 0])
    cube([64.0, 15.4, lcd_mount_t+wall_t+2*e]); // LCD active area
  //translate([4.23, -2.5, 0])
  //  cube([16*2.54, 5, lcd_mount_t+wall_t-0.8]); // pins cutout
}

module locklip_top_n(split) {
  translate([wall, d+wall, split+wall_t+e])
    rotate([180]) {
      locklip_n(28);
      translate([w-34,0,0]) locklip_n(34);
    }
}

module hm_base() {
  cube_bchamfer([w+2*wall, d+2*wall, h_b+2*wall_t],
    r=body_corner_radius, top=body_chamfer_height_t,
    bottom=body_chamfer_height_b, $fn=36);
  // extra thick by Pi connectors
  if (Pi_Model != "Zero" && Pi_Model != "1A+" && Pi_Model != "3A+")
    translate([-pic_ex,wall+d_off,wall_t])
      pic_ex_cube(0);
  // TC +/-
  if (Control_Probe == "Thermocouple")
    translate([w+wall*2-e,wall+d_off+10,wall_t+19]) tc_plusminus();

  // Eliminate the chamfer where the screws are
  translate([wall+inch(0.825)+0.5,wall+d_off+inch(0.1),0]) {
    cylinder(body_chamfer_height_b, d=6+2*wall, $fn=24);
    translate([inch(2.0),0,0]) cylinder(body_chamfer_height_b, d=6+2*wall, $fn=24);
  }
}

module hm43() {
difference() {
  union() {
    difference() {
      hm_base();

      // Main cutout
      translate([wall, wall, wall_t])
        cube_fillet([w, d, h_b],
          bottom=[pi_screw_t,pi_screw_t,pi_screw_t,pi_screw_t],
          top=[pi_screw_t,pi_screw_t,pi_screw_t,pi_screw_t],
          vertical=[body_corner_radius/2,body_corner_radius/2,body_corner_radius/2,body_corner_radius/2],
          $fn=[36,4,4]);
      if (Pi_Model != "Zero" && Pi_Model != "1A+" && Pi_Model != "3A+")
        translate([wall-pic_ex+e,wall+d_off,wall_t]) pic_ex_cube(1);
    } // main diff

    if (LCD) translate([wall+inch(1.925)+w_off, wall+d_off+inch(1.15), h_b+2*wall_t])
      translate([inch(1.3), inch(-0.05), led_inset]) {
        led_pillar();  //red
        translate([0, inch(0.35), 0]) led_pillar(); //yellow
        translate([0, inch(0.70), 0]) led_pillar(); //green
      }
  } // main union

  // Probe jack side
  translate([w+wall*0.5, wall+d_off, wall_t+probe_centerline]) {
    // Probe jacks
    if (Control_Probe == "Thermocouple")
      // TC jack
      translate([0,inch(0.4)-16.5/2,-1.1]) cube([2*wall, 16.5, 6.5]);
    else if (Control_Probe == "Thermistor")
      translate([0,inch(0.28),0]) phole();
    if (Control_Probe != "None") {
      translate([0,inch(0.95)+inch(0.37)*0,0]) phole();
      translate([0,inch(0.95)+inch(0.37)*1,0]) phole();
      translate([0,inch(0.95)+inch(0.37)*2,0]) phole();
    }
  }
  // Pi connector side
  translate([0,wall+d_off,wall_t]) {
    // Pi connectors
    translate([0,0,5]) {
      if (Pi_Model == "3B/2B/1B+") {
        // ethernet
        translate([0,81.5,-0.8]) jhole(15,13);
        translate([0,81.5,-1.8]) jhole(5,5);
        // USB 0+1
        translate([0,62.75,0]) jhole(13,14.8);
        translate([0,44.75,0]) jhole(13,14.8);
      }
    }
    // HeaterMeter connectors
    translate([0,0,0]) {
      // Blower/Servo output
      translate([0,25,1.7]) jhole(16.4,13);
      // HM power jack
      translate([0,inch(0.2),4.2]) jhole(9.4,11);
    }
  }

  // lcd hole
  if (LCD)
    translate([wall+10.375+w_off, wall+d_off+inch(2), h_b+wall_t-lcd_mount_t-e]) lcd_neg();

  // button holes
  if (LCD) translate([wall+inch(1.925)+w_off, wall+d_off+inch(1.15), h_b+2*wall_t]) {
    //translate([-inch(1.1)/2,-13/2,-wall_t-e]) cube([inch(1.1), 13, 0.5+e]);  // clear space between
    translate([-inch(1.1)/2,0,-wall_t-e]) btn_rnd();  // left
    translate([inch(1.1)/2,0,-wall_t-e]) btn_rnd();   // right
    translate([0,inch(0.9)/2,-wall_t-e]) btn_rnd();   // up
    translate([0,-inch(0.9)/2,-wall_t-e]) btn_rnd();  // down
    // LED holes
    translate([inch(1.3), inch(-0.05), led_inset]) {
      led();  //red
      translate([0, inch(0.35), 0]) led(); //yellow
      translate([0, inch(0.70), 0]) led(); //green
    }
  }
  // close screw holes
  translate([wall+inch(0.825)+0.5,wall+d_off+inch(0.1),0]) {
    screwhole();
    translate([inch(2.0),0,0]) screwhole();
  }
  // keyholes
  //translate([wall+24,wall+d_off+39,-e]) {
  //    translate([0,49,0]) screw_keyhole();
  //    translate([58,49,0]) screw_keyhole();
  //}
}  // END OF DIFFERENCE

  // Pi mounting screws
  translate([wall+24,wall+d_off+39,0]) {
    translate([0,0,0]) screw_pimount();
    translate([58,0,0]) screw_pimount();
    if (Pi_Model == "Zero") {
      translate([0,23,0]) screw_pimount();
      translate([58,23,0]) screw_pimount();
    }
    else {
      translate([0,49,0]) screw_pimount();
      translate([58,49,0]) screw_pimount();
      //translate([0,49,0]) screw_keyhole_p();
      //translate([58,49,0]) screw_keyhole_p();
    }
  }

  if (Pi_Model != "Zero") {
    // Pi right edge stop
    translate([wall+w-9.5, wall+d_off+35, wall_t])
      difference() {
        cube_fillet([9.5,d-d_off-35,4], vertical=[0,0,10/2], $fn=24);
        // Pi B+ microsd gap
        translate([-e,22,-e]) cube_fillet([5.5,14.5,4+2*e], vertical=[2,0,0,2], $fn=20);
      }
  } // if !Zero

  // close nut traps
  if (!is_jig) translate([wall+inch(0.825)+w_off,wall+d_off+inch(0.1),0]) {
      nuttrap();
      translate([inch(2.0),0,0]) nuttrap();
    }

  // Top locklip (negative)
  if (Pi_Model == "3B/2B/1B+")
    locklip_top_n(case_split);
  else
    locklip_top_n(probe_centerline);

  // LCD mount
  if (LCD) difference() {
    union() {
      // Filled block above LCD hole
      translate([wall, wall+d_off+78, h_b+wall_t-lcd_mount_t])
        cube([w,d-d_off-78,lcd_mount_t+e]);
      // LCD grab notch
      translate([wall+10.375+w_off, wall+d_off+inch(2), h_b+wall_t-lcd_mount_t])
        translate([(77.5-20)/2,34.0-wall_t,-(1.8+wall_t)])
          // 1.8=thickness of LCD pcb
          cube_fillet([20,1.8+wall_t+wall,1.8+wall_t+e], top=[0,0,1.8+wall_t+e],
            vertical=[wall/2,wall/2]);
    }
    translate([wall+10.375+w_off, wall+d_off+inch(2), h_b+wall_t-e]) {
      translate([0, 0, -lcd_mount_t]) lcd_neg();
      // vv Keep screw holes where they have been since the beginning
      translate([0, 0.5, 0]) lcd_mount();
    }
  }

  if (Pi_Model == "3B/2B/1B+")
    translate([-pic_ex, wall+d_off, wall_t+2]) {
      // USB pillar reinforcements
      translate([0, (44.75+62.75)/2-2.2/2, 1.5])
        cube_fillet([pic_ex+wall, 2.2, 20.8-1.5], bottom=[0,pic_ex,0,2], top=[0,pic_ex]);
      translate([0, 81.5-15/2-3, 1.5])
        cube_fillet([pic_ex+wall, 2.5, 20.8-1.5], bottom=[0,pic_ex,0,2], top=[0,pic_ex]);
    }
}

module lip_guide(l) {
  guide_offset = 0.2; // how much to recess the edge guides
  guide_h = 1.2; // how tall above the edge to extend
  linear_extrude(height=l) polygon([
    [0,-e], [wall+guide_offset,-e], [0, wall+guide_offset],
    [-guide_h, wall+guide_offset], [-guide_h, guide_offset],
    [0, guide_offset]
  ]);
}

module hm43_bottom_lips(split) {
  translate([wall, wall, split+wall_t]) {
    // bottom locklip (positive)
    translate([0,d,0]) {
      locklip_p(28, insert_inset=[0,0]);
      translate([w-34,0,0]) locklip_p(34-wall, insert_inset=[0,0]);
    }

    // front guide lip (left, mid, right)
    translate([body_corner_radius, 0, 0])
      rotate([0,90]) lip_guide(12);
    translate([w/2-9, 0, 0])
      rotate([0,90]) lip_guide(25);
    translate([w-body_corner_radius-12, 0, 0])
      rotate([0,90]) lip_guide(12);
    // left guide lip assortment
    translate([0, d_off+5.25+9.4/2, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(25.25-5.25-9.4/2-16.7/2);
    if (Pi_Model == "Zero" || Pi_Model == "1A+" || Pi_Model == "3A+")
      translate([0, d_off+40, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(45);
    else if (Pi_Model == "3B/2B/1B+") {
      *translate([-pic_ex, d_off+52, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
      *translate([-pic_ex, d_off+70, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
    }
  }
  // probe side guide lip
  translate([wall+w, wall+d-45, probe_centerline+wall_t])
    rotate([-90]) rotate(90) lip_guide(40);
}

module mouseears() {
  me_h = MouseEarHeight;
  me_d = 20;
  me_overlap = wall;
  me_outset_bottom = (me_d/2 - body_corner_radius/4 - body_chamfer_height_b - me_overlap) * 0.707;
  me_outset_top = (me_d/2 - body_corner_radius/4 - body_chamfer_height_t - me_overlap) * 0.707;

  // Bottom outside corners
  if (Pieces != "Top") {
    translate([-me_outset_bottom, d+2*wall+me_outset_bottom, 0])
      cylinder(me_h, d=me_d, $fn=24);
    translate([w+2*wall+me_outset_bottom, d+2*wall+me_outset_bottom, 0])
      cylinder(me_h, d=me_d, $fn=24);
  }

  // top outside corners
  if (Pieces != "Bottom") {
    translate([0,-1,me_h]) rotate([180]) {
      translate([w+2*wall+me_outset_top, d+2*wall+me_outset_top, 0])
        cylinder(me_h, d=me_d, $fn=24);
      translate([-me_outset_top, d+2*wall+me_outset_top, 0])
        cylinder(me_h, d=me_d, $fn=24);
    }
  }

  // common corner area
  translate([-me_outset_bottom/0.707, 0, 0])
    cylinder(me_h, d=me_d, $fn=24);
  translate([w+2*wall+me_outset_bottom/0.707, 0, 0])
    cylinder(me_h, d=me_d, $fn=24);
}

module mouseleg(isBottom) {
  MouseLegWidth = 2*0.63;
  if (isBottom)
    translate([body_chamfer_height_b+0.3, -MouseLegWidth/2, 0])
      cube([body_corner_radius+body_chamfer_height_b, MouseLegWidth, MouseLegHeight]);
  else
    translate([body_chamfer_height_t+0.3, -MouseLegWidth/2, h_b+2*wall_t-MouseLegHeight])
      cube([body_corner_radius+body_chamfer_height_t, MouseLegWidth, MouseLegHeight]);
}

module mouselegs(isBottom) {
  translate([body_chamfer_height_t+body_corner_radius/2,
    body_chamfer_height_t+body_corner_radius/2, 0])
    rotate(-135) mouseleg(isBottom);
  translate([w+2*wall-body_chamfer_height_t-body_corner_radius/2,
    body_chamfer_height_t+body_corner_radius/2, 0])
    rotate(-45) mouseleg(isBottom);
  translate([w+2*wall-body_chamfer_height_t-body_corner_radius/2,
    d+2*wall-body_chamfer_height_t-body_corner_radius/2, 0])
    rotate(45) mouseleg(isBottom);
  translate([body_chamfer_height_t+body_corner_radius/2,
    d+2*wall-body_chamfer_height_t-body_corner_radius/2, 0])
    rotate(135) mouseleg(isBottom);
}

module split_volume() {
  if (Pi_Model == "3B/2B/1B+") {
    difference() {
      translate([-pic_ex-1,-1,-1])
        cube([w+pic_ex+2*wall+2, d+2*wall+2, wall_t+case_split+1]);
      translate([w,0,wall_t+probe_centerline])
        cube([3*wall, d+2*wall+2*e, wall_t+probe_centerline+e]);
    }
  }
  else
    translate([-w,-d,-e])
      cube([3*w, 3*d, wall_t+probe_centerline+2*e]);
}

module hm43_split() {
  // bottom
  if (Pieces != "Top") translate([0,4,0]) {
    intersection() {
      hm43();
      split_volume();
    }
    if (Pi_Model == "3B/2B/1B+")
      hm43_bottom_lips(case_split);
    else
      hm43_bottom_lips(probe_centerline);
    if (MouseLegHeight > 0.0) mouselegs(true);
  } // if include bottom

  // top
  if (Pieces != "Bottom") {
    translate([0,-1,h_b+2*wall_t]) rotate([180]) {
      difference() {
        hm43();
        //translate([11,5,h_b+2*wall_t-0.24]) linear_extrude(0.5)
        //  text("HeaterMeter", font = "Liberation Sans:style=Bold Italic");
        split_volume();
      }
      if (MouseLegHeight > 0.0) mouselegs(false);
    }
  }  // if include top
  if (MouseEarHeight > 0.0)
    color("silver") mouseears();
}

/**********************                               **********************/
/********************** END OF CASE / LIBRARY FOLLOWS **********************/
/**********************                               **********************/

module cube_bchamfer(dim, r, top=0, bottom=0, $fn=$fn) {
  // bottom beaded area
  if (bottom != 0) hull(){
    translate([r,r,0]) cylinder(bottom, r1=r-bottom, r2=r, $fn=$fn);
    translate([dim[0]-r,r,0]) cylinder(bottom, r1=r-bottom, r2=r, $fn=$fn);
    translate([r,dim[1]-r,0]) cylinder(bottom, r1=r-bottom, r2=r, $fn=$fn);
    translate([dim[0]-r,dim[1]-r,0]) cylinder(bottom, r1=r-bottom, r2=r, $fn=$fn);
  }
  // center
  translate([0,0,bottom]) hull(){
    translate([r,r,0]) cylinder(dim[2]-top-bottom, r=r, $fn=$fn);
    translate([dim[0]-r,r,0]) cylinder(dim[2]-top-bottom, r=r, $fn=$fn);
    translate([r,dim[1]-r,0]) cylinder(dim[2]-top-bottom, r=r, $fn=$fn);
    translate([dim[0]-r,dim[1]-r,0]) cylinder(dim[2]-top-bottom, r=r, $fn=$fn);
  }
  // top beaded area
  if (top != 0) translate([0,0,dim[2]-top]) hull(){
    translate([r,r,0]) cylinder(top, r2=r-top, r1=r, $fn=$fn);
    translate([dim[0]-r,r,0]) cylinder(top, r2=r-top, r1=r, $fn=$fn);
    translate([r,dim[1]-r,0]) cylinder(top, r2=r-top, r1=r, $fn=$fn);
    translate([dim[0]-r,dim[1]-r,0]) cylinder(top, r2=r-top, r1=r, $fn=$fn);
  }
}

module fillet(radius, height=100, $fn=$fn) {
  if (radius != undef && radius > 0) {
    //this creates acutal fillet
    translate([-radius, -radius, -height / 2 - 0.02]) difference() {
        cube([radius * 2, radius * 2, height + 0.04]);
        if ($fn == 0 && (radius == 2 || radius == 3 || radius == 4)) {
            cylinder(r=radius, h=height + 0.04, $fn=4 * radius);
        } else {
            cylinder(r=radius, h=height + 0.04, $fn=$fn);
        }

    }
  }
}

module cube_fillet(size, radius=-1, vertical=[0,0,0,0], top=[0,0,0,0], bottom=[0,0,0,0], center=false, $fn=4){
    if (center) {
        cube_fillet_inside(size, radius, vertical, top, bottom, $fn);
    } else {
        translate([size[0]/2, size[1]/2, size[2]/2])
            cube_fillet_inside(size, radius, vertical, top, bottom, $fn);
    }
}

module cube_negative_fillet(size, radius=-1, vertical=[3,3,3,3], top=[0,0,0,0], bottom=[0,0,0,0], $fn=$fn){
    j=[1,0,1,0];

    fn_V = $fn[0] == undef ? $fn : $fn[0];
    fn_T = $fn[1] == undef ? $fn : $fn[1];
    fn_B = $fn[2] == undef ? $fn : $fn[2];

    for (i=[0:3]) {
        if (radius > -1) {
            rotate([0, 0, 90*i]) translate([size[1-j[i]]/2, size[j[i]]/2, 0]) fillet(radius, size[2], fn_V);
        } else {
            rotate([0, 0, 90*i]) translate([size[1-j[i]]/2, size[j[i]]/2, 0]) fillet(vertical[i], size[2], fn_V);
        }
        rotate([90*i, -90, 0]) translate([size[2]/2, size[j[i]]/2, 0 ]) fillet(top[i], size[1-j[i]], fn_T);
        rotate([90*(4-i), 90, 0]) translate([size[2]/2, size[j[i]]/2, 0]) fillet(bottom[i], size[1-j[i]], fn_B);

    }
}

module cube_fillet_inside(size, radius=-1, vertical=[3,3,3,3], top=[0,0,0,0], bottom=[0,0,0,0], $fn=$fn){
    //makes CENTERED cube with round corners
    // if you give it radius, it will fillet vertical corners.
    //othervise use vertical, top, bottom arrays
    //when viewed from top, it starts in upper right corner (+x,+y quadrant) , goes counterclockwise
    //top/bottom fillet starts in direction of Y axis and goes CCW too

    if (radius == 0) {
        cube(size, center=true);
    } else {
        difference() {
            cube(size, center=true);
            cube_negative_fillet(size, radius, vertical, top, bottom, $fn);
        }
    }
}

//cube_fillet([10,10,10]);

// preview[view:northeast, tilt:topdiagonal]

/* [Options] */
// Pit probe type
Control_Probe = "Thermocouple"; // [Thermocouple,Thermistor,None]
// Raspberry Pi Model
Pi_Model = "3B/2B/1B+"; // [3B/2B/1B+,Connectorless,1A+,Zero,3A+]
// Which case halves
Pieces = "Both"; // [Both,Top,Bottom]
// Include cutouts and mounts for LCD/Buttons
LCD = 1; // [0:None,1:2-line]
// Thickness of side walls (mm) - Set to trace width multiple
wall = 2.0;
/* [Advanced] */
// Thickness of top and bottom faces (mm)
wall_t = 1.45;
// Height of the body edge bead chamfer (mm)
body_chamfer_height = 1.5;
// Corner ear height (mm) - 0 to disable
MouseEarHeight = 0;
// Corner leg height (mm) - 0 to disable
MouseLegHeight = 0; //body_chamfer_height/2;
// Screw hardware
NutHardware = 0; // [0:Captive Nut,1:5mm Injection Threaded Insert,2:Threaded Insert]

/* [Hidden] */
// External corner radius on the body (mm)
body_corner_radius = wall*2-1/2;
w_off = 0.5; // offset the heatermeter origin from the left edge
d_off = 1.0; // offset the heatermeter origin from the front edge
w = inch(3.725)+0.7+w_off; // overall interior case width
d = inch(3.75)+1.0+d_off; // overall interior case depth
// 19.1+ headless Zero
// 22.4 headless Pi3 (limited by nuttrapps interfering with PCB to -6.7)
// 32 standard
h_b = [32.5-6.7, 32.5][LCD];  // overall interior case height

probe_centerline = 9.3; // case is split along probe centerline on the probe side
case_split = 12.4;  // and the case split on the other 3 sides

pic_ex = 1.7;
lcd_mount_t = 8.2 - (wall_t - 0.8);
pi_screw_t = 2.3;

body_chamfer_height_t = body_chamfer_height;
body_chamfer_height_b = body_chamfer_height;

led_dia = 2.9;
led_h = 4.6 - 1;
led_fudge = 0.3; // total extra diameter to expand the hole and head sphere
led_inset = 0.5; // amount to extend LED through case

e = 0.01;
is_jig = 0; // generate a jig for soldering LEDs

function inch(x) = x*25.4;

echo(str("Case dimensions (mm): ", w+2*wall, "x", d+2*wall, "x", h_b+2*wall_t));
main();

module main()
{
  if (is_jig) {
    intersection() {
      hm43_split();
      translate([wall+6,-d-wall+3,-e]) cube([w-12,d-8,15]);
    }

    difference() {
      cube([w-12,d-8,8 - wall_t]);
      translate([wall,wall,-e]) cube([w-12-2*wall, d-8-2*wall, 8 - wall_t +2*e]);
    }
  } /* is_jig */
  else
    hm43_split();
}

module cube_fillet_chamfer(size,f,c,$fn=32) {
  hull() {
    translate([f,f,c]) linear_extrude(height=size[2]-2*c) minkowski() {
      square([size[0]-2*f, size[1]-2*f]);
      circle(r=f, $fn=$fn);
    }
    translate([f+c,f+c,0]) linear_extrude(height=size[2]) minkowski() {
      square([size[0]-2*(f+c), size[1]-2*(f+c)]);
      circle(r=f, $fn=$fn);
    }
  }
}

module jhole(d, h) {
  translate([0,0,h/2]) cube([2*(wall+pic_ex),d,h], center=true);
}

module phole() {
  dia=4.5;
  // Hole for thermistor probe jack
  rotate([0,90,0]) cylinder(2*wall, d=dia, $fn=16);
  // because 3d print tends to curl up slightly between the probe holes,
  // do not go right to the edge on the final layer (stop 0.25mm short on each side)
  translate([0,-(dia+0.5)/2, -0.3]) cube([2*wall, dia+0.5, 0.6]);
}

module screwhole() {
  rotate(90) translate([0,0,-e]) cylinder(3.5, d=6, $fn=18);
}

module pic_ex_cube(interior) {
  translate([0,33.75+interior*(pic_ex+1.3),2])
    cube_fillet([pic_ex+e, 59.8-interior*(pic_ex*2+1.3+1.5), 20.8],
      vertical=[0, (1-interior)*pic_ex, (1-interior)*pic_ex, 0],
      top=[0,pic_ex,0,0],
      bottom=[0,pic_ex,0,0]);
}

module screw_pimount() {
  cylinder(wall_t+pi_screw_t, d=6.4, $fn=18);
  // alignment nubbies
  //cylinder(wall_t+pi_screw_t+1.4, d=2.4, $fn=12);
}

module screw_keyhole() {
  d=6.2;
  d2=3.5;
  off=4.15; // ideally would be D but runs into edge of case
  h=3;

  cylinder(wall_t+2*e, d=d, $fn=18);
  translate([-d2/2,0,0]) cube([d2,off,wall_t+2*e]);
  translate([0,off,0]) {
    cylinder(wall_t+2*e, d=d2, $fn=18);
    translate([0,0,wall_t+e]) cylinder(h+e, d=d, $fn=18);
  }
}

module screw_keyhole_p() {
  d=6.2+wall;
  h=2.3;

  difference() {
    cylinder(wall_t+h, d=d, $fn=18);
    translate([0,0,-e]) cylinder(wall_t+h+2*e, d=d-wall, $fn=18);
    translate([-d,0,-e]) cube([d*2, d/2, wall_t+h+2*e]);
  }
}

module btn_rnd() {
  dia = is_jig ? 6.0 : 7.2; // TPU button covers was 8.0
  cylinder(wall_t+2*e, d1=dia, d2=dia+1.5*wall_t, $fn=24);
  translate([-6.5,-6.5,0]) cube([13,13,0.5+e]);
}

module tc_plusminus() {
  T=0.75+e;
  H=2;
  W=6;
  // Minus
  translate([0,2,0])
    cube_fillet([T, W, H], top=[0,0,0,T]);
  translate([0,-(2+W),0]) {
    cube_fillet([T, W, H], top=[0,0,0,T]);
    translate([0,(W-H)/2,(H-W)/2])
      cube_fillet([T, H, W], top=[0,0,0,T]);
  }
}

module led() {
  translate([0, 0, -(led_h+1)]) {
    cylinder(1, d=led_dia+0.3+led_fudge, $fn=24);
    translate([0, 0, 1-e]) cylinder(led_h-led_dia/2+e, d=led_dia+led_fudge, $fn=24);
    translate([0, 0, 1+led_h-led_dia/2]) sphere(d=led_dia+led_fudge, $fn=24);
  }
}

module led_pillar() {
  translate([0,0,-led_h-e])
    cylinder(led_h-led_inset, d=led_dia+led_fudge+2*wall, $fn=24);
}

module nuttrap() {
  ww_w=3;
  ww_d=wall;
  nut_h=3.2;
  nut_ingress = 5.7;
  nut_d = nut_ingress / sin(60);
  nut_ingress_off = nut_ingress/sqrt(3)/2;
  oa_h=wall_t+0.4+nut_h+wall_t+[0,6.7][LCD];
  screw_l=[20,25][LCD];

  // bottom half for M3 socket cap screw (flush)
  difference() {
    translate([-5.5/2-ww_w, -nut_ingress/2-d_off-e, 0])
      cube_fillet([5.5+2*ww_w, nut_ingress+d_off+ww_d, 6], vertical=[3.4,3.4], $fn=20);
    // socket cap
    rotate(90) translate([0,0,-e]) cylinder(3.5, d=6, $fn=18);
    // screw shaft
    translate([0,0,3.5+0.3]) cylinder(oa_h-3, d=3.4, $fn=16);
    // rectangular hole to remove some of the solid layer material
    translate([-5.7/2, -(6*PI/18)/2, 0]) cube([5.7, 6*PI/18, 3.5+0.3+e]);
  }

  // top half M3 nut trap
  if (NutHardware == 0) { // Captive Nut Slots
    translate([0,0,h_b+wall_t-oa_h])
    difference() {
      translate([-(nut_d/2+ww_w), -nut_ingress/2-d_off, 0])
        cube_fillet([nut_d+2*ww_w, nut_ingress+d_off+ww_d, oa_h+e],
          vertical=[ww_d/2,3.4], $fn=20);
      // M3 screw
      translate([0,0,-e]) cylinder(wall_t, d1=4, d2=3.4, $fn=16);
      // nut hole / M3 extra
      translate([0,0,wall_t+0.3]) {
        // nut 2x for an elongated trap
        translate([-0.2,0,0]) cylinder(nut_h*1.5+e, d=nut_d, $fn=6);
        translate([+0.2,0,0]) cylinder(nut_h*1.5+e, d=nut_d, $fn=6);
        cylinder(oa_h-wall_t-0.3, d=4, $fn=16);  // M3 with plenty of clearance
        //translate([-50,-50,-100]) cube([100,100,100+e]); // cutaway top
      }
      // nut ingress (sideways trapezoid with wider side at ingress)
      translate([nut_ingress_off,-nut_ingress/2,wall_t+0.3])
         linear_extrude(nut_h+e) polygon([[0,0],
           [nut_d/2+ww_w-nut_ingress_off+0.4+e, -0.8],
           [nut_d/2+ww_w-nut_ingress_off+0.4+e, nut_ingress+0.8],
           [0,nut_ingress]]);
    }
  } else if (NutHardware == 1 || NutHardware == 2) { // Threaded inserts
    // 5mm injected molding threaded insert (M3 x 5.4 x 5.2 tall)
    // 5.0mm dia 5mm tall, with 5.6mm alignment area at top 1.2mm tall
    // Threaded insert (M3 x 4.6 x 5.7 tall)
    // 4.3mm dia 5.5mm tall, with 4.8mm alignment area at top 1.2mm tall
    nhd = [[5.0, 5.0, 5.6], [4.3, 5.5, 4.8]][NutHardware-1];
    translate([0,0,h_b+wall_t-(h_b-screw_l+2)])
    difference() {
      union() {
        cylinder((h_b-screw_l)+2, d=nhd[0]+2*wall, $fn=24);
        cylinder(1.2-e, d1=nhd[2]+2*wall, d2=nhd[0]+2*wall, $fn=24);
        translate([(nhd[0]+2*wall)/-2, -nut_ingress/2-d_off, 0])
          cube([nhd[0]+2*wall, nut_ingress/2+d_off, (h_b-screw_l)+2]);
      }
      cylinder(nhd[1]+1.2, d=nhd[0], $fn=24);
      translate([0,0,-e]) cylinder(1.2, d1=nhd[2], d2=nhd[0], $fn=24); // alignment helper
    }
  }
}

module locklip_p(l, l_offset=0,
  lip_insert_depth=2.0, // how deep V to insert
  lip_v_off=0.2, // extra height before starting insert
  lip_h_off=0.4, // back connector away from mating area
  lip_w=2.5,  // thickness of attachment beam
  insert_inset=[0,0] // inset the insert inside mating area
  ) {
  translate([l_offset,0,0]) rotate([90,0,90])
    difference() {
      linear_extrude(l-2*l_offset, convexity=11) polygon(points=[
        [0.1, -lip_w-lip_insert_depth-lip_h_off],  // 0.1 to add depth to keep extrusion manifold
        [-lip_w-lip_insert_depth-lip_h_off, 0],
        [-lip_w-lip_insert_depth-lip_h_off, lip_v_off+2*lip_insert_depth],
        [-lip_insert_depth-lip_h_off, lip_v_off+2*lip_insert_depth,],
        [-lip_h_off, lip_v_off+lip_insert_depth],
        [-lip_insert_depth-lip_h_off, lip_v_off],
        [-lip_insert_depth-lip_h_off, 0],
        [0.1, 0]
      ]);
      translate([-lip_w-lip_insert_depth-lip_h_off-e, 0, 0]) {
        if (insert_inset[0] > 0)
          translate([0,0,-e])
            cube([lip_w+lip_insert_depth+lip_h_off+0.1+2*e,
              lip_v_off+2*lip_insert_depth+e, insert_inset[0]+e]);
        if (insert_inset[1] > 0)
          translate([0, 0, l-2*l_offset-insert_inset[1]])
            cube([lip_w+lip_insert_depth+lip_h_off+0.1+2*e,
              lip_v_off+2*lip_insert_depth+e, insert_inset[1]+e]);
      }
    }
}

module locklip_n(l, l_offset=0,
  lip_insert_depth=2.2, // how deep V to insert
  lip_tip_clip=0.3, // how much to shave off the top tip
  ) {
  translate([l-l_offset,0,0]) rotate([90,0,0]) rotate([0,-90,0])
    linear_extrude(height=l-2*l_offset) polygon(points=[
      [0.1, -3*lip_insert_depth],  // 0.1 to add depth to keep extrusion manifold
      [0.1, -lip_tip_clip],
      [-lip_insert_depth+lip_tip_clip, -lip_tip_clip],
      [-e, -lip_insert_depth],
      [-lip_insert_depth, -2*lip_insert_depth]
    ]);
}

module lcd_screw() {
  rotate([180]) difference() {
    //cylinder(lcd_mount_t, d=2.5+3.2, $fn=16);
    cylinder(lcd_mount_t+e, d=2.5, $fn=16);
  }
}

module lcd_mount() {
  // Assuming starting at bottom left screw hole
  //lcd_screw();
  //translate([75,0,0]) lcd_screw(); // bottom right hole obscured by PCB
  translate([0,31,0]) lcd_screw();
  translate([75,31,0]) lcd_screw();
}

module lcd_neg() {
  // Assuming starting at bottom left screw hole
  // (Note LCD is not centered vertically on PCB, top is 5.15, bottom is 4.55mm)
  translate([1.0, 1.55, 0])
    cube([73.0, 27.3, lcd_mount_t+wall_t-0.8]); // black bezel inset
  translate([5.5, 7.5, 0])
    cube([64.0, 15.4, lcd_mount_t+wall_t+2*e]); // LCD active area
  //translate([4.23, -2.5, 0])
  //  cube([16*2.54, 5, lcd_mount_t+wall_t-0.8]); // pins cutout
}

module locklip_top_n(split) {
  translate([wall, d+wall, split+wall_t+e])
    rotate([180]) {
      locklip_n(28);
      translate([w-34,0,0]) locklip_n(34);
    }
}

module hm_base() {
  cube_bchamfer([w+2*wall, d+2*wall, h_b+2*wall_t],
    r=body_corner_radius, top=body_chamfer_height_t,
    bottom=body_chamfer_height_b, $fn=36);
  // extra thick by Pi connectors
  if (Pi_Model != "Zero" && Pi_Model != "1A+" && Pi_Model != "3A+")
    translate([-pic_ex,wall+d_off,wall_t])
      pic_ex_cube(0);
  // TC +/-
  if (Control_Probe == "Thermocouple")
    translate([w+wall*2-e,wall+d_off+10,wall_t+19]) tc_plusminus();

  // Eliminate the chamfer where the screws are
  translate([wall+inch(0.825)+0.5,wall+d_off+inch(0.1),0]) {
    cylinder(body_chamfer_height_b, d=6+2*wall, $fn=24);
    translate([inch(2.0),0,0]) cylinder(body_chamfer_height_b, d=6+2*wall, $fn=24);
  }
}

module hm43() {
difference() {
  union() {
    difference() {
      hm_base();

      // Main cutout
      translate([wall, wall, wall_t])
        cube_fillet([w, d, h_b],
          bottom=[pi_screw_t,pi_screw_t,pi_screw_t,pi_screw_t],
          top=[pi_screw_t,pi_screw_t,pi_screw_t,pi_screw_t],
          vertical=[body_corner_radius/2,body_corner_radius/2,body_corner_radius/2,body_corner_radius/2],
          $fn=[36,4,4]);
      if (Pi_Model != "Zero" && Pi_Model != "1A+" && Pi_Model != "3A+")
        translate([wall-pic_ex+e,wall+d_off,wall_t]) pic_ex_cube(1);
    } // main diff

    if (LCD) translate([wall+inch(1.925)+w_off, wall+d_off+inch(1.15), h_b+2*wall_t])
      translate([inch(1.3), inch(-0.05), led_inset]) {
        led_pillar();  //red
        translate([0, inch(0.35), 0]) led_pillar(); //yellow
        translate([0, inch(0.70), 0]) led_pillar(); //green
      }
  } // main union

  // Probe jack side
  translate([w+wall*0.5, wall+d_off, wall_t+probe_centerline]) {
    // Probe jacks
    if (Control_Probe == "Thermocouple")
      // TC jack
      translate([0,inch(0.4)-16.5/2,-1.1]) cube([2*wall, 16.5, 6.5]);
    else if (Control_Probe == "Thermistor")
      translate([0,inch(0.28),0]) phole();
    if (Control_Probe != "None") {
      translate([0,inch(0.95)+inch(0.37)*0,0]) phole();
      translate([0,inch(0.95)+inch(0.37)*1,0]) phole();
      translate([0,inch(0.95)+inch(0.37)*2,0]) phole();
    }
  }
  // Pi connector side
  translate([0,wall+d_off,wall_t]) {
    // Pi connectors
    translate([0,0,5]) {
      if (Pi_Model == "3B/2B/1B+") {
        // ethernet
        translate([0,81.5,-0.8]) jhole(15,13);
        translate([0,81.5,-1.8]) jhole(5,5);
        // USB 0+1
        translate([0,62.75,0]) jhole(13,14.8);
        translate([0,44.75,0]) jhole(13,14.8);
      }
    }
    // HeaterMeter connectors
    translate([0,0,0]) {
      // Blower/Servo output
      translate([0,25,1.7]) jhole(16.4,13);
      // HM power jack
      translate([0,inch(0.2),4.2]) jhole(9.4,11);
    }
  }

  // lcd hole
  if (LCD)
    translate([wall+10.375+w_off, wall+d_off+inch(2), h_b+wall_t-lcd_mount_t-e]) lcd_neg();

  // button holes
  if (LCD) translate([wall+inch(1.925)+w_off, wall+d_off+inch(1.15), h_b+2*wall_t]) {
    //translate([-inch(1.1)/2,-13/2,-wall_t-e]) cube([inch(1.1), 13, 0.5+e]);  // clear space between
    translate([-inch(1.1)/2,0,-wall_t-e]) btn_rnd();  // left
    translate([inch(1.1)/2,0,-wall_t-e]) btn_rnd();   // right
    translate([0,inch(0.9)/2,-wall_t-e]) btn_rnd();   // up
    translate([0,-inch(0.9)/2,-wall_t-e]) btn_rnd();  // down
    // LED holes
    translate([inch(1.3), inch(-0.05), led_inset]) {
      led();  //red
      translate([0, inch(0.35), 0]) led(); //yellow
      translate([0, inch(0.70), 0]) led(); //green
    }
  }
  // close screw holes
  translate([wall+inch(0.825)+0.5,wall+d_off+inch(0.1),0]) {
    screwhole();
    translate([inch(2.0),0,0]) screwhole();
  }
  // keyholes
  //translate([wall+24,wall+d_off+39,-e]) {
  //    translate([0,49,0]) screw_keyhole();
  //    translate([58,49,0]) screw_keyhole();
  //}
}  // END OF DIFFERENCE

  // Pi mounting screws
  translate([wall+24,wall+d_off+39,0]) {
    translate([0,0,0]) screw_pimount();
    translate([58,0,0]) screw_pimount();
    if (Pi_Model == "Zero") {
      translate([0,23,0]) screw_pimount();
      translate([58,23,0]) screw_pimount();
    }
    else {
      translate([0,49,0]) screw_pimount();
      translate([58,49,0]) screw_pimount();
      //translate([0,49,0]) screw_keyhole_p();
      //translate([58,49,0]) screw_keyhole_p();
    }
  }

  if (Pi_Model != "Zero") {
    // Pi right edge stop
    translate([wall+w-9.5, wall+d_off+35, wall_t])
      difference() {
        cube_fillet([9.5,d-d_off-35,4], vertical=[0,0,10/2], $fn=24);
        // Pi B+ microsd gap
        translate([-e,22,-e]) cube_fillet([5.5,14.5,4+2*e], vertical=[2,0,0,2], $fn=20);
      }
  } // if !Zero

  // close nut traps
  if (!is_jig) translate([wall+inch(0.825)+w_off,wall+d_off+inch(0.1),0]) {
      nuttrap();
      translate([inch(2.0),0,0]) nuttrap();
    }

  // Top locklip (negative)
  if (Pi_Model == "3B/2B/1B+")
    locklip_top_n(case_split);
  else
    locklip_top_n(probe_centerline);

  // LCD mount
  if (LCD) difference() {
    union() {
      // Filled block above LCD hole
      translate([wall, wall+d_off+78, h_b+wall_t-lcd_mount_t])
        cube([w,d-d_off-78,lcd_mount_t+e]);
      // LCD grab notch
      translate([wall+10.375+w_off, wall+d_off+inch(2), h_b+wall_t-lcd_mount_t])
        translate([(77.5-20)/2,34.0-wall_t,-(1.8+wall_t)])
          // 1.8=thickness of LCD pcb
          cube_fillet([20,1.8+wall_t+wall,1.8+wall_t+e], top=[0,0,1.8+wall_t+e],
            vertical=[wall/2,wall/2]);
    }
    translate([wall+10.375+w_off, wall+d_off+inch(2), h_b+wall_t-e]) {
      translate([0, 0, -lcd_mount_t]) lcd_neg();
      // vv Keep screw holes where they have been since the beginning
      translate([0, 0.5, 0]) lcd_mount();
    }
  }

  if (Pi_Model == "3B/2B/1B+")
    translate([-pic_ex, wall+d_off, wall_t+2]) {
      // USB pillar reinforcements
      translate([0, (44.75+62.75)/2-2.2/2, 1.5])
        cube_fillet([pic_ex+wall, 2.2, 20.8-1.5], bottom=[0,pic_ex,0,2], top=[0,pic_ex]);
      translate([0, 81.5-15/2-3, 1.5])
        cube_fillet([pic_ex+wall, 2.5, 20.8-1.5], bottom=[0,pic_ex,0,2], top=[0,pic_ex]);
    }
}

module lip_guide(l) {
  guide_offset = 0.2; // how much to recess the edge guides
  guide_h = 1.2; // how tall above the edge to extend
  linear_extrude(height=l) polygon([
    [0,-e], [wall+guide_offset,-e], [0, wall+guide_offset],
    [-guide_h, wall+guide_offset], [-guide_h, guide_offset],
    [0, guide_offset]
  ]);
}

module hm43_bottom_lips(split) {
  translate([wall, wall, split+wall_t]) {
    // bottom locklip (positive)
    translate([0,d,0]) {
      locklip_p(28, insert_inset=[0,0]);
      translate([w-34,0,0]) locklip_p(34-wall, insert_inset=[0,0]);
    }

    // front guide lip (left, mid, right)
    translate([body_corner_radius, 0, 0])
      rotate([0,90]) lip_guide(12);
    translate([w/2-9, 0, 0])
      rotate([0,90]) lip_guide(25);
    translate([w-body_corner_radius-12, 0, 0])
      rotate([0,90]) lip_guide(12);
    // left guide lip assortment
    translate([0, d_off+5.25+9.4/2, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(25.25-5.25-9.4/2-16.7/2);
    if (Pi_Model == "Zero" || Pi_Model == "1A+" || Pi_Model == "3A+")
      translate([0, d_off+40, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(45);
    else if (Pi_Model == "3B/2B/1B+") {
      *translate([-pic_ex, d_off+52, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
      *translate([-pic_ex, d_off+70, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
    }
  }
  // probe side guide lip
  translate([wall+w, wall+d-45, probe_centerline+wall_t])
    rotate([-90]) rotate(90) lip_guide(40);
}

module mouseears() {
  me_h = MouseEarHeight;
  me_d = 20;
  me_overlap = wall;
  me_outset_bottom = (me_d/2 - body_corner_radius/4 - body_chamfer_height_b - me_overlap) * 0.707;
  me_outset_top = (me_d/2 - body_corner_radius/4 - body_chamfer_height_t - me_overlap) * 0.707;

  // Bottom outside corners
  if (Pieces != "Top") {
    translate([-me_outset_bottom, d+2*wall+me_outset_bottom, 0])
      cylinder(me_h, d=me_d, $fn=24);
    translate([w+2*wall+me_outset_bottom, d+2*wall+me_outset_bottom, 0])
      cylinder(me_h, d=me_d, $fn=24);
  }

  // top outside corners
  if (Pieces != "Bottom") {
    translate([0,-1,me_h]) rotate([180]) {
      translate([w+2*wall+me_outset_top, d+2*wall+me_outset_top, 0])
        cylinder(me_h, d=me_d, $fn=24);
      translate([-me_outset_top, d+2*wall+me_outset_top, 0])
        cylinder(me_h, d=me_d, $fn=24);
    }
  }

  // common corner area
  translate([-me_outset_bottom/0.707, 0, 0])
    cylinder(me_h, d=me_d, $fn=24);
  translate([w+2*wall+me_outset_bottom/0.707, 0, 0])
    cylinder(me_h, d=me_d, $fn=24);
}

module mouseleg(isBottom) {
  MouseLegWidth = 2*0.63;
  if (isBottom)
    translate([body_chamfer_height_b+0.3, -MouseLegWidth/2, 0])
      cube([body_corner_radius+body_chamfer_height_b, MouseLegWidth, MouseLegHeight]);
  else
    translate([body_chamfer_height_t+0.3, -MouseLegWidth/2, h_b+2*wall_t-MouseLegHeight])
      cube([body_corner_radius+body_chamfer_height_t, MouseLegWidth, MouseLegHeight]);
}

module mouselegs(isBottom) {
  translate([body_chamfer_height_t+body_corner_radius/2,
    body_chamfer_height_t+body_corner_radius/2, 0])
    rotate(-135) mouseleg(isBottom);
  translate([w+2*wall-body_chamfer_height_t-body_corner_radius/2,
    body_chamfer_height_t+body_corner_radius/2, 0])
    rotate(-45) mouseleg(isBottom);
  translate([w+2*wall-body_chamfer_height_t-body_corner_radius/2,
    d+2*wall-body_chamfer_height_t-body_corner_radius/2, 0])
    rotate(45) mouseleg(isBottom);
  translate([body_chamfer_height_t+body_corner_radius/2,
    d+2*wall-body_chamfer_height_t-body_corner_radius/2, 0])
    rotate(135) mouseleg(isBottom);
}

module split_volume() {
  if (Pi_Model == "3B/2B/1B+") {
    difference() {
      translate([-pic_ex-1,-1,-1])
        cube([w+pic_ex+2*wall+2, d+2*wall+2, wall_t+case_split+1]);
      translate([w,0,wall_t+probe_centerline])
        cube([3*wall, d+2*wall+2*e, wall_t+probe_centerline+e]);
    }
  }
  else
    translate([-w,-d,-e])
      cube([3*w, 3*d, wall_t+probe_centerline+2*e]);
}

module hm43_split() {
  // bottom
  if (Pieces != "Top") translate([0,4,0]) {
    intersection() {
      hm43();
      split_volume();
    }
    if (Pi_Model == "3B/2B/1B+")
      hm43_bottom_lips(case_split);
    else
      hm43_bottom_lips(probe_centerline);
    if (MouseLegHeight > 0.0) mouselegs(true);
  } // if include bottom

  // top
  if (Pieces != "Bottom") {
    translate([0,-1,h_b+2*wall_t]) rotate([180]) {
      difference() {
        hm43();
        //translate([11,5,h_b+2*wall_t-0.24]) linear_extrude(0.5)
        //  text("HeaterMeter", font = "Liberation Sans:style=Bold Italic");
        split_volume();
      }
      if (MouseLegHeight > 0.0) mouselegs(false);
    }
  }  // if include top
  if (MouseEarHeight > 0.0)
    color("silver") mouseears();
}

/**********************                               **********************/
/********************** END OF CASE / LIBRARY FOLLOWS **********************/
/**********************                               **********************/

module cube_bchamfer(dim, r, top=0, bottom=0, $fn=$fn) {
  // bottom beaded area
  if (bottom != 0) hull(){
    translate([r,r,0]) cylinder(bottom, r1=r-bottom, r2=r, $fn=$fn);
    translate([dim[0]-r,r,0]) cylinder(bottom, r1=r-bottom, r2=r, $fn=$fn);
    translate([r,dim[1]-r,0]) cylinder(bottom, r1=r-bottom, r2=r, $fn=$fn);
    translate([dim[0]-r,dim[1]-r,0]) cylinder(bottom, r1=r-bottom, r2=r, $fn=$fn);
  }
  // center
  translate([0,0,bottom]) hull(){
    translate([r,r,0]) cylinder(dim[2]-top-bottom, r=r, $fn=$fn);
    translate([dim[0]-r,r,0]) cylinder(dim[2]-top-bottom, r=r, $fn=$fn);
    translate([r,dim[1]-r,0]) cylinder(dim[2]-top-bottom, r=r, $fn=$fn);
    translate([dim[0]-r,dim[1]-r,0]) cylinder(dim[2]-top-bottom, r=r, $fn=$fn);
  }
  // top beaded area
  if (top != 0) translate([0,0,dim[2]-top]) hull(){
    translate([r,r,0]) cylinder(top, r2=r-top, r1=r, $fn=$fn);
    translate([dim[0]-r,r,0]) cylinder(top, r2=r-top, r1=r, $fn=$fn);
    translate([r,dim[1]-r,0]) cylinder(top, r2=r-top, r1=r, $fn=$fn);
    translate([dim[0]-r,dim[1]-r,0]) cylinder(top, r2=r-top, r1=r, $fn=$fn);
  }
}

module fillet(radius, height=100, $fn=$fn) {
  if (radius != undef && radius > 0) {
    //this creates acutal fillet
    translate([-radius, -radius, -height / 2 - 0.02]) difference() {
        cube([radius * 2, radius * 2, height + 0.04]);
        if ($fn == 0 && (radius == 2 || radius == 3 || radius == 4)) {
            cylinder(r=radius, h=height + 0.04, $fn=4 * radius);
        } else {
            cylinder(r=radius, h=height + 0.04, $fn=$fn);
        }

    }
  }
}

module cube_fillet(size, radius=-1, vertical=[0,0,0,0], top=[0,0,0,0], bottom=[0,0,0,0], center=false, $fn=4){
    if (center) {
        cube_fillet_inside(size, radius, vertical, top, bottom, $fn);
    } else {
        translate([size[0]/2, size[1]/2, size[2]/2])
            cube_fillet_inside(size, radius, vertical, top, bottom, $fn);
    }
}

module cube_negative_fillet(size, radius=-1, vertical=[3,3,3,3], top=[0,0,0,0], bottom=[0,0,0,0], $fn=$fn){
    j=[1,0,1,0];

    fn_V = $fn[0] == undef ? $fn : $fn[0];
    fn_T = $fn[1] == undef ? $fn : $fn[1];
    fn_B = $fn[2] == undef ? $fn : $fn[2];

    for (i=[0:3]) {
        if (radius > -1) {
            rotate([0, 0, 90*i]) translate([size[1-j[i]]/2, size[j[i]]/2, 0]) fillet(radius, size[2], fn_V);
        } else {
            rotate([0, 0, 90*i]) translate([size[1-j[i]]/2, size[j[i]]/2, 0]) fillet(vertical[i], size[2], fn_V);
        }
        rotate([90*i, -90, 0]) translate([size[2]/2, size[j[i]]/2, 0 ]) fillet(top[i], size[1-j[i]], fn_T);
        rotate([90*(4-i), 90, 0]) translate([size[2]/2, size[j[i]]/2, 0]) fillet(bottom[i], size[1-j[i]], fn_B);

    }
}

module cube_fillet_inside(size, radius=-1, vertical=[3,3,3,3], top=[0,0,0,0], bottom=[0,0,0,0], $fn=$fn){
    //makes CENTERED cube with round corners
    // if you give it radius, it will fillet vertical corners.
    //othervise use vertical, top, bottom arrays
    //when viewed from top, it starts in upper right corner (+x,+y quadrant) , goes counterclockwise
    //top/bottom fillet starts in direction of Y axis and goes CCW too

    if (radius == 0) {
        cube(size, center=true);
    } else {
        difference() {
            cube(size, center=true);
            cube_negative_fillet(size, radius, vertical, top, bottom, $fn);
        }
    }
}

//cube_fillet([10,10,10]);


Add parametric stanley bin

Add parametric stanley bin

/* Note that the factory bins have draft which make them injection moldable; we
 * don't need that. */

/* The little circles that the legs fit into to keep the bins from moving around */
gLegCircleSize = 18;

/* Most of the bins have X in this orientation. */
gGridX = 40;
gGridY = 55;

/* So the bins don't fit *too* tightly together */
gTopSlop = 0.6;
// TODO I'm not sure this works, maybe because of poylgon circles?
gExtWidth = 0.45;

//gHeight=47.8; // for HF
gHeight=40.9; // for Stanley

module RoundRect(w,h,r=1) {
    hull()
    for(x_scale=[1,-1]) for(y_scale=[1,-1])
    scale([x_scale,y_scale])
    translate([(w-r)/2,(h-r)/2]) circle(r=r);
}

// TODO h for HF
// TODO locating feet, maybe a really short screw?
// TODO bottom inside rounding
module Bin(x=1,y=1,wall=gExtWidth*3) {
    linear_extrude(height=gHeight) difference() {
        RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=32);
        RoundRect((x*gGridX)-(wall*2)-gTopSlop,(y*gGridY)-(wall*2)-gTopSlop,3-wall,$fn=32);
    }
    // Floor
    linear_extrude(height=2)
    RoundRect(x*gGridX-gTopSlop,y*gGridY-gTopSlop,3,$fn=32);
}

Bin(4,1);

error: pathspec 'yellow-bins/bin.scad' did not match any file(s) known to git