Native OCCT history provider
This provider is compiled from the cached OCCT checkout selected by
OCCT_SOURCE_DIR and exposes native Boolean and PartDesign history APIs through
Emscripten Embind. The provider keeps TopoDS_Shape handles and history queries in one
WASM instance. A handle from @bitbybit-dev/occt must never be passed directly
to this module because WebAssembly linear memories are independent.
Build and verify it with the project wrapper:
./npmw run build:occt-history
./npmw run test:occt-history
The build also publishes the JS/WASM pair to
public/native/occt-history/, which is the default URL used by
NativeOcctHistoryWorkerProvider. The generated artifacts remain ignored; a
clean checkout must run the build after restoring the pinned OCCT source and
Emscripten toolchain.
FreeCAD private naming build boundary
The default build is intentionally OCCT-only. It does not export
freecadNamingAbiVersion, freecadNamingCapabilitiesJson, or
freecadNamingEvidenceJson, and it must not be described as exact FreeCAD
naming support.
A separate fail-closed build lane is available for an actual FreeCAD WASM SDK:
FREECAD_WASM_SDK_DIR=/absolute/path/to/sdk ./npmw run check:freecad-naming-sdk
FREECAD_WASM_SDK_DIR=/absolute/path/to/sdk ./npmw run build:freecad-naming-worker
./npmw run check:occt-history-artifact
The SDK layout is defined by freecad-wasm-sdk-manifest.example.json. The
checker requires the locked FreeCAD 1.1.1 commit, Emscripten 3.1.69, wasm Qt
and Python targets, private App headers, hashed static FreeCAD/Part/Qt/Python
libraries, and a hashed bridge source exporting all three callbacks. If any
input is absent, build:freecad-naming-worker exits before rebuilding or
publishing the OCCT-only artifact. A successful artifact probe still only
establishes ABI linkage; exact promotion additionally requires valid
MappedNameRef, StringHasher, ElementMap2, stage history, and round-trip evidence.
The repository also carries a prerequisite-only source probe. It builds QtBase
6.8.2 Qt6Core for wasm, then compiles the locked FreeCAD IndexedName.cpp,
MappedName.cpp, StringHasher.cpp, MappedElement.cpp,
ElementNamingUtils.cpp, ElementMap.cpp, and Base/Handle.cpp sources without
exporting the production naming callbacks:
./npmw run build:qt6-wasm-core
./npmw run build:freecad-naming-source-probe
./npmw run test:freecad-naming-source-probe
./npmw run check:freecad-naming-sdk-readiness
./npmw run check:freecad-private-naming-boundary
Passing this probe establishes that the private source subset and toolchain are
cross-compilable. Its runtime test covers mapped-name parsing, StringHasher
deduplication, SHA-1 threshold handling, indexed mapped-name references,
ElementMap encoding/lookup/history/save/restore, and stable mapped-element
ordering. It also feeds native MappedNameRef, StringHasher, and ElementMap2
resources through the strict Web ABI validator using deliberately separate
freecadNamingCandidate* callback names.
The seven locked FreeCAD source units are first compiled into the isolated
libFreeCADPrivateNamingProbe.a archive. The smoke test checks that it contains
exactly seven wasm object members before linking the runnable probe. This
archive remains host-adapter-bound and is not a substitute for FreeCADBase or
FreeCADApp.
Persistence, Python wrappers, type-system, logging, and Application/Document
lifecycle interfaces remain standalone host adapters. FreeCADApp/Part/Python
static libraries, real Application/Document integration, OCCT builder context,
and the production bridge are not linked. The candidate callbacks are never
published to the production Worker. The probe therefore does not close
EX-TSN-02 and does not change systemExact=false.
check:freecad-naming-sdk-readiness audits the pre-production SDK plan without
publishing anything. It parses every present .a archive and rejects native
ELF members; the current local resource set verifies QtCore plus its bundled
Pcre2/Zlib wasm dependencies, but still lacks FreeCADBase, FreeCADApp, Part,
Python, and the production bridge. generate:freecad-naming-sdk-manifest is
fail-closed and writes no manifest until all inputs are present. The complete
SDK checker applies the same wasm-archive rule, so an x86 static library cannot
satisfy the manifest by hash alone.
The versioned JSON request carries document objectId, stable positive
objectTag, prior naming evidence, result object identity, result tag, stage
DAG, STEP/BRep, and OCCT Generated/Modified/Deleted history. No Web fallback is
allowed to synthesize a FreeCAD private token.
The exported booleanHistory(object, tool, operation) function supports
fuse, cut, and common. It returns the native result shape, per-input
modified/generated/deleted records, a validity/topology/quality summary,
and capability flags. prismHistory(profile, dx, dy, dz) adds the first
PartDesign Pad contract: profile vertices can generate edges and profile edges
can generate faces, while the profile face is preserved as a result face.
booleanProbe and booleanResult are intentionally smaller operations for
Worker stress/ownership gates; callers must delete every returned Embind shape
handle. Primitive constructors cover positioned boxes, cylinders, spheres,
cones and a test rectangle Face. The provider also exports shapeToStep(shape),
booleanHistoryFromStep(objectStep, toolStep, operation) and
prismHistoryFromStep(profileStep, dx, dy, dz). The STEP functions are the
cross-WASM transport: Bitbybit exports STEP text, the native provider reads it
with STEPControl_Reader, returns the result again as resultStep, and no
linear-memory pointer crosses the boundary. Pad history currently requires a
valid planar Face profile; solids are rejected by OCCT rather than silently
treated as sketches. loftHistoryFromStep(firstSectionStep, secondSectionStep, ruled) adds a verified two-section solid loft using BRepOffsetAPI_ThruSections.
It records generated edges/faces for both source profiles; more than two
sections, closed lofts, and PartDesign base fusion/cut stay on the signature
history path until their multi-source contract is implemented.
pipeHistoryFromStep(profileStep, spineStep) adds a verified single-edge
open-spine solid sweep using BRepOffsetAPI_MakePipe. It records profile
vertex/edge generation, profile end caps, and spine edge/endpoint provenance;
multi-edge spines, Frenet/transition modes, and hollow profiles remain explicit
unsupported cases.
filletHistoryFromStep(baseStep, radius) enables the first
native dress-up slice: OCCT BRepFilletAPI_MakeFillet applies a fixed radius to
all source edges and returns per-edge/face Modified, Generated, and Deleted
relations with result-kind mapping. The native build enables TKFillet and its
TKBool dependency.
chamferHistoryFromStep(baseStep, distance) uses the same native history
collector with BRepFilletAPI_MakeChamfer for a symmetric, fixed-distance,
all-edge chamfer.
holeHistoryFromStep(baseStep, radius, depth, px, py, pz, dx, dy, dz) composes
an OCCT cylinder and BRepAlgoAPI_Cut for a straight cylindrical hole with
explicit position and direction, returning the Boolean source history. Thread,
counterbore, countersink, and face-attached hole semantics remain outside this
minimal native contract.
draftHistoryFromStep(baseStep, faceIndex, angle, direction, neutralPlane, reversed) uses BRepOffsetAPI_DraftAngle from TKOffset for one explicitly
selected source face. It returns native per-subshape history and rejects an
invalid face index, zero direction, zero angle, or invalid neutral plane before
publishing a result. Multi-face propagation and STEP files whose imported face
orientation OCCT cannot draft remain explicit unsupported/error cases.
thicknessHistoryFromStep(baseStep, faceIndex, offset, intersectionJoin) uses
BRepOffsetAPI_MakeThickSolid for one explicitly removed source face and
returns native offset history. Zero offsets and invalid face indexes are
rejected before execution.
linearPatternHistoryFromStep(baseStep, dx, dy, dz) creates one transformed
copy and fuses it with the source, preserving both Boolean input histories.
The contract intentionally covers exactly two whole-shape instances.
polarPatternHistoryFromStep(baseStep, axis, angle) creates one rotated copy
and fuses it with the source. It likewise covers exactly two whole-shape
instances around an explicit finite axis.
mirroredHistoryFromStep(baseStep, planeOrigin, planeNormal) creates one plane
reflected copy and fuses it with the source. The native contract is limited to
one whole-shape base, one standard document plane, and a fused result.
The multiTransformHistoryFromStep(baseStep, steps) binding accepts two to six
ordered two-instance linear, polar, or mirrored whole-shape steps. Each created
instance retains its transformed Base subshape mapping; one multi-argument Fuse
then aggregates every instance history back to the original Base indexes. The
single-step protocol continues to reuse the individual verified bindings.
The TypeScript boundary mapper is mapNativeOcctHistoryRecords; the reusable
createNativeOcctStepHistoryBridge exports Bitbybit ShapeHandles to STEP and
converts provider source names into document object IDs before calling
captureNativeTopologyHistory. The direct handle API remains for calls that
stay inside this single native WASM instance.