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).