From a7d5b9b022606596829afe7f83526ae2b113811e Mon Sep 17 00:00:00 2001 From: awi Date: Mon, 28 Sep 2026 23:56:53 +0200 Subject: [PATCH] fix(cad): keep laptop keys when switching to a larger servo The cut on the `keyboard` sketch uses one profile: the deck, with the key outlines as holes. After switching from a smaller to a larger servo, Fusion added the new key profiles to that cut, so those keys were cut away (SG90 after FS0307: 61 keys instead of 67). fix_keyboard_cut() finds that extrude by its sketch and re-selects the deck profile (the one with the most loops) after every servo change: in the Servo Configurator, in the Parts Exporter, and when the exporter restores the previous parameters. It does nothing when the selection is already correct and keeps the timeline marker where it was. Fixes #31 Co-Authored-By: Claude Opus 5.5 --- .../TinyEngineerTools/parts_exporter.py | 1 + 3d_models/fusion/TinyEngineerTools/servo.py | 58 +++++++++++++++++++ docs/3d/parametric-design.md | 6 ++ 3 files changed, 65 insertions(+) diff --git a/3d_models/fusion/TinyEngineerTools/parts_exporter.py b/3d_models/fusion/TinyEngineerTools/parts_exporter.py index 3fb5917..e9d354c 100644 --- a/3d_models/fusion/TinyEngineerTools/parts_exporter.py +++ b/3d_models/fusion/TinyEngineerTools/parts_exporter.py @@ -301,6 +301,7 @@ def _restore_params(design, snapshot): if to_set: design.modifyParameters(to_set, values) design.computeAll() + servo.fix_keyboard_cut(design) def _bool_input(inputs, input_id): diff --git a/3d_models/fusion/TinyEngineerTools/servo.py b/3d_models/fusion/TinyEngineerTools/servo.py index 4532815..357260b 100644 --- a/3d_models/fusion/TinyEngineerTools/servo.py +++ b/3d_models/fusion/TinyEngineerTools/servo.py @@ -16,6 +16,7 @@ WORKSPACE_ID = 'FusionSolidEnvironment' PANEL_ID = 'SolidScriptsAddinsPanel' SERVO_ID_PARAM = 'servo_id' +KEYBOARD_SKETCH = 'keyboard' _handlers = [] @@ -176,6 +177,63 @@ def _apply_servo(design, servo_data, show_errors=True): return False design.computeAll() + fix_keyboard_cut(design) + return True + + +def _profiles(extrude): + """Sketch profiles an extrude uses (skips sketch text and faces).""" + try: + selection = extrude.profile + except Exception: + return [] + items = [selection] + if not adsk.fusion.Profile.cast(selection) and hasattr(selection, 'count'): + items = [selection.item(i) for i in range(selection.count)] + return [p for p in map(adsk.fusion.Profile.cast, items) if p] + + +def _keyboard_cut(design): + """Return (sketch, extrude) for the cut that uses the keyboard sketch.""" + for component in design.allComponents: + sketch = component.sketches.itemByName(KEYBOARD_SKETCH) + if not sketch: + continue + for extrude in component.features.extrudeFeatures: + profiles = _profiles(extrude) + if profiles and profiles[0].parentSketch.name == KEYBOARD_SKETCH: + return sketch, extrude + return None, None + + +def fix_keyboard_cut(design): + """Re-select the keyboard cut profile after a servo size change. + + The extrude on the `keyboard` sketch cuts the deck region around the + keys: one profile whose inner loops are the key outlines. When the key + grid grows, Fusion also maps the new key profiles into that cut, so those + keys vanish (#31). Reset the selection to the profile with the most loops. + Returns True when the selection was changed. + """ + sketch, extrude = _keyboard_cut(design) + if not extrude or sketch.profiles.count == 0: + return False + + deck_loops = max(p.profileLoops.count for p in sketch.profiles) + selected = _profiles(extrude) + if len(selected) == 1 and selected[0].profileLoops.count == deck_loops: + return False + + timeline = design.timeline + marker = timeline.markerPosition + extrude.timelineObject.rollTo(True) + try: + extrude.profile = max( + sketch.profiles, key=lambda p: p.profileLoops.count + ) + finally: + timeline.markerPosition = marker + design.computeAll() return True diff --git a/docs/3d/parametric-design.md b/docs/3d/parametric-design.md index e4c53ea..89e7ed9 100644 --- a/docs/3d/parametric-design.md +++ b/docs/3d/parametric-design.md @@ -161,6 +161,12 @@ For each selected servo the add-in applies that preset’s parameters, then expo After the run, visibility and the design’s previous servo parameters are restored. A progress dialog stays up so Fusion can paint; Cancel stops after the current part. +### Keyboard cut after a servo change + +The extrude after the `keyboard` sketch cuts the deck around the keys: one profile whose inner loops are the key outlines. When a larger servo grows the key grid, Fusion also maps the new key profiles into that cut, so those keys disappear ([#31](https://github.com/jamro/tiny-engineer/issues/31)). + +Servo Configurator and Parts Exporter (including the parameter restore after export) re-select the deck profile — the one with the most loops — after every preset change (`fix_keyboard_cut()` in [`servo.py`](../../3d_models/fusion/TinyEngineerTools/servo.py)). If you change servo parameters by hand in **Modify → Change Parameters**, edit that extrude and select only the deck profile. + ## Add a new servo 1. Measure a real unit (prefer calipers over datasheet marketing sizes).