feat: advance FreeCAD parity and native OCCT history

This commit is contained in:
2026-08-03 08:04:10 -04:00
parent 2ce261b982
commit e5a5d74dbc
32 changed files with 1883 additions and 30 deletions

View File

@@ -1,6 +1,8 @@
import { BitByBitOCCT, OccStateEnum } from '@bitbybit-dev/occt-worker'
import type { Inputs } from '@bitbybit-dev/occt'
import type { ApplyPlacementInput, BooleanCutInput, BooleanIntersectionInput, BooleanUnionInput, ChamferInput, CreateBoxInput, CreateConeInput, CreateCylinderInput, CreateSphereInput, FilletInput, GeometryCapabilities, GeometryDocumentContext, GeometryFileExport, LinearFeatureParameters, MeshAsset, MirrorInput, PadInput, PlanarProfile, PocketInput, Point3, RevolutionInput, ShapeHandle, SubshapeRef, SubshapeTopology } from './types'
import type { ApplyPlacementInput, BooleanCutInput, BooleanIntersectionInput, BooleanUnionInput, ChamferInput, CreateBoxInput, CreateConeInput, CreateCylinderInput, CreateSphereInput, FilletInput, GeometryCapabilities, GeometryDocumentContext, GeometryFileExport, LinearFeatureParameters, MeshAsset, MirrorInput, NativeTopologyHistoryInput, NativeTopologyHistoryRecord, PadInput, PlanarProfile, PocketInput, Point3, RevolutionInput, ShapeHandle, SubshapeRef, SubshapeTopology } from './types'
import { mapNativeOcctHistoryRecords } from './nativeHistoryProvider'
import { NativeOcctHistoryCoordinator, type NativeOcctHistoryProvider } from './nativeHistoryProtocol'
import { createEdgeSubshapeRefs, createSubshapeRefs, createVertexSubshapeRefs } from './topologyNaming'
type KernelShapeReference = Inputs.OCCT.TopoDSShapePointer
@@ -230,6 +232,34 @@ export class BitbybitGeometryRuntime {
private sequence = 0
private readonly shapes = new Map<string, ShapeEntry>()
private readonly kernelReferences = new Map<number, KernelReferenceEntry>()
private nativeHistory: { provider: NativeOcctHistoryProvider; coordinator: NativeOcctHistoryCoordinator } | null = null
private readonly nativeHistoryDocumentVersions = new Map<string, number>()
configureNativeHistory(provider: NativeOcctHistoryProvider | null, timeoutMs = 120_000) {
this.nativeHistory?.coordinator.cancel()
if (provider === null) {
this.nativeHistory = null
this.nativeHistoryDocumentVersions.clear()
return
}
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) throw new RangeError('Native history timeoutMs must be a positive safe integer.')
this.nativeHistory = {
provider,
coordinator: new NativeOcctHistoryCoordinator((documentId) => this.nativeHistoryDocumentVersions.get(documentId) ?? null, timeoutMs),
}
}
nativeHistoryCapabilities() {
return this.nativeHistory?.provider.capabilities() ?? {
providerId: 'occt-native.history-step',
providerVersion: 'unconfigured',
occtVersion: 'unknown',
availability: 'unavailable' as const,
operations: [],
transport: 'step-text' as const,
reason: 'Native OCCT history provider is not configured.',
}
}
capabilities(): GeometryCapabilities { return { ...this.capabilitiesState } }
@@ -471,6 +501,30 @@ export class BitbybitGeometryRuntime {
return { faces: faceTopology.refs, edges: edgeTopology.refs, vertices: vertexTopology.refs, entries }
}
async topologyHistory(input: NativeTopologyHistoryInput): Promise<NativeTopologyHistoryRecord[]> {
const nativeHistory = this.nativeHistory
if (!nativeHistory) throw new Error('Native OCCT history provider is not configured.')
if (!input.operation) throw new Error('Native OCCT history requires a supported Boolean operation.')
if (input.inputs.length !== 2) throw new Error('Native OCCT history requires exactly an object and a tool input.')
validateDocumentContext(input)
this.nativeHistoryDocumentVersions.set(input.documentId, input.documentVersion)
const [objectInput, toolInput] = input.inputs
const [objectStep, toolStep] = await Promise.all([
this.exportStep(objectInput.shape, `${objectInput.objectId}.step`),
this.exportStep(toolInput.shape, `${toolInput.objectId}.step`),
])
const execution = await nativeHistory.coordinator.capture(nativeHistory.provider, {
documentId: input.documentId,
documentVersion: input.documentVersion,
operationId: input.operationId,
operation: input.operation,
objectStep: objectStep.text,
toolStep: toolStep.text,
})
if (execution.status !== 'completed' || !execution.response) throw new Error(`Native OCCT history ${execution.status}.`)
return mapNativeOcctHistoryRecords(execution.response.history, { object: objectInput.objectId, tool: toolInput.objectId })
}
async release(shape: ShapeHandle): Promise<void> {
const entry = this.shapes.get(shape.id)
if (!entry) return
@@ -487,6 +541,7 @@ export class BitbybitGeometryRuntime {
}
dispose() {
this.nativeHistory?.coordinator.cancel()
this.cancelInitialization?.()
this.shapes.clear()
this.kernelReferences.clear()
@@ -496,6 +551,8 @@ export class BitbybitGeometryRuntime {
this.worker = null
this.initialization = null
this.cancelInitialization = null
this.nativeHistoryDocumentVersions.clear()
this.nativeHistory = null
this.capabilitiesState = unavailableCapabilities()
}