[3d] Add Zero extra lip, extend bottom screw head diameter into chamfer

Also cuts out some of the material on the botom screw bridging area

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

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

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

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

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

body_chamfer_height_t = body_chamfer_height;
body_chamfer_height_b = body_chamfer_height;

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

function inch(x) = x*25.4;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

module led_hole() {
  dia = is_jig ? 3.2 : 3.4;
  cylinder(wall_t+2*e, d1=dia, d2=dia, $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];
  screw_l=[20,25][LCD];

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

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

module locklip_p(l, l_offset=0,
  lip_insert_depth=2.0, // how deep V to insert
  lip_v_off=0.2, // extra height before starting insert
  lip_h_off=0.4, // back connector away from mating area
  lip_w=2.5,  // thickness of attachment beam
  insert_inset=[0,0] // inset the insert inside mating area
  ) {
  translate([l_offset,0,0]) rotate([90,0,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
  // (Note LCD is not centered vertically on PCB, top is 5.15, bottom is 4.55mm)
  translate([1.0, 1.55, 0])
    cube([73.0, 27.3, lcd_mount_t+wall_t-0.8]); // black bezel inset
  translate([5.5, 7.5, 0])
    cube([64.0, 15.4, lcd_mount_t+wall_t+2*e]); // LCD active area
  //translate([4.23, -2.5, 0])
  //  cube([16*2.54, 5, lcd_mount_t+wall_t-0.8]); // pins cutout
}

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

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

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

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

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

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

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

  // button holes
  if (LCD) translate([wall+inch(1.925)+w_off, wall+d_off+inch(1.15), h_b+wall_t-e]) {
    translate([-inch(1.1)/2,0,0]) btn_rnd();  // left
    translate([inch(1.1)/2,0,0]) btn_rnd();   // right
    translate([0,inch(0.9)/2,0]) btn_rnd();   // up
    translate([0,-inch(0.9)/2,0]) btn_rnd();  // 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.825)+0.5,wall+d_off+inch(0.1),0]) {
    screwhole();
    translate([inch(2.0),0,0]) screwhole();
  }
  // keyholes
  //translate([wall+24,wall+d_off+39,-e]) {
  //    translate([0,49,0]) screw_keyhole();
  //    translate([58,49,0]) screw_keyhole();
  //}
}  // END OF DIFFERENCE

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

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

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

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

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

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

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

module hm43_bottom_lips(split) {
  translate([wall, wall, split+wall_t]) {
    // bottom locklip (positive)
    translate([0,d,0]) {
      locklip_p(28, insert_inset=[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, d_off+5.25+9.4/2, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(25.25-5.25-9.4/2-16.7/2);
    if (Pi_Model == "Zero" || Pi_Model == "1A+" || Pi_Model == "3A+")
      translate([0, d_off+40, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(45);
    else if (Pi_Model == "3B/2B/1B+") {
      *translate([-pic_ex, d_off+52, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
      *translate([-pic_ex, d_off+70, 0])
        rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
    }
  }
  // probe side guide lip
  translate([wall+w, wall+d-45, probe_centerline+wall_t])
    rotate([-90]) rotate(90) lip_guide(40);
}

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

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

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

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

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

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

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

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

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

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

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

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

    }
  }
}

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

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

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

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

    }
}

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

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

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

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

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

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

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

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

body_chamfer_height_t = body_chamfer_height;
body_chamfer_height_b = body_chamfer_height;

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

function inch(x) = x*25.4;

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

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

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

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

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

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

module screwhole() {
  translate([0,0,-e]) cylinder(3.5, d=6, $fn=18);
  translate([0,0,3+0.4]) cylinder(h_b-3.5-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(interior) {
  translate([0,33.75+interior*(pic_ex+1.3),2])
    cube_fillet([pic_ex+e, 59.8-interior*(pic_ex*2+1.3+1.5), 20.8],
      vertical=[0, (1-interior)*pic_ex, (1-interior)*pic_ex, 0],
      top=[0,pic_ex,0,0],
      bottom=[0,pic_ex,0,0]);
}

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

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

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

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

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

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

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

module led_hole() {
  dia = is_jig ? 3.2 : 3.4;
  cylinder(wall_t+2*e, d1=dia, d2=dia, $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];
  screw_l=[20,25][LCD];

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

module locklip_p(l, l_offset=0,
  lip_insert_depth=2.0, // how deep V to insert
  lip_v_off=0.2, // extra height before starting insert
  lip_h_off=0.4, // back connector away from mating area
  lip_w=2.5,  // thickness of attachment beam
  insert_inset=[0,0] // inset the insert inside mating area
  ) {
  translate([l_offset,0,0]) rotate([90,0,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
  // (Note LCD is not centered vertically on PCB, top is 5.15, bottom is 4.55mm)
  translate([1.0, 1.55, 0])
    cube([73.0, 27.3, lcd_mount_t+wall_t-0.8]); // black bezel inset
  translate([5.5, 7.5, 0])
    cube([64.0, 15.4, lcd_mount_t+wall_t+2*e]); // LCD active area
  //translate([4.23, -2.5, 0])
  //  cube([16*2.54, 5, lcd_mount_t+wall_t-0.8]); // pins cutout
}

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

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

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

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

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

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

  // button holes
  if (LCD) translate([wall+inch(1.925)+w_off, wall+d_off+inch(1.15), h_b+wall_t-e]) {
    translate([-inch(1.1)/2,0,0]) btn_rnd();  // left
    translate([inch(1.1)/2,0,0]) btn_rnd();   // right
    translate([0,inch(0.9)/2,0]) btn_rnd();   // up
    translate([0,-inch(0.9)/2,0]) btn_rnd();  // 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.825)+0.5,wall+d_off+inch(0.1),0]) {
    screwhole();
    translate([inch(2.0),0,0]) screwhole();
  }
  // keyholes
  //translate([wall+24,wall+d_off+39,-e]) {
  //    translate([0,49,0]) screw_keyhole();
  //    translate([58,49,0]) screw_keyhole();
  //}
}  // END OF DIFFERENCE

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

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

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

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

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

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

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

module hm43_bottom_lips(split) {
  translate([wall, wall, split+wall_t]) {
    // bottom locklip (positive)
    translate([0,d,0]) {
      locklip_p(28, insert_inset=[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, d_off+5.25+9.4/2, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(25.25-5.25-9.4/2-16.7/2);
    *translate([-pic_ex, d_off+52, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
    *translate([-pic_ex, d_off+70, 0])
      rotate([-90]) rotate(90) mirror([0,1,0]) lip_guide(3.5);
  }
  // probe side guide lip
  translate([wall+w, wall+d-45, probe_centerline+wall_t])
    rotate([-90]) rotate(90) lip_guide(40);
}

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

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

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

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

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

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

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

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

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

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

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

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

    }
  }
}

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

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

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

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

    }
}

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

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

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


"Симпатичный" код

"Симпатичный" код


try a square tube at Hamstring for foot triangle. This might be easier to fabricate

// try to draw Jansen linkage leg assembly as it might appear in steel

// these are for configuration "E" as referenced in other drawings
AC=17.5;
Ay =  3.318; Bx = 53.833;
BD = 48.020; BE = 46.977; BH = 40.359;
CD = 59.635; CH = 71.441; DE = 66.437;
EF = 47.467; FG = 75.792;
FH = 40.843; GH = 55.790;

DEleft = (DE*DE + BD*BD - BE*BE)/(2*DE);
FGleft = (FG*FG + FH*FH - GH*GH)/(2*FG);
DEperp = sqrt(BD*BD - DEleft*DEleft);
FGperp = sqrt(FH*FH - FGleft*FGleft);

echo(str("DEleft=",DEleft,"   DEperp=",DEperp));
echo(str("FGleft=",FGleft,"   FGperp=",FGperp));

//r38 = (3/8)*2.54/2;  // radius of 3/8" bolt

a=360*$t;
legAssembly(a,0);
mirror([1,0,0]) legAssembly(180-a,1.3);

//  -------------------------- crank-arm circle reference
%color([1,.5,.3,.4]) translate([0,Ay,-2]) difference() {
  cylinder(r=AC,h=1,$fn=48,center=true);
  cylinder(r=2,h=2,center=true);
}
color([0.3,.3,1,.7]) // axles
for(x=[-1,1]) translate([x*Bx,0,0]) difference() {
   cylinder(r=2.54/2,h=20,$fn=24,center=true);
   cylinder(r=2.54/2-.4,h=22,$fn=19,center=true);
}

%translate([0,-93,0]) cube([200,2,2],center=true); // ground
%color([0,0,1,.2]) translate([0,0,-8])
   cube([114,1.75*2.54,1.75*2.54],center=true);

// -----------------------------------------------------------------

// plot a link shadow, a different way, to check kinematic correctness
module checkLink(xa,ya,xb,yb,ab) {
  *translate([(xa+xb)/2,(ya+yb)/2,-9])
    rotate([0,0,atan2(yb-ya,xb-xa)]) cube([ab,1,1],center=true);
}

function triK1(ax,ay,ca,bx,by,cb) = (cb*cb - ca*ca + ax*ax -bx*bx +ay*ay -by*by)/(2*(ax-bx));

// ===================================================================

module legAssembly(ang=0,linkOffset=0) {

// solve kinematics for given crank angle
xC = AC*cos(ang);
yC = AC*sin(ang)+Ay;
echo("C",xC,yC);

k1=(yC*yC - Bx*Bx + BD*BD - CD*CD + xC*xC)/(2*yC);
k2=(xC+Bx)/yC;
c=k1*k1-BD*BD+Bx*Bx;
b=2*(Bx-k1*k2);
a=k2*k2+1;
sgnD=(yC<0)?1:-1;
xD=(-b+sgnD*sqrt(b*b-4*a*c))/(2*a);
yD=sqrt(BD*BD - pow(xD+Bx,2));
echo("D",xD,yD,"CD err",sqrt(pow(xC-xD,2)+pow(yC-yD,2))-CD);

hipRot = atan2(yD,Bx+xD)-atan2(DEperp,DEleft);
echo("hipRot",hipRot);
xE = cos(hipRot) * (DEleft-DE) - sin(hipRot) * DEperp - Bx;
yE = sin(hipRot) * (DEleft-DE) + cos(hipRot) * DEperp;
/*
ke1=(yD*yD + xD*xD + DE*DE + BE*BE)/(2*yD);
ke2=(xD+Bx)/yD;
ae=2+ke2*ke2;
be=2*(Bx-ke1*ke2);
ce=ke1*ke1 - BE*BE +Bx*Bx;
echo("E abc:",ae,be,ce);
xE=(-be+sqrt(be*be-4*ae*ce))/(2*ae);
yE=sqrt(BE*BE - pow(xE+Bx,2));
*/
echo("E",xE,yE);

k1h=(xC*xC + yC*yC + BH*BH - CH*CH - Bx*Bx)/(2*yC);
k2h=-(xC+Bx)/yC;
ah=k2h*k2h+1;
bh=2*(k1h*k2h + Bx);
ch=k1h*k1h + Bx*Bx - BH*BH;
sgnH=(yC>0)?1:-1;
xH=(-bh+sgnH*sqrt(bh*bh-4*ah*ch))/(2*ah);
yH=-sqrt(BH*BH - pow(xH+Bx,2));
bhRot=atan2(yH,xH+Bx);
echo("H",xH,yH,"rot",bhRot,"CH err",sqrt(pow(xH-xC,2)+pow(yH-yC,2))-CH);

/*
k1f=(FH*FH - EF*EF - yE*yE - yH*yH + xE*xE - xH*xH) / (2*(yE-yH));
k2f=(xH-xE)/(yE-yH);
af=k2f*k2f+1;
bf=2*(k1f*k2f - xH - k2f*yH);
cf=xH*xH + yH*yH - 2*k1f*yH + k1f*k1f - FH*FH;
xF=(-bf-sqrt(bf*bf - 4*af*cf))/(2*af);
yF=k1f+k2f*xF;
*/
k2f = (yE-yH)/(xE-xH);
k1f = triK1(xE,yE,EF,xH,yH,FH);
af=k2f*k2f+1;
bf=2*(k2f*xE - k1f*k2f -yE);
cf=k1f*k1f - 2*k1f*xE + xE*xE + yE*yE - EF*EF;
yF=(-bf-sqrt(bf*bf - 4*af*cf))/(2*af);
xF=k1f-k2f*yF;
echo("F",xF,yF,"EF err",sqrt(pow(xF-xE,2)+pow(yF-yE,2))-EF);
echo("FH err",sqrt(pow(xF-xH,2)+pow(yF-yH,2))-FH);

// draw the hip so that the main "plane", the ED centerline plane, is at z=0
translate([-Bx,0,0]) rotate([0,0,hipRot])
    translate([DEleft-DE,DEperp,0]) hip();
//translate([-Bx,0,2.5]) rotate([0,0,12.5]) translate([-DEleft,DEperp,0]) hip();
//translate([ Bx,0,0]) rotate([0,0,-20]) translate([DE-DEleft,DEperp,0]) mirror([1,0,0])
//  hip();
checkLink(-Bx,0,xD,yD,BD);
checkLink(-Bx,0,xE,yE,BE);
checkLink(xD,yD,xE,yE,DE);

translate([-Bx,0,0]) rotate([0,0,bhRot])
   BHlinks();
checkLink(-Bx,0,xH,yH,BH);

//------------------------------------------------------- CrankLinks
translate([xC,yC,linkOffset-.6]) rotate([0,0,atan2(yD-yC,xD-xC)])
  mirror([0,0,(linkOffset>0)?1:0]) crankLink(CD);
checkLink(xC,yC,xD,yD,CD);

translate([xC,yC,linkOffset+2]) rotate([0,0,atan2(yH-yC,xH-xC)])
  mirror([0,0,(linkOffset>0)?1:0]) crankLink(CH);
checkLink(xC,yC,xH,yH,CH);

translate([xF,yF,0]) rotate([0,0,atan2(yE-yF,xE-xF)]) EFlink();
checkLink(xE,yE,xF,yF,EF);

footRot = atan2(yH-yF,xH-xF)-atan2(FGperp,FGleft);
translate([xF,yF,0]) rotate([0,0,footRot])
   foot();
checkLink(xF,yF,xH,yH,FH);

} // end module legAssembly()

// include includes whole text.
// use just imports modules
use <util.scad>

// -----------------------------------------------------------------

module foot() {
useF698=0;
  hull() {  // FG
    translate([4,0,.2]) cube([4,2.5,2.5],center=true);
    translate([FG-1.3,0,.2]) cylinder(r=1.3,h=2.6,$fn=24,center=true);
  }

  // knee braces/fork
  for (z=[-1.3,7]) rotate([0,0,(z>0)?25*0:0]) difference() {
    hull() {
      // This was elongated as a brace for knee
      //translate([-5,2,1.5*z])scale([3,5,1])cylinder(r=1,h=.4,$fn=64,center=true);
      // wider knee shouldn't need brace
      translate([0,0,z]) cylinder(r=2.5,h=.4,$fn=24,center=true);
      translate([9,0,z])cylinder(r=1,h=.4,center=true);
    }
    #cylinder(r=.4,h=16,$fn=17,center=true);
  }

  // hinge holders at H
  if (useF698) {
    // F698 tubes for H-hinge fork
    %color([0,0,.6,.4])
    translate([FGleft,FGperp,-1]) difference() {
      cylinder(r=2.54/2,h=7,$fn=36);

      cylinder(r=1.9/2,h=22,center=true,$fn=24);
      translate([0,0,3.6])cube([3,3,2.8],center=true);
    }
    translate([FGleft,FGperp,-.87]) {
      rotate([180,0,0]) F698();
      translate([0,0,5.0]) rotate([180,0,0]) F698();
      translate([0,0,1.95]) F698();
      translate([0,0,6.75]) F698();
    }
  } else {
    // use SA8 Heim rod-ends for H fork
    // use some washers/spacers to get heim a few mm inside fork, to allow
    // room for connecting rod to clear fork
    for (z=[-.7+.3,5.6-.3]) translate([FGleft,FGperp,z])
      rotate([0,0,142]) SI8onBolt(); //SA8();
  }

  // vertical brace extension at knee
  translate([3.5,0,4.1]) cube([2.5,2.5,5.3],center=true);
  hull() {  // gusset
    translate([ 5.5,-1,1]) cylinder(r=.2,h=5.8,$fn=8);
    translate([18, 1,0]) cylinder(r=.3,h=1  ,$fn=8);
  }

  // FH segment bracing
  for (z=[-.3,5.1]) hull() {
    translate([   3    ,   1    ,z]) barEnd1();
    translate([FGleft-(useF698?1:-4),
               FGperp-(useF698?1:4),z]) barEnd1();
  }
  hull() {  // dig brace.  Could be gusset?
     translate([3,1,5.7]) barEnd1();
     translate([useF698?12:13+2,11,-.3]) barEnd1();
  }
  hull() {
      translate([FGleft-(useF698?7.2:5-4),FGperp-8,5.3]) barEnd1();
      translate([useF698?14:15.5+2.6,13.5,-.3]) barEnd1();
  }

  // GH braces
  hull() {
      translate([FG-8,0,-.3]) barEnd1();
      translate([FGleft+4,FGperp-3,-.3-1.7]) barEnd1();
  }
  hull() {
      translate([FG-10,0,0]) barEnd1();
      translate([FGleft+4,
                 FGperp-3,5.2+2]) barEnd1();
  }

  hull() {    // perp brace
      //translate([FGleft,FGperp-1,5.2]) barEnd1();
      translate([FGleft+4,FGperp-4,5.2]) barEnd1();
      translate([FGleft,0,0]) barEnd1();
  }
  hull() {    // perp brace
      //translate([FGleft,FGperp-1,-.3]) barEnd1();
      translate([FGleft+4,FGperp-4,-.3]) barEnd1();
      translate([FGleft,0,-.3]) barEnd1();
  }

  // extra truss braces
  hull() {
      translate([46,15,3]) barEnd1();
      translate([35, 0,0]) barEnd1();
  }

  // out-of-plane perp braces
  if (useF698) {
    translate([FGleft+2,FGperp-2,.3]) cylinder(r=.6,h=5);
    translate([FGleft,FGperp-2,.3]) cylinder(r=.6,h=5);
    translate([FGleft-2,FGperp-2,.3]) cylinder(r=.8,h=5,$fn=11);
  } else {
    ////translate([FGleft+1,FGperp-2,.3]) cylinder(r=.6,h=5);
    ////translate([FGleft-1,FGperp-4,.3]) cylinder(r=.8,h=5,$fn=11);
    //translate([FGleft+4,FGperp-4,.3]) cylinder(r=.8,h=5,$fn=11);
    translate([FGleft+4.5,FGperp-3.5,2.4]) rotate([0,0,142])
      cube([.75*2.54,.75*2.54,8],center=true);
  }
}

$fn=12;
module hip() {
  translate([DE/2,0,-.2]) rotate([0,90,0])      // top tube
    cylinder(r=.9,h=DE-4,$fn=17,center=true);

  translate([DE-DEleft,-DEperp,-1.2]) difference() {
     union() {
       cylinder(r=2.54*1.75/2,h=7.2,$fn=36);                // axle

       hull() {  // diag tubes
         translate([-1.6,1.4,1.2]) barEnd1();
         translate([DEleft-DE+2,DEperp,1.2]) barEnd1();
       }
       hull() {
         translate([0,0,1.2]) barEnd1();
         translate([DE-DEleft-1,DEperp,1.2]) barEnd1();
       }

       // extra brace
       translate([0,0,6.2]) rotate([-105,0,0]) cylinder(r=.8,h=20);
     }

     // bore out for 6805 bearing
     cylinder(r=3.7/2+.1,h=22,$fn=18,center=true);
  }

  // use cut-out square tube (or channel) for forks, instead of all welded.
  // easier to drill and maintain aligned holes
  // 14ga roughly 2mm thick
  translate([DE,0,0]) rotate([0,0,180]) channelFork();
  channelFork();

  // extra bracing
  translate([DE/2,-15,0]) rotate([0,90,0]) cylinder(r=.8,h=35,center=true);
}

// cut-out square tube (or channel) for forks
//     easier to drill and maintain aligned holes
module channelFork(od=2.54*1.24,gu=.2,len=3,hd=.82,co=.5)
translate([co,0,0]) difference() {
  cube([od     ,len  ,od     ],center=true);
  cube([od-2*gu,len+1,od-2*gu],center=true);

  // open one side of square tube to make a channel
  translate([-od/2,0,0]) cube([2.1*gu,len+1,od-2*gu-.05],center=true);

  // put hole at x=0,y=0 in part space.
  translate([-co,0,0]) cylinder(r=hd/2,h=od+1,$fn=17,center=true);
}

/*
module eFork() for(z=[-1,1]) difference() {
  hull() {
    translate([0,0,1.4*z]) cylinder(r=2,h=.3,$fn=24,center=true);
    translate([6,0,1.4*z]) cube([1,2,.3],center=true);
  }
  cylinder(r=.4,h=11,$fn=17,center=true);
}
*/

module crankLink(dx=CD) {
  %color([0,0,.6,.4]) difference() {
    cylinder(r=2.54/2,h=.9,$fn=36,center=true);
    cylinder(r=2.54/2-.17,h=2,$fn=24,center=true);
  }
  translate([0,0, .31]) F698(); //cylinder(r=1.4,h=.7,$fn=24,center=true);
  translate([0,0,-.31]) rotate([180,0,0]) F698();

  //translate([dx/2-1,0,0]) cube([dx-3,1.3,1.3],center=true);
  %color([0,.8,0,.6])
  translate([dx/2-1-1,0,0]) rotate([0,90,0])
     cylinder(r=.6,h=dx-3-3,$fn=11,center=true);

  translate([dx,0,0]) SI8onBolt(); //SA8();
}

module EFlink() {
  translate([EF,0,0]) SI8onBolt(); //SA8();
  translate([EF/2-.4-1.5,0,0]) rotate([0,90,0])
      cylinder(r=.8,h=EF-3-3,$fn=13,center=true);

    // ------ tube and knee bearings
    %color([0,0,.6,.4])
    translate([0,0,-.84]) difference() {
       cylinder(r=2.54/2,h=2.54*3-.25,$fn=36);
       cylinder(r=2.54/2-.3,h=22,$fn=36,center=true);
    }
    translate([0,0,6.41]) F698();
    translate([0,0,-.7]) rotate([180,0,0]) F698();

    hull() {  // gusset
      translate([1.3,0,2*2.54/2]) cylinder(r=.2,h=2.54*3,$fn=12,center=true);
      translate([9,0,   0    ]) cylinder(r=.2,h=1,$fn=12);
    }

  //translate([EF,0,0]) cylinder(r=r38,h=5,center=true);
    //                //cylinder(r=r38,h=55,center=true);
  //cylinder(r=1.2,h=22,$fn=36,center=true);
}

// this link drawn with B axle at origin, always.
module BHlinks() {
  difference() {  // main axle rings
    union() {
      // main axle tube
      translate([0,0,2.4]) cylinder(r=2.54*1.75/2,h=13.5,$fn=36,center=true);

      for(z=[-2.4,7.2]) {  // main side rails
        hull() {
          translate([BH-6,0,z]) barEnd1();
          translate([   0,0,z]) barEnd1();
        }
      }

    }

    translate([0,0,2.4]) cube([5,5,7.9],center=true);
    cylinder(r=3.7/2+.1,h=22,$fn=19,center=true);
  }

  // brace near axle
  translate([3.3,0,2.2]) cylinder(r=.8,h=10,$fn=19,center=true);

/* get rid of fork brace.  Comes close to hitting CH cranklink.
   let cross members hold forks apart
  //    fork brace
  translate([BH-7,0,2.43]) cube([2.5,2.5,7.6],center=true);

  hull() {
    translate([   4,.7,-1.6]) barEnd1();
    translate([BH-9,.7, 6.2]) barEnd1();
  }
  hull() {
    translate([   4,-.7, 6.2]) barEnd1();
    translate([BH-9,-.7,-1.6]) barEnd1();
  }
*/

  BHcrosses();

  // H forks
  for(z=[-1.7,6.2]) translate([BH,0,z]) difference() {
     hull() {
       cylinder(r=2,h=.33,$fn=36);
       translate([-8,0,0]) cylinder(r=1,h=.33);
     }
     #cylinder(r=.4,h=2+6,center=true);
  }

  // to check dimensions
  //%translate([BH/2,0,2.4]) cube([BH,1,7.8],center=true);
}

// diag braces.  Try to leave fork area open to allow clearance for CH link
module BHcrosses() {
h1=BH-4;  // node closest to H
b1=4;
spread=.7;
lo=-1.6;
hi=6.5;
rBar=.8;
  hull() {
    translate([b1, spread,lo]) barEnd1();
    translate([h1, spread,hi]) barEnd1();
  }
  hull() {
    translate([b1,-spread,hi]) barEnd1();
    translate([h1,-spread,lo]) barEnd1();
  }
}

/*
// diag braces.  Try to leave fork area open to allow clearance for CH link
module BHcrosses2() {
h1=BH-4;  // node closest to H
h2=h1-19;
b2=h2-4;
b1=4;
spread=.7;
lo=-1.6;
hi=6.5;
rBar=.8;
  hull() {
    translate([h2, spread,lo]) barEnd1();
    translate([h1, spread,hi]) barEnd1();
  }
  hull() {
    translate([h2,-spread,hi]) barEnd1();
    translate([h1,-spread,lo]) barEnd1();
  }
  hull() {
    translate([b1,-spread,lo]) barEnd1();
    translate([b2,-spread,hi]) barEnd1();
  }
  hull() {
    translate([b2,spread,lo]) barEnd1();
    translate([b1,spread,hi]) barEnd1();
  }
}
*/

module barEnd1() sphere(.8,$fn=12);

// try to draw Jansen linkage leg assembly as it might appear in steel

// these are for configuration "E" as referenced in other drawings
AC=17.5;
Ay =  3.318; Bx = 53.833;
BD = 48.020; BE = 46.977; BH = 40.359;
CD = 59.635; CH = 71.441; DE = 66.437;
EF = 47.467; FG = 75.792;
FH = 40.843; GH = 55.790;

DEleft = (DE*DE + BD*BD - BE*BE)/(2*DE);
FGleft = (FG*FG + FH*FH - GH*GH)/(2*FG);
DEperp = sqrt(BD*BD - DEleft*DEleft);
FGperp = sqrt(FH*FH - FGleft*FGleft);

echo(str("DEleft=",DEleft,"   DEperp=",DEperp));
echo(str("FGleft=",FGleft,"   FGperp=",FGperp));

//r38 = (3/8)*2.54/2;  // radius of 3/8" bolt

a=360*$t;
legAssembly(a,0);
mirror([1,0,0]) legAssembly(180-a,1.3);

//  -------------------------- crank-arm circle reference
%color([1,.5,.3,.4]) translate([0,Ay,-2]) difference() {
  cylinder(r=AC,h=1,$fn=48,center=true);
  cylinder(r=2,h=2,center=true);
}
color([0.3,.3,1,.7]) // axles
for(x=[-1,1]) translate([x*Bx,0,0]) difference() {
   cylinder(r=2.54/2,h=20,$fn=24,center=true);
   cylinder(r=2.54/2-.4,h=22,$fn=19,center=true);
}

%translate([0,-93,0]) cube([200,2,2],center=true); // ground
%color([0,0,1,.2]) translate([0,0,-8])
   cube([114,1.75*2.54,1.75*2.54],center=true);

// -----------------------------------------------------------------

// plot a link shadow, a different way, to check kinematic correctness
module checkLink(xa,ya,xb,yb,ab) {
  *translate([(xa+xb)/2,(ya+yb)/2,-9])
    rotate([0,0,atan2(yb-ya,xb-xa)]) cube([ab,1,1],center=true);
}

function triK1(ax,ay,ca,bx,by,cb) = (cb*cb - ca*ca + ax*ax -bx*bx +ay*ay -by*by)/(2*(ax-bx));

// ===================================================================

module legAssembly(ang=0,linkOffset=0) {

// solve kinematics for given crank angle
xC = AC*cos(ang);
yC = AC*sin(ang)+Ay;
echo("C",xC,yC);

k1=(yC*yC - Bx*Bx + BD*BD - CD*CD + xC*xC)/(2*yC);
k2=(xC+Bx)/yC;
c=k1*k1-BD*BD+Bx*Bx;
b=2*(Bx-k1*k2);
a=k2*k2+1;
sgnD=(yC<0)?1:-1;
xD=(-b+sgnD*sqrt(b*b-4*a*c))/(2*a);
yD=sqrt(BD*BD - pow(xD+Bx,2));
echo("D",xD,yD,"CD err",sqrt(pow(xC-xD,2)+pow(yC-yD,2))-CD);

hipRot = atan2(yD,Bx+xD)-atan2(DEperp,DEleft);
echo("hipRot",hipRot);
xE = cos(hipRot) * (DEleft-DE) - sin(hipRot) * DEperp - Bx;
yE = sin(hipRot) * (DEleft-DE) + cos(hipRot) * DEperp;
/*
ke1=(yD*yD + xD*xD + DE*DE + BE*BE)/(2*yD);
ke2=(xD+Bx)/yD;
ae=2+ke2*ke2;
be=2*(Bx-ke1*ke2);
ce=ke1*ke1 - BE*BE +Bx*Bx;
echo("E abc:",ae,be,ce);
xE=(-be+sqrt(be*be-4*ae*ce))/(2*ae);
yE=sqrt(BE*BE - pow(xE+Bx,2));
*/
echo("E",xE,yE);

k1h=(xC*xC + yC*yC + BH*BH - CH*CH - Bx*Bx)/(2*yC);
k2h=-(xC+Bx)/yC;
ah=k2h*k2h+1;
bh=2*(k1h*k2h + Bx);
ch=k1h*k1h + Bx*Bx - BH*BH;
sgnH=(yC>0)?1:-1;
xH=(-bh+sgnH*sqrt(bh*bh-4*ah*ch))/(2*ah);
yH=-sqrt(BH*BH - pow(xH+Bx,2));
bhRot=atan2(yH,xH+Bx);
echo("H",xH,yH,"rot",bhRot,"CH err",sqrt(pow(xH-xC,2)+pow(yH-yC,2))-CH);

/*
k1f=(FH*FH - EF*EF - yE*yE - yH*yH + xE*xE - xH*xH) / (2*(yE-yH));
k2f=(xH-xE)/(yE-yH);
af=k2f*k2f+1;
bf=2*(k1f*k2f - xH - k2f*yH);
cf=xH*xH + yH*yH - 2*k1f*yH + k1f*k1f - FH*FH;
xF=(-bf-sqrt(bf*bf - 4*af*cf))/(2*af);
yF=k1f+k2f*xF;
*/
k2f = (yE-yH)/(xE-xH);
k1f = triK1(xE,yE,EF,xH,yH,FH);
af=k2f*k2f+1;
bf=2*(k2f*xE - k1f*k2f -yE);
cf=k1f*k1f - 2*k1f*xE + xE*xE + yE*yE - EF*EF;
yF=(-bf-sqrt(bf*bf - 4*af*cf))/(2*af);
xF=k1f-k2f*yF;
echo("F",xF,yF,"EF err",sqrt(pow(xF-xE,2)+pow(yF-yE,2))-EF);
echo("FH err",sqrt(pow(xF-xH,2)+pow(yF-yH,2))-FH);

// draw the hip so that the main "plane", the ED centerline plane, is at z=0
translate([-Bx,0,0]) rotate([0,0,hipRot])
    translate([DEleft-DE,DEperp,0]) hip();
//translate([-Bx,0,2.5]) rotate([0,0,12.5]) translate([-DEleft,DEperp,0]) hip();
//translate([ Bx,0,0]) rotate([0,0,-20]) translate([DE-DEleft,DEperp,0]) mirror([1,0,0])
//  hip();
checkLink(-Bx,0,xD,yD,BD);
checkLink(-Bx,0,xE,yE,BE);
checkLink(xD,yD,xE,yE,DE);

translate([-Bx,0,0]) rotate([0,0,bhRot])
   BHlinks();
checkLink(-Bx,0,xH,yH,BH);

//------------------------------------------------------- CrankLinks
translate([xC,yC,linkOffset-.6]) rotate([0,0,atan2(yD-yC,xD-xC)])
  mirror([0,0,(linkOffset>0)?1:0]) crankLink(CD);
checkLink(xC,yC,xD,yD,CD);

translate([xC,yC,linkOffset+2]) rotate([0,0,atan2(yH-yC,xH-xC)])
  mirror([0,0,(linkOffset>0)?1:0]) crankLink(CH);
checkLink(xC,yC,xH,yH,CH);

translate([xF,yF,0]) rotate([0,0,atan2(yE-yF,xE-xF)]) EFlink();
checkLink(xE,yE,xF,yF,EF);

footRot = atan2(yH-yF,xH-xF)-atan2(FGperp,FGleft);
translate([xF,yF,0]) rotate([0,0,footRot])
   foot();
checkLink(xF,yF,xH,yH,FH);

} // end module legAssembly()

// include includes whole text.
// use just imports modules
use <util.scad>

// -----------------------------------------------------------------

module foot() {
useF698=0;
  hull() {  // FG
    translate([4,0,.2]) cube([4,2.5,2.5],center=true);
    translate([FG-1.3,0,.2]) cylinder(r=1.3,h=2.6,$fn=24,center=true);
  }

  // knee braces/fork
  for (z=[-1.3,7]) rotate([0,0,(z>0)?25*0:0]) difference() {
    hull() {
      // This was elongated as a brace for knee
      //translate([-5,2,1.5*z])scale([3,5,1])cylinder(r=1,h=.4,$fn=64,center=true);
      // wider knee shouldn't need brace
      translate([0,0,z]) cylinder(r=2.5,h=.4,$fn=24,center=true);
      translate([9,0,z])cylinder(r=1,h=.4,center=true);
    }
    #cylinder(r=.4,h=16,$fn=17,center=true);
  }

  // hinge holders at H
  if (useF698) {
    // F698 tubes for H-hinge fork
    %color([0,0,.6,.4])
    translate([FGleft,FGperp,-1]) difference() {
      cylinder(r=2.54/2,h=7,$fn=36);

      cylinder(r=1.9/2,h=22,center=true,$fn=24);
      translate([0,0,3.6])cube([3,3,2.8],center=true);
    }
    translate([FGleft,FGperp,-.87]) {
      rotate([180,0,0]) F698();
      translate([0,0,5.0]) rotate([180,0,0]) F698();
      translate([0,0,1.95]) F698();
      translate([0,0,6.75]) F698();
    }
  } else {
    // use SA8 Heim rod-ends for H fork
    // use some washers/spacers to get heim a few mm inside fork, to allow
    // room for connecting rod to clear fork
    for (z=[-.7+.3,5.6-.3]) translate([FGleft,FGperp,z])
      rotate([0,0,142]) SI8onBolt(); //SA8();
  }

  // vertical brace extension at knee
  translate([3.5,0,4.1]) cube([2.5,2.5,5.3],center=true);
  hull() {  // gusset
    translate([ 5.5,-1,1]) cylinder(r=.2,h=5.8,$fn=8);
    translate([18, 1,0]) cylinder(r=.3,h=1  ,$fn=8);
  }

  // FH segment bracing
  for (z=[-.3,5.1]) hull() {
    translate([   3    ,   1    ,z]) barEnd1();
    translate([FGleft-(useF698?1:-4),
               FGperp-(useF698?1:4),z]) barEnd1();
  }
  hull() {  // dig brace.  Could be gusset?
     translate([3,1,5.7]) barEnd1();
     translate([useF698?12:13+2,11,-.3]) barEnd1();
  }
  hull() {
      translate([FGleft-(useF698?7.2:5-4),FGperp-8,5.3]) barEnd1();
      translate([useF698?14:15.5+2.6,13.5,-.3]) barEnd1();
  }

  // GH braces
  hull() {
      translate([FG-8,0,-.3]) barEnd1();
      translate([FGleft+5,FGperp-4,-.3]) barEnd1();
  }
  hull() {
      translate([FG-10,0,0]) barEnd1();
      translate([FGleft+5,
                 FGperp-4,5.2]) barEnd1();
  }

  hull() {    // perp brace
      //translate([FGleft,FGperp-1,5.2]) barEnd1();
      translate([FGleft+4,FGperp-4,5.2]) barEnd1();
      translate([FGleft,0,0]) barEnd1();
  }
  hull() {    // perp brace
      //translate([FGleft,FGperp-1,-.3]) barEnd1();
      translate([FGleft+4,FGperp-4,-.3]) barEnd1();
      translate([FGleft,0,-.3]) barEnd1();
  }

  // extra truss braces
  hull() {
      translate([46,15,3]) barEnd1();
      translate([35, 0,0]) barEnd1();
  }

  // out-of-plane perp braces
  if (useF698) {
    translate([FGleft+2,FGperp-2,.3]) cylinder(r=.6,h=5);
    translate([FGleft,FGperp-2,.3]) cylinder(r=.6,h=5);
    translate([FGleft-2,FGperp-2,.3]) cylinder(r=.8,h=5,$fn=11);
  } else {
    //translate([FGleft+1,FGperp-2,.3]) cylinder(r=.6,h=5);
    //translate([FGleft-1,FGperp-4,.3]) cylinder(r=.8,h=5,$fn=11);
    translate([FGleft+4,FGperp-4,.3]) cylinder(r=.8,h=5,$fn=11);
  }
}

$fn=12;
module hip() {
  translate([DE/2,0,-.2]) rotate([0,90,0])      // top tube
    cylinder(r=.9,h=DE-4,$fn=17,center=true);

  translate([DE-DEleft,-DEperp,-1.2]) difference() {
     union() {
       cylinder(r=2.54*1.75/2,h=7.2,$fn=36);                // axle

       hull() {  // diag tubes
         translate([-1.6,1.4,1.2]) barEnd1();
         translate([DEleft-DE+2,DEperp,1.2]) barEnd1();
       }
       hull() {
         translate([0,0,1.2]) barEnd1();
         translate([DE-DEleft-1,DEperp,1.2]) barEnd1();
       }

       // extra brace
       translate([0,0,6.2]) rotate([-105,0,0]) cylinder(r=.8,h=20);
     }

     // bore out for 6805 bearing
     cylinder(r=3.7/2+.1,h=22,$fn=18,center=true);
  }

  // use cut-out square tube (or channel) for forks, instead of all welded.
  // easier to drill and maintain aligned holes
  // 14ga roughly 2mm thick
  translate([DE,0,0]) rotate([0,0,180]) channelFork();
  channelFork();

  // extra bracing
  translate([DE/2,-15,0]) rotate([0,90,0]) cylinder(r=.8,h=35,center=true);
}

// cut-out square tube (or channel) for forks
//     easier to drill and maintain aligned holes
module channelFork(od=2.54*1.24,gu=.2,len=3,hd=.82,co=.5)
translate([co,0,0]) difference() {
  cube([od     ,len  ,od     ],center=true);
  cube([od-2*gu,len+1,od-2*gu],center=true);

  // open one side of square tube to make a channel
  translate([-od/2,0,0]) cube([2.1*gu,len+1,od-2*gu-.05],center=true);

  // put hole at x=0,y=0 in part space.
  translate([-co,0,0]) cylinder(r=hd/2,h=od+1,$fn=17,center=true);
}

/*
module eFork() for(z=[-1,1]) difference() {
  hull() {
    translate([0,0,1.4*z]) cylinder(r=2,h=.3,$fn=24,center=true);
    translate([6,0,1.4*z]) cube([1,2,.3],center=true);
  }
  cylinder(r=.4,h=11,$fn=17,center=true);
}
*/

module crankLink(dx=CD) {
  %color([0,0,.6,.4]) difference() {
    cylinder(r=2.54/2,h=.9,$fn=36,center=true);
    cylinder(r=2.54/2-.17,h=2,$fn=24,center=true);
  }
  translate([0,0, .31]) F698(); //cylinder(r=1.4,h=.7,$fn=24,center=true);
  translate([0,0,-.31]) rotate([180,0,0]) F698();

  //translate([dx/2-1,0,0]) cube([dx-3,1.3,1.3],center=true);
  %color([0,.8,0,.6])
  translate([dx/2-1-1,0,0]) rotate([0,90,0])
     cylinder(r=.6,h=dx-3-3,$fn=11,center=true);

  translate([dx,0,0]) SI8onBolt(); //SA8();
}

module EFlink() {
  translate([EF,0,0]) SI8onBolt(); //SA8();
  translate([EF/2-.4-1.5,0,0]) rotate([0,90,0])
      cylinder(r=.8,h=EF-3-3,$fn=13,center=true);

    // ------ tube and knee bearings
    %color([0,0,.6,.4])
    translate([0,0,-.84]) difference() {
       cylinder(r=2.54/2,h=2.54*3-.25,$fn=36);
       cylinder(r=2.54/2-.3,h=22,$fn=36,center=true);
    }
    translate([0,0,6.41]) F698();
    translate([0,0,-.7]) rotate([180,0,0]) F698();

    hull() {  // gusset
      translate([1.3,0,2*2.54/2]) cylinder(r=.2,h=2.54*3,$fn=12,center=true);
      translate([9,0,   0    ]) cylinder(r=.2,h=1,$fn=12);
    }

  //translate([EF,0,0]) cylinder(r=r38,h=5,center=true);
    //                //cylinder(r=r38,h=55,center=true);
  //cylinder(r=1.2,h=22,$fn=36,center=true);
}

// this link drawn with B axle at origin, always.
module BHlinks() {
  difference() {  // main axle rings
    union() {
      // main axle tube
      translate([0,0,2.4]) cylinder(r=2.54*1.75/2,h=13.5,$fn=36,center=true);

      for(z=[-2.4,7.2]) {  // main side rails
        hull() {
          translate([BH-6,0,z]) barEnd1();
          translate([   0,0,z]) barEnd1();
        }
      }

    }

    translate([0,0,2.4]) cube([5,5,7.9],center=true);
    cylinder(r=3.7/2+.1,h=22,$fn=19,center=true);
  }

  // brace near axle
  translate([3.3,0,2.2]) cylinder(r=.8,h=10,$fn=19,center=true);

/* get rid of fork brace.  Comes close to hitting CH cranklink.
   let cross members hold forks apart
  //    fork brace
  translate([BH-7,0,2.43]) cube([2.5,2.5,7.6],center=true);

  hull() {
    translate([   4,.7,-1.6]) barEnd1();
    translate([BH-9,.7, 6.2]) barEnd1();
  }
  hull() {
    translate([   4,-.7, 6.2]) barEnd1();
    translate([BH-9,-.7,-1.6]) barEnd1();
  }
*/

  BHcrosses();

  // H forks
  for(z=[-1.7,6.2]) translate([BH,0,z]) difference() {
     hull() {
       cylinder(r=2,h=.33,$fn=36);
       translate([-8,0,0]) cylinder(r=1,h=.33);
     }
     #cylinder(r=.4,h=2+6,center=true);
  }

  // to check dimensions
  //%translate([BH/2,0,2.4]) cube([BH,1,7.8],center=true);
}

// diag braces.  Try to leave fork area open to allow clearance for CH link
module BHcrosses() {
h1=BH-4;  // node closest to H
b1=4;
spread=.7;
lo=-1.6;
hi=6.5;
rBar=.8;
  hull() {
    translate([b1, spread,lo]) barEnd1();
    translate([h1, spread,hi]) barEnd1();
  }
  hull() {
    translate([b1,-spread,hi]) barEnd1();
    translate([h1,-spread,lo]) barEnd1();
  }
}

/*
// diag braces.  Try to leave fork area open to allow clearance for CH link
module BHcrosses2() {
h1=BH-4;  // node closest to H
h2=h1-19;
b2=h2-4;
b1=4;
spread=.7;
lo=-1.6;
hi=6.5;
rBar=.8;
  hull() {
    translate([h2, spread,lo]) barEnd1();
    translate([h1, spread,hi]) barEnd1();
  }
  hull() {
    translate([h2,-spread,hi]) barEnd1();
    translate([h1,-spread,lo]) barEnd1();
  }
  hull() {
    translate([b1,-spread,lo]) barEnd1();
    translate([b2,-spread,hi]) barEnd1();
  }
  hull() {
    translate([b2,spread,lo]) barEnd1();
    translate([b1,spread,hi]) barEnd1();
  }
}
*/

module barEnd1() sphere(.8,$fn=12);


added option to print names on each holder part

include <Write.scad>

/*
 * configuration
 */
brushes_count = 2;
//$fn=200;

/*
 * modules
 */

module lower_tub()
{
  translate([0, (20+4)/2, 0])
    difference()
    {
      cylinder(h=10+3, r=(20+3)/2);
      translate([0,0,3])
        cylinder(h=10+3, r2=20/2, r1=16/2);
    }
}

module upper_ring()
{
  translate([0, (24+5)/2, 0])
    difference()
    {
      cylinder(h=10, r=(24+5)/2);
      cylinder(h=10, r=(24+0)/2);
    }
}

module back_wall()
{
  // lower part
  translate([-40/2, 0, 0])
    cube([40, 3, 20]);
  // upper part
  translate([-40/2, 0, 100-10])
    cube([40, 3, 20]);
  // connector
  translate([-10/2, 0, 0])
    cube([10, 3, 100]);
}

module brush_holder()
{
  lower_tub();
  translate([0,0,100-5])
    upper_ring();
  back_wall();
  // lower mounting
  difference()
  {
    translate([-15/2, 3, 0])
      cube([15, 4, 10+3]);
    translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
      hull()
        lower_tub();
  }
  // upper mounting
  translate([0, 0, 100-5])
  {
    difference()
    {
      translate([-25/2, 3, 0])
        cube([25, 10, 10]);
      translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
        hull()
          upper_ring();
    }
  }
}

/*
 * main program
 */
name = ["name1", "name2"];

rotate([90, 0, 0])
{
  for(i = [0:brushes_count-1])
  {
    translate([i*30,4,55])
      rotate([-90, 90, 0])
        scale([2,1,1])
          write(name[i], t=2, h=8, center=true);
    translate(i * [30, 0, 0])
      brush_holder();
  }
}

/*
 * configuration
 */
brushes_count = 2;
//$fn=200;

/*
 * modules
 */

module lower_tub()
{
  translate([0, (20+4)/2, 0])
    difference()
    {
      cylinder(h=10+3, r=(20+3)/2);
      translate([0,0,3])
        cylinder(h=10+3, r2=20/2, r1=16/2);
    }
}

module upper_ring()
{
  translate([0, (24+5)/2, 0])
    difference()
    {
      cylinder(h=10, r=(24+5)/2);
      cylinder(h=10, r=(24+0)/2);
    }
}

module back_wall()
{
  // lower part
  translate([-40/2, 0, 0])
    cube([40, 3, 20]);
  // upper part
  translate([-40/2, 0, 100-10])
    cube([40, 3, 20]);
  // connector
  translate([-10/2, 0, 0])
    cube([10, 3, 100]);
}

module brush_holder()
{
  lower_tub();
  translate([0,0,100-5])
    upper_ring();
  back_wall();
  // lower mounting
  difference()
  {
    translate([-15/2, 3, 0])
      cube([15, 4, 10+3]);
    translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
      hull()
        lower_tub();
  }
  // upper mounting
  translate([0, 0, 100-5])
  {
    difference()
    {
      translate([-25/2, 3, 0])
        cube([25, 10, 10]);
      translate([0, 0.1, 0]) // needed to make model simple (aka: manifold)
        hull()
          upper_ring();
    }
  }
}

/*
 * main program
 */

rotate([90, 0, 0])
{
  for(offset = [0:brushes_count-1])
  {
    translate(offset * [30, 0, 0])
      brush_holder();
  }
}


bearing: add assert

bearing: add assert

include <system.scad>
include <units.scad>
include <materials.scad>

use <transforms.scad>;
use <shapes.scad>
include <bearing_data.scad>
include <misc.scad>

module bearing(bearing_type, extra_h=0, override_h=U, orient=Z, align=N)
{
    assert(bearing_type != U);
    h = fallback(override_h, bearing_type[2]) + extra_h;
    size_align(size=[bearing_type[1],bearing_type[1],h], align=align ,orient=orient)
    material(Mat_Aluminium)
    difference()
    {
        // outer
        cylindera(h=h, d=bearing_type[1], align=N);
        // inner
        cylindera(h=h+.1, d=bearing_type[0], align=N);
        // clips
        if(len(bearing_type) > 3)
        {
            clip_depth=.5;
            for(z=[-1,1])
            translate([0,0,z*bearing_type[3]/2])
            difference()
            {
                cylindera(h=1*mm, d=bearing_type[2]+1, align=-z*Z);
                cylindera(h=1*mm+.1, d=bearing_type[4]-clip_depth, align=-z*Z);
            }

        }
    }
}

module bearing_mount_holes(bearing_type, extra_h=0, override_h=U, ziptie_type=[2*mm, 3*mm], ziptie_bearing_distance=3*mm, tolerance=1.01, align=N, orient=Z, ziptie_dist=U, with_zips=true)
{
    ziptie_thickness = ziptie_type[0];
    ziptie_width = ziptie_type[1]+0.6*mm;

    ziptie_dist_ = fallback(ziptie_dist, bearing_type[3]/2);

    h = fallback(override_h, bearing_type[2]) + extra_h;
    size_align(size=[bearing_type[1],bearing_type[1],h], align=align ,orient=orient)
    {
        // Main bearing cut
        cylindera(h=bearing_type[2]*tolerance, d=bearing_type[1]*tolerance, orient=Z);

        if(with_zips)
        {
            for(z=[-1,1])
                translate([0,0,z*ziptie_dist_ - z*1/2])
                    hollow_cylinder(
                            d=bearing_type[1]+ziptie_bearing_distance+ziptie_thickness,
                            thickness = ziptie_thickness*2,
                            h = ziptie_width,
                            taper=false,
                            orient=Z,
                            align=N
                            );
        }

        // for linear rod
        cylindera(d=bearing_type[0]+2*mm, h=100, orient=Z);

        if($show_vit)
        {
            %bearing(bearing_type=bearing_type);

            material(Mat_Ziptie)
            for(z=[-1,1])
            translate([0,0,z*ziptie_dist_])
            {
                %hollow_cylinder(
                        d=bearing_type[1]+ziptie_bearing_distance+ziptie_thickness,
                        thickness = ziptie_thickness,
                        h = ziptie_width,
                        taper=false,
                        orient=Z,
                        align=-z*Z
                        );
            }
        }
    }
}

if(false)
{
    b=bearing_igus_rj4jp_01_12;
    bearing_mount_holes(b, orient=Z);
    translate([30,0,0])
    bearing(b, orient=Z);
}

include <system.scad>
include <units.scad>
include <materials.scad>

use <transforms.scad>;
use <shapes.scad>
include <bearing_data.scad>
include <misc.scad>

module bearing(bearing_type, extra_h=0, override_h=U, orient=Z, align=N)
{
    h = fallback(override_h, bearing_type[2]) + extra_h;
    size_align(size=[bearing_type[1],bearing_type[1],h], align=align ,orient=orient)
    material(Mat_Aluminium)
    difference()
    {
        // outer
        cylindera(h=h, d=bearing_type[1], align=N);
        // inner
        cylindera(h=h+.1, d=bearing_type[0], align=N);
        // clips
        if(len(bearing_type) > 3)
        {
            clip_depth=.5;
            for(z=[-1,1])
            translate([0,0,z*bearing_type[3]/2])
            difference()
            {
                cylindera(h=1*mm, d=bearing_type[2]+1, align=-z*Z);
                cylindera(h=1*mm+.1, d=bearing_type[4]-clip_depth, align=-z*Z);
            }

        }
    }
}

module bearing_mount_holes(bearing_type, extra_h=0, override_h=U, ziptie_type=[2*mm, 3*mm], ziptie_bearing_distance=3*mm, tolerance=1.01, align=N, orient=Z, ziptie_dist=U, with_zips=true)
{
    ziptie_thickness = ziptie_type[0];
    ziptie_width = ziptie_type[1]+0.6*mm;

    ziptie_dist_ = fallback(ziptie_dist, bearing_type[3]/2);

    h = fallback(override_h, bearing_type[2]) + extra_h;
    size_align(size=[bearing_type[1],bearing_type[1],h], align=align ,orient=orient)
    {
        // Main bearing cut
        cylindera(h=bearing_type[2]*tolerance, d=bearing_type[1]*tolerance, orient=Z);

        if(with_zips)
        {
            for(z=[-1,1])
                translate([0,0,z*ziptie_dist_ - z*1/2])
                    hollow_cylinder(
                            d=bearing_type[1]+ziptie_bearing_distance+ziptie_thickness,
                            thickness = ziptie_thickness*2,
                            h = ziptie_width,
                            taper=false,
                            orient=Z,
                            align=N
                            );
        }

        // for linear rod
        cylindera(d=bearing_type[0]+2*mm, h=100, orient=Z);

        if($show_vit)
        {
            %bearing(bearing_type=bearing_type);

            material(Mat_Ziptie)
            for(z=[-1,1])
            translate([0,0,z*ziptie_dist_])
            {
                %hollow_cylinder(
                        d=bearing_type[1]+ziptie_bearing_distance+ziptie_thickness,
                        thickness = ziptie_thickness,
                        h = ziptie_width,
                        taper=false,
                        orient=Z,
                        align=-z*Z
                        );
            }
        }
    }
}

if(false)
{
    b=bearing_igus_rj4jp_01_12;
    bearing_mount_holes(b, orient=Z);
    translate([30,0,0])
    bearing(b, orient=Z);
}