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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
"""Export commands — STEP, STL, 3MF, F3D, FBX.
All 3D export commands require an active Fusion Design (3D PCB view or a CAD
design document). Switch to 3D PCB view with fusion_show_3d_board before
exporting from an electronics project.
"""
import adsk.core
import adsk.fusion
import os
import time
from pathlib import Path
# Shared hint constants — reused across every export handler.
_HINT_NO_OUTPUT_PATH = (
"Provide an outputPath arg like {\"outputPath\": \"C:\\\\path\\\\out.step\"}. "
"The path must include the file extension matching the export format."
)
_HINT_FORMAT_UNAVAILABLE = (
"This format isn't available in this Fusion build. "
"Use fusion_export_step or fusion_export_stl as a fallback."
)
_HINT_EXPORT_FAILED = (
"If this persists, call fusion_screenshot_fusion to check for a blocking dialog, "
"then fusion_send_key with 'escape' or 'enter' to dismiss it. "
"Wrong-workspace errors: call fusion_show_3d_board first."
)
_HINT_FILE_LOCKED = (
"Close the file in any viewer (Blender, Windows Explorer preview, etc.), "
"or call fusion_close_document first if it's open in Fusion, then retry."
)
def _get_active_design(app: adsk.core.Application):
"""Get the active Fusion design, or None."""
product = app.activeProduct
if product and product.productType == "DesignProductType":
return adsk.fusion.Design.cast(product)
return None
def _no_design_error():
"""Standard error when no active design is available."""
return {
"success": False,
"error": "No active design open in Fusion 360.",
"_hint": "If you're in PCB Editor (2D board), call fusion_show_3d_board to switch to 3D view. Otherwise call fusion_open_cloud_file to open a Design document, then retry.",
"data": {
"hint": "3D exports require a Design product. If you're in PCB Editor (2D board), "
"switch to 3D view first with fusion_show_3d_board.",
"recoverySteps": [
"Run fusion_show_3d_board to switch to 3D PCB view",
"Or open a .f3d design document",
"Then retry the export command",
],
},
}
def _export_success(fmt: str, output_path: str, extra_msg: str = "") -> dict:
"""Standard success response for 3D exports."""
size = os.path.getsize(output_path) if os.path.exists(output_path) else 0
return {
"success": True,
"output": f"Exported {fmt} to {output_path}",
"message": f"{fmt} file exported ({size // 1024} KB). "
f"{extra_msg}"
"Use pull_file to transfer to Docker.",
"data": {
"outputPath": output_path,
"format": fmt.lower(),
"fileSizeKB": size // 1024,
"nextSteps": [
"Use pull_file to transfer the file back to Docker",
"Export other formats: fusion_export_step, fusion_export_stl, fusion_export_3mf, fusion_export_f3d, fusion_export_fbx",
],
},
}
def handle_export_step(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as a STEP file (.step).
Industry-standard CAD interchange format. Best for mechanical CAD
interoperability (SolidWorks, CATIA, Creo, etc.).
Args:
outputPath: Full path for the output .step file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
export_mgr = design.exportManager
options = export_mgr.createSTEPExportOptions(output_path)
result = export_mgr.execute(options)
if result:
return _export_success("STEP", output_path,
"Industry-standard CAD interchange — opens in any CAD tool. ")
return {"success": False, "error": "STEP export failed", "_hint": _HINT_EXPORT_FAILED}
def handle_export_stl(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as an STL file (.stl).
Mesh-based format for 3D printing and visualization.
Args:
outputPath: Full path for the output .stl file (required).
refinement: "low", "medium", or "high" (default: "medium").
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
root = design.rootComponent
export_mgr = design.exportManager
options = export_mgr.createSTLExportOptions(root, output_path)
refinement = args.get("refinement", "medium").lower()
refinement_map = {
"low": adsk.fusion.MeshRefinementSettings.MeshRefinementLow,
"medium": adsk.fusion.MeshRefinementSettings.MeshRefinementMedium,
"high": adsk.fusion.MeshRefinementSettings.MeshRefinementHigh,
}
options.meshRefinement = refinement_map.get(
refinement, adsk.fusion.MeshRefinementSettings.MeshRefinementMedium
)
result = export_mgr.execute(options)
if result:
return _export_success("STL", output_path,
f"Mesh refinement: {refinement}. Good for 3D printing. ")
return {"success": False, "error": "STL export failed", "_hint": _HINT_EXPORT_FAILED}
def handle_export_3mf(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as a 3MF file (.3mf).
Modern 3D printing format — supports color, materials, and multi-body.
Args:
outputPath: Full path for the output .3mf file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
root = design.rootComponent
export_mgr = design.exportManager
options = export_mgr.createC3MFExportOptions(root, output_path)
result = export_mgr.execute(options)
if result:
return _export_success("3MF", output_path,
"Modern 3D printing format with color/material support. ")
return {"success": False, "error": "3MF export failed", "_hint": _HINT_EXPORT_FAILED}
def handle_export_f3d(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as a Fusion 360 archive (.f3d).
Native Fusion format — preserves all parametric features, sketches,
timeline history, and component references. Best for archiving or
sharing with other Fusion 360 users.
Args:
outputPath: Full path for the output .f3d file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
export_mgr = design.exportManager
options = export_mgr.createFusionArchiveExportOptions(output_path)
result = export_mgr.execute(options)
if result:
return _export_success("F3D", output_path,
"Fusion 360 native archive — preserves all features and history. ")
return {"success": False, "error": "F3D export failed", "_hint": _HINT_EXPORT_FAILED}
def handle_export_usdz(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as a USDZ file (.usdz).
Universal Scene Description (USD) — Pixar's scene-graph format adopted
by Apple, NVIDIA, and Autodesk. Preserves:
- Full component hierarchy (board, copper layers, soldermask, packages)
- PBR materials and colors
- Named nodes for toggling parts on/off in viewers
Best format for digital twins, AR (Apple Quick Look), and GLB conversion
via Blender. The component hierarchy maps directly to toggleable nodes
in GLB viewers.
Pipeline: Fusion → USDZ → Blender → GLB (or view USDZ directly on iOS)
Args:
outputPath: Full path for the output .usdz file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
# Ensure .usdz extension
if not output_path.lower().endswith(('.usdz', '.usd', '.usda', '.usdc')):
output_path = str(Path(output_path).with_suffix('.usdz'))
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
# Pre-delete to avoid overwrite dialogs
if os.path.exists(output_path):
try:
os.remove(output_path)
except PermissionError:
return {
"success": False,
"error": f"Cannot overwrite {output_path} — file is locked.",
"_hint": _HINT_FILE_LOCKED,
"data": {
"hint": "Close the file in any viewer and retry.",
"outputPath": output_path,
},
}
export_mgr = design.exportManager
try:
options = export_mgr.createUSDExportOptions(output_path)
except AttributeError:
return {
"success": False,
"error": "USDZ export not available in this Fusion 360 version.",
"_hint": _HINT_FORMAT_UNAVAILABLE,
"data": {
"hint": "createUSDExportOptions requires a recent Fusion 360 build. "
"Export as STEP instead and convert via Blender.",
"recoverySteps": [
"Export STEP: fusion_export_step (then convert in Blender)",
"Export OBJ: fusion_export_fbx (falls back to OBJ)",
],
},
}
result = export_mgr.execute(options)
if result and os.path.exists(output_path):
size = os.path.getsize(output_path)
# Get component hierarchy for the response
components = []
try:
root = design.rootComponent
for i in range(root.occurrences.count):
occ = root.occurrences.item(i)
components.append(occ.name)
except Exception:
pass
return {
"success": True,
"output": f"Exported USDZ to {output_path}",
"message": f"USDZ exported ({size // 1024} KB) with full component hierarchy. "
f"Contains {len(components)} top-level components that map to "
f"toggleable nodes in GLB viewers. "
f"Use pull_file to transfer to Docker.",
"data": {
"outputPath": output_path,
"format": "usdz",
"fileSizeKB": size // 1024,
"components": components,
"hint": "USDZ preserves PBR materials and scene hierarchy. "
"Each component (Board, copper layers, soldermask, Packages) "
"becomes a separate toggleable node in the GLB.",
"nextSteps": [
"Use pull_file to transfer the USDZ to Docker",
"Convert to GLB: blender --background --python-expr "
"\"import bpy; bpy.ops.wm.read_factory_settings(use_empty=True); "
"bpy.ops.wm.usd_import(filepath='input.usdz'); "
"bpy.ops.export_scene.gltf(filepath='output.glb')\"",
"Or view directly on iOS/macOS — USDZ is Apple's native AR format",
"Or import into Unity/Unreal Engine for interactive 3D",
],
},
}
return {"success": False, "error": "USDZ export failed", "_hint": _HINT_EXPORT_FAILED}
def handle_export_fbx(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as an FBX file (.fbx).
Autodesk FBX format — widely used for 3D interchange with Blender,
Unity, Unreal Engine, and other DCC tools.
NOTE: FBX export is not available in current Fusion 360 builds via the API.
This command will fail clearly — use fusion_export_usdz or fusion_export_step
instead. No silent fallbacks.
Args:
outputPath: Full path for the output .fbx file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
# Pre-delete to avoid overwrite dialogs
if os.path.exists(output_path):
try:
os.remove(output_path)
except PermissionError:
return {
"success": False,
"error": f"Cannot overwrite {output_path} — file is locked.",
"_hint": _HINT_FILE_LOCKED,
"data": {
"hint": "The file may be open in another application. Close it and retry.",
"outputPath": output_path,
},
}
# Try FBX via exportManager API
export_mgr = design.exportManager
try:
options = export_mgr.createFBXExportOptions(output_path)
result = export_mgr.execute(options)
if result and os.path.exists(output_path):
return _export_success("FBX", output_path,
"Convert to GLB via Blender: "
"blender --background --python-expr "
"\"import bpy; bpy.ops.import_scene.fbx(filepath='<fbx>'); "
"bpy.ops.export_scene.gltf(filepath='<glb>')\" ")
except (AttributeError, Exception):
pass # createFBXExportOptions doesn't exist in this Fusion build
# FBX not available — fail clearly, no fallbacks
return {
"success": False,
"error": "FBX export not available in this Fusion 360 build. "
"Use fusion_export_usdz or fusion_export_step instead.",
"_hint": _HINT_FORMAT_UNAVAILABLE,
"data": {
"hint": "FBX is not supported via the Fusion API in current builds. "
"Use fusion_export_usdz (best for Blender/GLB pipeline, preserves "
"PBR materials and scene hierarchy) or fusion_export_step "
"(highest geometric fidelity).",
"alternatives": ["fusion_export_usdz", "fusion_export_step", "fusion_export_stl"],
},
}
def handle_export_dxf(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as a DXF file (.dxf).
AutoCAD DXF (Drawing Exchange Format) — universal 2D/3D CAD interchange.
Widely supported by all CAD tools, laser cutters, CNC routers, and
PCB fabrication houses.
Args:
outputPath: Full path for the output .dxf file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
if os.path.exists(output_path):
try:
os.remove(output_path)
except PermissionError:
return {
"success": False,
"error": f"Cannot overwrite {output_path} — file is locked.",
"_hint": _HINT_FILE_LOCKED,
"data": {"outputPath": output_path},
}
export_mgr = design.exportManager
try:
options = export_mgr.createDXFExportOptions(output_path)
result = export_mgr.execute(options)
if result and os.path.exists(output_path):
return _export_success("DXF", output_path,
"Universal CAD interchange — opens in AutoCAD, LibreCAD, etc. ")
except (AttributeError, Exception):
pass
return {
"success": False,
"error": "DXF export failed or not available in this Fusion 360 build.",
"_hint": _HINT_FORMAT_UNAVAILABLE,
"data": {
"hint": "Try fusion_export_step for CAD interchange instead.",
"alternatives": ["fusion_export_step", "fusion_export_dwg"],
},
}
def handle_export_dwg(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as a DWG file (.dwg).
AutoCAD native format — the industry standard for 2D/3D technical
drawings. Best for sharing with AutoCAD, BricsCAD, DraftSight users.
Args:
outputPath: Full path for the output .dwg file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
if os.path.exists(output_path):
try:
os.remove(output_path)
except PermissionError:
return {
"success": False,
"error": f"Cannot overwrite {output_path} — file is locked.",
"_hint": _HINT_FILE_LOCKED,
"data": {"outputPath": output_path},
}
export_mgr = design.exportManager
try:
options = export_mgr.createDWGExportOptions(output_path)
result = export_mgr.execute(options)
if result and os.path.exists(output_path):
return _export_success("DWG", output_path,
"AutoCAD native format — opens in AutoCAD, BricsCAD, etc. ")
except (AttributeError, Exception):
pass
return {
"success": False,
"error": "DWG export failed or not available in this Fusion 360 build.",
"_hint": _HINT_FORMAT_UNAVAILABLE,
"data": {
"hint": "Try fusion_export_dxf or fusion_export_step instead.",
"alternatives": ["fusion_export_dxf", "fusion_export_step"],
},
}
def handle_export_iges(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as an IGES file (.igs/.iges).
Legacy CAD interchange format. Still used by some older CAD systems
and manufacturing tools. STEP is preferred for modern workflows.
Args:
outputPath: Full path for the output .igs file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
if os.path.exists(output_path):
try:
os.remove(output_path)
except PermissionError:
return {
"success": False,
"error": f"Cannot overwrite {output_path} — file is locked.",
"_hint": _HINT_FILE_LOCKED,
"data": {"outputPath": output_path},
}
export_mgr = design.exportManager
try:
options = export_mgr.createIGESExportOptions(output_path)
result = export_mgr.execute(options)
if result and os.path.exists(output_path):
return _export_success("IGES", output_path,
"Legacy CAD interchange — STEP is preferred for modern use. ")
except (AttributeError, Exception):
pass
return {
"success": False,
"error": "IGES export failed or not available in this Fusion 360 build.",
"_hint": _HINT_FORMAT_UNAVAILABLE,
"data": {
"hint": "Try fusion_export_step instead (modern replacement for IGES).",
"alternatives": ["fusion_export_step"],
},
}
def handle_export_obj(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as an OBJ file (.obj).
Wavefront OBJ — simple mesh format widely supported by 3D viewers,
game engines, and DCC tools. No materials or hierarchy.
Args:
outputPath: Full path for the output .obj file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
if os.path.exists(output_path):
try:
os.remove(output_path)
except PermissionError:
return {
"success": False,
"error": f"Cannot overwrite {output_path} — file is locked.",
"_hint": _HINT_FILE_LOCKED,
"data": {"outputPath": output_path},
}
export_mgr = design.exportManager
try:
options = export_mgr.createOBJExportOptions(output_path)
result = export_mgr.execute(options)
if result and os.path.exists(output_path):
return _export_success("OBJ", output_path,
"Simple mesh format — opens in Blender, Unity, etc. ")
except (AttributeError, Exception):
pass
return {
"success": False,
"error": "OBJ export failed or not available in this Fusion 360 build.",
"_hint": _HINT_FORMAT_UNAVAILABLE,
"data": {
"hint": "Try fusion_export_stl or fusion_export_usdz instead.",
"alternatives": ["fusion_export_stl", "fusion_export_usdz"],
},
}
def handle_export_sat(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as a SAT file (.sat).
ACIS SAT — solid modeling kernel format. Used by SolidWorks,
SpaceClaim, and other ACIS-based CAD tools.
Args:
outputPath: Full path for the output .sat file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
if os.path.exists(output_path):
try:
os.remove(output_path)
except PermissionError:
return {
"success": False,
"error": f"Cannot overwrite {output_path} — file is locked.",
"_hint": _HINT_FILE_LOCKED,
"data": {"outputPath": output_path},
}
export_mgr = design.exportManager
try:
options = export_mgr.createSATExportOptions(output_path)
result = export_mgr.execute(options)
if result and os.path.exists(output_path):
return _export_success("SAT", output_path,
"ACIS solid modeling format — opens in SolidWorks, SpaceClaim. ")
except (AttributeError, Exception):
pass
return {
"success": False,
"error": "SAT export failed or not available in this Fusion 360 build.",
"_hint": _HINT_FORMAT_UNAVAILABLE,
"data": {
"hint": "Try fusion_export_step instead.",
"alternatives": ["fusion_export_step"],
},
}
def handle_export_skp(app: adsk.core.Application, args: dict) -> dict:
"""Export the active design as a SketchUp file (.skp).
SketchUp format — popular for architectural visualization and
simple 3D modeling.
Args:
outputPath: Full path for the output .skp file (required).
"""
output_path = args.get("outputPath", "")
if not output_path:
return {"success": False, "error": "No outputPath specified", "_hint": _HINT_NO_OUTPUT_PATH}
design = _get_active_design(app)
if not design:
return _no_design_error()
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
if os.path.exists(output_path):
try:
os.remove(output_path)
except PermissionError:
return {
"success": False,
"error": f"Cannot overwrite {output_path} — file is locked.",
"_hint": _HINT_FILE_LOCKED,
"data": {"outputPath": output_path},
}
export_mgr = design.exportManager
try:
options = export_mgr.createSKPExportOptions(output_path)
result = export_mgr.execute(options)
if result and os.path.exists(output_path):
return _export_success("SKP", output_path,
"SketchUp format — opens in SketchUp, Trimble viewers. ")
except (AttributeError, Exception):
pass
return {
"success": False,
"error": "SketchUp export failed or not available in this Fusion 360 build.",
"_hint": _HINT_FORMAT_UNAVAILABLE,
"data": {
"hint": "Try fusion_export_step or fusion_export_stl instead.",
"alternatives": ["fusion_export_step", "fusion_export_stl"],
},
}