diff --git a/designs/guitar_tuning_machine_head/family.json b/designs/guitar_tuning_machine_head/family.json new file mode 100644 index 0000000..d62ff01 --- /dev/null +++ b/designs/guitar_tuning_machine_head/family.json @@ -0,0 +1,8 @@ +{ + "family": "guitar_tuning_machine_head", + "standard": null, + "base_plane": "XY", + "description": "Sealed geared guitar tuning machine (Grover Rotomatic style): baseplate, sealed gear housing, string post with press-in bushing collar, worm/key shaft out the housing side, and a locating-screw hole. One solid.", + "source": "Grover Rotomatic 102 + Gotoh SG381 dimensioned drawings (post/bushing/housing/mount); proportion for housing depth and key shaft", + "contributor": "BenchCAD-org" +} diff --git a/designs/guitar_tuning_machine_head/part.py b/designs/guitar_tuning_machine_head/part.py new file mode 100644 index 0000000..54983eb --- /dev/null +++ b/designs/guitar_tuning_machine_head/part.py @@ -0,0 +1,50 @@ +"""guitar_tuning_machine_head — the parametric part. + +A sealed geared guitar tuning machine (Grover Rotomatic style): a baseplate that +sits against the headstock rear, a sealed gear housing behind it, a string post +standing up through the peg-hole with a press-in bushing collar at its base, a +worm/key shaft out the side of the housing, and a small locating-screw hole in +the baseplate. Built in one piece (as cast + assembled), a single solid. + +Interface + examples: docs/DESIGN_SPEC.md +""" + +import cadquery as cq + + +def build(baseplate_l, baseplate_w, plate_t, housing_w, housing_h, + post_d, post_h, bushing_od, worm_d, mount_d, worm_both_sides): + # baseplate against the headstock, centred at z=0 + result = cq.Workplane("XY").box(baseplate_l, baseplate_w, plate_t) + + # sealed gear housing behind the baseplate (-Z) + result = result.union( + cq.Workplane("XY").box(housing_w, baseplate_w * 0.85, housing_h) + .translate((0.0, 0.0, -(plate_t / 2.0 + housing_h / 2.0))) + ) + + # string post up through the peg-hole, with the press-in bushing collar + result = result.union( + cq.Workplane("XY").workplane(offset=plate_t / 2.0).circle(post_d / 2.0).extrude(post_h) + ) + result = result.union( + cq.Workplane("XY").workplane(offset=plate_t / 2.0).circle(bushing_od / 2.0).extrude(plate_t * 0.7) + ) + + # worm / key shaft out the side of the housing (one side, or both) + z_house = -(plate_t / 2.0 + housing_h / 2.0) + reach = baseplate_w * 0.9 + shaft = cq.Workplane("XZ").circle(worm_d / 2.0).extrude(reach) # -Y direction + shaft = shaft.translate((0.0, baseplate_w * 0.3, z_house)) + result = result.union(shaft) + if worm_both_sides: + shaft2 = cq.Workplane("XZ").circle(worm_d / 2.0).extrude(-reach) # +Y direction + shaft2 = shaft2.translate((0.0, -baseplate_w * 0.3, z_house)) + result = result.union(shaft2) + + # locating-screw hole near the +X end of the baseplate + mount_x = baseplate_l / 2.0 - mount_d - 2.0 + hole = cq.Workplane("XY").circle(mount_d / 2.0).extrude(200.0).translate((mount_x, 0.0, -100.0)) + result = result.cut(hole) + + return result diff --git a/designs/guitar_tuning_machine_head/preview.png b/designs/guitar_tuning_machine_head/preview.png new file mode 100644 index 0000000..5a26dbb Binary files /dev/null and b/designs/guitar_tuning_machine_head/preview.png differ diff --git a/designs/guitar_tuning_machine_head/preview_extremes.png b/designs/guitar_tuning_machine_head/preview_extremes.png new file mode 100644 index 0000000..9f80b9c Binary files /dev/null and b/designs/guitar_tuning_machine_head/preview_extremes.png differ diff --git a/designs/guitar_tuning_machine_head/preview_hard_zoom.png b/designs/guitar_tuning_machine_head/preview_hard_zoom.png new file mode 100644 index 0000000..13b7d4c Binary files /dev/null and b/designs/guitar_tuning_machine_head/preview_hard_zoom.png differ diff --git a/designs/guitar_tuning_machine_head/preview_views.png b/designs/guitar_tuning_machine_head/preview_views.png new file mode 100644 index 0000000..6270165 Binary files /dev/null and b/designs/guitar_tuning_machine_head/preview_views.png differ diff --git a/designs/guitar_tuning_machine_head/spec.py b/designs/guitar_tuning_machine_head/spec.py new file mode 100644 index 0000000..fc5c887 --- /dev/null +++ b/designs/guitar_tuning_machine_head/spec.py @@ -0,0 +1,121 @@ +"""guitar_tuning_machine_head — the benchmark generator spec. + +A sealed geared guitar tuning machine (Grover Rotomatic / Gotoh SG381 style). +PARAM_SPEC declares each build() parameter; check() holds the engineering rules +a reviewer audits. Nothing is coupled, so there is no refine(). +Spec: docs/DESIGN_SPEC.md + +Sources: +- Grover Rotomatic 102 dimensioned drawing (WD Music): post Ø6, bushing Ø7.8-9.9, + M8 bushing thread, housing ~27 x 24 mm, post height ~27 mm, mount screw Ø2.6. +- Gotoh SG381 drawing: post Ø6, hex bushing OD 14 mm, housing ~27 x 22 mm. +- Gear ratio (14:1-18:1) is internal and not a CAD dimension here. +""" + + +# ── 1. PARAM_SPEC ──────────────────────────────────────────────────────────── +PARAM_SPEC = { + "baseplate_l": dict( + desc="baseplate length (along the string line)", + unit="mm", + range={"easy": (25.0, 30.0), "medium": (22.0, 38.0), "hard": (20.0, 45.0)}, + source="Grover/Gotoh housing footprint ~27 mm -> proportion", + askable=True, + ), + "baseplate_w": dict( + desc="baseplate width", + unit="mm", + range={"easy": (12.0, 15.0), "medium": (11.0, 16.0), "hard": (10.0, 18.0)}, + source="Grover baseplate ~13-15 mm -> proportion", + askable=True, + ), + "plate_t": dict( + desc="baseplate thickness", + unit="mm", + range={"easy": (1.2, 2.0), "medium": (1.0, 2.5), "hard": (1.0, 3.0)}, + source="proportion (die-cast baseplate)", + askable=True, + ), + "housing_w": dict( + desc="gear-housing width", + unit="mm", + range={"easy": (18.0, 24.0), "medium": (15.0, 26.0), "hard": (14.0, 28.0)}, + source="Grover/Gotoh sealed housing ~24-27 mm -> proportion", + askable=True, + ), + "housing_h": dict( + desc="gear-housing depth behind the baseplate", + unit="mm", + range={"easy": (12.0, 18.0), "medium": (10.0, 20.0), "hard": (10.0, 24.0)}, + source="proportion (sealed worm-gear housing depth)", + askable=True, + ), + "post_d": dict( + desc="string-post diameter", + unit="mm", + range={"easy": (5.8, 6.3), "medium": (5.5, 6.5), "hard": (5.5, 6.5)}, + source="Grover 6.0 / Grover Super 6.3 / Gotoh 6.0 mm", + askable=True, + ), + "post_h": dict( + desc="string-post height above the baseplate", + unit="mm", + range={"easy": (24.0, 28.0), "medium": (18.0, 30.0), "hard": (15.0, 32.0)}, + source="Grover post height ~27 mm -> proportion", + askable=True, + ), + "bushing_od": dict( + desc="press-in bushing collar outer diameter", + unit="mm", + range={"easy": (7.5, 10.0), "medium": (7.0, 14.0), "hard": (7.0, 15.0)}, + source="Grover Ø7.8 / Ø9.9 collar; Gotoh Ø14 hex bushing", + askable=True, + ), + "worm_d": dict( + desc="worm / key-shaft diameter", + unit="mm", + range={"easy": (5.0, 7.0), "medium": (4.0, 8.0), "hard": (4.0, 9.0)}, + source="proportion (worm-gear key shaft)", + askable=True, + ), + "mount_d": dict( + desc="locating-screw hole diameter", + unit="mm", + range={"easy": (2.4, 2.8), "medium": (2.0, 3.2), "hard": (2.0, 3.5)}, + source="Grover mounting screw Ø2.6 mm", + askable=True, + ), + "worm_both_sides": dict( + desc="key shaft exits both sides of the housing (1) vs one side (0)", + unit="", + range={"easy": (0, 0), "medium": (0, 1), "hard": (0, 1)}, + source="in-line (both-sides) vs single-side key-shaft variants", + choices={"easy": [0], "medium": [0, 1], "hard": [0, 1]}, + feature=True, + ), +} + + +# ── 2. check ───────────────────────────────────────────────────────────────── +def check(p: dict) -> list[str]: + """Engineering constraints (empty = valid). Each cites its rule.""" + bad = [] + + # bushing must surround the post with a wall, and fit on the baseplate + if p["bushing_od"] < p["post_d"] + 1.5: + bad.append("bushing_od < post_d+1.5: bushing collar must surround the post with wall") + if p["bushing_od"] > p["baseplate_w"] - 1.0: + bad.append("bushing_od > baseplate_w-1: bushing wider than the baseplate") + + # housing sits on the baseplate; worm fits inside the housing depth + if p["housing_w"] > p["baseplate_l"]: + bad.append("housing_w > baseplate_l: gear housing overhangs the baseplate") + if p["worm_d"] > p["housing_h"] - 2.0: + bad.append("worm_d > housing_h-2: key shaft does not fit the housing depth") + + # the locating-screw hole (near the +X end) must clear the bushing collar + mount_x = p["baseplate_l"] / 2.0 - p["mount_d"] - 2.0 + if mount_x - p["mount_d"] / 2.0 < p["bushing_od"] / 2.0 + 2.0: + bad.append("mount hole overlaps the bushing: baseplate too short for the collar + screw") + + return bad