SpurGear - 3D CAD Model
blueprint

SpurGear - 3D CAD Model

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.

3D Model Components

Blueprint Script (TypeScript)

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 }