// NOTE regarding Patents: Since 1977 the LEGO Group has produced "Technic"
// elements with gear teeth, axles, axle-holes, and other features closely
// resembling the design(s) in this file, as part of their "Expert Builder"
// and "Technical Sets", now called "Technic" (see for example set 961,
// peeron.com/inv/sets/961-1?showpic=9288 ). By 1989 they had added pieces
// with rounded ends (see peeron.com/inv/sets/5264-1?showpic=8542 (set 5264
// from 1987) and peeron.com/inv/sets/5110-2?showpic=8543 (set 5110 from 1989))
// The object(s) produced by this SCAD file are different from real LEGO(r)
// elements, and any similarities of features, such as the shapes of axles
// and axle-holes, are functional in nature. The functions in question
// resemble those in LEGO patents that have already expired (or, if not
// patented, became prior art when the product(s) became available for
// purchase, i.e. 1989 at the latest). Nevertheless, one must not infringe
// on non-expired patents and any non-patent rights, such as LEGO(r)
// trademarks and brand identity. An example of such infringement would be
// to make objects and then try to "pass them off" as LEGO products. See
// for example the Kirkbi AG v. Ritvik Holdings Inc. case, (Supreme Court of
// Canada [2005] 3 S.C.R. 302).

// Contains some code from thing 29989 by bjepson on Thingiverse, thing 40410
// by Robert Munafo, and MCAD library. myGear() from 40410 is here, but with
// different (better) involute gear teeth shape, and with some tweaks to make
// better gears without degenerate faces.
//
// Everything other is (c)oded by me, Vitaliy Filippov. License is GNU LGPL2.1+
//
// Also I HIGHLY recommend to build OpenSCAD from my fork: github.com/vitalif/openscad,
// because there's a patch that makes OpenSCAD to use Delaunay triangulation which
// greatly improves the resulting model quality.

// Standard LEGO dimensions:
// Stud spacing = 8mm
// Studded beam height = 9.6mm
// Most beams, mounts and etc are 7.2mm thick in cross direction (i.e. beam is 8mm x 7.2mm, half-bush has 7.2mm radius etc)
// Hole diameter (ideal) = 4.8mm
// Hole diameter (for FDM printing) = 5.0-5.1mm
// Pin holes (on beams etc) have ending notches that are 0.8mm deep and 3mm in radius (i.e. diameter is 6mm)
// 0.8mm thick are also various borders like ones on the end of a full bush
// Inner bush diameter = 5.7mm
// Axle diameter = 4.6mm
// Axle mesh thickness = 1.7mm
// Screw gear outer radius = 9.8mm
// Thickness of normal (non-bevel) gear wheels is 3.8mm
// Standard 40t gear: root_radius=18.75 outer_radius=20.85 (mm_per_tooth =~ 3.1 by root)
// Standard 24t gear: root_radius=10.7 outer_radius=12.8 (mm_per_tooth =~ 3.06 by root)
// Standard 16t gear: root_radius=6.8 outer_radius=8.7 (mm_per_tooth =~ 3.05 by root)
// Standard 8t gear: root_radius=2.8 outer_radius=4.9 (mm_per_tooth =~ 3.05 by root)
// Standard 12t bevel gear: 12.7mm bottom diameter, 1.6mm top teeth length, 10.6mm top teeth outer diameter, 7.7mm top teeth root diameter, 2.5mm gear height + 0.5mm flat base + 1mm spacer at bottom
// Standard 20t idler bevel gear: 20.4mm bottom diameter, 18.4mm top teeth outer diameter, 1.8mm top teeth length, 15.6mm top teeth root diameter
// Standard 36t double bevel gear: 37.2mm teeth outer diameter, 31.6mm teeth root diameter, 2.5mm central non-bevel part thickness, 7.2mm total thickness

// Formulas for gear radiuses:
//outer_radius = mm_per_tooth*(n_teeth/2+1)/3.1415926 - clearance;
//root_radius = mm_per_tooth*(n_teeth/2-1)/3.1415926;

HOLE_RADIUS = 2.5;
HOLE_RING_HEIGHT = 0.9;

//-------------------------------------------
// Overview of this file
//-------------------------------------------

/* 32-teeth planetary assembly */

/*rotate([0, 0, 10])
translate([0, 0, 0.9])
  ring_gear_2u_32t();
translate([0, 0, 8.1])
  satellites_32t();
translate([0, 0, 16+7.2])
  color([0.5, 1, 1, 1])
  rotate([180, 0, 0])
  carrier_32t_easy();
translate([0, 0, -8])
  color([0.5, 1, 0.5, 1])
  sun_drive_32t();
translate([20, 0, 0])
  color([1, 0.5, 0.5, 1])
  rotate([0, 0, -19]) std_gear8();
translate([20, 0, -8])
  color([1, 0.5, 0.5, 1])
  rotate([0, 0, -22.5]) std_gear8();
translate([20, 0, 16])
  color([1, 0.5, 0.5, 1])
  rotate([0, 0, -22.5]) std_gear8();*/

/* (experiment) 1-stud thick variant of 32-teeth ring gear */

//ring_gear_1u_32t();
//ring_gear_2u_32t();
//carrier_32t_easy();
//carrier_32t_not_so_easy();
//sun_drive_32t();
//sun_drive_32t_simpler();

double_bevel_36t();

/* Standard 8-teeth gear */

//std_gear8();

/* 40-teeth (both inside and outside) planetary ring gear */

//rotate([180, 0, 0]) planetary_ring_gear(plane_height=1);

/* Solid clutch gear, like standard 16-teeth */

//clutchGear(32);

/* Gear with clutch fully offset to one side - useful for compact gearbox
   because allows to make clutch gears smaller than 16t */

//offsetClutchGear(9, gear_height=4);
//offsetClutchGear(23, gear_height=3.8);

/* Solid gear fully offset to one side */

//offsetGear(number_of_teeth=8, gear_height=3.8);

/* Sample offset gear with 28 teeth and 4 holes */

//gear28_4holes();

/* Gear with offset teeth, axle hole and sparse body */

//offsetGearSparse(20, gear_height=3.8, jaggy=1);

/* Half-stud parametric gear with some holes */

//myGear(16);

/* Generic gear with involute teeth */

//gear(mm_per_tooth=3.05, number_of_teeth=16, thickness=4, pressure_angle=20);

/* Same, but using an experimental OpenSCAD feature - vector concat() function */

//gear_concat(mm_per_tooth=3.05, number_of_teeth=24, thickness=3.8);

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

module sun_drive_32t() {
  root = 3.06*(32/2-1)/3.1415926;
  segments = 32*2; // so gear teeth edge never match cylinder segment edges
  difference() {
    union() {
      translate([0, 0, 2]) gear_teeth(mm_per_tooth=3.06, number_of_teeth=32, thickness=4, pressure_angle=20);
      cylinder($fn=100, h=7.2, r=root);
      cylinder($fn=100, h=8, r=3.6);
    }
    rotate([0, 0, 45]) axleCut(0, 0, -0.2, 10);

    translate([-8, 0, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([0, -8, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([0, 8, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([8, 0, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);

    translate([-8, 0, -0.1]) cylinder($fn=32, h=1+0.1, r=3);
    translate([0, -8, -0.1]) cylinder($fn=32, h=1+0.1, r=3);
    translate([0, 8, -0.1]) cylinder($fn=32, h=1+0.1, r=3);
    translate([8, 0, -0.1]) cylinder($fn=32, h=1+0.1, r=3);
  }
}

module sun_drive_32t_simpler() {
  root = 3.06*(32/2-1)/3.1415926;
  segments = 32*2; // so gear teeth edge never match cylinder segment edges
  union() {
    difference() {
      union() {
        gear_teeth(mm_per_tooth=3.06, number_of_teeth=32, thickness=7.2, pressure_angle=20);
        difference() {
          cylinder($fn=32*2, h=7.2, r=root);
          translate([0, 0, 1]) cylinder($fn=32*2, h=7.1, r=root-2);
        }
        cylinder($fn=100, h=8, r=1.95+2);
        union() {
          translate([-1, -root+0.5, 0.8]) cube(size=[2, root*2-1, 6.4]);
          rotate([0, 0, 90]) translate([-1, -root+0.5, 0.8]) cube(size=[2, root*2-1, 6.4]);
        }
      }
      rotate([0, 0, 45]) axle(0, 0, -0.2, 10);
    }
  }
}

// Symmetric planetary carrier gear (not so easy to print because it needs support)
module carrier_32t_not_so_easy() {
  root = 3.1*(32/2-1)/3.1415926;
  fn = 32*2; // so gear teeth edge never match cylinder segment edges
  difference() {
    union() {
      translate([0, 0, 2]) gear_teeth(mm_per_tooth=3.1, number_of_teeth=32, thickness=4, pressure_angle=20);
      translate([0, 0, HOLE_RING_HEIGHT]) cylinder($fn=fn, r=root, h=8-2*HOLE_RING_HEIGHT);
      cylinder($fn=32, r=HOLE_RADIUS+1.2, h=8);
    }
    translate([0, 0, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([-8, 0, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([0, -8, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([0, 8, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([8, 0, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
  }
}

// Easy to print planetary carrier gear
module carrier_32t_easy() {
  root = 3.1*(32/2-1)/3.1415926;
  fn = 32*2; // so gear teeth edge never match cylinder segment edges
  difference() {
    union() {
      gear_teeth(mm_per_tooth=3.1, number_of_teeth=32, thickness=8-2*HOLE_RING_HEIGHT, pressure_angle=20);
      cylinder($fn=fn, r=root, h=8-2*HOLE_RING_HEIGHT);
      cylinder($fn=32, r=HOLE_RADIUS+1.2, h=8-HOLE_RING_HEIGHT); // 1.2 = 0.4(nozzle)*3 perimeters
    }
    translate([0, 0, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([-8, 0, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([0, -8, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([0, 8, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
    translate([8, 0, -0.2]) cylinder($fn=32, h=10, r=HOLE_RADIUS);
  }
}

module carrier_32t() {
  root = 3.06*(32/2-1)/3.1415926;
  segments = 32*2; // so gear teeth edge never match cylinder segment edges
  difference() {
    union() {
      difference() {
        translate([0, 0, 2]) gear_teeth(mm_per_tooth=3.06, number_of_teeth=32, thickness=4, pressure_angle=20);
        cylinder($fn=100, h=8, r=root-0.5);
      }
      difference() {
        translate([0, 0, 0.8]) cylinder($fn=32*2, h=7.2, r=root);
        translate([0, 0, -0.1]) cylinder($fn=32*2, h=7.3, r=root-1);
        translate([0, 0, 7.2-0.1]) cylinder($fn=32*2, h=1, r=3.06*(24/2)/3.1415926);
      }
      translate([0, 0, 3.5]) cylinder($fn=32*2, h=1, r=root-0.5);
      cylinder($fn=100, h=8, r=3.5);
      difference() {
        union() {
          translate([-8, 0, 0]) cylinder($fn=32*2, h=8, r=3.5);
          translate([0, -8, 0]) cylinder($fn=32*2, h=8, r=3.5);
          translate([0, 8, 0]) cylinder($fn=32*2, h=8, r=3.5);
          translate([8, 0, 0]) cylinder($fn=32*2, h=8, r=3.5);
          translate([-0.5, -root+0.5, 0]) cube(size=[1, root*2-1, 8]);
          rotate([0, 0, 90]) translate([-0.5, -root+0.5, 0]) cube(size=[1, root*2-1, 8]);
        }
        translate([-20, -20, 8-0.8]) cube(size=[40, 40, 1]);
        translate([-20, -20, -0.2]) cube(size=[40, 40, 1]);
      }
    }
    translate([-8, 0, -0.2]) cylinder($fn=32*2, h=10, r=2.5);
    translate([0, -8, -0.2]) cylinder($fn=32*2, h=10, r=2.5);
    translate([0, 0, -0.2]) cylinder($fn=32*2, h=10, r=2.5);
    translate([0, 8, -0.2]) cylinder($fn=32*2, h=10, r=2.5);
    translate([8, 0, -0.2]) cylinder($fn=32*2, h=10, r=2.5);
  }
}

// Most compact planetary ring gear: 1 unit thick, 32 teeth outside, 24 teeth inside
module ring_gear_1u_32t() {
  difference() {
    gear(mm_per_tooth=3.06, number_of_teeth=32, thickness=7.8, pressure_angle=20);
    translate([0, 0, 1.7]) gear(mm_per_tooth=3.28, number_of_teeth=24, thickness=4.4, pressure_angle=20);
    translate([0, 0, -0.1]) cylinder($fn=32*2, h=8.2, r=3.28*(24/2-1)/3.1415926);
  }
}

// Solid easy-to-print 2U 32T ring gear, with mounting holes
// Print with 0.1mm layer, 300% first layer and 1 perimeter
module ring_gear_2u_32t() {
  fn = 32*2;
  difference() {
    union() {
      // 0.9mm spacing (skipped) + 6.2mm outer gear + 1mm spacing
      // + 6.9mm inner gear + 1mm spacing (skipped)
      gear_teeth(mm_per_tooth=3.1, number_of_teeth=32, thickness=6.2, pressure_angle=20);
      cylinder($fn=fn, h=14.1, r=3.1*(32/2-1)/3.1415926);
    }
    translate([0, 0, 6.2]) difference() {
      // inner gear radius should be 26.4mm, but we make it slightly bigger
      gear(mm_per_tooth=3.22, number_of_teeth=24, thickness=10, pressure_angle=20);
      cylinder($fn=fn, h=1, r=HOLE_RADIUS+1.2);
    }
    translate([0, 0, -0.1]) cylinder($fn=fn, h=20, r=HOLE_RADIUS);
    // holes
    translate([0, -8, -0.1]) cylinder($fn=fn, h=20, r=HOLE_RADIUS);
    translate([0, 8, -0.1]) cylinder($fn=fn, h=20, r=HOLE_RADIUS);
    translate([-8, 0, -0.1]) cylinder($fn=fn, h=20, r=HOLE_RADIUS);
    translate([8, 0, -0.1]) cylinder($fn=fn, h=20, r=HOLE_RADIUS);
  }
}

// Satellites and sun gear for 32t ring gear
module satellites_32t() {
  color([1, 0.5, 0.5, 1]) rotate([0, 0, -5]) std_gear8();
  color([0.5, 0.5, 1, 1]) translate([8, 0, 0]) rotate([0, 0, 30]) std_gear8();
  color([0.5, 0.5, 1, 1]) translate([-8, 0, 0]) rotate([0, 0, 30]) std_gear8();
  color([0.5, 0.5, 1, 1]) translate([0, 8, 0]) rotate([0, 0, 30]) std_gear8();
  color([0.5, 0.5, 1, 1]) translate([0, -8, 0]) rotate([0, 0, 30]) std_gear8();
}

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

// An attempt to make something like standard 36t double bevel gear...
module double_bevel_36t() {
  intersection() {
    union() {
      translate([0, 0, 1.5])
        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.53, number_of_teeth=36, backlash=2, clearance=0.8, trim_factor=0.7);
      translate([0, 0, -1.5])
        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.53, number_of_teeth=36, backlash=2, clearance=0.8, trim_factor=0.7);
      translate([0, 0, -4])
        cylinder($fn=48, h=8, r=3.14*(36/2-1)/3.1415926);
      translate([0, 0, -1.5])
        cylinder($fn=48, h=3, r=3.14*(36/2+1)/3.1415926);
    }
    translate([0, 0, -8]) linear_extrude(slices=5, height=16)
      gear_concat_flat(mm_per_tooth=3.14, number_of_teeth=36);
  }
}

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

// An attempt to make something like standard 20t bevel gear...
module bevel_20t() {
  difference() {
    union() {
      translate([0, 0, 1.5])
        linear_extrude(slices=5, height=2.5, scale=(20/2+1-2.5*tan(45))/(20/2+1), twist=0)
        gear_concat_flat(mm_per_tooth=3.53, number_of_teeth=20, backlash=2, clearance=0.8, trim_factor=0.7);
      cylinder($fn=48, h=1.5, r=3.53*(20/2-1)/3.1415926);
      cylinder($fn=48, h=4, r=3.53*(20/2-1)/3.1415926*(20/2+1-2.5*tan(45))/(20/2+1));
    }
    translate([0, 0, -0.1])
    difference() {
      cylinder($fn=48, h=8, r=100);
      translate([0, 0, -0.1]) cylinder($fn=48, h=8.2, r=3.53*(20/2-1)/3.1415926);
    }
    axle(0, 0, -0.2, 10);
  }
}

// An attempt to make something like standard 12t bevel gear...
module bevel_12t() {
  difference() {
    union() {
      translate([0, 0, 1.5])
        linear_extrude(slices=5, height=2.5, scale=(12/2+1-2.5*tan(45))/(12/2+1), twist=0)
        gear_concat_flat(mm_per_tooth=3.76, number_of_teeth=12, backlash=1, clearance=0.4, trim_factor=0.75);
      cylinder($fn=48, h=1.5, r=3.76*(12/2-1)/3.1415926);
      cylinder($fn=48, h=4, r=3.76*(12/2-1)/3.1415926*(12/2+1-2.5*tan(45))/(12/2+1));
    }
    translate([0, 0, -0.1])
    difference() {
      cylinder($fn=48, h=8, r=10);
      translate([0, 0, -0.1]) cylinder($fn=48, h=8.2, r=3.76*(12/2-1)/3.1415926);
    }
    axle(0, 0, -0.2, 10);
  }
}

// Standard 8-teeth gear
module std_gear8() {
  difference() {
    union() {
      cylinder($fn=50, h=8, r=3.05*(8/2-1)/3.1415926);
      translate([0, 0, 2.1]) gear(mm_per_tooth=3.05, number_of_teeth=8, thickness=3.8, pressure_angle=20);
    }
    axle(0, 0, -1, 10);
}}

// Planetary ring gear, 2 stud thick
module planetary_ring_gear() {
  difference() {
    union() {
      cylinder($fn=80, h=8, r=(1.2+3.28*(40/2+1)/3.1415926));
      translate([0, 0, 8]) cylinder($fn=80, h=2, r=(3.1*(40/2+1)/3.1415926));
      translate([0, 0, 10]) gear(mm_per_tooth=3.1, number_of_teeth=40, thickness=4, pressure_angle=20);
      translate([0, 0, 13]) cylinder($fn=80, h=2.2, r=(3.1*(40/2-1)/3.1415926));
      translate([0, 0, 8]) cylinder($fn=40, h=8, r=HOLE_RADIUS+1.2);
    }
    translate([0, 0, -0.1]) gear(mm_per_tooth=3.28, number_of_teeth=40, thickness=7.2+0.1, pressure_angle=20);
    difference() {
      translate([0, 0, 7.2-0.1]) cylinder($fn=80, h=0.9, r=(3.28*(40/2-1)/3.1415926)-1);
      translate([0, 0, 7.2-0.2]) cylinder($fn=50, h=1, r=HOLE_RADIUS+1.2);
    }
    translate([-8, -8, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
    translate([-8, 0, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
    translate([-8, 8, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
    translate([0, -8, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
    translate([0, 0, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
    translate([0, 8, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
    translate([8, -8, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
    translate([8, 0, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
    translate([8, 8, 0]) cylinder($fn=50, h=20, r=HOLE_RADIUS);
  }
}

// Example 28-teeth offset gear with 4 pin holes
module gear28_4holes() {
  difference() {
    offsetGear(28, 3.9);
    translate([-8, 0, 0]) pinHole(3.9);
    translate([0, 8, 0]) pinHole(3.9);
    translate([8, 0, 0]) pinHole(3.9);
    translate([0, -8, 0]) pinHole(3.9);
  }
}

// Hole for a standard LEGO pin with height parameter intended to be cut
// from some solid part (like half or full beam)
module pinHole(height=8) {
  union() {
    translate([0, 0, -0.1]) cylinder($fs=0.5, h=height+0.2, r=2.5);
    translate([0, 0, -0.1]) cylinder($fs=0.5, h=0.8+0.1, r=3);
    translate([0, 0, height-0.8]) cylinder($fs=0.5, h=0.8+0.1, r=3);
  }
}

// Gear with offset teeth, axle hole and sparse body
module offsetGearSparse(number_of_teeth, gear_height=3.9, jaggy=1) {
  root_radius = 3.05*number_of_teeth/3.1415926/2 - 3.05/3.1415926;
  difference() {
    offsetGear(number_of_teeth, gear_height, jaggy);
    translate([0, 0, -0.1])
    difference() {
      cylinder($fn=50, h=gear_height+0.2, r=root_radius-1.5);
      cylinder($fn=50, h=gear_height+0.2, r=3.5);
      translate([-root_radius-1, 3.1-0.75, 0]) cube(size=[root_radius*2+2, 1.5, 2*gear_height+0.4], center=false);
      translate([-root_radius-1, -3.1-0.75, 0]) cube(size=[root_radius*2+2, 1.5, 2*gear_height+0.4], center=false);
      rotate([0, 0, 90]) {
        translate([-root_radius-1, 3.1-0.75, 0]) cube(size=[root_radius*2+2, 1.5, 2*gear_height+0.4], center=false);
        translate([-root_radius-1, -3.1-0.75, 0]) cube(size=[root_radius*2+2, 1.5, 2*gear_height+0.4], center=false);
      }
    }
  }
}

// Gear with offset teeth and axle hole
module offsetGear(number_of_teeth, gear_height=3.9, jaggy=1) {
  difference() {
    union() {
      gear(number_of_teeth=number_of_teeth, mm_per_tooth=3.05, thickness=gear_height, pressure_angle=20);
      translate([0, 0, gear_height]) cylinder($fs=0.5, h=8-gear_height, r=3.5);
    }
    // axle hole
    rotate([0, 0, -jaggy*360/number_of_teeth]) axle(0, 0, -1, 10);
  }
}

// Gear with interface for driving ring, with offset teeth
// => The minimal printable size of such gear is 9 teeth...
// (8 teeth will probably be too thin at root circle)
module offsetClutchGear(number_of_teeth, gear_height=3.8) {
  union() {
    difference() {
      union() {
        gear(number_of_teeth=number_of_teeth, mm_per_tooth=3.05, thickness=gear_height+0.1);
        translate([0, 0, gear_height]) cylinder($fn=max(32,2*number_of_teeth), h=8-gear_height, r=6.8);
      }
      difference() {
        translate([0, 0, 4.8]) cylinder($fn=50, h=4, r=5.8);
        translate([0, 0, 4.8]) cylinder($fn=50, h=4, r=3.3);
      }
      if (number_of_teeth > 13) {
        difference() {
          translate([0, 0, -0.1]) cylinder($fn=50, h=gear_height-1+0.1, r=3.05*(number_of_teeth/2-1)/3.1415926-1);
          translate([0, 0, -0.2]) cylinder($fn=50, h=4, r=3.5);
          translate([-0.5, -20, -0.1]) cube(size=[1, 40, 10]);
          translate([-20, -0.5, -0.1]) cube(size=[40, 1, 10]);
        }
      }
      // round hole
      translate([0, 0, -2]) cylinder($fn=50, h=12, r=2.5);
    }
    translate([0, 0, 2.9]) union() {
      clutchTeeth();
      rotate([0, 0, 90]) clutchTeeth();
      rotate([0, 0, 180]) clutchTeeth();
      rotate([0, 0, -90]) clutchTeeth();
    }
  }
}

// Axle
module axle(x, y, z, height) {
  axle_gap = 1.95;
  union() {
    translate([x - HOLE_RADIUS, y - axle_gap/2, z])
      roundedRect([HOLE_RADIUS * 2, axle_gap, height], .2);
    translate([x - axle_gap/2, y - HOLE_RADIUS, z])
      roundedRect([axle_gap, HOLE_RADIUS * 2, height], .2);
  }
}

// Axle hole (for difference()) with fixing slit
module axleCut(x, y, z, height) {
  axle_gap = 1.95;
  axle_gap_cut = 1.8;
  union() {
    translate([x - HOLE_RADIUS, y - axle_gap/2, z])
      roundedRect([HOLE_RADIUS * 2, axle_gap, height], .2);
    translate([x - axle_gap_cut/2, y - HOLE_RADIUS, z])
      roundedRect([axle_gap_cut, HOLE_RADIUS * 2, height], .2);
    translate([x-0.25, y-5, z]) cube(size=[0.5, 10, height]);	
    translate([x, y, z]) difference() {
      cylinder($fn=32, r=4, h=height);
      translate([0, 0, -0.1]) cylinder($fn=32, r=3.6, h=height+0.2);
      translate([-2, -5, -0.1]) cube(size=[4, 10, height+0.2]);
    }
  }
}

// Gear with interface for driving ring, with centered teeth
// => The minimal size of such gear is 16 teeth
module clutchGear(number_of_teeth) {
  union() {
    difference() {
      union() {
        gear(thickness=3.7, mm_per_tooth=3.05, number_of_teeth=number_of_teeth, pressure_angle=20);
        translate([0, 0, 1]) cylinder($fn=number_of_teeth*2, h=5.8-1, r=6.8);
        translate([0, 0, -2]) cylinder($fn=number_of_teeth*2, h=2+1, r=3.7);
      }
      difference() {
        translate([0, 0, 1.8]) cylinder($fn=number_of_teeth*2, h=4.1, r=5.8);
        translate([0, 0, 1.7]) cylinder($fn=number_of_teeth*2, h=4.1+0.2, r=3.3);
      }
      translate([0, 0, -3]) cylinder($fn=number_of_teeth*2, h=10, r=HOLE_RADIUS);
      translate([0, 0, -2.2]) cylinder($fn=number_of_teeth*2, h=1, r=3.1);
    }
    clutchTeeth();
    rotate([0, 0, 90]) clutchTeeth();
    rotate([0, 0, 180]) clutchTeeth();
    rotate([0, 0, -90]) clutchTeeth();
  }
}

// Clutch: 0.7 from the top, 1.5 width, 1 thickness
module clutchTeeth() {
  translate([5.8+0.1, 0, 1.8-0.1])
  rotate([0, -90, 0])
  linear_extrude(height = 1+0.1, center = false)
    polygon([
      [0, -0.8],
      [0, 0.8],
      [3.2+0.1, 0.1],
      [3.2+0.1, -0.1],
    ]);
}

module myGear(n_teeth) {
  if (n_teeth >= 56) {
    myGearParamed(n_teeth, 6, 4, 5, 5);
  } else if (n_teeth >= 48) {
    myGearParamed(n_teeth, 4, 4, 5, 3);
  } else if (n_teeth >= 40) {
    myGearParamed(n_teeth, 4, 2, 3, 3);
  } else if (n_teeth >= 32) {
    myGearParamed(n_teeth, 2, 2, 3, 3);
  } else if (n_teeth >= 26) {
    myGearParamed(n_teeth, 2, 2, 3, 1);
  } else if (n_teeth >= 21) {
    myGearParamed(n_teeth, 2, 2, 1, 1);
  } else {
    myGearParamed(n_teeth, 0, 0, 1, 1);
  }
}

module myGearParamed(num_teeth, holes_row, holes_col, plus_row, plus_col)
{
  jaggy_angle = 0;
  beam_width = 7.8;  // Needs to be a bit less than the LEGO stud spacing = 7.99 mm
  gearHeight = 3.67;

  axle_gap = 1.9;   // axle thickness is actually about 1.8 mm

  difference() {
    gear(thickness=gearHeight, number_of_teeth=num_teeth, mm_per_tooth=3.06, pressure_angle=20);

    // We do two rectangular grids of holes, rotate 90 degrees and do them
    // again.
    for (i = [jaggy_angle, jaggy_angle+90]) {
      rotate([0,0,i]) {
        // Cut the cross-axle holes (8.0mm = stud spacing)
        if (plus_row > 0) {
          for (x=[-4 * (plus_row - 1 ): 8.0 : 4 * (plus_row -1 )]) {
            for (y=[-4 * (plus_col - 1 ): 8.0 : 4 * (plus_col -1)]) {
              translate([x - HOLE_RADIUS, y - axle_gap/2, -0.1])
                roundedRect([HOLE_RADIUS * 2, axle_gap, gearHeight+0.2], .2);
              translate([x - axle_gap/2, y - HOLE_RADIUS, -0.1])
                roundedRect([axle_gap, HOLE_RADIUS * 2, gearHeight+0.2], .2);
            }
          }
        }

        if (holes_row > 0) {
          // Cut the round holes
          for (x=[-4 * (holes_row-1) : 8.0 : 4 * (holes_row-1)]) {
            for (y=[-4 * (holes_col-1) : 8.0 : 4 * (holes_col-1)]) {
              translate([x, y, -0.1]) {
                cylinder(r=HOLE_RADIUS, h = gearHeight+0.2, $fs=0.5);
              }
            }
          }
        }

      }
    }

  }
}


// ---------------------------------------------------------------------------------
//
// module for rounded rectangles by tlrobinson on Thingiverse in a
// comment posted to http://www.thingiverse.com/thing:9347 on March
// 29, 2012, 3:57:46 AM EDT
//
module roundedRect(size, radius) {
  x = size[0];
  y = size[1];
  z = size[2];

  linear_extrude(height=z)
  hull() {
    translate([radius, radius, 0])
      circle(r=radius);

    translate([x - radius, radius, 0])
      circle(r=radius);

    translate([x - radius, y - radius, 0])
      circle(r=radius);

    translate([radius, y - radius, 0])
      circle(r=radius);
  }
}

///////////////////////////////////////////////


// Good gear() module -- without experimental OpenSCAD features
// Does not support hole_diameter and teeth_to_hide parameters,
// but generates good solid gears with no face problems
module gear(
	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
	thickness       = 6,    //thickness of gear in mm
	twist           = 0,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	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
) {
  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-(c-p)-clearance;                        //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
  difference() {
    union() {
      // $fn: so gear teeth edge never match cylinder segment edges
      translate([0, 0, -1]) cylinder($fn=number_of_teeth*2, h=thickness+2, r=r);
      translate([0, 0, -0.5])
      for (i = [0:number_of_teeth-1])
        rotate([0,0,i*360/number_of_teeth])
          linear_extrude(height = thickness+1, center = false, convexity = 10, twist = twist)
            polygon(
              points=[
                [0, 0],
                polar(r, -181/number_of_teeth),
                polar(r*0.9, r<b ? k : -180/number_of_teeth),
                q7(0/5,r,b,c,k,1),
                q7(1/5,r,b,c,k,1),
                q7(2/5,r,b,c,k,1),
                q7(3/5,r,b,c,k,1),
                q7(4/5,r,b,c,k,1),
                q7(5/5,r,b,c,k,1),
                q7(5/5,r,b,c,k,-1),
                q7(4/5,r,b,c,k,-1),
                q7(3/5,r,b,c,k,-1),
                q7(2/5,r,b,c,k,-1),
                q7(1/5,r,b,c,k,-1),
                q7(0/5,r,b,c,k,-1),
                polar(r*0.9, r<b ? -k : 180/number_of_teeth),
                polar(r, 181/number_of_teeth),
              ],
              paths=[[2,3,4,5,6,7,8,9,10,11,12,13,14,15]]
            );
    }
    translate([-c-1, -c-1, -2]) cube(size=[2*c+2, 2*c+2, 2]);
    translate([-c-1, -c-1, thickness]) cube(size=[2*c+2, 2*c+2, 2]);
  }
};

// Gear teeth without body
module gear_teeth(
	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
	thickness       = 6,    //thickness of gear in mm
	twist           = 0,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	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
) {
  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-(c-p)-clearance;                        //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
  union() {
    for (i = [0:number_of_teeth-1])
      rotate([0,0,i*360/number_of_teeth])
        linear_extrude(height = thickness, center = false, convexity = 10, twist = twist)
          polygon(
            points=[
              [0, 0],
              polar(r, -181/number_of_teeth),
              polar(r*0.9, r<b ? k : -180/number_of_teeth),
              q7(0/5,r,b,c,k,1),
              q7(1/5,r,b,c,k,1),
              q7(2/5,r,b,c,k,1),
              q7(3/5,r,b,c,k,1),
              q7(4/5,r,b,c,k,1),
              q7(5/5,r,b,c,k,1),
              q7(5/5,r,b,c,k,-1),
              q7(4/5,r,b,c,k,-1),
              q7(3/5,r,b,c,k,-1),
              q7(2/5,r,b,c,k,-1),
              q7(1/5,r,b,c,k,-1),
              q7(0/5,r,b,c,k,-1),
              polar(r*0.9, r<b ? -k : 180/number_of_teeth),
              polar(r, 181/number_of_teeth),
            ],
            paths=[[2,3,4,5,6,7,8,9,10,11,12,13,14,15]]
          );
  }
};

// Same gear, but using recursion and experimental OpenSCAD vector
// concat() function which must be enabled at compile time with CONFIG+=experimental
// Clean and slightly faster
module gear_concat(
	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
	thickness       = 6,    //thickness of gear in mm
	twist           = 0,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	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
) {
  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-(c-p)-clearance;                        //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
  linear_extrude(height = thickness, center = false, convexity = 10, twist = twist)
    polygon(
      points = gear_points(r, b, c, k, number_of_teeth, number_of_teeth),
      paths = [ range(15*(number_of_teeth)) ]
    );
};

// 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-(c-p)-clearance;                        //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 gear_points(r, b, c, k, number_of_teeth, i) =
  (i <= 0 ? [] : concat(
    gear_points(r, b, c, k, number_of_teeth, i-1),
    tooth_base_points(r, b, c, k, number_of_teeth, i)
  ));

function tooth_base_points(r, b, c, k, number_of_teeth, i) =
  (r < b ? concat(
    [ polar(r, i*360/number_of_teeth + k) ],
    tooth_points(r, b, c, k, number_of_teeth, i),
    [ polar(r, i*360/number_of_teeth - k),
      polar(r, i*360/number_of_teeth + 180/number_of_teeth) ]
  ) : concat(
    tooth_points(r, b, c, k, number_of_teeth, i),
    [ polar(r, 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));


// Original gear() module
// Teeth shape is good, but the body is composed from individual sectors;
// this sometimes results in bad "degenerate" faces due to floating point issues.
// Can be checked with FreeCAD 'mesh evaluation'
module gear_original(
	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
	thickness       = 6,    //thickness of gear in mm
	hole_diameter   = 0,    //diameter of the hole in the center, in mm
	twist           = 0,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	teeth_to_hide   = 0,    //number of teeth to delete to make this only a fraction of a circle
	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
) {
  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-(c-p)-clearance;                        //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
  difference() {
    for (i = [0:number_of_teeth-teeth_to_hide-1])
      rotate([0,0,i*360/number_of_teeth])
        linear_extrude(height = thickness, center = false, convexity = 10, twist = twist)
          polygon(
            points=[
              [0, -hole_diameter/10],
              polar(r, -181/number_of_teeth),
              polar(r, r<b ? k : -180/number_of_teeth),
              q7(0/5,r,b,c,k, 1),q7(1/5,r,b,c,k, 1),q7(2/5,r,b,c,k, 1),q7(3/5,r,b,c,k, 1),q7(4/5,r,b,c,k, 1),q7(5/5,r,b,c,k, 1),
              q7(5/5,r,b,c,k,-1),q7(4/5,r,b,c,k,-1),q7(3/5,r,b,c,k,-1),q7(2/5,r,b,c,k,-1),q7(1/5,r,b,c,k,-1),q7(0/5,r,b,c,k,-1),
              polar(r, r<b ? -k : 180/number_of_teeth),
              polar(r, 181/number_of_teeth),
            ],
            paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]]
          );
    cylinder(h=2*thickness+1, r=hole_diameter/2, center=true, $fn=20);
  }
};

// these 4 functions are used by gear
function polar(r,theta)   = r*[sin(theta), cos(theta)];                            //convert polar to cartesian coordinates
function iang(r1,r2)      = sqrt((r2/r1)*(r2/r1) - 1)/3.1415926*180 - acos(r1/r2); //unwind a string this many degrees to go from radius r1 to radius r2
function q7(f,r,b,r2,t,s) = q6(b,s,t,(1-f)*max(b,r)+f*r2);                         //radius a fraction f up the curved side of the tooth
function q6(b,s,t,d)      = polar(d,s*(iang(b,d)+t));                              //point at radius d on the involute curve
