# Gear

> Spur Gear Generator

- Skill: `ghbalf/gear` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ghbalf/gear`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ghbalf/gear/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ghbalf (https://skillmd.com/u/ghbalf)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/ghbalf/gear

---

# Spur Gear Generator

Create an involute spur gear using FreeCAD's Part module.

## Parameters to extract from user request
- **Module** (m): tooth size parameter, default 2.0mm
- **Number of teeth** (z): default 20
- **Pressure angle**: default 20 degrees
- **Face width** (thickness): default 10mm
- **Bore diameter**: center hole, default 5mm (0 = no bore)

## Derived dimensions
- Pitch diameter: d = m * z
- Tip diameter: da = m * (z + 2)
- Root diameter: df = m * (z - 2.5)

## Construction

Use `execute_code` with the complete gear generation code below.
Replace the parameter values at the top with the user's requested values.

```python
import math
import Part
import FreeCAD as App

# === PARAMETERS (replace with user values) ===
module = 2.0
teeth = 20
pressure_angle_deg = 20
face_width = 10.0
bore_dia = 5.0

# === DERIVED ===
pitch_r = module * teeth / 2.0
base_r = pitch_r * math.cos(math.radians(pressure_angle_deg))
tip_r = pitch_r + module
root_r = pitch_r - 1.25 * module

def involute_pts(base_r, tip_r, n=20):
    max_angle = math.sqrt((tip_r / base_r) ** 2 - 1)
    pts = []
    for i in range(n):
        t = i * max_angle / (n - 1)
        x = base_r * (math.cos(t) + t * math.sin(t))
        y = base_r * (math.sin(t) - t * math.cos(t))
        pts.append(App.Vector(x, y, 0))
    return pts

def rotate_pt(pt, angle_rad):
    c, s = math.cos(angle_rad), math.sin(angle_rad)
    return App.Vector(c * pt.x - s * pt.y, s * pt.x + c * pt.y, 0)

def pt_angle(pt):
    return math.atan2(pt.y, pt.x)

# Involute half-tooth thickness angle
inv_alpha = math.tan(math.radians(pressure_angle_deg)) - math.radians(pressure_angle_deg)
half_thick_angle = math.pi / (2 * teeth) + inv_alpha

# Generate involute profile for one tooth, centered on +Y axis
right_involute = involute_pts(base_r, tip_r, 20)
right_side = [rotate_pt(p, -half_thick_angle + math.pi / 2) for p in right_involute]
left_side = [App.Vector(-p.x, p.y, 0) for p in right_side]

# Key points
left_base, left_tip = left_side[0], left_side[-1]
right_base, right_tip = right_side[0], right_side[-1]
lb_a, rb_a = pt_angle(left_base), pt_angle(right_base)

# Points on root circle at flank angles
lb_on_root = App.Vector(root_r * math.cos(lb_a), root_r * math.sin(lb_a), 0)
rb_on_root = App.Vector(root_r * math.cos(rb_a), root_r * math.sin(rb_a), 0)

# Build closed tooth profile: root → left involute → tip arc → right involute → root arc
tooth_edges = []

# Radial: left root → left base
if lb_on_root.distanceToPoint(left_base) > 0.01:
    tooth_edges.append(Part.LineSegment(lb_on_root, left_base).toShape())

# Left involute BSpline (base → tip)
bs_left = Part.BSplineCurve()
bs_left.interpolate(left_side)
tooth_edges.append(bs_left.toShape())

# Tip arc (left tip → right tip)
tip_mid_a = (pt_angle(left_tip) + pt_angle(right_tip)) / 2
tip_mid = App.Vector(tip_r * math.cos(tip_mid_a), tip_r * math.sin(tip_mid_a), 0)
tooth_edges.append(Part.Arc(left_tip, tip_mid, right_tip).toShape())

# Right involute BSpline (tip → base)
bs_right = Part.BSplineCurve()
bs_right.interpolate(list(reversed(right_side)))
tooth_edges.append(bs_right.toShape())

# Radial: right base → right root
if right_base.distanceToPoint(rb_on_root) > 0.01:
    tooth_edges.append(Part.LineSegment(right_base, rb_on_root).toShape())

# Root arc (right root → left root, closing the profile)
root_mid_a = (lb_a + rb_a) / 2
root_mid = App.Vector(root_r * math.cos(root_mid_a), root_r * math.sin(root_mid_a), 0)
tooth_edges.append(Part.Arc(rb_on_root, root_mid, lb_on_root).toShape())

# Create one tooth solid
tooth_wire = Part.Wire(tooth_edges)
tooth_face = Part.Face(tooth_wire)
tooth_solid = tooth_face.extrude(App.Vector(0, 0, face_width))

# Build gear: base cylinder + all teeth fused together
gear = Part.makeCylinder(root_r, face_width)
for t in range(teeth):
    rotated = tooth_solid.copy()
    if t > 0:
        rotated.rotate(App.Vector(0, 0, 0), App.Vector(0, 0, 1), t * 360.0 / teeth)
    gear = gear.fuse(rotated)
gear = gear.removeSplitter()

# Cut bore hole
if bore_dia > 0:
    bore = Part.makeCylinder(bore_dia / 2, face_width + 2, App.Vector(0, 0, -1))
    gear = gear.cut(bore)

# Add to document
gear_obj = App.ActiveDocument.addObject("Part::Feature", "Gear_M{}_Z{}".format(module, teeth))
gear_obj.Label = "Gear M{} Z{}".format(module, teeth)
gear_obj.Shape = gear
App.ActiveDocument.recompute()

print("Gear created: pitch dia={:.1f}mm, tip dia={:.1f}mm, root dia={:.1f}mm".format(
    pitch_r * 2, tip_r * 2, root_r * 2))
```

## Important
- Copy the COMPLETE code block above into `execute_code`, only changing the parameter values at the top
- Do NOT try to use PartDesign Body/Sketch for the gear — use Part::Feature directly
- Do NOT use `App.Gui` — it may not be available in the sandbox
- Label the gear clearly with its parameters: "Gear M2 Z24"
- The gear is centered at the origin

