app
Adom Desktop - Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Desktop: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
"""Video-friendly view control for the Fusion Electronics (EAGLE) editor.
One bridge call = a whole smooth motion, because the loop runs HERE inside the add-in
(tight timing + a repaint per frame), not as N round-trips from the bridge server.
fusion_electron_zoom - smooth zoom in/out (or fit)
fusion_electron_pan - smooth pan/zoom to a board-coordinate box
fusion_electron_select - select a part by name (or x/y) and surface its properties
All EAGLE motion is `app.executeTextCommand("Electron.run WINDOW ...")`; each frame is
forced to render with `Electron.ImmediateRepaint /full` (the 2D analogue of vp.refresh()).
"""
import time
import adsk.core
# Last view box we set, in board mm (x1, y1, x2, y2). Lets pan interpolate from where
# the view actually is. None until the first pan establishes it.
_view_box = None
def _run(app, eagle_cmd):
return app.executeTextCommand(f"Electron.run {eagle_cmd}")
def _repaint(app):
try:
app.executeTextCommand("Electron.ImmediateRepaint /full")
except Exception:
pass
def handle_electron_zoom(app: adsk.core.Application, args: dict) -> dict:
"""Smoothly zoom the schematic / 2D board view.
Args:
fit: bool - zoom to fit the whole sheet/board (ignores factor).
factor: float - total zoom multiple (>1 = in, <1 = out). Default 2.0.
steps: int - frames over which to ramp (smoothness). Default 16.
frameDelayMs: int - ms between frames. Default 35.
"""
if args.get("fit"):
_run(app, "WINDOW FIT")
_repaint(app)
return {"success": True, "output": "Zoomed to fit."}
factor = float(args.get("factor", 2.0))
steps = max(1, int(args.get("steps", 16)))
delay = float(args.get("frameDelayMs", 35)) / 1000.0
per = factor ** (1.0 / steps) # geometric per-frame step -> perceptually even
for _ in range(steps):
_run(app, f"WINDOW {per:.5f}")
_repaint(app)
adsk.doEvents()
time.sleep(delay)
return {
"success": True,
"output": f"Zoomed {'in' if factor > 1 else 'out'} {factor}x over {steps} frames.",
}
def handle_electron_pan(app: adsk.core.Application, args: dict) -> dict:
"""Smoothly pan + zoom the view to frame a board-coordinate box.
Coords are board millimetres (get part positions from fusion_board_info). The view
animates from wherever it last was (tracked) to the target box.
Args:
x1, y1, x2, y2: required floats - the target rectangle (board mm).
steps: int - frames. Default 16.
frameDelayMs: int - ms between frames. Default 35.
"""
global _view_box
try:
tx1, ty1 = float(args["x1"]), float(args["y1"])
tx2, ty2 = float(args["x2"]), float(args["y2"])
except (KeyError, TypeError, ValueError):
return {"success": False, "error": "Need x1,y1,x2,y2 (board mm). Get part positions from fusion_board_info."}
steps = max(1, int(args.get("steps", 16)))
delay = float(args.get("frameDelayMs", 35)) / 1000.0
if _view_box is None:
_run(app, f"WINDOW ({tx1:.3f} {ty1:.3f}) ({tx2:.3f} {ty2:.3f})")
_repaint(app)
_view_box = (tx1, ty1, tx2, ty2)
return {"success": True, "output": "Framed region (no animation - first view set; later pans animate).",
"data": {"box": _view_box}}
sx1, sy1, sx2, sy2 = _view_box
for i in range(1, steps + 1):
t = i / steps
x1 = sx1 + (tx1 - sx1) * t
y1 = sy1 + (ty1 - sy1) * t
x2 = sx2 + (tx2 - sx2) * t
y2 = sy2 + (ty2 - sy2) * t
_run(app, f"WINDOW ({x1:.3f} {y1:.3f}) ({x2:.3f} {y2:.3f})")
_repaint(app)
adsk.doEvents()
time.sleep(delay)
_view_box = (tx1, ty1, tx2, ty2)
return {"success": True,
"output": f"Panned to ({tx1:.1f},{ty1:.1f})-({tx2:.1f},{ty2:.1f}) over {steps} frames.",
"data": {"box": _view_box}}
def handle_electron_select(app: adsk.core.Application, args: dict) -> dict:
"""Select a part (by reference designator, or by x/y board coords) and read its
properties - the way clicking a part in the editor reveals the properties panel.
Args:
name (or component): str - reference designator (e.g. "R1"). Cleanest.
x, y: floats - board-mm point to click/select (alternative).
properties: bool - also return the selected device's info. Default true.
"""
name = args.get("name") or args.get("component")
try:
if name:
sel = app.executeTextCommand(f"Electron.SelectbyName {name}")
elif "x" in args and "y" in args:
app.executeTextCommand(f"Electron.LeftButtonDown {args['x']} {args['y']}")
sel = app.executeTextCommand(f"Electron.Select {args['x']} {args['y']}")
else:
return {"success": False, "error": "Provide {name} (reference designator) or {x,y} (board mm)."}
except Exception as e:
return {"success": False, "error": f"Select failed: {e}"}
_repaint(app)
info = ""
if args.get("properties", True):
try:
info = app.executeTextCommand("Electron.sch_get_deviceinfo")
except Exception:
pass
return {
"success": True,
"output": f"Selected {name or (str(args.get('x')) + ',' + str(args.get('y')))}.",
"data": {"selection": str(sel)[:300], "deviceInfo": str(info)[:600]},
}