$fn=32;

delta      = 0.01;

linewidth  = 0.4;
layerdepth = 0.2;

overlap_height = 0.5; // avoid gap between blocks that lets light through
                      // by creating a slight tapered edge between halves

testing_pins = true;
printgap   = 3; // separate two parts

// test object is centered on [0,0,0]
// We need to know dimensions because OpenSCAD doesn't let you determine them.
objdimens = [24, 25, 10];
objwidth  = objdimens.x;  // absolute left to right
objdepth  = objdimens.y;  // absolute front to back
objheight = objdimens.z;  // absolute top to bottom
objsplit  = 2;   // absolute z level of split of object centered on 0,0,0

module tapered_cube(taper, length, width, height) {
  // used to build '+'-shaped pins
  hull() {
    translate([taper/2,taper/2,delta/2])
      cube([length-taper, width-taper, delta]);
    translate([0,0,height-delta/2])
      cube([length, width, delta]);
    // There are many tricks that can be used to build better pins
    // - watch these video for some ideas:
    //    https://www.youtube.com/watch?v=vsHpiHhB3RU   Ultimate Guide to Connecting 3D Printed Parts | Pins, Fins, Slots, & Snaps
    //    https://www.youtube.com/watch?v=uMA-Wt-z_BU   Design Unbreakable Pins with Perfect Tolerances
    //    https://www.youtube.com/watch?v=djm5tCFn9S0   Connect 3D Printed Parts | Design for Mass Production 3D Printing
    //    https://www.youtube.com/watch?v=Bd7Yyn61XWQ   Design Better Holes | Improve Tolerances | Reduce Sagging | Design for Mass Production 3D Printing
    //    https://www.youtube.com/watch?v=UfJIMn4nvsA   Holes That Won't Break | Design for 3D Printing
    //    https://www.youtube.com/watch?v=WfP-ZOnlFPM   Rods | Design for Mass Production 3D Printing
    //    https://www.youtube.com/watch?v=Lq-SoGgKOcQ   3x Part Strength Without Slicer Settings | Design for Mass Production 3D Printing
    //    https://www.youtube.com/watch?v=Nsv3YSTDYmA   Stop Gluing Your Prints the Old Way | Design for Mass Production 3D Printing
    //    https://www.youtube.com/watch?v=RTQjvYENR7w   Joining Features | Design for Mass Production 3D Printing

    //  I've only implemented a basic pin so far as a placeholder.
  }
}

module ledge(split_at_z=0, want_ledge=false, object_is_concave_and_has_no_holes=false) {
  // Render the final solid slice
  if (want_ledge) {
    if (object_is_concave_and_has_no_holes) {
      if (want_ledge) hull() {
        translate([objwidth/2,objdepth/2,0])
        linear_extrude(height=delta)
        offset(r=-1) projection(cut = true)
          { translate([0,0,-split_at_z]) the_object(); }
        translate([objwidth/2,objdepth/2,overlap_height])
        linear_extrude(height=delta)
        projection(cut = true) { translate([0,0,-split_at_z]) the_object(); }
      }
    } else {
      for(slice = [0:1:3]) {
        translate([objwidth/2,objdepth/2,(slice-1)*layerdepth])
        //intersection() {
          linear_extrude(height=layerdepth+delta)
          offset(r=-(4-slice)*linewidth)
          projection(cut = true) { translate([0,0,-split_at_z]) the_object(); }

          //linear_extrude(height=0.1)
          //projection(cut = true) { translate([0,0,-split_at_z]) the_object(); }
        //}
      }
    }
  }
}

module pin(pinsize, taper=linewidth) {
  width = pinsize.x; height = pinsize.y;
  color("red") translate([0,0,-height+delta]) union() {
    // The pins have a taper
    translate([-width/2,-width/(2*5),layerdepth*2]) tapered_cube(taper, width,width/5,height-layerdepth*2);
    translate([-width/(2*5),-width/2,layerdepth*2]) tapered_cube(taper, width/5,width,height-layerdepth*2);
  }
}

module pinhole(pinsize) {
  pin(pinsize, 0); // The holes have no taper
}

module split_object(raise_base=0,
                    split_at_z=0,
                    pinsize=[8,4],
                    holes=[[0,0]],
                    want_ledge=false,
                    object_is_concave_and_has_no_holes=false) {
                    
  translate([0,0,raise_base]) {

    // UPPER (with pins)
    union() {
      translate([objdimens.x*3/2+printgap,objdimens.y/2,0])
      rotate([0,180,0])
      union() {
        intersection() {
          children();
          translate([-500,-500,split_at_z]) cube([1000,1000,1000]);
        }
        for (hole = holes) {
          if (len(hole)==2) {
            translate([hole.x,hole.y,split_at_z]) pin(pinsize);
          } else {
            translate([hole.x,hole.y,hole.z+split_at_z]) pin(pinsize);
          }
        }

        color("green")
        translate([-objdimens.x/2,-objdimens.y/2,objsplit-overlap_height]) ledge(want_ledge=want_ledge, object_is_concave_and_has_no_holes=object_is_concave_and_has_no_holes);
      }
    }
    
    // LOWER (with pinholes)
    difference() {
      translate([objdimens.x/2,objdimens.y/2,0]) difference() {
        intersection() {
          children();
          translate([-500,-500,split_at_z-1000]) cube([1000,1000,1000]);
        }
        for (hole = holes) {
          if (len(hole)==2) {
            translate([hole.x,hole.y,split_at_z]) pinhole(pinsize);
          } else {
            translate([hole.x,hole.y,hole.z+split_at_z]) pinhole(pinsize);
          }
        }
      }

      color("green")
      translate([0,0,objsplit-overlap_height]) ledge(want_ledge=want_ledge,object_is_concave_and_has_no_holes=object_is_concave_and_has_no_holes);
    }
  }
}


// TEST OBJECT
module the_object() {
  difference() {
    cube([objwidth,objdepth,objheight], center=true);
    translate([4,-4,0]) sphere(r=3); // ensure a hole for testing
    translate([12,-4,0]) sphere(r=3); // ensure concavity for testing
  }
}

if (testing_pins) {
// SOME TEST EXAMPLES

// (hole z-heights are relative to the split_z level and can be omitted.)
split_object(
             objheight/2,
             split_at_z=objsplit,
             pinsize=[8,4],
             holes=[[-7,-7], [-3,3], [7,7]],
             want_ledge=true
             //,object_is_concave_and_has_no_holes=false
             ) the_object();
             
translate([0,35,0]) split_object(
             objheight/2,
             split_at_z=objsplit,
             pinsize=[8,4],
             holes=[[-7,-7], [-3,3], [7,7]]
             //,want_ledge=false,
             //,object_is_concave_and_has_no_holes=false
             ) the_object();

translate([60,0,0]) split_object(
             objheight/2,
             split_at_z=objsplit,
             pinsize=[8,4],
             holes=[[-7,-7], [-3,3], [7,7]],
             want_ledge=true,
             object_is_concave_and_has_no_holes=true
             ) cube([objwidth,objdepth,objheight], center=true);;
             
translate([60,35,0]) split_object(
             objheight/2,
             split_at_z=objsplit,
             pinsize=[8,4],
             holes=[[-7,-7], [-3,3], [7,7]],
             want_ledge=false,
             object_is_concave_and_has_no_holes=true
             ) cube([objwidth,objdepth,objheight], center=true);;

}
