subagent transcript
session 17f09f89 · workflow wf_77616857
delegated task
GOAL: Produce a CONCRETE roadmap to FULL FreeCAD desktop feature parity for the FreeCAD->WebAssembly port. Be evidence-based: grep/read the actual sou
▸ Magik
GOAL: Produce a CONCRETE roadmap to FULL FreeCAD desktop feature parity for the FreeCAD->WebAssembly port. Be evidence-based: grep/read the actual sources, don't reason from general FreeCAD knowledge alone. Cite file paths.
CURRENT STATE (verified working in the browser build):
- FreeCAD 26.3.0dev compiled to wasm. Toolchain: emscripten 4.0.12, ASYNCIFY, JS-exceptions, **single-thread (NO pthreads, QT_FEATURE_thread=-1)**, static CPython 3.14, static Qt 6.11.1 (asyncify), static OCCT, static Coin3D. **NO dlopen** — every C-extension is registered via PyImport_AppendInittab in generated src/Main/WasmInittab*.cpp. **NO subprocess/QProcess, NO threads, NO native OS dialogs.** Browser file I/O via upload/download (deploy/index.html). FreeCAD.wasm ~243MB.
- 3D viewport RENDERS (offscreen FBO + QPainter blit; Coin3D/OpenGL via WebGL).
- **PySide6 FULLY WORKS**: QtCore+QtGui+QtWidgets bindings + shiboken runtime + signals/slots; `from PySide import QtGui` compat shim works; `Gui.getMainWindow()` returns a real PySide6 QMainWindow (shiboken wrapInstance); Python macro scripting drives geometry + GUI.
- Workbenches LIVE (7): Part, PartDesign, Sketcher, Surface, Spreadsheet, Material (+None/Start). Measure + Import modules built-in.
- Built-in C-extensions (inittab): Import, ImportGui, Materials, MatGui, Measure, MeasureGui, Part, PartGui, PartDesign, PartDesignGui, Sketcher, SketcherGui, Spreadsheet, SpreadsheetGui, StartGui, Surface, SurfaceGui.
- MISSING modules (present in src/Mod but NOT built/registered): Draft, BIM, CAM, Fem, Mesh, MeshPart, Points, TechDraw, Assembly, Plot, Show, OpenSCAD, Web, Inspection, ReverseEngineering, Robot, JtReader, Help, AddonManager, Test, Tux, TemplatePyMod.
PATHS:
- FreeCAD source: /home/magik6k/lcad-wasm/freecad-port/FreeCAD (src/Mod/<WB>, src/Gui, src/App). Each module has CMakeLists.txt, App/ (C++), Gui/ (C++), and often a Python workbench tree (Init.py/InitGui.py + *.py).
- wasm build tree: /opt/toolchains/src/freecad-gui-build (CMakeCache.txt, build.ninja, src/Main/WasmInittab*.cpp).
- deploy: /home/magik6k/lcad-wasm/freecad-port/deploy (index.html = browser file I/O + boot; *.data = packaged FS).
- The module-enablement pipeline already used for the live modules: add C-ext to inittab (RegName=CInitSuffix form via FREECAD_WASM_EXTRA_PY_MODULES + the SHARED->STATIC rewrite in FreeCAD/CMakeLists.txt:91-107), package the module's Python tree onto the wasm FS via file_packager (freecad-mods.data), drop unbuildable OCCT deps, fix dependency gates.
- External-tool deps that are a problem in wasm: Fem (calculix/gmsh solvers via subprocess), BIM (ifcopenshell, needs building for wasm + network for downloads), CAM (post-processors), AddonManager (git/network), OpenSCAD (external openscad binary), Web (QtWebEngine — almost certainly not portable).
For each thing you assess, classify wasm-viability: WORKS (already), EASY (module-enable pipeline, no hard blocker), MEDIUM (needs a dep built for wasm or a moderate workaround), HARD (needs a wasm-native re-architecture e.g. web worker/fetch/browser API), BLOCKED (fundamentally not portable — say why + what a degraded fallback looks like). Give a rough effort (S/M/L/XL) and cite evidence (CMakeLists deps, Init.py imports, subprocess/QProcess/network usage grep).
DIMENSION 2 — Python/PySide workbenches (now unblocked by working PySide). Deep-dive Draft, BIM, Plot, Show, Assembly, TemplatePyMod. For each: what C++ helper modules does it need (e.g. does Draft need a C++ ext or is it pure Python? check src/Mod/Draft), what Python packages it imports (grep its Init.py/*.py for imports — numpy? ifcopenshell? lxml? requests?), what external tools/network it uses, and exactly what packaging+enable steps are needed. Draft is the highest priority (2D drafting, DXF/SVG). Determine if Draft is pure-Python-on-Part (easy) or needs more. Give precise per-workbench enable recipes.
Fable
thinking
I'm looking into the FreeCAD WebAssembly port, specifically examining the Python and PySide-based workbenches like Draft, BIM, Plot, Show, Assembly, and TemplatePyMod to understand how they're structured. Let me start by checking the actual source code directories.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod && ls -d Draft BIM Plot Show Assembly TemplatePyMod 2>/dev/null && echo \"---DRAFT---\" && ls Draft && echo \"---DRAFT CMakeLists---\" && cat Draft/CMakeLists.txt 2>/dev/n…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod && for m in Draft BIM Plot Show Assembly TemplatePyMod; do echo \"=== $m ===\"; ls $m 2>/dev/null | head -40; echo; done", "description": "List contents of each module"}
Fable
thinking
I'm checking each module's App directory for C++ extensions, looking at what gets imported in Init.py, and tracing their dependencies, starting with the Draft module.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft && echo \"=== Draft/App ===\" && ls App && echo \"=== Draft/App/CMakeLists ===\" && cat App/CMakeLists.txt 2>/dev/null && echo \"=== Init.py ===\" && cat Init.py", "…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft && echo \"=== InitGui.py head ===\" && head -80 InitGui.py", "description": "Draft InitGui"}
Fable
thinking
Draft relies on a C++ extension called DraftUtils that depends on Part and FreeCADApp, plus it uses pivy for Coin3D bindings. I need to check whether pivy is available in the wasm build since the current setup has Coin3D static and PySide — Draft imports directly from pivy, which is a significant dependency. Let me search the build and source tree to see if pivy is present and check what other Python imports Draft needs.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft && echo \"=== all imports across Draft py ===\" && grep -rhoE \"^\\s*(import|from)\\s+[A-Za-z0-9_.]+\" --include=*.py . | grep -vE \"import (os|sys|re|math|copy|time…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft && echo \"=== numpy/lxml/requests/pivy/scipy usage ===\" && grep -rlE \"import (numpy|lxml|requests|scipy|matplotlib|ifcopenshell|pivy)|from (numpy|lxml|requests|sci…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && echo \"=== pivy in tree? ===\" && find . -iname \"*.py\" -path \"*pivy*\" 2>/dev/null | head; find /opt/toolchains -iname \"coin.py\" 2>/dev/null | head; find /opt/toolchains -…
Fable
thinking
I'm running into a zsh globbing issue, so I'll switch to bash with proper quoting. Pivy is a third-party Python binding to Coin3D built via SWIG as a native extension, and I need to check if it's included in the wasm build and verify its presence in the inittab by searching through the build tree.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft && grep -rhoE \"^[[:space:]]*(import|from)[[:space:]]+[A-Za-z0-9_.]+\" --include='*.py' . 2>/dev/null | sed -E 's/^[[:space:]]+//' | grep -vE \"^(import|from) (os|sy…
Fable
⚙ Bash {"command": "grep -rin \"pivy\\|_coin\\|SoDB\\|InitGui.*Coin\" /opt/toolchains/src/freecad-gui-build/src/Main/ 2>/dev/null | head -20; echo \"=== find pivy built .so/.wasm ===\"; find /opt/toolchains -path '*pivy*' -name '*.py' 2>/dev/null …
Fable
thinking
I'm realizing that Draft relies heavily on pivy.coin with over a dozen imports, making Pivy the critical dependency here. I need to verify whether Pivy is actually built into the WASM build—it's not showing up in the toolchain directory. Let me dig into the deploy data and build configuration more carefully. Since Pivy is a SWIG-generated binding to Coin3D (a native C++ extension module called `_coin`), and there's no dlopen available, it would need to be registered in the inittab. I should check what Pivy targets are actually being built.
Beyond that, there's also `lazy_loader` and `Draft_rc` (a compiled resource file) being used, plus imports from TechDraw and PartGui. Let me verify the P …
Fable
⚙ Bash {"command": "grep -rin \"pivy\" /opt/toolchains/src/freecad-gui-build/CMakeCache.txt /opt/toolchains/src/freecad-gui-build/build.ninja 2>/dev/null | head -20; echo \"=== pivy CMakeLists ===\"; sed -n '1,120p' /home/magik6k/lcad-wasm/freecad…
Fable
⚙ Bash {"command": "echo \"=== pivy in deploy data ===\"; ls -la /home/magik6k/lcad-wasm/freecad-port/deploy/*.data 2>/dev/null; for d in /home/magik6k/lcad-wasm/freecad-port/deploy/*.js; do echo \"-- $d\"; grep -o \"pivy[^\\\"']*coin[^\\\"']*\" \…
Fable
thinking
I need to verify whether the native `_coin` extension module is actually built and registered in the initialization table, since pivy.coin depends on it through a direct import. This is a critical dependency for Draft to function properly, so I should check the inittab to confirm _coin is available.
Fable
⚙ Bash {"command": "grep -rin \"coin\\|pivy\\|_coin\" /opt/toolchains/src/freecad-gui-build/src/Main/WasmInittab*.cpp 2>/dev/null | head; echo \"=== full inittab list ===\"; grep -oE 'PyImport_AppendInittab\\(\"[^\"]+\"' /opt/toolchains/src/freeca…
Fable
thinking
The core issue is that `_coin` isn't registered in the inittab, which blocks Draft's GUI initialization with an ImportError. However, I'm realizing Draft's object model might not actually depend on pivy—only the workbench GUI does—so there could be a distinction between what's needed to load the interface versus what's needed for the underlying Draft objects themselves.
Looking deeper, Draft's viewproviders do use pivy to build custom Coin scene graphs in Python for things like dimension lines and text rendering, so pivy is genuinely required for proper visualization. The real question becomes whether pivy's `_coin` extension can be compiled statically and registered in the inittab like oth …
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft; echo \"=== files importing pivy ===\"; grep -rl \"import pivy\\|from pivy\" --include='*.py' . | sed 's#^\\./##' | sort; echo; echo \"=== does draftmake (object cre…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft; echo \"=== importDXF imports (heavy?) ===\"; grep -hE \"^\\s*(import|from) \" importDXF.py | grep -viE \"FreeCAD|Draft|Part|os |sys|re|math\" | head; echo \"=== imp…
Fable
thinking
Looking at the Draft module architecture, the C++ extension with DraftUtils is straightforward to build like other modules, but pivy.coin is the critical dependency—it's needed for the workbench initialization and all the view providers that handle dimensions, text, labels, layers, and trackers. The object creation in draftmake relies on pivy through the annotation view providers, though the core geometry handling appears to be separate from that dependency.
Beyond pivy, lazy_loader is just a pip package that's pure Python and easy to include, while importDXF depends on dxfLibrary and dxfColorMap which were historically downloaded through the Addon Manager but may now be bundled with newer …
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft; echo \"=== dxfLibrary present? ===\"; ls -la dxf* dxfLibrary.py dxfColorMap.py 2>/dev/null; find . -iname 'dxflibrary*' -o -iname 'dxfcolormap*' 2>/dev/null; echo \…
Fable
⚙ Bash {"command": "echo \"=== lazy_loader bundled in build/deploy? ===\"; find /opt/toolchains -iname 'lazy_loader*' 2>/dev/null | head; grep -rl \"lazy_loader\" /home/magik6k/lcad-wasm/freecad-port/deploy/*.js 2>/dev/null; echo \"=== python site…
Fable
thinking
Looking at what's available in the WASM environment, lazy_loader is already bundled which is helpful. Numpy isn't available as a Python module though some components might depend on it—I need to verify which modules actually require it since Draft doesn't. For DXF import/export, the legacy system downloads the dxfLibrary and dxfColorMap from GitHub through ArchCommands.download, which I should check if that's still functional in this context.
The real blocker is pivy. I'm wondering if Draft could partially work without it—the InitGui would fail to load the workbench, but I'm curious whether the basic geometry objects like Line, Wire, Rectangle, and Circle would still render using the standa …
Fable
⚙ Bash {"command": "cat /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/pivy/interfaces/CMakeLists.txt 2>/dev/null | head -80; echo \"=== interfaces dir ===\"; ls /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/pivy/interfaces/…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft; echo \"=== view_base.py: does every draft obj use coin? ===\"; sed -n '1,60p' draftviewproviders/view_base.py | grep -nE \"import|coin|class|So\"; echo \"=== _coin …
Fable
thinking
The issue is that pivy's coin.py is generated by SWIG during the build process, and while it exists in the deployed freecad.data, it's completely absent from the wasm build configuration. Since pivy isn't included in the wasm build.ninja at all, the native _coin module never gets built, which is why Draft fails to load. To fix this, I need to set up SWIG to generate the coin.i bindings for the wasm build.
The real challenge is compiling the massive coin_wrap.cxx file (which wraps the entire Coin API) with emscripten and linking it against the static Coin library — this is a large, time-consuming compilation similar to what MSVC handles with bigobj flags. Once that's done, I'd register _coin …
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod\necho \"########## PLOT ##########\"\ncat Plot/CMakeLists.txt | head -30\necho \"=== Plot imports ===\"\ngrep -rhoE \"^[[:space:]]*(import|from)[[:space:]]+[A-Za-z0-9_.]+\…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod\necho \"########## SHOW ##########\"\ncat Show/CMakeLists.txt | head -20\necho \"=== Show imports ===\"\ngrep -rhoE \"^[[:space:]]*(import|from)[[:space:]]+[A-Za-z0-9_.]+\…
Fable
thinking
Plot is a hard blocker for wasm since it depends on matplotlib, which requires numpy and C extensions that can't be compiled for wasm—even Pyodide's custom build doesn't help here. This is a massive dependency chain that's essentially blocked, so I'm marking it as degraded and skipping it for now.
Show is much simpler—it's a pure Python helper library that depends on pivy, but once pivy is built, packaging it is straightforward. It's not a user-facing workbench, just a visibility utility, so the effort drops to small once pivy exists.
Moving on to Assembly, BIM, and TemplatePyMod next.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod\necho \"########## ASSEMBLY ##########\"\nsed -n '1,60p' Assembly/CMakeLists.txt\necho \"=== Assembly/App CMake libs ===\"\ngrep -iE \"LINK_LIBRARIES|_LIBS|Ondsel|Solver|f…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod\necho \"=== Assembly App source libs full ===\"; grep -riE \"Ondsel|MbD|Solver\" Assembly/App/CMakeLists.txt | head\necho \"=== OndselSolver / MbD dep search in tree & too…
Fable
thinking
Looking at the Assembly and Python dependencies for this project. The C++ components depend on OndselSolver, a bundled multibody dynamics solver that's pure C++ and should be compilable to WebAssembly, though that's a medium-to-large effort. On the Python side, there are several dependencies including pivy, numpy, PIL, and av with varying complexity levels.
The av library (PyAV) is used for video export in simulations and is a hard blocker but optional since it can be stubbed out. PIL is a medium blocker for textures but potentially optional. Numpy is critical and needed across multiple modules, making it a significant shared blocker since it's not available in WebAssembly Python. Pivy is a …
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod\necho \"=== Assembly numpy/PIL/av locations ===\"; grep -rln \"import numpy\\|from numpy\\|import av\\b\\|from PIL\\|import PIL\" --include='*.py' Assembly/\necho \"######…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod\necho \"########## BIM ##########\"\nls BIM/*.py | wc -l; echo \"BIM has App/ C++?\"; ls -d BIM/App 2>/dev/null || echo \"NO App dir (pure python)\"\necho \"=== BIM CMakeL…
Fable
thinking
So BIM is pure Python but has a critical dependency chain: it relies heavily on Draft, which needs pivy to work, and it also depends on ifcopenshell—a large C++ library with Python bindings that would be extremely difficult to build for WebAssembly, though there's apparently an ongoing effort to port it.
TemplatePyMod is just a developer example that needs pivy plus some C++ modules that aren't built yet, so it's lower priority. Assembly's core functionality (joints, solving, BOM) depends on OndselSolver and pivy, with video export features degrading since numpy and PIL are isolated to one file. Plot is blocked on matplotlib, which isn't essential for CAD. Show is straightforward once pivy …
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod\necho \"=== ifcopenshell import style (top-level vs guarded) ===\"; grep -rn \"import ifcopenshell\" --include='*.py' BIM/ | grep -v \"^\\s*#\" | head -8\necho \"=== BIM I…
Fable
thinking
The ifcopenshell library uses lazy imports throughout its modules, so it's possible to use the Arch workbench for basic modeling without IFC support, though that defeats the main purpose of BIM. NumPy isn't available for WebAssembly, which is a constraint. I should verify the shared Draft resource configuration and confirm that DraftUtils is the only C++ component involved.
Now I'm mapping out the gaps systematically: Draft, pivy as both an infrastructure blocker and its own risk item, BIM, Plot, Show, Assembly, and TemplatePyMod, plus the shared blockers around pivy._coin and numpy. I'll structure this with viability assessments, effort estimates, and evidence-based requirements.
For the …
Fable
I have enough evidence. Let me do two final confirmations on the DXF/Mesh guard and DraftUtils being the sole C++ ext.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Draft\necho \"=== importDXF Mesh import guarded? ===\"; grep -n \"import Mesh\" importDXF.py\necho \"=== Draft App produces only DraftUtils ext ===\"; grep -c \"add_librar…
Fable
thinking
I've confirmed that `importDXF`'s mesh handling is safely contained within a function and only triggered for the mesh-to-DXF conversion path, DraftUtils is the sole C++ extension from Draft/App, and importSVG relies entirely on Python's built-in XML parsing without any network calls. Now I'm ready to put together the structured response.
Fable
I have comprehensive evidence. Composing the roadmap for Dimension 2.
Fable
⚙ StructuredOutput {"dimension": "Python/PySide workbenches (Draft, BIM, Plot, Show, Assembly, TemplatePyMod)", "worksNow": ["PySide6 (QtCore/QtGui/QtWidgets) + shiboken + `from PySide import ...` compat shim work, so ANY of these workbenches' Qt UI layer is …