Bevel Gear

Bevel Gear render

gears/bevel/bevel_gear.pyOBJECT_OT_bevel_gear (object.bevel_gear, "Bevel Gear")

Straight-tooth bevel gear — a toothed frustum for shafts meeting at 90°, built via Tredgold's approximation (each axial slice is the standard flat involute profile, uniformly scaled toward the cone apex — not a true spherical involute). See README.md for family-wide conventions.

Bevel gears in this library have no hand property (straight teeth only, no spiral-bevel support) and no bore feature — the two biggest differences from every other single-gear primitive.

Properties

Property Type Default Range Notes
targetObject pointergears with bmech_moduleMatch Target; runs sync_bevel — copies module/PA, and sets mate_teeth to the target's own tooth_count
tooth_countInt168–120 (soft)This gear's own tooth count
mate_teethInt168–120 (soft)Tooth count of the mating gear — sets this gear's cone angle
moduleFloat (mm)2.00.1–20.0 (soft)Module at the large end (back face)
pressure_angle_degFloat (°)20.010–45
face_width_mmFloat (mm)8.01–60 (soft)Tooth length along the cone slant — keep ≤ cone distance / 3
n_slicesInt122–64 (soft)Axial divisions — more = smoother taper

Note the minimum tooth count here is 8, not the 5 used by spur/helical/herringbone — keeps the cone-angle math and small-end tooth size sane at the apex.

[BUG, fixed] mate_teeth/module/pressure_angle_deg weren't frozen in draw() when a target was setsync_bevel itself always updated them correctly on pick, but the panel never grayed them out the way every other gear generator does (see spur_gear.md's own module_row.enabled = not has_target), so a target pick looked like it silently did nothing, and the driven values could be immediately overwritten by hand with no visual cue that they were supposed to be locked. tooth_count stays outside the frozen column deliberately — it's this gear's own choice, not something sync_bevel touches (the target's tooth_count becomes this gear's mate_teeth, not its own tooth_count).

Making a mating pair

Two bevel gears mesh at 90° with coincident apices when their cone angles are complementary: delta_1 + delta_2 = 90°. This library derives cone angle from atan(tooth_count / mate_teeth), so to build the mating gear, create a second bevel gear with tooth_count and mate_teeth swapped — or just point its target at the first gear and let sync_bevel do the swap for you automatically.

delta         = atan(tooth_count / mate_teeth)      # this gear's cone half-angle
pitch_r       = module * tooth_count / 2             # at the large end
cone_dist L   = pitch_r / sin(delta)                 # apex-to-back-face slant distance
z_apex        = L * cos(delta) = pitch_r * mate_teeth / tooth_count
z_top         = face_width_mm * cos(delta)           # axial height of the small end
scale_top     = (L - face_width_mm) / L               # uniform XY scale at the small end

Build method

One 2D involute profile is built once at full size (the large end). For each of n_slices axial layers from z=0 to z=z_top, every vertex of that profile is uniformly scaled by (z_apex - z) / z_apex and placed at height z — this uniform per-slice scaling is Tredgold's approximation. No twist, no hand.

End caps at both z=0 and z=z_top are each a single n-gon face — no center vertex, no boolean. This is explicitly why there's no bore feature: the cap already covers the hub area under the root circle, so there's no boolean step to hook a bore into without also cutting into tooth material.

Cap fix

This used to fan each end cap from an added center vertex (like cluster_gear.py's shoulders still do). That degenerates: the shared _build_gear_profile inserts a dedendum-circle point at the same angle as the adjacent involute departure point wherever base_r > ded_r — a real undercut-flank segment, not a bug, but it puts that profile point and the newly-added center vertex on the same ray through the origin, making the fan triangle there exactly zero area. Confirmed before the fix: 28 zero-area cap faces at the default tooth_count=16, across both caps at every slice count and pressure angle tried — non_manifold was always 0 even with the defect present (a zero-area triangle still shares its short edge with exactly one real neighboring face, so edge-manifoldness was never actually broken), which is why this was cosmetic rather than a hard build failure and was safely deferred while higher-severity issues in other generators were fixed first.

Unlike the annulus family's caps (which bridge an inner AND outer loop and need bmesh.ops.triangle_fill to do that correctly — see README.md), a bevel gear's cap is a single closed loop with nothing to bridge, so the fix is simpler: a single n-gon face per cap, matching how helical_gear.py/ herringbone_gear.py already cap their own extrusions. No per-point triangles means this class of degenerate face can't occur regardless of tooth count or pressure angle. Verified clean (0 zero-area faces, 0 non-manifold edges) across tooth counts {8, 12, 16, 20, 30, 45, 60} × mate-tooth ratios {8, 16, 24, 45} × pressure angles {14.5°, 20°, 25°, 30°} — 112 combinations, all clean.

Panel warnings

The info box also shows: cone angle, mate cone angle (90° - delta), pitch/tip/root diameter at the large end, cone distance, height, tip scale, pitch diameter at the small end, and the N:Nm ratio.

Output

One object, stamped:

gear_matching.stamp_gear(obj, "bevel", module, pressure_angle_deg,
                          tooth_count=tooth_count)

Note mate_teeth and hand are not stamped — unlike helical/ herringbone gears, the bevel-matching system relies entirely on sync_bevel reading the target object's live bmech_tooth_count at pick-time, not on any stamped mate/cone data on the gear itself.

On success: self.report({'INFO'}, "Bevel gear: %d/%d teeth, δ=%.1f°, module %.1f, face %.1f mm").