[3d] Fix lip edge geometry not perfectly lining up on on side of probewrap

The rounded portion of the lip at the 180 degree point wasn't flush with
the flat lip on the stackable portions

// Total number of probes supported (0 for just an endpiece)
count = 4; // [0,1,2,3,4]

// Wall thickness, set to multiple of perimeter width
wall = 2.5;
// Flat thickness
wall_t = 2;

// Overall width of flat
oa_w = 60;
// Overall depth of flat
oa_d = 95;
// Interior height between flats
height = 7;
// Center hole ratio
center_ratio = 3;

// Diameter of probe-holding hole
probe_d = 4.75;
// Length of probe holder
probe_l = 25;
// Distance of probe holder offset
probe_off = 7;
// Reduce thickness of probe holder (allow wire to pass more easily)
probe_cut = 0.75;

// Include holes around the perimeter of the flat?
flat_has_holes = true;
// Include the probe holder flat stub on endpiece as well? (can help retain wire)
end_has_stub = true;

/* [Hidden] */
// Center hole width
center_w = oa_w / center_ratio;
// Center hole depth
center_d = (oa_d - oa_w) + center_w;
// Diameter of holes in flat
flat_hole_d = (oa_w - center_w) / 4 - 0.5;

e=0.01;

do_render();

module do_render() {
  if (count == 0)
    half(true);
  else if (count == 1)
    single();
  else if (count == 2)
    complete_2();
  else if (count == 3)
    complete_3();
  else if (count == 4)
    complete_4();
}

module single() {
  half(false);
  translate([-oa_w - 1,0,0]) rotate(180) half(true);
}

module complete_2() {
  half(false);
  translate([oa_w + probe_off + probe_d + 1, 0, 0]) rotate(180) half(false);
  translate([0, oa_d + 1,0]) rotate(180) half(true);
}

module complete_3() {
  complete_2();
  translate([oa_w + probe_off + probe_d + 1, oa_d + 1, 0]) rotate(180) half(false);
}

module complete_4() {
  single();
  translate([oa_w + probe_off + probe_d + 1, 0, 0]) rotate(180) half(false);
  translate([0, oa_d + 1, 0]) half(false);
  translate([oa_w + probe_off + probe_d + 1, oa_d + 1, 0]) rotate(180) half(false);
}

module rextrude_180(diameter, h) {
  intersection() {
    translate([-diameter/2-e, -e, -e])
      cube([diameter+2*e, diameter/2+e, h+2*e]);
    rotate_extrude()
      children();
  }
}

module half(is_end) {
  probe_x=oa_w/2 + probe_off;
  probe_y=-probe_l - 5; // -oa_w/2 - 0;

  difference() {
    union() {
      flat();

      if (!is_end) {
        // probe holster
        intersection() {
          translate([probe_x, probe_y, probe_d/2+wall]) rotate([-90]) {
            difference() {
              union() {
                cylinder(probe_l, d=probe_d+2*wall, $fn=24);
                translate([-probe_d/2-wall,0,0])
                  cube([probe_d+2*wall, probe_d/2+wall, probe_l]);
              }
              translate([0,0,-e]) cylinder(probe_l+2*e, d=probe_d, $fn=16);
            }
          }
          // Keep the height of the cylinder less than the height of the layer
          translate([probe_x-probe_d/2-wall, probe_y, 0])
            cube([probe_d+2*wall, probe_l, height+wall_t-probe_cut]);
        } // interection
      } // if !is_end

      if (!is_end)
        // connect to holster
        translate([center_w/2+wall, probe_y, 0])
          cube([probe_x-center_w/2+probe_d/2, probe_l, wall_t]);
      else if (end_has_stub)
        // connect to holster
        mirror([0,1,0])
          translate([center_w/2+wall, probe_y, 0])
            cube([probe_x-center_w/2+probe_d/2, probe_l, wall_t]);

      // lip round
      translate([0,-(center_d-center_w)/2,0]) rotate(180)
        rextrude_180(oa_w, height*4, $fn=46)
        translate([center_w/2,0,0]) lip_poly(is_end, height);

      translate([0,(center_d-center_w)/2,0])
        rextrude_180(oa_w, height*4, $fn=46)
        translate([center_w/2,0,0]) lip_poly(is_end, height);
      // lip flat
      translate([center_w/2,(center_d-center_w)/2,0]) rotate([90])
        linear_extrude(center_d-center_w) lip_poly(is_end, height);
      translate([-center_w/2,-(center_d-center_w)/2,0]) rotate([90,0,180])
        linear_extrude(center_d-center_w) lip_poly(is_end, height);
    }  // end union

    if (is_end)
      mirror([0,1,0]) flat_holes();
    else
       flat_holes();

    // chip away a bit of snap in mating area
    translate([-4/2, -center_d/2-wall-e, wall_t+height-wall])
      cube([4, center_d+2*(wall+e), height*2]);
  } // end diff
}

module flat_hole() {
  rotate(20) {
    cylinder(wall_t+2*e, d=flat_hole_d);
    cube([flat_hole_d/2,flat_hole_d/2,wall_t+2*e]);
  }
}

module flat_holes() {
  flat_hole_offset = (oa_w - center_w)/4 + center_w/2;

  if (flat_has_holes) {
    translate([0, -(oa_d-oa_w)/2, 0]) for (rot=[0:180/4:180])
      rotate(-rot) translate([flat_hole_offset,0,-e]) flat_hole();
    translate([0, (oa_d-oa_w)/2, 0]) for (rot=[0:180/4:180])
      rotate(rot) translate([flat_hole_offset,0,-e]) flat_hole();
    translate([flat_hole_offset,0,-e]) flat_hole();
    translate([-flat_hole_offset,0,-e]) rotate(180) flat_hole();
  }
}

module flat() {
  difference() {
    hull() {
      translate([0, -(oa_d-oa_w)/2, 0]) cylinder(wall_t, d=oa_w, $fn=60);
      translate([0, (oa_d-oa_w)/2, 0]) cylinder(wall_t, d=oa_w, $fn=60);
    }

    // Center hole
    hull() {
      translate([0, -(center_d-center_w)/2, -e]) cylinder(wall_t+2*e, d=center_w+2, $fn=34);
      translate([0, (center_d-center_w)/2, -e]) cylinder(wall_t+2*e, d=center_w+2, $fn=34);
    }
  }
}

module lip_poly(is_end, interior_h) {
  if (is_end)
    lip_poly_stack_end();
  else
    lip_poly_stack(interior_h);
}

module lip_poly_stack(interior_h) {
  h=interior_h+wall_t;
  t_sock=wall;
  t_plug=t_sock;
  // Back the top snap portion away from the mating area by this amount
  // The lip is only 0.6mm so err needs to be less than this for any engagement to occur
  err_h=0.15;
  // Raise the snap lip this extra amount to account for a non-smooth surface
  // or layer rounding error, 1/2 a layer height is good
  err_v=0.12;

  polygon([
    // origin is bottom center, moving counterclockwise
    [0.5, 0], [t_sock,0], [t_sock, h], [-err_h, h],
    [-err_h, h+1+err_v],
      [0.6-err_h, h+1+0.6+err_v], [0.6-err_h, h+1+0.6+0.4+err_v],
    [-err_h, h+1+0.8+0.8+err_v],
    [-t_plug-err_h, h+1+0.8+0.8], [-t_plug-err_h, h], [0, h-t_plug-err_h],
    [0, 1+0.8+0.8], [0.8, 1+0.8], [0, 1], [0, 0.5]
  ]);
}

module lip_poly_stack_end() {
  t_sock = wall;
  stub = wall_t - 1.8;

  polygon([
    [0, 0], [t_sock, 0],
    // these points define the height, should be 1+0.8+0.8=2.6
    // but cut down to wall_t to be flush with flat(), this means wall_t
    // needs to be at least 1.8 to form a proper-sided socket
    [t_sock, wall_t], [0.5, wall_t], [0, wall_t-0.5], [0, wall_t-1],
    [0.8, wall_t-1-0.8], [0.8-stub, wall_t-1-0.8-stub]
  ]);
}

// Total number of probes supported (0 for just an endpiece)
count = 4; // [0,1,2,3,4]

// Wall thickness, set to multiple of perimeter width
wall = 2.5;
// Flat thickness
wall_t = 2;

// Overall width of flat
oa_w = 60;
// Overall depth of flat
oa_d = 95;
// Interior height between flats
height = 7;
// Center hole ratio
center_ratio = 3;

// Diameter of probe-holding hole
probe_d = 4.75;
// Length of probe holder
probe_l = 25;
// Distance of probe holder offset
probe_off = 7;
// Reduce thickness of probe holder (allow wire to pass more easily)
probe_cut = 0.75;

// Include holes around the perimeter of the flat?
flat_has_holes = true;
// Include the probe holder flat stub on endpiece as well? (can help retain wire)
end_has_stub = true;

/* [Hidden] */
// Center hole width
center_w = oa_w / center_ratio;
// Center hole depth
center_d = (oa_d - oa_w) + center_w;
// Diameter of holes in flat
flat_hole_d = (oa_w - center_w) / 4 - 0.5;

e=0.01;

do_render();

module do_render() {
  if (count == 0)
    half(true);
  else if (count == 1)
    single();
  else if (count == 2)
    complete_2();
  else if (count == 3)
    complete_3();
  else if (count == 4)
    complete_4();
}

module single() {
  half(false);
  translate([-oa_w - 1,0,0]) rotate(180) half(true);
}

module complete_2() {
  half(false);
  translate([oa_w + probe_off + probe_d + 1, 0, 0]) rotate(180) half(false);
  translate([0, oa_d + 1,0]) rotate(180) half(true);
}

module complete_3() {
  complete_2();
  translate([oa_w + probe_off + probe_d + 1, oa_d + 1, 0]) rotate(180) half(false);
}

module complete_4() {
  single();
  translate([oa_w + probe_off + probe_d + 1, 0, 0]) rotate(180) half(false);
  translate([0, oa_d + 1, 0]) half(false);
  translate([oa_w + probe_off + probe_d + 1, oa_d + 1, 0]) rotate(180) half(false);
}

module rextrude_180(diameter, h, $fn) {
  intersection() {
    translate([-diameter/2-e, e, -e])
      cube([diameter+2*e, diameter/2+e, h+2*e]);
    rotate_extrude($fn=$fn)
      children();
  }
}

module half(is_end) {
  probe_x=oa_w/2 + probe_off;
  probe_y=-probe_l - 5; // -oa_w/2 - 0;

  difference() {
    union() {
      flat();

      if (!is_end) {
        // probe holster
        intersection() {
          translate([probe_x, probe_y, probe_d/2+wall]) rotate([-90]) {
            difference() {
              union() {
                cylinder(probe_l, d=probe_d+2*wall, $fn=24);
                translate([-probe_d/2-wall,0,0])
                  cube([probe_d+2*wall, probe_d/2+wall, probe_l]);
              }
              translate([0,0,-e]) cylinder(probe_l+2*e, d=probe_d, $fn=16);
            }
          }
          // Keep the height of the cylinder less than the height of the layer
          translate([probe_x-probe_d/2-wall, probe_y, 0])
            cube([probe_d+2*wall, probe_l, height+wall_t-probe_cut]);
        } // interection
      } // if !is_end

      if (!is_end)
        // connect to holster
        translate([center_w/2+wall, probe_y, 0])
          cube([probe_x-center_w/2+probe_d/2, probe_l, wall_t]);
      else if (end_has_stub)
        // connect to holster
        mirror([0,1,0])
          translate([center_w/2+wall, probe_y, 0])
            cube([probe_x-center_w/2+probe_d/2, probe_l, wall_t]);

      // lip round
      translate([0,-(center_d-center_w)/2,0]) rotate(180)
        rextrude_180(oa_w, height*4, $fn=45)
        translate([center_w/2,0,0]) lip_poly(is_end, height);

      translate([0,(center_d-center_w)/2,0])
        rextrude_180(oa_w, height*4, $fn=45)
        translate([center_w/2,0,0]) lip_poly(is_end, height);
      // lip flat
      translate([center_w/2,(center_d-center_w)/2,0]) rotate([90])
        linear_extrude(center_d-center_w) lip_poly(is_end, height);
      translate([-center_w/2,-(center_d-center_w)/2,0]) rotate([90,0,180])
        linear_extrude(center_d-center_w) lip_poly(is_end, height);
    }  // end union

    if (is_end)
      mirror([0,1,0]) flat_holes();
    else
       flat_holes();

    // chip away a bit of snap in mating area
    translate([-4/2, -center_d/2-wall-e, wall_t+height-wall])
      cube([4, center_d+2*(wall+e), height*2]);
  } // end diff
}

module flat_hole() {
  rotate(20) {
    cylinder(wall_t+2*e, d=flat_hole_d);
    cube([flat_hole_d/2,flat_hole_d/2,wall_t+2*e]);
  }
}

module flat_holes() {
  flat_hole_offset = (oa_w - center_w)/4 + center_w/2;

  if (flat_has_holes) {
    translate([0, -(oa_d-oa_w)/2, 0]) for (rot=[0:180/4:180])
      rotate(-rot) translate([flat_hole_offset,0,-e]) flat_hole();
    translate([0, (oa_d-oa_w)/2, 0]) for (rot=[0:180/4:180])
      rotate(rot) translate([flat_hole_offset,0,-e]) flat_hole();
    translate([flat_hole_offset,0,-e]) flat_hole();
    translate([-flat_hole_offset,0,-e]) rotate(180) flat_hole();
  }
}

module flat() {
  difference() {
    hull() {
      translate([0, -(oa_d-oa_w)/2, 0]) cylinder(wall_t, d=oa_w, $fn=60);
      translate([0, (oa_d-oa_w)/2, 0]) cylinder(wall_t, d=oa_w, $fn=60);
    }

    // Center hole
    hull() {
      translate([0, -(center_d-center_w)/2, -e]) cylinder(wall_t+2*e, d=center_w+2, $fn=34);
      translate([0, (center_d-center_w)/2, -e]) cylinder(wall_t+2*e, d=center_w+2, $fn=34);
    }
  }
}

module lip_poly(is_end, interior_h) {
  if (is_end)
    lip_poly_stack_end();
  else
    lip_poly_stack(interior_h);
}

module lip_poly_stack(interior_h) {
  h=interior_h+wall_t;
  t_sock=wall;
  t_plug=t_sock;
  // Back the top snap portion away from the mating area by this amount
  // The lip is only 0.6mm so err needs to be less than this for any engagement to occur
  err_h=0.15;
  // Raise the snap lip this extra amount to account for a non-smooth surface
  // or layer rounding error, 1/2 a layer height is good
  err_v=0.12;

  polygon([
    // origin is bottom center, moving counterclockwise
    [0.5, 0], [t_sock,0], [t_sock, h], [-err_h, h],
    [-err_h, h+1+err_v],
      [0.6-err_h, h+1+0.6+err_v], [0.6-err_h, h+1+0.6+0.4+err_v],
    [-err_h, h+1+0.8+0.8+err_v],
    [-t_plug-err_h, h+1+0.8+0.8], [-t_plug-err_h, h], [0, h-t_plug-err_h],
    [0, 1+0.8+0.8], [0.8, 1+0.8], [0, 1], [0, 0.5]
  ]);
}

module lip_poly_stack_end() {
  t_sock = wall;
  stub = wall_t - 1.8;

  polygon([
    [0, 0], [t_sock, 0],
    // these points define the height, should be 1+0.8+0.8=2.6
    // but cut down to wall_t to be flush with flat(), this means wall_t
    // needs to be at least 1.8 to form a proper-sided socket
    [t_sock, wall_t], [0.5, wall_t], [0, wall_t-0.5], [0, wall_t-1],
    [0.8, wall_t-1-0.8], [0.8-stub, wall_t-1-0.8-stub]
  ]);
}


[3d] Rearrange some stuff to make it clearer

// 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]
// 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.5;
/* [Advanced] */
// Thickness of top and bottom faces (mm)
wall_t = 2;
// External corner radius on the body (mm)
body_corner_radius = 4;
// Height of the body edge bead chamfer (mm)
body_chamfer_height = 1.5;
// Corner ear height (mm) - 0 to disable
MouseEarHeight = 0;

/* [Hidden] */
w = inch(3.75)+0.5; // overall interior case width
d = inch(3.75)-0.0; // 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-6.7, 32][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 = 2;
lcd_mount_t = 8.2 - (wall_t - 0.8);

body_chamfer_height_t = body_chamfer_height;
body_chamfer_height_b = body_chamfer_height;

e = 0.01;

hm43_split();

function inch(x) = x*25.4;

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() {
  // Screw hole for bottom of case (including nut)
  //translate([0,0,-e]) cylinder(h_b+wall_t, d=3.4, $fn=18);
  translate([0,0,-e]) {
    cylinder(0.4, d1=7.4, d2=6.6, $fn=6);
    translate([0,0,0.4]) cylinder(3, d=6.6, $fn=6);
  }
  translate([0,0,3+0.2]) cylinder(h_b+2*wall_t, d=3.4, $fn=18);
  translate([0,0,h_b+wall_t]) cylinder(3, d=5.5, $fn=18);
}

module screwhole2() {
  translate([0,0,-e]) cylinder(3, d=6, $fn=18);
  translate([0,0,3+0.4]) cylinder(h_b-3-0.4+wall_t, d=3.4, $fn=18);
  translate([0.0,0,h_b+wall_t-lcd_mount_t/2-e])
    cylinder(lcd_mount_t/2+e, d=(2.9*2)/sin(60), $fn=6);
}

module pic_ex_cube() {
  translate([0,33.75,2])
    cube_fillet([pic_ex+e, 59.5, 20.8],
      vertical=[0, pic_ex, pic_ex, 0],
      top=[0,pic_ex,0,0],
      bottom=[0,pic_ex,0,0]);
}

module screw_pimount() {
  // 2mm height is good for screw holes
  difference() {
    cylinder(h=2.25, d=6.4, $fn=18);
    //cylinder(h=2+e, d=2.5, $fn=16);
  }
}

module btn_rnd(dia=10) {
  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.75+e]);
}

module btn_square(sq=13) {
  translate([-sq/2,-sq/2,0]) cube([sq,sq,wall_t+2*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_hole() {
  cylinder(wall_t+2*e, d=3.4, $fn=16);
}

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

  // bottom half for M3 socket cap screw (flush)
  difference() {
    translate([-5.5/2-ww_w, -nut_ingress/2-e, 0])
      cube_fillet([5.5+2*ww_w, nut_ingress+ww_d*0.8+e, 6], vertical=[3.4,3.4], $fn=20);
    // socket cap
    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=18);
  }

  // top half M3 nut trap
  translate([0,0,h_b+wall_t-oa_h])
  difference() {
    translate([-(nut_d/2+ww_w), -nut_ingress/2, 0])
      cube_fillet([nut_d+2*ww_w, nut_ingress+ww_d, oa_h+e],
        vertical=[3.4,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+e/sin(60), $fn=6);
      translate([+0.2,0,0]) cylinder(nut_h*1.5+e, d=nut_d+e/sin(60), $fn=6);
      cylinder(oa_h-wall_t-0.3, d=4, $fn=16);  // M3 with plenty of clearance
    }
    // 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]]);
  }
}

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,0]) rotate([0,90,0])
    difference() {
      linear_extrude(height=l-2*l_offset) polygon(points=[
        [0.1, -lip_w-lip_insert_depth-lip_h_off],  // 0.1 to add depth to keep extrusion manifold
        [0.1, 0],
        [-lip_insert_depth-lip_h_off, 0],
        [-lip_insert_depth-lip_h_off, lip_v_off],
        [-lip_h_off, lip_v_off+lip_insert_depth],
        [-lip_insert_depth-lip_h_off, lip_v_off+2*lip_insert_depth,],
        [-lip_w-lip_insert_depth-lip_h_off, lip_v_off+2*lip_insert_depth],
        [-lip_w-lip_insert_depth-lip_h_off, 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+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+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
  translate([1.0, 2.1, 0])
    cube([73.0, 27.3, lcd_mount_t+wall_t-0.8]); // black bezel inset
  translate([5.5, 8.0, 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+")
    translate([-pic_ex,wall,wall_t])
      pic_ex_cube();
  // TC +/-
  if (Control_Probe == "Thermocouple")
    translate([w+wall*2-e,wall+10,wall_t+18]) tc_plusminus();
}

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

  // Main cutout
  translate([wall, wall, wall_t])
    cube_fillet([w, d, h_b+e], bottom=[2,2,2,2],
    top=[lcd_mount_t/2,lcd_mount_t/2,[wall_t,lcd_mount_t/2][LCD],lcd_mount_t/2],
    vertical=[1,1]);
  if (Pi_Model != "Zero" && Pi_Model != "1A+")
    translate([wall-pic_ex+e,wall,wall_t]) pic_ex_cube();

  // Probe jack side
  translate([w+wall*0.5,wall,wall_t]) {
    // Probe jacks
    translate([0,24.25,probe_centerline]) {
      if (Control_Probe == "Thermocouple")
        // TC jack
        translate([0,inch(-0.55)-16.5/2,-1.4]) cube([2*wall, 16.5, 6.5]);
      else if (Control_Probe == "Thermistor")
        translate([0,-17.25,0]) phole();
      if (Control_Probe != "None") {
        translate([0,inch(0.37)*0,0]) phole();
        translate([0,inch(0.37)*1,0]) phole();
        translate([0,inch(0.37)*2,0]) phole();
      }
    }
  }
  // Pi connector side
  translate([wall*-0.5,wall,wall_t]) {
    // Pi connectors
    translate([0,0,5]) {
      if (Pi_Model == "3B/2B/1B+") {
        // ethernet
        translate([0,81.5,-0.5]) jhole(15,13);
        translate([0,81.5,-1.5]) 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.25,1.1]) jhole(16.7,13);
      // HM power jack
      translate([0,5.25,3.4]) jhole(9.4,11);
    }
  }

  // lcd hole
  if (LCD)
    translate([wall+10.7, wall+51.5, h_b+wall_t-lcd_mount_t-e]) lcd_neg();

  // button holes
  if (LCD) translate([wall+48.7, wall+29.4, h_b+wall_t-e]) {
    translate([-inch(1.1)/2,0,0]) btn_rnd(7.2);  // left
    translate([inch(1.1)/2,0,0]) btn_rnd(7.2);   // right
    translate([0,inch(0.9)/2,0]) btn_rnd(7.2);   // up
    translate([0,-inch(0.9)/2,0]) btn_rnd(7.2);  // down
    // LED holes
    translate([inch(1.3), inch(-0.05), 0]) {
      led_hole();  //red
      translate([0, inch(0.35), 0]) led_hole(); //yellow
      translate([0, inch(0.70), 0]) led_hole(); //green
    }
  }
  // close screw holes
  translate([wall+inch(0.8)+1.3,wall+2.9,0]) {
    screwhole2();
    translate([inch(2.0),0,0]) screwhole2();
  }
}  // END OF DIFFERENCE

  // Pi mounting screws
  translate([wall+24,wall+38.8,wall_t]) {
    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();
    }
  }

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

  // close nut traps
  translate([wall+inch(0.8)+1.3,wall+2.85,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-17, h_b+wall_t-lcd_mount_t])
        cube_fillet([w,17,lcd_mount_t+e],
          bottom=[lcd_mount_t/2,lcd_mount_t/2,0,lcd_mount_t/2]);
      // LCD grab notch
      translate([wall+10.7, wall+51.5, 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.7, wall+51.5, 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([wall-pic_ex, wall, wall_t]) {
      // USB pillar reinforcements
      translate([0, (44.75+62.75)/2-1, 3.5])
        cube_fillet([pic_ex+1, 2, h_b-3.5], bottom=[0,0,0,2]);
      translate([0, 81.5-15/2-3, 3.5])
        cube_fillet([pic_ex+1, 2.5, h_b-3.5], bottom=[0,0,0,2]);
      // Near pic_ex fill
      translate([0, 33.75, 0]) cube([pic_ex, 3, h_b]);
      // Far pic_ex fill (near ethernet)
      translate([0, d-5, 3.5])
       cube_fillet([pic_ex+1, 5+e, h_b-3.5], bottom=[0,0,0,2]);
    }
}

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=[2,0]);
      translate([w-34,0,0]) locklip_p(34-wall, insert_inset=[0,1]);
    }

    // front guide lip
    translate([w/2-9, 0, 0])
      rotate([0,90]) lip_guide(25);
    // left guide lip assortment
    translate([0, 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);
    *translate([-pic_ex, 52, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
    *translate([-pic_ex, 70, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
  }
  // probe side guide lip
  translate([wall, wall, probe_centerline+wall_t])
    translate([w, d-45, 0])
      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 split_volume() {
  if (Pi_Model == "3B/2B/1B+") {
    difference() {
      translate([-pic_ex-e,-e,-e])
        cube([w+pic_ex+2*wall+2*e, d+2*wall+2*e, wall_t+case_split+2*e]);
      translate([w,0,wall_t+probe_centerline])
        cube([3*wall, d+2*wall+2*e, wall_t+probe_centerline+2*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,1,0]) {
    intersection() {
      hm43();
      split_volume();
    }
    if (Pi_Model == "3B/2B/1B+")
      hm43_bottom_lips(case_split);
    else
      hm43_bottom_lips(probe_centerline);
  } // if include bottom

  // top
  if (Pieces != "Bottom") {
    translate([0,-1,h_b+2*wall_t]) rotate([180]) difference() {
      hm43();
      split_volume();
    }
  }  // 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 > 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];

    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);
        } else {
            rotate([0, 0, 90*i]) translate([size[1-j[i]]/2, size[j[i]]/2, 0]) fillet(vertical[i], size[2], $fn);
        }
        rotate([90*i, -90, 0]) translate([size[2]/2, size[j[i]]/2, 0 ]) fillet(top[i], size[1-j[i]], $fn);
        rotate([90*(4-i), 90, 0]) translate([size[2]/2, size[j[i]]/2, 0]) fillet(bottom[i], size[1-j[i]], $fn);

    }
}

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]
// Which case halves
Pieces = "Both"; // [Both,Top,Bottom]
// Include cutouts and mounts for LCD/Buttons
LCD = 1; // [0:No,1:Yes]
// Thickness of side walls (mm) - Set to trace width multiple
wall = 2.5;
/* [Advanced] */
// Thickness of top and bottom faces (mm)
wall_t = 2;
// External corner radius on the body (mm)
body_corner_radius = 4;
// Height of the body edge bead chamfer (mm)
body_chamfer_height = 1.5;
// Corner ear height (mm) - 0 to disable
MouseEarHeight = 0;

/* [Hidden] */
w = inch(3.75)+0.5; // overall interior case width
d = inch(3.75)-0.0; // 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-6.7, 32][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 = 2;
lcd_mount_t = 7;

body_chamfer_height_t = body_chamfer_height;
body_chamfer_height_b = body_chamfer_height;

e = 0.01;

hm43_split();

function inch(x) = x*25.4;

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() {
  // Screw hole for bottom of case (including nut)
  //translate([0,0,-e]) cylinder(h_b+wall_t, d=3.4, $fn=18);
  translate([0,0,-e]) {
    cylinder(0.4, d1=7.4, d2=6.6, $fn=6);
    translate([0,0,0.4]) cylinder(3, d=6.6, $fn=6);
  }
  translate([0,0,3+0.2]) cylinder(h_b+2*wall_t, d=3.4, $fn=18);
  translate([0,0,h_b+wall_t]) cylinder(3, d=5.5, $fn=18);
}

module screwhole2() {
  translate([0,0,-e]) cylinder(3, d=6, $fn=18);
  translate([0,0,3+0.4]) cylinder(h_b-3-0.4+wall_t, d=3.4, $fn=18);
  translate([0.0,0,h_b+wall_t-lcd_mount_t/2-e])
    cylinder(lcd_mount_t/2+e, d=(2.9*2)/sin(60), $fn=6);
}

module pic_ex_cube() {
  translate([0,33.75,2])
    cube_fillet([pic_ex+e, 59.5, 20.8],
      vertical=[0, pic_ex, pic_ex, 0],
      top=[0,pic_ex,0,0],
      bottom=[0,pic_ex,0,0]);
}

module screw_pimount() {
  // 2mm height is good for screw holes
  difference() {
    cylinder(h=2.25, d=6.4, $fn=18);
    //cylinder(h=2+e, d=2.5, $fn=16);
  }
}

module btn_rnd(dia=10) {
  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.75+e]);
}

module btn_square(sq=13) {
  translate([-sq/2,-sq/2,0]) cube([sq,sq,wall_t+2*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_hole() {
  cylinder(wall_t+2*e, d=3.4, $fn=16);
}

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

  // bottom half for M3 socket cap screw (flush)
  difference() {
    translate([-5.5/2-ww_w, -nut_ingress/2-e, 0])
      cube_fillet([5.5+2*ww_w, nut_ingress+ww_d*0.8+e, 6], vertical=[3.4,3.4], $fn=20);
    // socket cap
    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=18);
  }

  // top half M3 nut trap
  translate([0,0,h_b+wall_t-oa_h])
  difference() {
    translate([-(nut_d/2+ww_w), -nut_ingress/2, 0])
      cube_fillet([nut_d+2*ww_w, nut_ingress+ww_d, oa_h+e],
        vertical=[3.4,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+e/sin(60), $fn=6);
      translate([+0.2,0,0]) cylinder(nut_h*1.5+e, d=nut_d+e/sin(60), $fn=6);
      cylinder(oa_h-wall_t-0.3, d=4, $fn=16);  // M3 with plenty of clearance
    }
    // 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]]);
  }
}

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,0]) rotate([0,90,0])
    difference() {
      linear_extrude(height=l-2*l_offset) polygon(points=[
        [0.1, -lip_w-lip_insert_depth-lip_h_off],  // 0.1 to add depth to keep extrusion manifold
        [0.1, 0],
        [-lip_insert_depth-lip_h_off, 0],
        [-lip_insert_depth-lip_h_off, lip_v_off],
        [-lip_h_off, lip_v_off+lip_insert_depth],
        [-lip_insert_depth-lip_h_off, lip_v_off+2*lip_insert_depth,],
        [-lip_w-lip_insert_depth-lip_h_off, lip_v_off+2*lip_insert_depth],
        [-lip_w-lip_insert_depth-lip_h_off, 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+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+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
  translate([1.0, 2.1, 0])
    cube([73.0, 27.3, lcd_mount_t+wall_t-0.8]); // black bezel inset
  translate([5.5, 8.0, 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 hm43() {
difference() {
  union() {
    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+")
      translate([-pic_ex,wall,wall_t])
        pic_ex_cube();
    // TC +/-
    if (Control_Probe == "Thermocouple")
      translate([w+wall*2-e,wall+10,wall_t+18]) tc_plusminus();
  }

  // Main cutout
  translate([wall, wall, wall_t])
    cube_fillet([w, d, h_b+e], bottom=[2,2,2,2],
    top=[lcd_mount_t/2,lcd_mount_t/2,[wall_t,lcd_mount_t/2][LCD],lcd_mount_t/2],
    vertical=[1,1]);
  if (Pi_Model != "Zero" && Pi_Model != "1A+")
    translate([wall-pic_ex+e,wall,wall_t]) pic_ex_cube();

  // Probe jack side
  translate([w+wall*0.5,wall,wall_t]) {
    // Probe jacks
    translate([0,24.25,probe_centerline]) {
      if (Control_Probe == "Thermocouple")
        // TC jack
        translate([0,inch(-0.55)-16.5/2,-1.4]) cube([2*wall, 16.5, 6.5]);
      else if (Control_Probe == "Thermistor")
        translate([0,-17.25,0]) phole();
      if (Control_Probe != "None") {
        translate([0,inch(0.37)*0,0]) phole();
        translate([0,inch(0.37)*1,0]) phole();
        translate([0,inch(0.37)*2,0]) phole();
      }
    }
  }
  // Pi connector side
  translate([wall*-0.5,wall,wall_t]) {
    // Pi connectors
    translate([0,0,5]) {
      if (Pi_Model == "3B/2B/1B+") {
        // ethernet
        translate([0,81.5,-0.5]) jhole(15,13);
        translate([0,81.5,-1.5]) 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.25,1.1]) jhole(16.7,13);
      // HM power jack
      translate([0,5.25,3.4]) jhole(9.4,11);
    }
  }

  // lcd hole
  if (LCD)
    translate([wall+10.7, wall+51.5, h_b+wall_t-lcd_mount_t-e]) lcd_neg();

  // button holes
  if (LCD) translate([wall+48.7, wall+29.4, h_b+wall_t-e]) {
    translate([-inch(1.1)/2,0,0]) btn_rnd(7.2);  // left
    translate([inch(1.1)/2,0,0]) btn_rnd(7.2);   // right
    translate([0,inch(0.9)/2,0]) btn_rnd(7.2);   // up
    translate([0,-inch(0.9)/2,0]) btn_rnd(7.2);  // down
    // LED holes
    translate([inch(1.3), inch(-0.05), 0]) {
      led_hole();  //red
      translate([0, inch(0.35), 0]) led_hole(); //yellow
      translate([0, inch(0.70), 0]) led_hole(); //green
    }
  }
  // close screw holes
  translate([wall+inch(0.8)+1.3,wall+2.9,0]) {
    screwhole2();
    translate([inch(2.0),0,0]) screwhole2();
  }
}  // END OF DIFFERENCE

  // Pi mounting screws
  translate([wall+24,wall+38.8,wall_t]) {
    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();
    }
  }

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

  // close nut traps
  translate([wall+inch(0.8)+1.3,wall+2.85,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-17, h_b+wall_t-lcd_mount_t])
        cube_fillet([w,17,lcd_mount_t+e],
          bottom=[lcd_mount_t/2,lcd_mount_t/2,0,lcd_mount_t/2]);
      // LCD grab notch
      translate([wall+10.7, wall+51.5, 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.7, wall+51.5, 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([wall-pic_ex, wall, wall_t]) {
      // USB pillar reinforcements
      translate([0, (44.75+62.75)/2-1, 3.5])
        cube_fillet([pic_ex+1, 2, h_b-3.5], bottom=[0,0,0,2]);
      translate([0, 81.5-15/2-3, 3.5])
        cube_fillet([pic_ex+1, 2.5, h_b-3.5], bottom=[0,0,0,2]);
      // Near pic_ex fill
      translate([0, 33.75, 0]) cube([pic_ex, 3, h_b]);
      // Far pic_ex fill (near ethernet)
      translate([0, d-5, 3.5])
       cube_fillet([pic_ex+1, 5+e, h_b-3.5], bottom=[0,0,0,2]);
    }
}

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=[2,0]);
      translate([w-34,0,0]) locklip_p(34-wall, insert_inset=[0,1]);
    }

    // front guide lip
    translate([w/2-9, 0, 0])
      rotate([0,90]) lip_guide(25);
    // left guide lip assortment
    translate([0, 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);
    *translate([-pic_ex, 52, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
    *translate([-pic_ex, 70, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
  }
  // probe side guide lip
  translate([wall, wall, probe_centerline+wall_t])
    translate([w, d-45, 0])
      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 split_volume() {
  if (Pi_Model == "3B/2B/1B+") {
    difference() {
      translate([-pic_ex-e,-e,-e])
        cube([w+pic_ex+2*wall+2*e, d+2*wall+2*e, wall_t+case_split+2*e]);
      translate([w,0,wall_t+probe_centerline])
        cube([3*wall, d+2*wall+2*e, wall_t+probe_centerline+2*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,1,0]) {
    intersection() {
      hm43();
      split_volume();
    }
    if (Pi_Model == "3B/2B/1B+")
      hm43_bottom_lips(case_split);
    else
      hm43_bottom_lips(probe_centerline);
  } // if include bottom

  // top
  if (Pieces != "Bottom") {
    translate([0,-1,h_b+2*wall_t]) rotate([180]) difference() {
      hm43();
      split_volume();
    }
  }  // 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 > 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];

    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);
        } else {
            rotate([0, 0, 90*i]) translate([size[1-j[i]]/2, size[j[i]]/2, 0]) fillet(vertical[i], size[2], $fn);
        }
        rotate([90*i, -90, 0]) translate([size[2]/2, size[j[i]]/2, 0 ]) fillet(top[i], size[1-j[i]], $fn);
        rotate([90*(4-i), 90, 0]) translate([size[2]/2, size[j[i]]/2, 0]) fillet(bottom[i], size[1-j[i]], $fn);

    }
}

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


holder for guide created

holder for guide created

/**
 * rally.scad
 *
 * Created by Diego Viejo
 *
 * 11/Dic/2015
 *
 * This is my attemp on creating a chassis for a rally slot car
 *
 */

//guide arm's holder
//work in progress: check for all the sizes is still needed
numSlots = 3;
guideWidth = 40;
guideInternalWidth = 30;
guideHeight = 6;
guideHoleHeight = 3;
guideSupportLength = 7;
guideLength = guideSupportLength + 2 + numSlots*4;
engineHolderHoleSep = 20;

module guide(chassisLink=1)
difference()
{
    cube([guideWidth, guideLength, guideHeight], center=true);

    translate([0, guideSupportLength, 0]) cube([guideInternalWidth, guideLength, guideHeight+2], center=true);
    translate([0, 0, guideHeight/2]) cube([guideInternalWidth, guideLength+2, guideHeight], center=true);

    for(i=[1:10])
    {
        translate([-guideWidth/2-1, -guideLength/2 + guideSupportLength +i*4, -guideHeight/2+guideHoleHeight])
            rotate([0, 90, 0]) cylinder(d=2, h=guideWidth+2);
    }

    if(chassisLink==1)
    for(i=[-1,1])
    {
        translate([i*engineHolderHoleSep/2, (guideSupportLength-guideLength)/2, -guideHeight/2-1+guideHeight/4+1+0.3]) cylinder(d=2.5, h=guideHeight/2+2);
        translate([i*engineHolderHoleSep/2, (guideSupportLength-guideLength)/2, -guideHeight/2-1]) cylinder(d=5, h=guideHeight/4+1);
    }
}

guide();

/**
 * rally.scad
 *
 * Created by Diego Viejo
 *
 * 11/Dic/2015
 *
 * This is my attemp on creating a chassis for a rally slot car
 *
 */

//guide arm's holder
//work in progress: check for all the sizes is still needed
numSlots = 3;
guideWidth = 40;
guideInternalWidth = 30;
guideHeight = 6;
guideHoleHeight = 3;
guideSupportLength = 7;
guideLength = guideSupportLength + 2 + numSlots*4;

module guide()
difference()
{
    cube([guideWidth, guideLength, guideHeight], center=true);

    translate([0, guideSupportLength, 0]) cube([guideInternalWidth, guideLength, guideHeight+2], center=true);
    translate([0, 0, guideHeight/2]) cube([guideInternalWidth, guideLength+2, guideHeight], center=true);

    for(i=[1:10])
    {
        translate([-guideWidth/2-1, -guideLength/2 + guideSupportLength +i*4, -guideHeight/2+guideHoleHeight])
            rotate([0, 90, 0]) cylinder(d=2, h=guideWidth+2);
    }
}

guide();


Well... iT worked

Well... iT worked

// 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_size = 50;
iteration_multiplier = 0.4;
iterations = 2;

echo(version());

module aux_cubes(current_iter, starting_pos) {
    iter_size = init_size * pow(iteration_multiplier, current_iter);
    displacement = (init_size * (pow(iteration_multiplier, current_iter) + pow(iteration_multiplier, current_iter - 1))) / 2;

    for(i = [[displacement, 0, 0], [-displacement, 0, 0], [0, displacement, 0], [0, -displacement, 0], [0, 0, displacement], [0, 0, -displacement]]) {
        new_pos = i + starting_pos;

        translate(i + starting_pos) {
            cube(iter_size, center = true);
        }

        if(current_iter < iterations) {
            aux_cubes(current_iter + 1, new_pos);
        }
    }
}

union() {
    cube(init_size, center = true);

    aux_cubes(1, [0, 0, 0]);
}

// 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_size = 50;
iteration_multiplier = 0.4;
iterations = 2;

echo(version());

module aux_cubes(current_iter, starting_pos) {
    iter_size = init_size * pow(iteration_multiplier, current_iter);
    displacement = (init_size * (pow(iteration_multiplier, current_iter) + pow(iteration_multiplier, current_iter - 1))) / 2;

    for(i = [[displacement, 0, 0], [-displacement, 0, 0], [0, displacement, 0], [0, -displacement, 0], [0, 0, displacement], [0, 0, -displacement]]) {
        new_pos = i + starting_pos;

        translate(i + starting_pos) {
            cube(iter_size, center = true);
        }

        if(current_iter < iterations) {
            echo("in here");

            // aux_cubes(current_iter + 1, new_pos);
        }
    }
}

union() {
    cube(init_size, center = true);

    aux_cubes(1, [0, 0, 0]);
}


complete string prototype

complete string prototype

$fn = 64;

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

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

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

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

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

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

module lid() {
    string();
}

lid();

$fn = 64;

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

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

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

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

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

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

module lid() {
    string();
}

lid();


use cube in minkowsi

use cube in minkowsi

use <lid.scad>;
use <fillet.scad>;

debug = false;
no_fillet = false;

print = true;

$fa = 12; // 12 for printing
$fs = 2; // 2 for printing
$fn = 0;

render_outer = false;
render_full = false;
render_all = true;

// thickness of the shell
wall = 3;

// handle dimensions
handle_max_width = 35;
handle_min_width = 20;
handle_length = 75;

// used to avoid phantom wall when cutting objects
space = 1;

// dimensions of the 4 switches
switch_r = 4;
switch_offset = [7,7]; // relative to top and bottom corner

// dimensions of the led
led_r = 3;
led_offset = 2; // relative to bottom of the gimbal

// dimensions of the module bay
module_bay = [60,45,18];
cutout = [13,4];
cutout_offset = [3,3];
module_bay_lip = 3;

// gimbal dimensions
gimbal = [58,58,30];
gimbal_r = 25;

// dimensions of 4 gimbal screws per gimbal
gimbal_screw_offset = [5,5];
gimbal_screw_r = 1.5;
gimbal_space = [0,0];

// power switch dimension
switch_cutout = [10,20];

// angle of left and right side of controller
side_angle = 10;

// inclination of the handles
tilt_angle = 25;

// screw dimensions
nut_r = 2.1;
screw_head_r = 2;
screw_body_r = 1;
holder_r = 3.5;
separator = 4;

// trim switches
trim_switch_r = 6.5/2;

dims = [gimbal[0]*2+gimbal_space[0]*2,
        max(gimbal[1]+2*gimbal_space[1], module_bay[1]+2*wall+2*module_bay_lip),
        gimbal[2]];

// needs to be bigger than model
box_size = (max(dims[0],dims[1],dims[2]) + handle_length) * 10;

module module_bay_cutout() {
    cube([15,2*wall,16], center=true);
}

module module_bay(solid=false) {
    translate([0,0,-wall])
    difference() {
        union() {
            translate([0,0,module_bay[2]/2+wall/2])
                cube([module_bay[0]+2*wall, module_bay[1]+2*wall, module_bay[2]+wall], center=true);
            difference() {
                translate([0,0,(module_bay_lip+wall)/2+module_bay[2]])
                    cube([module_bay[0]+2*wall+2*module_bay_lip,
                        module_bay[1]+2*wall+2*module_bay_lip,
                        module_bay_lip+wall], center=true);
                if (!solid)
                    translate([0,0,module_bay[2]/2+wall])
                        cube([module_bay[0]+2*wall,
                              module_bay[1]+2*wall,
                              module_bay[2]+wall], center=true);
            }
        }

        if (!solid) {
            translate([0,0,module_bay[2]+module_bay_lip/2+wall])
                cube([module_bay[0]+2*module_bay_lip,
                      module_bay[1]+2*module_bay_lip,
                      module_bay_lip], center=true);

            translate([0,0,wall+module_bay[2]/2])
                cube([module_bay[0], module_bay[1], module_bay[2]], center=true);

            translate([module_bay[0]/2-cutout[0]-cutout_offset[0],
                       module_bay[1]/2-cutout[1]-cutout_offset[1],
                       -space])
                cube([cutout[0], cutout[1], wall+2*space]);

            translate([0,module_bay[1]/2+wall,wall+8]) module_bay_cutout();
            translate([0,-module_bay[1]/2-wall,wall+8]) module_bay_cutout();
        }
    }
}

//!module_bay();

module core(d = [10,10,10], offset = 0, solid=false) {
    scale_cylinder = [1,0.1,1];

    addition = sin(side_angle)*d[1];

    minkowski() {
        hull() {
            translate([0,-d[1]/2,0]) scale(scale_cylinder)
                cylinder(1,d[0]/2+addition,d[0]/2,center=true);
            translate([0,d[1]/2,0]) scale(scale_cylinder)
                cylinder(1,r=d[0]/2,center=true);
        }
        scale([0.6,0.6,1]) sphere(d[2]/2 - offset);
    }
}

module core_with_bay(solid=false, debug=false) {
    if (!debug && !no_fillet) {
        fillet(r=25,steps=20) {
            core(dims, solid=true);
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                module_bay(solid=true);
        }
    } else {
        union() {
            core(dims, solid=true);
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                module_bay(solid=true);
        }
    }
}

module handle(debug=false) {
    addition = (handle_length+dims[1])*sin(side_angle);
    tilt_vector = tilt_angle*[cos(side_angle),sin(side_angle),0];
    sf = 0.75;
    scale_vector = [1,1,sf];

    hull() {
        difference() {
            core_with_bay(debug=debug);
            translate([-handle_max_width,0,0])
                rotate([0,0,side_angle+5])
                    cube(dims[0],center=true);
            translate([0,11,0])
                rotate([0,0,side_angle+5])
                    cube([2*dims[0],dims[0],dims[0]],center=true);
        }
        scale(scale_vector)
        rotate(tilt_vector)
        translate([dims[0]/2+addition-handle_min_width/2,
                   -dims[1]/2-handle_length,
                   dims[2]/2-sf*handle_min_width/2])
            sphere(handle_min_width);
    }
}

module core_with_bay_and_handles(debug=false) {
    union() {
        core_with_bay(debug=debug);
        handle(debug=debug);
        mirror() handle(debug=debug);
    }
}

module skin(debug=false, thickness=5, box=box_size) {
    if (!debug) intersection() {
        children();
        minkowski() {
            cube(2*thickness,center=true);
            //cylinder(thickness,thickness,0,$fn=3,center=true);
            difference() {
                cube(box,center=true);
                children();
            }
        }
    }

    if (debug) children();
}

module cutouts() {

    h = 2*wall+2*space;

    // gimbal cutout
    translate([gimbal[0]/2,0,dims[2]/2])
        cylinder(h, r=gimbal_r, center=true);

    // gimbal screw cutout
    translate([gimbal_screw_offset[0],gimbal[1]/2-gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal[0] - gimbal_screw_offset[0],gimbal[1]/2-gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal_screw_offset[0],-gimbal[1]/2+gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal[0] - gimbal_screw_offset[0],-gimbal[1]/2+gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    // switches
    translate([-gimbal[0]+switch_offset[0], gimbal[1]/2, dims[2]/2-switch_offset[1]])
        rotate ([90,0,0])
            cylinder(gimbal[1]/2, r=switch_r, center=true);

    translate([-gimbal[0]+switch_offset[0], gimbal[1]/2, -dims[2]/2+switch_offset[1]])
        rotate ([90,0,0])
            cylinder(gimbal[1]/2, r=switch_r, center=true);

     // power switch
     translate([0,dims[1]/2+dims[2]/2,0])
        rotate([0,90,90])
            cube([switch_cutout[0], switch_cutout[1], gimbal[1]/2], center=true);

     // led
     translate([0,-gimbal[1]/2-led_offset-led_r,gimbal[2]/2])
        cylinder(h, r=led_r, center=true);

     // trim switches
     translate([-dims[0]/2+handle_min_width+trim_switch_r-5, -dims[1]/2-16, 0])
        rotate([0,45,-25])
            translate([0,0,dims[2]/2])
                cylinder(h=dims[2], r=trim_switch_r, center=true);
}

module screw_cylinder(h=30, solid=false) {
    spacing = 2;
    l = (h-separator)/2;
    union() {
        translate([0,0,separator/2+l/2+spacing/2]) difference() {
            cylinder(l, r=holder_r, center=true);
            if (!solid) cylinder(l, r=nut_r, $fn=6, center=true);
        }
        translate([0,0,separator/2+spacing/2]) difference() {
            cylinder(separator/2, r=holder_r, center=true);
            if (!solid) cylinder(separator/2, r=screw_body_r, center=true);
        }
        translate([0,0,-separator/2-spacing/2]) difference() {
            cylinder(separator/2, r=holder_r, center=true);
            if (!solid) cylinder(separator/2, r=screw_body_r, center=true);
        }
        translate([0,0,-separator/2-l/2-spacing/2]) difference() {
            cylinder(l, r=holder_r, center=true);
            if (!solid) cylinder(l, r=screw_head_r, center=true);
        }
    }
}

module screw__(solid=false) {
    addition = (handle_length+dims[1])*sin(side_angle);
    sf = 0.75;

    translate([dims[0]/2+addition-handle_min_width/2,
               -dims[1]/2-handle_length-handle_min_width+4,
               dims[2]/2-sf*handle_min_width/2-3])
        rotate([tilt_angle,0,0])
            translate([0,0,-handle_max_width-7])
                mirror([0,0,1])
                    screw_cylinder(2*handle_max_width, solid=solid);
}

module screw___(solid=false) {
    screw_offset = -3;

    translate([-module_bay[0]/2+screw_offset-holder_r,gimbal[1]/2+holder_r+1,0])
        mirror([0,0,1])
            screw_cylinder(2*(gimbal[2]+module_bay[2]+wall), solid=solid);
}

module screw_(solid=false, debug=false) {
    intersection() {
        union() {
            screw__(solid=solid);
            screw___(solid=solid);
        }
        core_with_bay_and_handles(debug=debug);
    }
}

//screw_cylinder();
module screws(solid=false, debug=false) {
    screw_(solid=solid, debug=debug);
    mirror() screw_(solid=solid, debug=debug);
}

module body(solid = false, w = wall) {
    difference() {
        union() {
            difference() {
                skin(debug=debug,thickness=w, box=box_size) {
                    core_with_bay_and_handles(debug=debug);
                }
                translate([0,0,-dims[2]/2]) rotate([0,180,90])
                    module_bay(true);
                screws(solid=true, debug=debug);
            }
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                    module_bay(solid);
            screws(solid=false, debug=debug);
        }

        if (!solid) cutouts();
        if (!solid) mirror() cutouts();
    }
}

module full_skin() render() {
    if (render_full || render_all) {
        body(w=wall);
    } else {
        import("full_skin.stl");
    }
}

module inner_skin() render() {
    difference() {
        full_skin();
        outer_skin();
    }
}

module outer_skin() render() {
    if (render_outer || render_all) {
        body(w=wall/2);
    } else {
        import("outer_skin.stl");
    }
}

module bottom(box=box_size) {
    union () {
        difference() {
            outer_skin();
            translate([0,0,box/2]) cube(box, center=true);
            translate([0,-gimbal[1]/2,0])
                rotate([tilt_angle,0,0])
                    translate([0,0,box/2])
                        cube(box, center=true);
        }

        difference() {
            full_skin();
            translate([0,0,box/2-wall/2]) cube(box, center=true);
            translate([0,-gimbal[1]/2,0])
                rotate([tilt_angle,0,0])
                    translate([0,0,box/2-wall/2])
                        cube(box, center=true);
        }
    }
}

module cut_in_halves() {
    union() {
        translate([-100,0,gimbal[2]/2]) difference() {
            bottom(box=box_size);
            battery_compartments(solid=true);
        }

        translate([100,0,0]) difference() {
            full_skin();
            bottom(box=box_size);
        }
    }
}

module battery_compartments(solid=false) {
    union() {
        battery_compartment(solid);
        mirror() battery_compartment(solid);
    }
}

batt = [61,21,24];
batt_pos = [-dims[0]/2-2,-66,-16];
batt_rot = [90,26,-101];

module battery_compartment_(solid = false) {
    offset = solid ? wall : 0;
    union() {
        intersection() {
            outer_skin();
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                translate([0,0,offset/2])
                cube([batt[0]+wall,batt[1],batt[2]-offset],center=true);
        }

        intersection() {
            inner_skin();
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                translate([0,0,wall/4+offset/2])
                cube([batt[0],batt[1],batt[2]+wall/2-offset],center=true);
        }

        // cutout to open compartment
        if (solid) {
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                    union() {
                        translate([0,0,wall/2])
                            cube([batt[0],batt[1],batt[2]-wall],center=true);
                        translate([0,0,-batt[2]/2])
                            cube([batt[2]/2,batt[1],2*wall],center=true);
                    }
        }
    }
}

module battery_compartment(solid=false) {
    difference() {
        union() {
            difference() {
                battery_compartment_(solid);

                // cutout block to be able to unlatch
                if (!solid) {
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2+wall])
                            cube([batt[2],batt[1],wall],center=true);
                }
            }

            if (!solid) {
                scale([1.01,1.01,1.01])
                union() {
                    hull() {
                        intersection() {
                            battery_compartment_();
                            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                                rotate(batt_rot)
                                translate([0,0,-batt[2]/2+wall/4])
                                    cube([batt[2],batt[1],wall/2],center=true);
                        }

                        translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                            rotate(batt_rot)
                            translate([0,batt[1]/2-batt[1]/96,-batt[2]/2+wall/4+2*wall])
                                cube([batt[2],batt[1]/48,wall/2],center=true);
                    }

                    hull() {
                        intersection() {
                            battery_compartment_();
                            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                                rotate(batt_rot)
                                translate([0,0,-batt[2]/2+wall/4+2*wall])
                                    cube([batt[2],batt[1],wall/2],center=true);
                        }

                        translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                            rotate(batt_rot)
                            translate([0,batt[1]/2-batt[1]/96,-batt[2]/2+wall/4+2*wall])
                                cube([batt[2],batt[1]/48,wall/2],center=true);

                    }
                }
            }
        }

        if (!solid) {
            difference() {
                intersection() {
                    battery_compartment_();
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2])
                            cube([batt[2],batt[1],wall+2],center=true);
                }

                intersection() {
                    battery_compartment_();
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2])
                            cube([batt[2]/2,batt[1],wall+2],center=true);
                }
            }
        }
    }
}

if (render_outer) {
    outer_skin();
} else if (render_full) {
    full_skin();
} else {
    union() {
        cut_in_halves();
        translate([-100,dims[1]/2+2*module_bay[1],0]) rotate([0,180,90]) module_cover();
        translate([-100,-dims[1]/2-2*module_bay[1],0]) battery_compartments();
    }
}

use <lid.scad>;
use <fillet.scad>;

debug = false;
no_fillet = false;

print = true;

$fa = 12; // 12 for printing
$fs = 2; // 2 for printing
$fn = 0;

render_outer = false;
render_full = false;
render_all = true;

// thickness of the shell
wall = 3;

// handle dimensions
handle_max_width = 35;
handle_min_width = 20;
handle_length = 75;

// used to avoid phantom wall when cutting objects
space = 1;

// dimensions of the 4 switches
switch_r = 4;
switch_offset = [7,7]; // relative to top and bottom corner

// dimensions of the led
led_r = 3;
led_offset = 2; // relative to bottom of the gimbal

// dimensions of the module bay
module_bay = [60,45,18];
cutout = [13,4];
cutout_offset = [3,3];
module_bay_lip = 3;

// gimbal dimensions
gimbal = [58,58,30];
gimbal_r = 25;

// dimensions of 4 gimbal screws per gimbal
gimbal_screw_offset = [5,5];
gimbal_screw_r = 1.5;
gimbal_space = [0,0];

// power switch dimension
switch_cutout = [10,20];

// angle of left and right side of controller
side_angle = 10;

// inclination of the handles
tilt_angle = 25;

// screw dimensions
nut_r = 2.1;
screw_head_r = 2;
screw_body_r = 1;
holder_r = 3.5;
separator = 4;

// trim switches
trim_switch_r = 6.5/2;

dims = [gimbal[0]*2+gimbal_space[0]*2,
        max(gimbal[1]+2*gimbal_space[1], module_bay[1]+2*wall+2*module_bay_lip),
        gimbal[2]];

// needs to be bigger than model
box_size = (max(dims[0],dims[1],dims[2]) + handle_length) * 10;

module module_bay_cutout() {
    cube([15,2*wall,16], center=true);
}

module module_bay(solid=false) {
    translate([0,0,-wall])
    difference() {
        union() {
            translate([0,0,module_bay[2]/2+wall/2])
                cube([module_bay[0]+2*wall, module_bay[1]+2*wall, module_bay[2]+wall], center=true);
            difference() {
                translate([0,0,(module_bay_lip+wall)/2+module_bay[2]])
                    cube([module_bay[0]+2*wall+2*module_bay_lip,
                        module_bay[1]+2*wall+2*module_bay_lip,
                        module_bay_lip+wall], center=true);
                if (!solid)
                    translate([0,0,module_bay[2]/2+wall])
                        cube([module_bay[0]+2*wall,
                              module_bay[1]+2*wall,
                              module_bay[2]+wall], center=true);
            }
        }

        if (!solid) {
            translate([0,0,module_bay[2]+module_bay_lip/2+wall])
                cube([module_bay[0]+2*module_bay_lip,
                      module_bay[1]+2*module_bay_lip,
                      module_bay_lip], center=true);

            translate([0,0,wall+module_bay[2]/2])
                cube([module_bay[0], module_bay[1], module_bay[2]], center=true);

            translate([module_bay[0]/2-cutout[0]-cutout_offset[0],
                       module_bay[1]/2-cutout[1]-cutout_offset[1],
                       -space])
                cube([cutout[0], cutout[1], wall+2*space]);

            translate([0,module_bay[1]/2+wall,wall+8]) module_bay_cutout();
            translate([0,-module_bay[1]/2-wall,wall+8]) module_bay_cutout();
        }
    }
}

//!module_bay();

module core(d = [10,10,10], offset = 0, solid=false) {
    scale_cylinder = [1,0.1,1];

    addition = sin(side_angle)*d[1];

    minkowski() {
        hull() {
            translate([0,-d[1]/2,0]) scale(scale_cylinder)
                cylinder(1,d[0]/2+addition,d[0]/2,center=true);
            translate([0,d[1]/2,0]) scale(scale_cylinder)
                cylinder(1,r=d[0]/2,center=true);
        }
        scale([0.6,0.6,1]) sphere(d[2]/2 - offset);
    }
}

module core_with_bay(solid=false, debug=false) {
    if (!debug && !no_fillet) {
        fillet(r=25,steps=20) {
            core(dims, solid=true);
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                module_bay(solid=true);
        }
    } else {
        union() {
            core(dims, solid=true);
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                module_bay(solid=true);
        }
    }
}

module handle(debug=false) {
    addition = (handle_length+dims[1])*sin(side_angle);
    tilt_vector = tilt_angle*[cos(side_angle),sin(side_angle),0];
    sf = 0.75;
    scale_vector = [1,1,sf];

    hull() {
        difference() {
            core_with_bay(debug=debug);
            translate([-handle_max_width,0,0])
                rotate([0,0,side_angle+5])
                    cube(dims[0],center=true);
            translate([0,11,0])
                rotate([0,0,side_angle+5])
                    cube([2*dims[0],dims[0],dims[0]],center=true);
        }
        scale(scale_vector)
        rotate(tilt_vector)
        translate([dims[0]/2+addition-handle_min_width/2,
                   -dims[1]/2-handle_length,
                   dims[2]/2-sf*handle_min_width/2])
            sphere(handle_min_width);
    }
}

module core_with_bay_and_handles(debug=false) {
    union() {
        core_with_bay(debug=debug);
        handle(debug=debug);
        mirror() handle(debug=debug);
    }
}

module skin(debug=false, thickness=5, box=box_size) {
    if (!debug) intersection() {
        children();
        minkowski() {
            cylinder(thickness,thickness,0,$fn=3,center=true);
            difference() {
                cube(box,center=true);
                children();
            }
        }
    }

    if (debug) children();
}

module cutouts() {

    h = 2*wall+2*space;

    // gimbal cutout
    translate([gimbal[0]/2,0,dims[2]/2])
        cylinder(h, r=gimbal_r, center=true);

    // gimbal screw cutout
    translate([gimbal_screw_offset[0],gimbal[1]/2-gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal[0] - gimbal_screw_offset[0],gimbal[1]/2-gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal_screw_offset[0],-gimbal[1]/2+gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    translate([gimbal[0] - gimbal_screw_offset[0],-gimbal[1]/2+gimbal_screw_offset[1],dims[2]/2])
        cylinder(h, r=gimbal_screw_r, center=true);

    // switches
    translate([-gimbal[0]+switch_offset[0], gimbal[1]/2, dims[2]/2-switch_offset[1]])
        rotate ([90,0,0])
            cylinder(gimbal[1]/2, r=switch_r, center=true);

    translate([-gimbal[0]+switch_offset[0], gimbal[1]/2, -dims[2]/2+switch_offset[1]])
        rotate ([90,0,0])
            cylinder(gimbal[1]/2, r=switch_r, center=true);

     // power switch
     translate([0,dims[1]/2+dims[2]/2,0])
        rotate([0,90,90])
            cube([switch_cutout[0], switch_cutout[1], gimbal[1]/2], center=true);

     // led
     translate([0,-gimbal[1]/2-led_offset-led_r,gimbal[2]/2])
        cylinder(h, r=led_r, center=true);

     // trim switches
     translate([-dims[0]/2+handle_min_width+trim_switch_r-5, -dims[1]/2-16, 0])
        rotate([0,45,-25])
            translate([0,0,dims[2]/2])
                cylinder(h=dims[2], r=trim_switch_r, center=true);
}

module screw_cylinder(h=30, solid=false) {
    spacing = 2;
    l = (h-separator)/2;
    union() {
        translate([0,0,separator/2+l/2+spacing/2]) difference() {
            cylinder(l, r=holder_r, center=true);
            if (!solid) cylinder(l, r=nut_r, $fn=6, center=true);
        }
        translate([0,0,separator/2+spacing/2]) difference() {
            cylinder(separator/2, r=holder_r, center=true);
            if (!solid) cylinder(separator/2, r=screw_body_r, center=true);
        }
        translate([0,0,-separator/2-spacing/2]) difference() {
            cylinder(separator/2, r=holder_r, center=true);
            if (!solid) cylinder(separator/2, r=screw_body_r, center=true);
        }
        translate([0,0,-separator/2-l/2-spacing/2]) difference() {
            cylinder(l, r=holder_r, center=true);
            if (!solid) cylinder(l, r=screw_head_r, center=true);
        }
    }
}

module screw__(solid=false) {
    addition = (handle_length+dims[1])*sin(side_angle);
    sf = 0.75;

    translate([dims[0]/2+addition-handle_min_width/2,
               -dims[1]/2-handle_length-handle_min_width+4,
               dims[2]/2-sf*handle_min_width/2-3])
        rotate([tilt_angle,0,0])
            translate([0,0,-handle_max_width-7])
                mirror([0,0,1])
                    screw_cylinder(2*handle_max_width, solid=solid);
}

module screw___(solid=false) {
    screw_offset = -3;

    translate([-module_bay[0]/2+screw_offset-holder_r,gimbal[1]/2+holder_r+1,0])
        mirror([0,0,1])
            screw_cylinder(2*(gimbal[2]+module_bay[2]+wall), solid=solid);
}

module screw_(solid=false, debug=false) {
    intersection() {
        union() {
            screw__(solid=solid);
            screw___(solid=solid);
        }
        core_with_bay_and_handles(debug=debug);
    }
}

//screw_cylinder();
module screws(solid=false, debug=false) {
    screw_(solid=solid, debug=debug);
    mirror() screw_(solid=solid, debug=debug);
}

module body(solid = false, w = wall) {
    difference() {
        union() {
            difference() {
                skin(debug=debug,thickness=w, box=box_size) {
                    core_with_bay_and_handles(debug=debug);
                }
                translate([0,0,-dims[2]/2]) rotate([0,180,90])
                    module_bay(true);
                screws(solid=true, debug=debug);
            }
            translate([0,0,-dims[2]/2]) rotate([0,180,90])
                    module_bay(solid);
            screws(solid=false, debug=debug);
        }

        if (!solid) cutouts();
        if (!solid) mirror() cutouts();
    }
}

module full_skin() render() {
    if (render_full || render_all) {
        body(w=wall);
    } else {
        import("full_skin.stl");
    }
}

module inner_skin() render() {
    difference() {
        full_skin();
        outer_skin();
    }
}

module outer_skin() render() {
    if (render_outer || render_all) {
        body(w=wall/2);
    } else {
        import("outer_skin.stl");
    }
}

module bottom(box=box_size) {
    union () {
        difference() {
            outer_skin();
            translate([0,0,box/2]) cube(box, center=true);
            translate([0,-gimbal[1]/2,0])
                rotate([tilt_angle,0,0])
                    translate([0,0,box/2])
                        cube(box, center=true);
        }

        difference() {
            full_skin();
            translate([0,0,box/2-wall/2]) cube(box, center=true);
            translate([0,-gimbal[1]/2,0])
                rotate([tilt_angle,0,0])
                    translate([0,0,box/2-wall/2])
                        cube(box, center=true);
        }
    }
}

module cut_in_halves() {
    union() {
        translate([-100,0,gimbal[2]/2]) difference() {
            bottom(box=box_size);
            battery_compartments(solid=true);
        }

        translate([100,0,0]) difference() {
            full_skin();
            bottom(box=box_size);
        }
    }
}

module battery_compartments(solid=false) {
    union() {
        battery_compartment(solid);
        mirror() battery_compartment(solid);
    }
}

batt = [61,21,24];
batt_pos = [-dims[0]/2-2,-66,-16];
batt_rot = [90,26,-101];

module battery_compartment_(solid = false) {
    offset = solid ? wall : 0;
    union() {
        intersection() {
            outer_skin();
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                translate([0,0,offset/2])
                cube([batt[0]+wall,batt[1],batt[2]-offset],center=true);
        }

        intersection() {
            inner_skin();
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                translate([0,0,wall/4+offset/2])
                cube([batt[0],batt[1],batt[2]+wall/2-offset],center=true);
        }

        // cutout to open compartment
        if (solid) {
            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                rotate(batt_rot)
                    union() {
                        translate([0,0,wall/2])
                            cube([batt[0],batt[1],batt[2]-wall],center=true);
                        translate([0,0,-batt[2]/2])
                            cube([batt[2]/2,batt[1],2*wall],center=true);
                    }
        }
    }
}

module battery_compartment(solid=false) {
    difference() {
        union() {
            difference() {
                battery_compartment_(solid);

                // cutout block to be able to unlatch
                if (!solid) {
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2+wall])
                            cube([batt[2],batt[1],wall],center=true);
                }
            }

            if (!solid) {
                scale([1.01,1.01,1.01])
                union() {
                    hull() {
                        intersection() {
                            battery_compartment_();
                            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                                rotate(batt_rot)
                                translate([0,0,-batt[2]/2+wall/4])
                                    cube([batt[2],batt[1],wall/2],center=true);
                        }

                        translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                            rotate(batt_rot)
                            translate([0,batt[1]/2-batt[1]/96,-batt[2]/2+wall/4+2*wall])
                                cube([batt[2],batt[1]/48,wall/2],center=true);
                    }

                    hull() {
                        intersection() {
                            battery_compartment_();
                            translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                                rotate(batt_rot)
                                translate([0,0,-batt[2]/2+wall/4+2*wall])
                                    cube([batt[2],batt[1],wall/2],center=true);
                        }

                        translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                            rotate(batt_rot)
                            translate([0,batt[1]/2-batt[1]/96,-batt[2]/2+wall/4+2*wall])
                                cube([batt[2],batt[1]/48,wall/2],center=true);

                    }
                }
            }
        }

        if (!solid) {
            difference() {
                intersection() {
                    battery_compartment_();
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2])
                            cube([batt[2],batt[1],wall+2],center=true);
                }

                intersection() {
                    battery_compartment_();
                    translate([batt_pos[0],batt_pos[1],batt_pos[2]-batt[2]/2])
                        rotate(batt_rot)
                        translate([0,0,-batt[2]/2+wall/2])
                            cube([batt[2]/2,batt[1],wall+2],center=true);
                }
            }
        }
    }
}

if (render_outer) {
    outer_skin();
} else if (render_full) {
    full_skin();
} else {
    union() {
        cut_in_halves();
        translate([-100,dims[1]/2+2*module_bay[1],0]) rotate([0,180,90]) module_cover();
        translate([-100,-dims[1]/2-2*module_bay[1],0]) battery_compartments();
    }
}