// LEGO-compatible self-locking Torsen-like differential housing
// With either double-bevel or single-bevel 36t gear
// (c) Vitaliy Filippov 2015
// License: GNU GPL 3.0 or later

// How to use it - see http://www.thingiverse.com/thing:1236953

$fn = 100;

torsen_diff_36t();

module torsen_diff_36t() {
    rotate([90, 0, 0])
    union() {
        difference() {

            // double bevel:
            rotate([90, 0, 0]) double_bevel_36t();
            translate([0, 4, 0]) rotate([90, 0, 0]) cylinder(r=3.8, h=8, center=true);

            //// single bevel:
            // rotate([-90, 0, 0]) single_bevel(36);

            rotate([90, 0, 0]) cylinder(r=2.6, h=10, center=true);
        }
        translate([0, 0, 8]) difference() {
            union() {
                translate([0, 8, 0]) rotate([90, 0, 90]) cylinder(r=2.8, h=7.2, center=true);
                translate([0, 8-2.8, 0]) cube([7.2, 2.8*2, 2.8*2], center=true);
                translate([-3.2, 8, 0]) rotate([90, 0, 90]) cylinder(r=3.6, h=0.8, center=true);
                translate([-3.2, 8-3.2, 0]) cube([0.8, 3.6*2, 3.6*2], center=true);
                translate([3.2, 8, 0]) rotate([90, 0, 90]) cylinder(r=3.6, h=0.8, center=true);
                translate([3.2, 8-3.2, 0]) cube([0.8, 3.6*2, 3.6*2], center=true);
                translate([0, 8, 0]) rotate([90, 0, 90]) axleround(h=16);
            }
        }
        translate([0, 0, -8]) difference() {
            union() {
                translate([0, 8, 0]) rotate([90, 0, 90]) cylinder(r=2.8, h=7.2, center=true);
                translate([0, 8-2.8, 0]) cube([7.2, 2.8*2, 2.8*2], center=true);
                translate([-3.2, 8, 0]) rotate([90, 0, 90]) cylinder(r=3.6, h=0.8, center=true);
                translate([-3.2, 8-3.2, 0]) cube([0.8, 3.6*2, 3.6*2], center=true);
                translate([3.2, 8, 0]) rotate([90, 0, 90]) cylinder(r=3.6, h=0.8, center=true);
                translate([3.2, 8-3.2, 0]) cube([0.8, 3.6*2, 3.6*2], center=true);
                translate([0, 8, 0]) rotate([90, 0, 90]) axleround(h=16);
            }
        }
    }
}

module axleround(h = 24, axleradius = 2.3, axlemesh = 1.7) {
    translate([0, 0, -h/2]) difference() {
        cylinder($fn=16, r=axleradius, h=h);
        translate([axlemesh/2, axlemesh/2, -1]) cube([axleradius, axleradius, h+2]);
        translate([axlemesh/2, -axlemesh/2-axleradius, -1]) cube([axleradius, axleradius, h+2]);
        translate([-axlemesh/2-axleradius, axlemesh/2, -1]) cube([axleradius, axleradius, h+2]);
        translate([-axlemesh/2-axleradius, -axlemesh/2-axleradius, -1]) cube([axleradius, axleradius, h+2]);
    }
}

module single_bevel(nteeth = 28) {
    union() {
        intersection() {
            translate([0, 0, 1.5])
                linear_extrude(slices=5, height=2.5, scale=(nteeth/2+1-2.5*tan(45))/(nteeth/2+1))
                gear_concat_flat(mm_per_tooth=3.4, number_of_teeth=nteeth, backlash=2, clearance=0.8, trim_factor=0.7);
            cylinder($fn=nteeth*2, h=4, r=2.98*(nteeth/2+1)/3.1415926+0.5);
        }
        cylinder($fn=nteeth*2, h=4, r=2.98*(nteeth/2-1)/3.1415926);
        cylinder($fn=nteeth*2, h=1.5, r=2.98*(nteeth/2+1)/3.1415926+0.5);
    }
}

// An attempt to make something like standard 36t double bevel gear...
// std lego 36t gear outer radius = ~36.5
// std lego 36t gear root radius = ~32.2
module double_bevel_36t() {
  intersection() {
    union() {
      translate([0, 0, 1.1])
        linear_extrude(slices=5, height=2.5, scale=(36/2+1-2.5*tan(45))/(36/2+1))
        gear_concat_flat(mm_per_tooth=3.4, number_of_teeth=36, backlash=2, clearance=0.8, trim_factor=0.7);
      translate([0, 0, -1.1])
        rotate([180, 0, 0])
        linear_extrude(slices=5, height=2.5, scale=(36/2+1-2.5*tan(45))/(36/2+1))
        gear_concat_flat(mm_per_tooth=3.4, number_of_teeth=36, backlash=2, clearance=0.8, trim_factor=0.7);
      translate([0, 0, -3.6])
        cylinder($fn=36*2, h=7.2, r=2.98*(36/2-1)/3.1415926);
      translate([0, 0, -1.1])
        cylinder($fn=36*2, h=2.2, r=2.98*(36/2+1)/3.1415926+0.5);
    }
    translate([0, 0, -8]) linear_extrude(slices=5, height=16)
      gear_concat_flat(mm_per_tooth=2.98, number_of_teeth=36, pressure_angle=15, backlash=-0.5, clearance=-0.3);
  }
}

// Same in 2D (non-extruded)
module gear_concat_flat(
	mm_per_tooth    = 3,    //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
	number_of_teeth = 11,   //total number of teeth around the entire perimeter
	pressure_angle  = 28,   //Controls how straight or bulged the tooth sides are. In degrees.
	clearance       = 0.0,  //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
	backlash        = 0.0,  //gap between two meshing teeth, in the direction along the circumference of the pitch circle
  trim_factor      = 1
) {
  pi = 3.1415926;
  p  = mm_per_tooth * number_of_teeth / pi / 2;  //radius of pitch circle
  c  = p + mm_per_tooth / pi - clearance;        //radius of outer circle
  b  = p*cos(pressure_angle);                    //radius of base circle
  r  = p - mm_per_tooth / pi;                    //radius of root circle
  t  = mm_per_tooth/2-backlash/2;                //tooth thickness at pitch circle
  k  = -iang(b, p) - t/2/p/pi*180;               //angle to where involute meets base circle on each side of tooth
    polygon(
      points = gear_points_trimmed(r, b, c, k, number_of_teeth, number_of_teeth, trim_factor),
      paths = [ range(15*(number_of_teeth)) ]
    );
};
function gear_points_trimmed(r, b, c, k, number_of_teeth, i, f) =
  (i <= 0 ? [] : concat(
    gear_points_trimmed(r, b, c, k, number_of_teeth, i-1, f),
    tooth_base_points_trimmed(r, b, c, k, number_of_teeth, i, f)
  ));
function tooth_base_points_trimmed(r, b, c, k, number_of_teeth, i, f) =
  (r < b ? concat(
    [ polar(r*f, i*360/number_of_teeth + k) ],
    tooth_points(r, b, c, k, number_of_teeth, i),
    [ polar(r*f, i*360/number_of_teeth - k),
      polar(r*f, i*360/number_of_teeth + 180/number_of_teeth) ]
  ) : concat(
    tooth_points(r, b, c, k, number_of_teeth, i),
    [ polar(r*f, i*360/number_of_teeth + 180/number_of_teeth) ]
  ));
function tooth_points(r, b, c, k, number_of_teeth, i) = [
  q7r(0/5,r,b,c,k,1,i*360/number_of_teeth),
  q7r(1/5,r,b,c,k,1,i*360/number_of_teeth),
  q7r(2/5,r,b,c,k,1,i*360/number_of_teeth),
  q7r(3/5,r,b,c,k,1,i*360/number_of_teeth),
  q7r(4/5,r,b,c,k,1,i*360/number_of_teeth),
  q7r(5/5,r,b,c,k,1,i*360/number_of_teeth),
  q7r(5/5,r,b,c,k,-1,i*360/number_of_teeth),
  q7r(4/5,r,b,c,k,-1,i*360/number_of_teeth),
  q7r(3/5,r,b,c,k,-1,i*360/number_of_teeth),
  q7r(2/5,r,b,c,k,-1,i*360/number_of_teeth),
  q7r(1/5,r,b,c,k,-1,i*360/number_of_teeth),
  q7r(0/5,r,b,c,k,-1,i*360/number_of_teeth),
];

function range(n) = (n >= 0 ? concat(range(n-1), [n]) : []);

// radius a fraction f up the curved side of the tooth, rotated at 'rot' angle
function q7r(f,r,b,r2,t,s,rot) = q6r(b,s,t,(1-f)*max(b,r)+f*r2,rot);
// point at radius d on the involute curve, rotated at 'rot' angle
function q6r(b,s,t,d,rot)      = polar(d,rot+s*(iang(b,d)+t));
// convert polar to cartesian coordinates
function polar(r,theta)   = r*[sin(theta), cos(theta)];
//unwind a string this many degrees to go from radius r1 to radius r2
function iang(r1,r2)      = sqrt((r2/r1)*(r2/r1) - 1)/3.1415926*180 - acos(r1/r2);
