// Lovcad public example: single-stage planetary gear reducer (5:1). // Involute gears (20° pressure angle) on a fixed ring; the carrier is the output. // Assembly condition: (sun + ring) / planets is an integer, so the planets sit 120° apart. const DEG = Math.PI / 180; const inv = (a) => Math.tan(a) - a; // One closed outline of an involute gear as a pen path: each flank is a // three-point arc through the involute (a few faces per tooth keeps the STEP // small), the tip and root follow their circles. "Teeth" point outward; a ring // gear uses the same outline as its cavity, so its teeth point inward. // backlash (mm on the pitch circle) thins the teeth; a cavity passes it negative. function gearPen(R, z, m, from, to, phase, backlash) { const r = (m * z) / 2; const rb = r * Math.cos(20 * DEG); const half = Math.PI / (2 * z) + inv(20 * DEG) - backlash / (2 * r); const flank = (rr) => half - inv(Math.acos(Math.min(1, rb / rr))); const start = Math.max(from, rb); const mid = (start + to) / 2; const at = (rr, a) => [rr * Math.cos(a), rr * Math.sin(a)]; const pitch = (2 * Math.PI) / z; let pen = null; for (let i = 0; i < z; i++) { const c = phase + i * pitch; const first = at(from, c - flank(start)); if (!pen) pen = R.draw(first); else pen = pen.threePointsArcTo(first, at(from, c - pitch / 2)); if (from < rb) pen = pen.lineTo(at(start, c - flank(start))); pen = pen .threePointsArcTo(at(to, c - flank(to)), at(mid, c - flank(mid))) .threePointsArcTo(at(to, c + flank(to)), at(to, c)) .threePointsArcTo(at(start, c + flank(start)), at(mid, c + flank(mid))); if (from < rb) pen = pen.lineTo(at(from, c + flank(start))); } return pen .threePointsArcTo( at(from, phase - flank(start)), at(from, phase - pitch / 2) ) .close(); } const gearPrism = (drawing, z0, height) => drawing.sketchOnPlane('XY', z0).extrude(height); function cutAll(shape, tools) { let out = shape; for (const tool of tools) { const next = out.cut(tool); out.delete(); tool.delete(); out = next; } return out; } function fuseAll(shapes) { let out = shapes[0]; for (const shape of shapes.slice(1)) { const next = out.fuse(shape); out.delete(); shape.delete(); out = next; } return out; } export function build(R, p) { const { module: m, sunTeeth: zs, planetTeeth: zp, faceWidth: w } = p; const zr = zs + 2 * zp; const centre = (m * (zs + zp)) / 2; const planets = [0, 1, 2].map((i) => (i * 2 * Math.PI) / 3); // Mesh phasing: the sun has a tooth at 0°; each planet turns so a gap faces // the sun, and the ring is turned so the first planet's tooth sits in a ring gap. const betaOf = (phi) => phi + Math.PI - (Math.PI - phi * zs) / zp; const beta0 = betaOf(planets[0]); const gamma = planets[0] - ((planets[0] - beta0) * zp) / zr; // Ring gear housing: toothed bore above the carrier pocket, bolted flange. const ringOuter = (m * zr) / 2 + 3.5 * m; let ring = R.makeCylinder(ringOuter, w + p.flange + 6, [0, 0, -p.flange - 6]); const flange = R.makeCylinder(ringOuter + 12, p.flange, [ 0, 0, -p.flange - 6, ]); ring = fuseAll([ring, flange]); const cavity = gearPrism( gearPen( R, zr, m, (m * zr) / 2 - m, (m * zr) / 2 + 1.25 * m, gamma, -p.backlash ), 0, w + 1 ); const pocket = R.makeCylinder(centre + 12, 7, [0, 0, -6.5]); const bore = R.makeCylinder(14, p.flange + 2, [0, 0, -p.flange - 7]); const bolts = Array.from({ length: p.bolts }, (_, i) => { const a = (i * 2 * Math.PI) / p.bolts + Math.PI / p.bolts; return R.makeCylinder(p.boltHole / 2, p.flange + 2, [ (ringOuter + 6) * Math.cos(a), (ringOuter + 6) * Math.sin(a), -p.flange - 7, ]); }); ring = cutAll(ring, [cavity, pocket, bore, ...bolts]); // Sun gear with its input shaft and a key flat. let sun = gearPrism( gearPen(R, zs, m, (m * zs) / 2 - 1.25 * m, (m * zs) / 2 + m, 0, p.backlash), 0, w ); const input = R.makeCylinder(p.inputShaft / 2, p.shaftLength, [0, 0, w]); sun = fuseAll([sun, input]); const key = R.makeBox( [p.inputShaft / 2 - 1.2, -2.5, w + 6], [p.inputShaft / 2 + 1, 2.5, w + p.shaftLength + 1] ); sun = cutAll(sun, [key]); // Three planets, each with a bore for its carrier pin. const planetParts = planets.map((phi) => { const gear = gearPrism( gearPen( R, zp, m, (m * zp) / 2 - 1.25 * m, (m * zp) / 2 + m, 0, p.backlash ), 0, w ); const hole = R.makeCylinder(p.pin / 2 + 0.3, w + 2, [0, 0, -1]); const holed = cutAll(gear, [hole]); return holed .rotate(betaOf(phi) / DEG, [0, 0, 0], [0, 0, 1]) .translate([centre * Math.cos(phi), centre * Math.sin(phi), 0]); }); // Carrier: plate, three pins and the output shaft. const plate = R.makeCylinder(centre + 10, 5, [0, 0, -6]); const pins = planets.map((phi) => R.makeCylinder(p.pin / 2, w + 5, [ centre * Math.cos(phi), centre * Math.sin(phi), -1, ]) ); const output = R.makeCylinder(p.outputShaft / 2, p.shaftLength + p.flange, [ 0, 0, -6 - p.shaftLength - p.flange, ]); const carrier = fuseAll([plate, ...pins, output]); return [ { shape: ring, name: 'Ring gear housing', color: '#d8d2c5' }, { shape: sun, name: 'Sun gear and input shaft', color: '#e0432b' }, ...planetParts.map((shape, i) => ({ shape, name: `Planet gear ${i + 1}`, color: '#9aa0a3', })), { shape: carrier, name: 'Carrier and output shaft', color: '#4b4f52' }, ]; }