FlowCad is the fast, free, and precise web app for 3D design and printing. Build professional parametric models with ease-no programming required, but it's there if you need it.
const { sin, cos, tan, atan2, sqrt, pow, max, PI } = Math
interface GearParams {
circularPitch: number
pressureDeg: number
depthRatio: number
clearance: number
twist: number
n?: number
}
const PARAMS: GearParams & { dim: string; teeth: number; height: number } = {
dim: 'disabled',
teeth: 12,
height: 1,
circularPitch: 10,
pressureDeg: 20,
depthRatio: 0.75,
clearance: 0,
twist: 0,
}
/**
* Create a 3D spur or helical gear.
*/
function spurGear({
teeth,
height,
circularPitch,
pressureDeg,
depthRatio,
clearance,
twist,
}: { teeth: number; height: number } & GearParams): Manifold {
const n = getCircularSegments(circularPitch / 6)
const nDiv = (n * twist * height) / circularPitch
const twistDeg = (twist * height * 360) / (teeth * circularPitch)
return gear2D(teeth, { circularPitch, pressureDeg, depthRatio, clearance, n }).extrude(
height,
nDiv,
twistDeg
)
}
/**
* Create a 2D CrossSection of an involute gear.
*/
export function gear2D(
teeth: number,
{
circularPitch = 10,
pressureDeg = 20,
depthRatio = 0.75,
clearance = 0,
n = getCircularSegments(circularPitch / 6),
}: Partial<GearParams> = {}
): CrossSection {
const pressureRad = (pressureDeg * PI) / 180
const pitchRadius = (teeth * circularPitch) / (2 * PI)
const baseRadius = pitchRadius * cos(pressureRad)
const depth = circularPitch / (2 * tan(pressureRad))
const outerRadius =
clearance < 0 ? pitchRadius + depth / 2 - clearance : pitchRadius + depth / 2
const rootRadius1 = pitchRadius - depth / 2 - clearance / 2
const rootRadius = clearance < 0 && rootRadius1 < baseRadius ? baseRadius : rootRadius1
const backlashDeg = ((clearance / (pitchRadius * cos(pressureRad))) * 180) / PI
const halfThickDeg = 90 / teeth - backlashDeg / 2
if (halfThickDeg <= 0) {
throw new Error('Clearance is too large for the circular pitch.')
}
const pitchPoint = involute(baseRadius, involuteIntersectRad(baseRadius, pitchRadius))
const pitchDeg = (atan2(pitchPoint[1], pitchPoint[0]) * 180) / PI
const minRadius = max(baseRadius, rootRadius)
const startRad = max(involuteIntersectRad(baseRadius, minRadius) - n, 0)
const stopRad = involuteIntersectRad(baseRadius, outerRadius)
const step = (stopRad - startRad) / n
const p: Vec2[] = [[0, 0]]
for (let i = 0; i <= n; ++i) {
p.push(involute(baseRadius, i * step + startRad))
}
const halftooth = new CrossSection(p)
.rotate(-pitchDeg - halfThickDeg)
.subtract(CrossSection.square(2 * outerRadius))
const tooth = halftooth.add(halftooth.mirror([0, 1]))
let gear = CrossSection.circle(
max(rootRadius, pitchRadius - (depthRatio * circularPitch) / 2 - clearance / 2),
teeth * 2
).rotate(90 / teeth)
for (let i = 0; i < teeth; ++i) {
gear = gear.add(tooth.rotate((i * 360) / teeth))
}
return gear.intersect(
CrossSection.circle(
pitchRadius + (depthRatio * circularPitch) / 2 - clearance / 2,
teeth * 3
).rotate(90 / teeth)
)
}
function involuteIntersectRad(baseRadius: number, radius: number): number {
return sqrt(pow(radius / baseRadius, 2) - 1)
}
function involute(baseRadius: number, involuteRad: number): Vec2 {
return [
baseRadius * (cos(involuteRad) + involuteRad * sin(involuteRad)),
baseRadius * (sin(involuteRad) - involuteRad * cos(involuteRad)),
]
}
// @ts-ignore: Environment-specific return
return { result: spurGear(PARAMS), PARAMS }