Files
Web_FreeCAD_Bitbybit/native/occt-history
wangdequan f97eae4153
Some checks failed
real-verification / chrome (push) Has been cancelled
real-verification / freecad-oracle (push) Has been cancelled
real-verification / wasm (push) Has been cancelled
feat: add candidate FreeCAD naming SDK and attachment oracles
Build and verify the candidate-only FreeCAD naming bridge and OCCT worker path, including three-stage StringHasher restoration. Add Datum, ShapeBinder, attachment-mode, and PartDesign structure oracles plus offline SDK build plans and CI boundary checks.
2026-08-13 17:16:07 -04:00
..

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.