Drawing a Spur Gear from Scratch
using RasmahOverview
A spur gear is the standard machine element of mechanical power transmission: two gears mesh and exchange torque in the ratio of their tooth counts — a 20-tooth gear driving a 10-tooth gear doubles the speed and halves the torque. Gears are everywhere a shaft turns: gearboxes, lathes, pumps, wind-turbine yaw drives, automotive timing trains.
This tutorial draws a real one from scratch, the way a CAD program would: first the tooth’s involute flank (the curve that makes gear teeth roll smoothly instead of sliding), then one tooth as a closed sketch, then the 20-tooth blank by a circular pattern over a root disc, an extrusion to the face width, and a boolean bore for the shaft. Along the way every drawing feature gets its moment: the profile sketch, the pattern, the extrusion, the boolean — and the result is verified against the module-system formulas from the machinery’s handbook: the outside diameter m(z+2), the tooth thickness at the pitch circle πm/2, and the mass of the finished part.
The module system
Gear dimensions are governed by one number: the module m (in millimetres), the ratio of the pitch diameter to the tooth count z. All the standard diameters follow from m and z — the pitch circle, where the two meshing gears touch:
\[ d = m z, \]
the addendum circle (the tooth tips, the outside diameter):
\[ d_a = m(z + 2), \]
the dedendum circle (the tooth roots):
\[ d_f = m(z - 2.5), \]
and the base circle from which the involute flanks unwind, set by the pressure angle (20° is the standard) d_b = d\cos 20°. Our gear: module 2, 20 teeth, 12 mm face width, with a Ø12 shaft bore.
m, z, φ = 2e-3, 20, deg2rad(20.0) # module 2, 20 teeth, 20° pressure angle
rp = m * z / 2 # pitch radius (20 mm)
ra = rp + m # addendum (tip) radius (22 mm)
rf = rp - 1.25 * m # dedendum (root) radius (17.5 mm)
rb = rp * cos(φ) # base radius (~18.8 mm)
println("pitch diameter: ", round(2rp * 1e3; digits = 1), " mm")
println("outside diameter: ", round(2ra * 1e3; digits = 1), " mm (= m(z+2) = 44 mm)")
println("root diameter: ", round(2rf * 1e3; digits = 1), " mm")
println("base diameter: ", round(2rb * 1e3; digits = 1), " mm")pitch diameter: 40.0 mm
outside diameter: 44.0 mm (= m(z+2) = 44 mm)
root diameter: 35.0 mm
base diameter: 37.6 mm
The involute flank
Two gears roll without slipping when their teeth touch along a curve called the involute of the base circle. The picture behind it: wrap a string around the base circle, fix a pen at the string’s end, and unwind it while keeping it taut — the pen traces the involute. Unwinding by roll angle t places the pen at
\[ x(t) = r_b (\cos t + t \sin t), \qquad y(t) = r_b (\sin t - t \cos t), \]
which starts on the base circle (t = 0) and rolls out to the tooth tip at t = sqrt((r_a/r_b)² − 1). A real tooth is built from two mirrored involute flanks, offset from the tooth’s centerline by half the tooth’s angular thickness at the base circle, δ = πm/(2d) + inv φ (the involute function inv φ = tan φ − φ), closed by a tip arc and a root arc:
inv(a) = tan(a) - a
δ = (π * m / 2) / (2 * rp) + inv(φ) # half tooth angle at the base circle
tmax = sqrt((ra / rb)^2 - 1) # roll angle reaching the tooth tip
involute(t) = rb .* [cos(t) + t * sin(t), sin(t) - t * cos(t)]
rot(p, θ) = [p[1]*cos(θ) - p[2]*sin(θ), p[1]*sin(θ) + p[2]*cos(θ)]
flank1 = [rot(involute(t), -δ) for t in range(0, tmax; length = 10)]
tip_a1 = -δ + inv(acos(rb / ra)) # flank tip angle
tip = [[ra*cos(θ), ra*sin(θ)] for θ in range(tip_a1, -tip_a1; length = 9)]
flank2 = [[x, -y] for (x, y) in reverse(flank1)]
root = [[rf*cos(θ), rf*sin(θ)] for θ in range(δ, -δ; length = 7)]
tooth = polygon_2d(vcat(flank1, tip[2:end], flank2[2:end], root[2:end]))
println("one tooth area: ", round(ustrip(uconvert(u"mm^2", area(tooth))); digits = 2), " mm²")one tooth area: 12.83 mm²
That tooth is one closed polygon_2d sketch — an exact straight-edged profile whose area is the shoelace formula. Extruded to the face width, it is the single tooth of the gear:
vtk(extrude(tooth, 12e-3); n = 24)From one tooth to the gear blank
The gear is the root disc plus 20 teeth spaced one angular pitch apart — the exact job of the circular pattern:
# 1. one tooth, 2. repeated 20× about the centre, 3. fused to the root disc
sketch = union(circle_2d(rf), circular_pattern(tooth, z))
gear = extrude(sketch, 12e-3)Extrude{Union2D{Circle2D{Float64}, CircularPattern2D{Polygon2D{Float64}, Int64}}, Float64}(Union2D{Circle2D{Float64}, CircularPattern2D{Polygon2D{Float64}, Int64}}(Circle2D{Float64}(0.0175), CircularPattern2D{Polygon2D{Float64}, Int64}(Polygon2D{Float64}([[0.01871185984919612, -0.0017536218542883628], [0.018754762491244293, -0.0017556986072587589], [0.0188836049802581, -0.0017541869593180259], [0.0190977059623901, -0.0017374870665281146], [0.019395210511419285, -0.0016941012664606987], [0.019773102610928947, -0.0016127395107191531], [0.0202272265066444, -0.001482423457236963], [0.02075231679676249, -0.0012925885067626604], [0.021342037072044656, -0.00103318308022131], [0.02198902686247645, -0.0006947644502195804] … [0.0190977059623901, 0.0017374870665281146], [0.0188836049802581, 0.0017541869593180259], [0.018754762491244293, 0.0017556986072587589], [0.01871185984919612, 0.0017536218542883628], [0.01746605390817197, 0.0010894773411276907], [0.017491511418299584, 0.000545003030721174], [0.0175, 0.0], [0.017491511418299584, -0.000545003030721174], [0.01746605390817197, -0.0010894773411276907], [0.017423652166549113, -0.00163289472382896]]), 20)), 0.012)
println("gear face area: ", round(ustrip(uconvert(u"mm^2", area(sketch))); digits = 1), " mm²")
println("handbook: ", round((π * rf^2 + z * ustrip(area(tooth))) * 1e6; digits = 1), " mm² (disc + z teeth)")gear face area: 1217.1 mm²
handbook: 1218.7 mm² (disc + z teeth)
The two agree to a fraction of a percent — the sketch area is the disc plus twenty tooth areas, the decomposition the handbook would write down. The patterned gear blank:
vtk(gear; n = 16)The tooth thickness at the pitch circle
The defining gear check is the tooth thickness at the pitch circle: by construction it must equal half the circular pitch, πm/2 = 3.14 mm — this is the number that makes one gear’s tooth exactly fill half a pitch, so its mate fills the other half. Measure it from the sketch itself: scan the signed distance field along the pitch circle and count the angular span where material is present (teeth stick out past the root disc, so at r = r_p only teeth register):
θs = range(0, 2π; length = 4000)
inside = sketch.(rp .* cos.(θs), rp .* sin.(θs)) .< 0
thickness = rp * sum(inside) / 4000 * 2π / z # per tooth
println("measured tooth thickness: ", round(thickness * 1e3; digits = 3), " mm")
println("handbook π m / 2: ", round(π * m / 2 * 1e3; digits = 3), " mm")measured tooth thickness: 3.135 mm
handbook π m / 2: 3.142 mm
The involute does what the theory says: the flank leaves exactly the right width of metal at the pitch circle for a conjugate mate to roll against.
The shaft bore and the mass
Finish the part the way a drawing office would: drill the shaft seat with a boolean difference, then weigh the result:
gear = difference(gear, cylinder(6e-3, 0.014)) # Ø12 bore through the hub
V = (ustrip(area(sketch)) - π * (6e-3)^2) * 0.012 # exact: face area − bore, × width
mass = density(steel) * V * 1e3
println("finished mass: ", round(mass; digits = 1), " g (", round(V * 1e9; digits = 0), " mm³)")finished mass: 104.0 g (13248.0 mm³)
A 20-tooth steel gear blank of module 2 weighs about 104 g — the number the purchasing department asks for. The classic estimate a designer would use — a solid disc to the pitch diameter, minus the bore — comes out a bit heavier, because it fills in the tooth spaces:
V_blank = (π * rp^2 - π * (6e-3)^2) * 0.012
println("pitch-blank estimate: ", round(density(steel) * V_blank * 1e3; digits = 1), " g (over by ",
round((V_blank / V - 1) * 100; digits = 1), "%)")pitch-blank estimate: 107.7 g (over by 3.6%)
Verify against the handbook, and mesh
The last check is geometric: the outside diameter must be m(z+2) = 44 mm. Read it straight from the gear’s bounds, and mesh a copy of the finished part for display:
lo, hi = bounds(gear)
println("outside diameter: ", round((hi[1] - lo[1]) * 1e3; digits = 1), " mm (handbook 44.0 mm)")outside diameter: 44.0 mm (handbook 44.0 mm)
sm = surface_mesh(gear; n = 20)
vtk(sm)The bounds check is exact (44.0 mm), and the mass above came from the exact face-area arithmetic rather than the mesh — on a part with thin tooth tips a display mesh fine enough for volume would need to be much finer than the one used here, which is why the exact area-based volume is the number to trust. The part is done: one involute tooth, patterned, extruded, bored, verified — a parametric gear, ready to be mated with its partner in assembly mates or cut on a turning or milling machine.
A meshing pair
A gear alone transmits nothing — gears work in pairs. The construction above was one line away from parametric all along: pack it into a function of the tooth count, and the 20-tooth gear’s mate is a 10-tooth pinion made by the same recipe:
spur(z) = begin
rp, ra, rf = m * z / 2, m * z / 2 + m, m * z / 2 - 1.25m
rb = rp * cos(φ)
δ = (π * m / 2) / (2 * rp) + inv(φ)
tmax = sqrt((ra / rb)^2 - 1)
involute(t) = rb .* [cos(t) + t * sin(t), sin(t) - t * cos(t)]
rot(p, θ) = [p[1]*cos(θ) - p[2]*sin(θ), p[1]*sin(θ) + p[2]*cos(θ)]
f1 = [rot(involute(t), -δ) for t in range(0, tmax; length = 10)]
tip_a1 = -δ + inv(acos(rb / ra))
tip = [[ra*cos(θ), ra*sin(θ)] for θ in range(tip_a1, -tip_a1; length = 9)]
f2 = [[x, -y] for (x, y) in reverse(f1)]
root = [[rf*cos(θ), rf*sin(θ)] for θ in range(δ, -δ; length = 7)]
union(circle_2d(rf), circular_pattern(polygon_2d(vcat(f1, tip[2:end], f2[2:end], root[2:end])), z))
end
pinion = spur(10)Union2D{Circle2D{Float64}, CircularPattern2D{Polygon2D{Float64}, Int64}}(Circle2D{Float64}(0.0075), CircularPattern2D{Polygon2D{Float64}, Int64}(Polygon2D{Float64}([[0.009258294922556257, -0.0016081658132536976], [0.00929464094312232, -0.0016122961208620422], [0.009404304132431054, -0.001616104417586764], [0.009586941749739022, -0.0016066773247084161], [0.009840505717465078, -0.0015712095887893312], [0.010161263437205654, -0.0014972039572971757], [0.01054384052869658, -0.0013726678374806074], [0.010981285052572756, -0.0011863044344969493], [0.01146515254282593, -0.0009276961201552371], [0.011985610946512555, -0.0005874778624241584] … [0.009586941749739022, 0.0016066773247084161], [0.009404304132431054, 0.001616104417586764], [0.00929464094312232, 0.0016122961208620422], [0.009258294922556257, 0.0016081658132536976], [0.007450756478512776, 0.0008580372357304208], [0.007487678999157409, 0.00042972456943615777], [0.0075, 0.0], [0.007487678999157409, -0.00042972456943615777], [0.007450756478512776, -0.0008580372357304208], [0.007389353750708224, -0.0012835307346901752]]), 10))
To mesh, the pinion’s gap must face the gear’s tooth: rotate the pinion by half of one angular pitch, and place its centre one centre-distance away — a = (z1 + z2)·m/2 = 30 mm, exactly r_p1 + r_p2:
pair = union(sketch, translate(rotate(pinion, π / 10), 30e-3, 0.0))
println("gear ratio: ", 20, ":", 10, " = 2:1")
println("centre distance: ", round((20 + 10) * m / 2 * 1e3; digits = 1), " mm = r_p1 + r_p2")gear ratio: 20:10 = 2:1
centre distance: 30.0 mm = r_p1 + r_p2
vtk(extrude(pair, 12e-3); n = 16)The pair turns at 2:1 — the pinion does two revolutions for every one of the gear — and because both flanks came from the same involute, the teeth roll on each other without sliding or binding: that is the entire reason the involute exists. Swap the tooth count, and the same six lines draw a 15:12 reduction, a 60-tooth timing gear, or the idler in between.
Next steps
- Feature operations: shell, mirror, patterns, loft — the pattern here in its full setting, alongside the other modeling features.
- Parametric CAD: engineering parts from scratch — the shaft this gear mounts on, plus flanges and brackets drawn the same way.
- Assembly mates and kinematic joints — mate two gears and watch the ratio, or drive a screw and cam.
- CAM toolpaths — cut the gear profile (gear hobbing is just a specialized toolpath).
