@faicad/faijs 0.18.0 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/extrude.d.ts.map +1 -1
- package/dist/api/extrude.js +52 -13
- package/dist/api/extrude.js.map +1 -1
- package/dist/api/profile.d.ts +10 -6
- package/dist/api/profile.d.ts.map +1 -1
- package/dist/api/profile.js +106 -24
- package/dist/api/profile.js.map +1 -1
- package/dist/api/transform.d.ts +12 -8
- package/dist/api/transform.d.ts.map +1 -1
- package/dist/api/transform.js +62 -32
- package/dist/api/transform.js.map +1 -1
- package/dist/brepkit-kernel/brepkitWasm.d.ts +2 -1
- package/dist/brepkit-kernel/brepkitWasm.d.ts.map +1 -1
- package/dist/brepkit-kernel/brepkitWasm.js +48 -12
- package/dist/brepkit-kernel/brepkitWasm.js.map +1 -1
- package/dist/occt-kernel/occtKernel.d.ts +3 -1
- package/dist/occt-kernel/occtKernel.d.ts.map +1 -1
- package/dist/occt-kernel/occtKernel.js +73 -29
- package/dist/occt-kernel/occtKernel.js.map +1 -1
- package/package.json +2 -3
|
@@ -1,17 +1,54 @@
|
|
|
1
1
|
import { getSolidColorsOrdered } from './stepColorParser.js';
|
|
2
|
-
let kernelInstance = null;
|
|
3
|
-
let initPromise = null;
|
|
4
2
|
// F5 设计意图说明:
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
// OCCT WASM 内核是环境级单例,不是实例级。在整个浏览器页面/Node 进程中只应
|
|
4
|
+
// 初始化一次;多个 CadRuntime 实例共享同一个 kernel 是正确的行为。
|
|
5
|
+
//
|
|
6
|
+
// 2026-09-26 跨实例共享(随 0.18.2 发布):
|
|
7
|
+
// kernelInstance / initPromise / customInitFn 从"模块级单例"提升为挂在
|
|
8
|
+
// globalThis 上的共享状态(与 runtime-state.ts 的 `__FAICAD_FAIJS_RUNTIME__`
|
|
9
|
+
// 同款设计)。原因:CDN 装载的第三方库(sheetmetal/cq-compat/fai-cq-gears)
|
|
10
|
+
// 在 jsDelivr +esm 打包时把 peer `@faicad/faijs` 外部化为**独立模块实例**
|
|
11
|
+
// (根入口与 /api/ 子路径是不同 URL → 不同 bundle),模块级单例导致:
|
|
12
|
+
// - 库实例的 occt 内核从未初始化 → `occt-wasm kernel not initialized`;
|
|
13
|
+
// - 各实例持有不同内核 → 句柄不互通 → `meshShape: Invalid shape ID`。
|
|
14
|
+
// 挂在 globalThis 后,任一实例 initOcctWasm() 产出的内核全局可见,句柄互通。
|
|
15
|
+
// 构建期去重(external / dedupe)仍是主手段,这里是兜底(同 runtime-state 注释)。
|
|
16
|
+
const OCCT_KERNEL_KEY = '__FAICAD_FAIJS_OCCT_KERNEL__';
|
|
17
|
+
const OCCT_KERNEL_STATE_VERSION = 1;
|
|
18
|
+
/**
|
|
19
|
+
* 获取 occt 内核共享状态(单例)。
|
|
20
|
+
*
|
|
21
|
+
* 挂在 globalThis 上是为了让"两份 faijs 代码"(宿主 bundle 一份、第三方库
|
|
22
|
+
* 外部化一份)共享同一份内核状态——否则两份 kernelInstance 导致内核各自
|
|
23
|
+
* 初始化、句柄不互通(几何孤岛)。
|
|
24
|
+
*
|
|
25
|
+
* @returns the shared occt kernel state singleton.
|
|
26
|
+
*/
|
|
27
|
+
function getOcctKernelSharedState() {
|
|
28
|
+
const g = globalThis;
|
|
29
|
+
const existing = g[OCCT_KERNEL_KEY];
|
|
30
|
+
if (existing) {
|
|
31
|
+
if (existing.stateVersion !== OCCT_KERNEL_STATE_VERSION) {
|
|
32
|
+
throw new Error(`[faijs] occt kernel state version mismatch: loaded=${existing.stateVersion}, expected=${OCCT_KERNEL_STATE_VERSION}`);
|
|
33
|
+
}
|
|
34
|
+
return existing;
|
|
35
|
+
}
|
|
36
|
+
const created = {
|
|
37
|
+
stateVersion: OCCT_KERNEL_STATE_VERSION,
|
|
38
|
+
kernelInstance: null,
|
|
39
|
+
initPromise: null,
|
|
40
|
+
customInitFn: null,
|
|
41
|
+
};
|
|
42
|
+
g[OCCT_KERNEL_KEY] = created;
|
|
43
|
+
return created;
|
|
44
|
+
}
|
|
10
45
|
/**
|
|
11
46
|
* Let the browser host inject the OCCT initialization function.
|
|
12
47
|
*
|
|
13
48
|
* The browser host calls this to register its own init logic (dev/prod/CDN/e2e
|
|
14
|
-
* branches); faijs's initOcctWasm() prefers the injected function.
|
|
49
|
+
* branches); faijs's initOcctWasm() prefers the injected function. 注入的挂点
|
|
50
|
+
* 写入共享状态——任意 faijs 实例(含 CDN 外部化实例)调 initOcctWasm() 时
|
|
51
|
+
* 都优先使用同一份宿主初始化函数。
|
|
15
52
|
*
|
|
16
53
|
* When nothing is injected, initOcctWasm() reads the WASM from node_modules in
|
|
17
54
|
* a Node environment.
|
|
@@ -19,7 +56,10 @@ let customInitFn = null;
|
|
|
19
56
|
* @param fn - the initialization function to use, or null to clear
|
|
20
57
|
*/
|
|
21
58
|
export function setOcctWasmInitFn(fn) {
|
|
22
|
-
|
|
59
|
+
const s = getOcctKernelSharedState();
|
|
60
|
+
s.customInitFn = fn;
|
|
61
|
+
// 挂点变化后丢弃进行中的初始化(下一次 init 用新挂点重来)。
|
|
62
|
+
s.initPromise = null;
|
|
23
63
|
}
|
|
24
64
|
/**
|
|
25
65
|
* Resolve the occt-wasm WASM file path (linker-agnostic).
|
|
@@ -55,15 +95,16 @@ export function resolveOcctWasmPath() {
|
|
|
55
95
|
* in brep/engine/adapters/occt.ts as an explicit object literal).
|
|
56
96
|
*/
|
|
57
97
|
export async function initOcctWasm() {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
98
|
+
const s = getOcctKernelSharedState();
|
|
99
|
+
if (s.kernelInstance)
|
|
100
|
+
return s.kernelInstance;
|
|
101
|
+
if (s.initPromise)
|
|
102
|
+
return s.initPromise;
|
|
103
|
+
s.initPromise = (async () => {
|
|
104
|
+
// 优先使用浏览器 host 注入的初始化函数(共享挂点,跨实例生效)
|
|
105
|
+
if (s.customInitFn) {
|
|
106
|
+
s.kernelInstance = await s.customInitFn();
|
|
107
|
+
return s.kernelInstance;
|
|
67
108
|
}
|
|
68
109
|
// Node.js 环境:从 node_modules 读取 WASM 文件
|
|
69
110
|
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
@@ -72,12 +113,12 @@ export async function initOcctWasm() {
|
|
|
72
113
|
const wasmPath = resolveOcctWasmPath();
|
|
73
114
|
const buf = readFileSync(wasmPath);
|
|
74
115
|
const wasmBinary = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
75
|
-
kernelInstance = await Ctor.init({ wasm: wasmBinary });
|
|
76
|
-
return kernelInstance;
|
|
116
|
+
s.kernelInstance = await Ctor.init({ wasm: wasmBinary });
|
|
117
|
+
return s.kernelInstance;
|
|
77
118
|
}
|
|
78
119
|
throw new Error('initOcctWasm: no init function set and not in Node environment. Call setOcctWasmInitFn() first.');
|
|
79
120
|
})();
|
|
80
|
-
return initPromise;
|
|
121
|
+
return s.initPromise;
|
|
81
122
|
}
|
|
82
123
|
/**
|
|
83
124
|
* Get the initialized kernel instance (initOcctWasm must be called first).
|
|
@@ -85,9 +126,10 @@ export async function initOcctWasm() {
|
|
|
85
126
|
* @returns the initialized OCCT kernel
|
|
86
127
|
*/
|
|
87
128
|
export function getKernel() {
|
|
88
|
-
|
|
129
|
+
const k = getOcctKernelSharedState().kernelInstance;
|
|
130
|
+
if (!k)
|
|
89
131
|
throw new Error('occt-wasm kernel not initialized — call initOcctWasm() first');
|
|
90
|
-
return
|
|
132
|
+
return k;
|
|
91
133
|
}
|
|
92
134
|
/**
|
|
93
135
|
* Native-face alias for getKernel() (plan D3 ②): symmetric with
|
|
@@ -97,10 +139,11 @@ export function getKernel() {
|
|
|
97
139
|
export const getOcctKernel = getKernel;
|
|
98
140
|
/** Release the kernel instance (call when the app unloads). */
|
|
99
141
|
export function disposeOcctWasm() {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
kernelInstance
|
|
103
|
-
|
|
142
|
+
const s = getOcctKernelSharedState();
|
|
143
|
+
if (s.kernelInstance) {
|
|
144
|
+
s.kernelInstance[Symbol.dispose]();
|
|
145
|
+
s.kernelInstance = null;
|
|
146
|
+
s.initPromise = null;
|
|
104
147
|
}
|
|
105
148
|
}
|
|
106
149
|
/**
|
|
@@ -402,7 +445,8 @@ export function meshesToStep(positions, indices, tolerance = 0.01) {
|
|
|
402
445
|
* @param handle - the shape handle to release
|
|
403
446
|
*/
|
|
404
447
|
export function releaseShape(handle) {
|
|
405
|
-
|
|
406
|
-
|
|
448
|
+
const k = getOcctKernelSharedState().kernelInstance;
|
|
449
|
+
if (k)
|
|
450
|
+
k.release(handle);
|
|
407
451
|
}
|
|
408
452
|
//# sourceMappingURL=occtKernel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"occtKernel.js","sourceRoot":"","sources":["../../src/occt-kernel/occtKernel.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * occt-wasm 封装层 — 替代 occt-import-js\n *\n * 提供 STEP/BREP → Mesh 转换和 Mesh → STEP 导出能力。\n * 利用 occt-wasm 的 meshShape() 获取 faceGroups(面→三角形映射),\n * 利用 wireframe() 获取边折线数据,利用 edgeToFaceMap() 获取边→面关联。\n */\nimport type { OcctKernel, ShapeHandle, Mesh, EdgeData, SurfaceKind, CurveKind, BoundingBox, Vec3, XCAFDocument, LabelInfo, LabelTag } from 'occt-wasm'\n\nimport { getSolidColorsOrdered } from './stepColorParser'\n\n/** A single tessellated mesh produced by the OCCT kernel. */\nexport interface WasmTessellatedMesh {\n positions: Float32Array\n normals: Float32Array\n indices: Uint32Array\n}\n\n/** Result of importing a shape into the OCCT kernel. */\nexport interface WasmImportResult {\n shapeHandle: ShapeHandle\n meshes: WasmTessellatedMesh[]\n meshWithGroups: Mesh\n}\n\nlet kernelInstance: OcctKernel | null = null\nlet initPromise: Promise<OcctKernel> | null = null\n\n// F5 设计意图说明:\n// kernelInstance / initPromise 是环境级单例,不是实例级。\n// 在整个浏览器页面/Node 进程中,OCCT WASM 只应初始化一次。\n// 多个 CadRuntime 实例共享同一个 kernel 是正确的行为。\n\n/** 浏览器 host 注入的 OCCT 初始化函数 */\nlet customInitFn: (() => Promise<OcctKernel>) | null = null\n\n/**\n * Let the browser host inject the OCCT initialization function.\n *\n * The browser host calls this to register its own init logic (dev/prod/CDN/e2e\n * branches); faijs's initOcctWasm() prefers the injected function.\n *\n * When nothing is injected, initOcctWasm() reads the WASM from node_modules in\n * a Node environment.\n *\n * @param fn - the initialization function to use, or null to clear\n */\nexport function setOcctWasmInitFn(fn: (() => Promise<OcctKernel>) | null): void {\n customInitFn = fn\n}\n\n/**\n * Resolve the occt-wasm WASM file path (linker-agnostic).\n *\n * Walks the package exports (occt-wasm@3.8.4 exports \"./dist/occt-wasm.wasm\"),\n * independent of cwd / hoisting / pnpm|npm linker (verified pattern C).\n * Replaces the old 4-level `..` path guessing + process.cwd() fallback.\n *\n * Lazy-fetches node:module (Node >= 20.16 process.getBuiltinModule): a static\n * `import { createRequire } from 'node:module'` must not be used — occtKernel is\n * on the browser build's (demo vite build) import chain, and a static node:*\n * import would be externalized and error. This function is only called in the\n * Node branch (initOcctWasm); it never runs in a browser environment.\n *\n * @returns the resolved WASM file path\n */\nexport function resolveOcctWasmPath(): string {\n const builtin = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule?.('node:module') as\n | { createRequire: (filename: string) => { resolve: (id: string) => string } }\n | undefined\n if (!builtin) {\n throw new Error('[faijs] resolveOcctWasmPath requires Node.js (process.getBuiltinModule)')\n }\n return builtin.createRequire(import.meta.url).resolve('occt-wasm/dist/occt-wasm.wasm')\n}\n\n/**\n * Initialize the occt-wasm kernel (singleton).\n *\n * - Node.js test environment: read the WASM file from node_modules, pass an ArrayBuffer.\n * - Development: direct ESM import, Vite resolves node_modules. The WASM file is loaded from public/wasm/occt-wasm.wasm.\n * - Production: dynamically import the whole module + WASM from a CDN.\n *\n * @returns the initialized raw OCCT kernel (native L2 face — NOT BrepEngineApi;\n * the L1 contract face is built separately by createOcctPrimitives()\n * in brep/engine/adapters/occt.ts as an explicit object literal).\n */\nexport async function initOcctWasm(): Promise<OcctKernel> {\n if (kernelInstance) return kernelInstance\n if (initPromise) return initPromise\n\n initPromise = (async () => {\n // 优先使用浏览器 host 注入的初始化函数\n if (customInitFn) {\n kernelInstance = await customInitFn()\n return kernelInstance\n }\n\n // Node.js 环境:从 node_modules 读取 WASM 文件\n if (typeof process !== 'undefined' && process.versions?.node) {\n const { OcctKernel: Ctor } = await import('occt-wasm')\n const { readFileSync } = await import('node:fs')\n const wasmPath = resolveOcctWasmPath()\n const buf = readFileSync(wasmPath)\n const wasmBinary = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer\n kernelInstance = await Ctor.init({ wasm: wasmBinary })\n return kernelInstance\n }\n\n throw new Error('initOcctWasm: no init function set and not in Node environment. Call setOcctWasmInitFn() first.')\n })()\n\n return initPromise\n}\n\n/**\n * Get the initialized kernel instance (initOcctWasm must be called first).\n *\n * @returns the initialized OCCT kernel\n */\nexport function getKernel(): OcctKernel {\n if (!kernelInstance) throw new Error('occt-wasm kernel not initialized — call initOcctWasm() first')\n return kernelInstance\n}\n\n/**\n * Native-face alias for getKernel() (plan D3 ②): symmetric with\n * getBrepkitKernel() on the brepkit side. Platform-specific code uses this\n * name; the original getKernel() stays for existing host code.\n */\nexport const getOcctKernel = getKernel\n\n/** Release the kernel instance (call when the app unloads). */\nexport function disposeOcctWasm(): void {\n if (kernelInstance) {\n kernelInstance[Symbol.dispose]()\n kernelInstance = null\n initPromise = null\n }\n}\n\n/** Options controlling mesh deflection during tessellation. */\nexport interface MeshDeflectionOptions {\n linearDeflection?: number\n angularDeflection?: number\n /** If true, linearDeflection is multiplied by the bounding-box diagonal\n * (matches OCCT's BRepMesh_IncrementalMesh relative=true behaviour). */\n relative?: boolean\n}\n\n/**\n * Compute the effective linear deflection, applying relative scaling\n * exactly as OCCT does internally: `linDefl * bboxDiagonal` when relative=true.\n *\n * @param kernel - the OCCT kernel\n * @param shape - the shape whose bounding box drives relative scaling\n * @param options - the requested deflection options\n * @returns the effective linear and angular deflection values\n */\nexport function computeEffectiveDeflection<S>(\n kernel: { getBoundingBox(shape: S, useTriangulation?: boolean): BoundingBox },\n shape: S,\n options: MeshDeflectionOptions = {},\n): { linearDeflection: number; angularDeflection: number } {\n const ld = options.linearDeflection ?? 0.1\n const ad = options.angularDeflection ?? 0.5\n const relative = options.relative ?? false\n if (!relative) return { linearDeflection: ld, angularDeflection: ad }\n\n // OCCT: BRepBndLib::AddOptimal → bbox diagonal → deflection *= diagonal\n // 浏览器 WASM 中 compound shape 可能使 getBoundingBox 抛异常,需要容错\n let bb: BoundingBox\n try {\n bb = kernel.getBoundingBox(shape, false)\n } catch {\n try {\n bb = kernel.getBoundingBox(shape, true)\n } catch {\n // 无法获取 bbox → 返回非相对模式的默认值\n return { linearDeflection: ld, angularDeflection: ad }\n }\n }\n const dx = bb.xmax - bb.xmin\n const dy = bb.ymax - bb.ymin\n const dz = bb.zmax - bb.zmin\n const diag = Math.sqrt(dx * dx + dy * dy + dz * dz)\n return { linearDeflection: ld * diag, angularDeflection: ad }\n}\n\n/**\n * Import a STEP file to ShapeHandle + Mesh (including face groups).\n *\n * Uses meshShape() rather than tessellate(), so face-group data is available\n * (each face's triangle range [triStart, triCount, faceHash]).\n *\n * @param stepData - STEP file bytes\n * @param deflection - optional mesh deflection options\n * @returns the import result with shape handle and meshes\n */\nexport async function importStepToMesh(\n stepData: ArrayBuffer | Uint8Array,\n deflection?: MeshDeflectionOptions,\n): Promise<WasmImportResult> {\n const kernel = await initOcctWasm()\n const ab = stepData instanceof Uint8Array ? stepData.buffer.slice(stepData.byteOffset, stepData.byteOffset + stepData.byteLength) as ArrayBuffer : stepData\n const shape = kernel.importStep(ab)\n\n const eff = computeEffectiveDeflection(kernel, shape, deflection)\n const mesh = kernel.meshShape(shape, { linearDeflection: eff.linearDeflection, angularDeflection: eff.angularDeflection })\n\n return {\n shapeHandle: shape,\n meshes: [{\n positions: mesh.positions,\n normals: mesh.normals,\n indices: mesh.indices,\n }],\n meshWithGroups: mesh,\n }\n}\n\n// ── Assembly (XCAF) support ──\n\n/** A node in the XCAF assembly tree (mirrors the STEP_T occurrence concept). */\nexport interface AssemblyPartNode {\n /** Dotted path: 'o0', 'o0.o1', 'o0.o1.o2' — matches STEP_T occurrence path. */\n labelPath: string\n /** XCAF label name (falls back to labelPath if empty). */\n name: string\n /** True if this node is an assembly (has child components). */\n isAssembly: boolean\n /** True if this node is a component reference (leaf part with location). */\n isComponent: boolean\n /** Shape handle with location baked in (for components) or raw shape (for roots).\n * Caller must release this handle. Null for empty assembly labels. */\n shapeHandle: ShapeHandle | null\n /** Deduplication key for prototype shapes (same geometry = same key).\n * Only set for leaf (non-assembly) nodes. */\n prototypeKey: string | null\n /** RGB color [r,g,b] in 0..1 range, or null if no color. */\n color: [number, number, number] | null\n /** Child nodes (recursive). Empty for leaf parts. */\n children: AssemblyPartNode[]\n}\n\nconst IDENTITY_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]\n\n/**\n * Import a STEP file via XCAF (STEPCAFControl_Reader), preserving the assembly\n * tree, names, colors, and per-component locations.\n *\n * Unlike `importStepToMesh` (which flattens via STEPControl_Reader.OneShape()),\n * this function returns the full assembly tree structure.\n *\n * The caller is responsible for releasing all shapeHandle values in the tree\n * (use `releaseAssemblyTree`).\n *\n * @param stepData - STEP file bytes\n * @returns the root assembly-part nodes\n */\nexport async function importAssemblyFromStep(\n stepData: ArrayBuffer | Uint8Array,\n): Promise<AssemblyPartNode[]> {\n const kernel = (await initOcctWasm()) as unknown as OcctKernel\n const stepText = stepData instanceof Uint8Array\n ? new TextDecoder().decode(stepData)\n : new TextDecoder().decode(new Uint8Array(stepData))\n\n // Parse sub-shape colors from STEP text (fallback for when XCAF label has no color).\n // STEP files from NX/SolidWorks often store colors as STYLED_ITEM on individual\n // solids rather than on XCAF labels, so getLabelInfo returns hasColor=false.\n const solidColors = getSolidColorsOrdered(stepText)\n\n // XCAFDocument.fromSTEP uses STEPCAFControl_Reader with color/name/layer modes\n const doc = kernel.importXCAFFromSTEP(stepText)\n try {\n const rootTags = doc.getRoots()\n const nodes: AssemblyPartNode[] = []\n const cursor = { idx: 0 }\n for (let i = 0; i < rootTags.length; i++) {\n nodes.push(walkLabel(kernel, doc, rootTags[i], `o${i}`, solidColors, cursor))\n }\n return nodes\n } finally {\n doc.close()\n }\n}\n\n/** Walk-context: tracks the current position in the solidColors array across\n * recursive walkLabel calls so that multi-solid compounds consume colors in order. */\ninterface WalkCtx {\n idx: number\n}\n\nfunction walkLabel(\n kernel: OcctKernel,\n doc: XCAFDocument,\n tag: LabelTag,\n path: string,\n solidColors: ([number, number, number] | null)[],\n ctx: WalkCtx,\n): AssemblyPartNode {\n const info: LabelInfo = doc.getLabelInfo(tag)\n const name = info.name || path\n const color = info.hasColor ? [info.color[0], info.color[1], info.color[2]] as [number, number, number] : null\n\n if (info.isAssembly) {\n const childTags = doc.getChildren(tag)\n const children: AssemblyPartNode[] = []\n for (let i = 0; i < childTags.length; i++) {\n children.push(walkLabel(kernel, doc, childTags[i], `${path}.o${i}`, solidColors, ctx))\n }\n // Assembly nodes have no shape of their own; release if one was returned\n if (info.shapeHandle) kernel.release(info.shapeHandle)\n return {\n labelPath: path,\n name,\n isAssembly: true,\n isComponent: false,\n shapeHandle: null,\n prototypeKey: null,\n color,\n children,\n }\n }\n\n // Leaf component — shapeHandle has location baked in (OCCT GetShape semantics)\n // Check if this is a compound with multiple solids — if so, split into\n // separate child nodes so each solid becomes its own mesh/scene-tree node.\n // This handles STEP files that have a single PRODUCT containing a compound\n // of multiple MANIFOLD_SOLID_BREP entities (common in NX/SolidWorks exports).\n if (info.shapeHandle) {\n const solids = kernel.getSubShapes(info.shapeHandle, 'solid')\n if (solids.length > 1) {\n // Multi-solid compound → split into virtual assembly children\n // Each child gets its own solid shapeHandle (already at correct location\n // because getSubShapes extracts from the located compound).\n const children: AssemblyPartNode[] = []\n for (let si = 0; si < solids.length; si++) {\n const childPath = `${path}.s${si}`\n const childName = `${name} [${si + 1}]`\n let childProtoKey: string | null\n try {\n childProtoKey = String(kernel.hashCode(solids[si], 2147483647))\n } catch {\n childProtoKey = null\n }\n // Use STEP-parsed color if label has no color (sub-shape level color)\n const childColor = color ?? (solidColors[ctx.idx] ?? null)\n ctx.idx++\n children.push({\n labelPath: childPath,\n name: childName,\n isAssembly: false,\n isComponent: true,\n shapeHandle: solids[si],\n prototypeKey: childProtoKey,\n color: childColor,\n children: [],\n })\n }\n // Release the compound shape itself — children hold individual solids\n kernel.release(info.shapeHandle)\n return {\n labelPath: path,\n name,\n isAssembly: true, // mark as assembly so it becomes a group node\n isComponent: false,\n shapeHandle: null,\n prototypeKey: null,\n color,\n children,\n }\n }\n // Single solid (or non-solid) — release the extra sub-shape handles\n for (const s of solids) kernel.release(s)\n }\n\n let prototypeKey: string | null = null\n if (info.shapeHandle) {\n // Get un-located shape for dedup (located with identity = remove location)\n try {\n const unlocated = kernel.located(info.shapeHandle, IDENTITY_MATRIX)\n prototypeKey = String(kernel.hashCode(unlocated, 2147483647))\n kernel.release(unlocated)\n } catch {\n // Fallback: use shapeHandle hash directly (less accurate dedup)\n prototypeKey = String(kernel.hashCode(info.shapeHandle, 2147483647))\n }\n }\n\n // For single-solid leaf parts, also try STEP-parsed color as fallback\n const finalColor = color ?? (solidColors[ctx.idx] ?? null)\n ctx.idx++\n\n return {\n labelPath: path,\n name,\n isAssembly: false,\n isComponent: info.isComponent,\n shapeHandle: info.shapeHandle,\n prototypeKey,\n color: finalColor,\n children: [],\n }\n}\n\n/** Recursively release all shapeHandle values in an assembly tree.\n *\n * @param kernel - the OCCT kernel used to release handles\n * @param nodes - the assembly tree nodes to release\n */\nexport function releaseAssemblyTree(kernel: OcctKernel, nodes: AssemblyPartNode[]): void {\n for (const node of nodes) {\n if (node.shapeHandle) kernel.release(node.shapeHandle)\n if (node.children.length > 0) releaseAssemblyTree(kernel, node.children)\n }\n}\n\n/** Collect all leaf (non-assembly) nodes from an assembly tree.\n *\n * @param nodes - the assembly tree nodes to traverse\n * @returns the collected leaf part nodes in depth-first order\n */\nexport function collectLeafParts(nodes: AssemblyPartNode[]): AssemblyPartNode[] {\n const leaves: AssemblyPartNode[] = []\n function walk(node: AssemblyPartNode) {\n if (node.isAssembly) {\n for (const child of node.children) walk(child)\n } else {\n leaves.push(node)\n }\n }\n for (const node of nodes) walk(node)\n return leaves\n}\n\n/**\n * Import BREP text to ShapeHandle + Mesh.\n *\n * @param brepData - BREP file content as a string\n * @param deflection - optional mesh deflection options\n * @returns the import result with shape handle and meshes\n */\nexport async function importBrepToMesh(\n brepData: string,\n deflection?: MeshDeflectionOptions,\n): Promise<WasmImportResult> {\n const kernel = await initOcctWasm()\n const shape = kernel.fromBREP(brepData)\n\n const eff = computeEffectiveDeflection(kernel, shape, deflection)\n const mesh = kernel.meshShape(shape, { linearDeflection: eff.linearDeflection, angularDeflection: eff.angularDeflection })\n\n return {\n shapeHandle: shape,\n meshes: [{\n positions: mesh.positions,\n normals: mesh.normals,\n indices: mesh.indices,\n }],\n meshWithGroups: mesh,\n }\n}\n\n/**\n * Mesh → STEP export: rebuild a STEP solid from a triangle mesh.\n * Follows the pattern from occt-wasm/examples/stl2step.mjs.\n *\n * @param positions - vertex array [x0,y0,z0, x1,y1,z1, ...]\n * @param indices - index array [i0,i1,i2, i3,i4,i5, ...]\n * @param tolerance - sewing tolerance in mm (default 0.01)\n * @returns the STEP file content string\n */\nexport function meshesToStep(\n positions: Float32Array,\n indices: Uint32Array,\n tolerance = 0.01,\n): string {\n const kernel = getKernel()\n const faces: ShapeHandle[] = []\n let solid: ShapeHandle | null = null\n\n try {\n for (let i = 0; i < indices.length; i += 3) {\n const ia = indices[i] * 3\n const ib = indices[i + 1] * 3\n const ic = indices[i + 2] * 3\n const face = kernel.buildTriFace(\n { x: positions[ia], y: positions[ia + 1], z: positions[ia + 2] },\n { x: positions[ib], y: positions[ib + 1], z: positions[ib + 2] },\n { x: positions[ic], y: positions[ic + 1], z: positions[ic + 2] },\n )\n faces.push(face)\n }\n\n solid = kernel.sewAndSolidify(faces, tolerance)\n const unified = kernel.unifySameDomain(solid)\n kernel.release(solid)\n solid = unified\n return kernel.exportStep(solid)\n } finally {\n if (solid !== null) kernel.release(solid)\n for (const f of faces) kernel.release(f)\n }\n}\n\n/**\n * Release a shape handle.\n *\n * @param handle - the shape handle to release\n */\nexport function releaseShape(handle: ShapeHandle): void {\n if (kernelInstance) kernelInstance.release(handle)\n}\n\n/** 导出类型供外部使用 */\nexport type { OcctKernel, ShapeHandle, Mesh, EdgeData, SurfaceKind, CurveKind, BoundingBox, Vec3, XCAFDocument, LabelInfo, LabelTag }\n"]}
|
|
1
|
+
{"version":3,"file":"occtKernel.js","sourceRoot":"","sources":["../../src/occt-kernel/occtKernel.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * occt-wasm 封装层 — 替代 occt-import-js\n *\n * 提供 STEP/BREP → Mesh 转换和 Mesh → STEP 导出能力。\n * 利用 occt-wasm 的 meshShape() 获取 faceGroups(面→三角形映射),\n * 利用 wireframe() 获取边折线数据,利用 edgeToFaceMap() 获取边→面关联。\n */\nimport type { OcctKernel, ShapeHandle, Mesh, EdgeData, SurfaceKind, CurveKind, BoundingBox, Vec3, XCAFDocument, LabelInfo, LabelTag } from 'occt-wasm'\n\nimport { getSolidColorsOrdered } from './stepColorParser'\n\n/** A single tessellated mesh produced by the OCCT kernel. */\nexport interface WasmTessellatedMesh {\n positions: Float32Array\n normals: Float32Array\n indices: Uint32Array\n}\n\n/** Result of importing a shape into the OCCT kernel. */\nexport interface WasmImportResult {\n shapeHandle: ShapeHandle\n meshes: WasmTessellatedMesh[]\n meshWithGroups: Mesh\n}\n\n// F5 设计意图说明:\n// OCCT WASM 内核是环境级单例,不是实例级。在整个浏览器页面/Node 进程中只应\n// 初始化一次;多个 CadRuntime 实例共享同一个 kernel 是正确的行为。\n//\n// 2026-09-26 跨实例共享(随 0.18.2 发布):\n// kernelInstance / initPromise / customInitFn 从\"模块级单例\"提升为挂在\n// globalThis 上的共享状态(与 runtime-state.ts 的 `__FAICAD_FAIJS_RUNTIME__`\n// 同款设计)。原因:CDN 装载的第三方库(sheetmetal/cq-compat/fai-cq-gears)\n// 在 jsDelivr +esm 打包时把 peer `@faicad/faijs` 外部化为**独立模块实例**\n// (根入口与 /api/ 子路径是不同 URL → 不同 bundle),模块级单例导致:\n// - 库实例的 occt 内核从未初始化 → `occt-wasm kernel not initialized`;\n// - 各实例持有不同内核 → 句柄不互通 → `meshShape: Invalid shape ID`。\n// 挂在 globalThis 后,任一实例 initOcctWasm() 产出的内核全局可见,句柄互通。\n// 构建期去重(external / dedupe)仍是主手段,这里是兜底(同 runtime-state 注释)。\n\nconst OCCT_KERNEL_KEY = '__FAICAD_FAIJS_OCCT_KERNEL__'\nconst OCCT_KERNEL_STATE_VERSION = 1\n\n/** occt 内核共享状态(跨 faijs 模块实例,浏览器/Node 双环境)。 */\ninterface OcctKernelSharedState {\n stateVersion: number\n kernelInstance: OcctKernel | null\n initPromise: Promise<OcctKernel> | null\n /** 浏览器 host 注入的 OCCT 初始化函数 */\n customInitFn: (() => Promise<OcctKernel>) | null\n}\n\n/**\n * 获取 occt 内核共享状态(单例)。\n *\n * 挂在 globalThis 上是为了让\"两份 faijs 代码\"(宿主 bundle 一份、第三方库\n * 外部化一份)共享同一份内核状态——否则两份 kernelInstance 导致内核各自\n * 初始化、句柄不互通(几何孤岛)。\n *\n * @returns the shared occt kernel state singleton.\n */\nfunction getOcctKernelSharedState(): OcctKernelSharedState {\n const g = globalThis as unknown as Record<string, unknown>\n const existing = g[OCCT_KERNEL_KEY] as OcctKernelSharedState | undefined\n if (existing) {\n if (existing.stateVersion !== OCCT_KERNEL_STATE_VERSION) {\n throw new Error(\n `[faijs] occt kernel state version mismatch: loaded=${existing.stateVersion}, expected=${OCCT_KERNEL_STATE_VERSION}`,\n )\n }\n return existing\n }\n const created: OcctKernelSharedState = {\n stateVersion: OCCT_KERNEL_STATE_VERSION,\n kernelInstance: null,\n initPromise: null,\n customInitFn: null,\n }\n g[OCCT_KERNEL_KEY] = created\n return created\n}\n\n/**\n * Let the browser host inject the OCCT initialization function.\n *\n * The browser host calls this to register its own init logic (dev/prod/CDN/e2e\n * branches); faijs's initOcctWasm() prefers the injected function. 注入的挂点\n * 写入共享状态——任意 faijs 实例(含 CDN 外部化实例)调 initOcctWasm() 时\n * 都优先使用同一份宿主初始化函数。\n *\n * When nothing is injected, initOcctWasm() reads the WASM from node_modules in\n * a Node environment.\n *\n * @param fn - the initialization function to use, or null to clear\n */\nexport function setOcctWasmInitFn(fn: (() => Promise<OcctKernel>) | null): void {\n const s = getOcctKernelSharedState()\n s.customInitFn = fn\n // 挂点变化后丢弃进行中的初始化(下一次 init 用新挂点重来)。\n s.initPromise = null\n}\n\n/**\n * Resolve the occt-wasm WASM file path (linker-agnostic).\n *\n * Walks the package exports (occt-wasm@3.8.4 exports \"./dist/occt-wasm.wasm\"),\n * independent of cwd / hoisting / pnpm|npm linker (verified pattern C).\n * Replaces the old 4-level `..` path guessing + process.cwd() fallback.\n *\n * Lazy-fetches node:module (Node >= 20.16 process.getBuiltinModule): a static\n * `import { createRequire } from 'node:module'` must not be used — occtKernel is\n * on the browser build's (demo vite build) import chain, and a static node:*\n * import would be externalized and error. This function is only called in the\n * Node branch (initOcctWasm); it never runs in a browser environment.\n *\n * @returns the resolved WASM file path\n */\nexport function resolveOcctWasmPath(): string {\n const builtin = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule?.('node:module') as\n | { createRequire: (filename: string) => { resolve: (id: string) => string } }\n | undefined\n if (!builtin) {\n throw new Error('[faijs] resolveOcctWasmPath requires Node.js (process.getBuiltinModule)')\n }\n return builtin.createRequire(import.meta.url).resolve('occt-wasm/dist/occt-wasm.wasm')\n}\n\n/**\n * Initialize the occt-wasm kernel (singleton).\n *\n * - Node.js test environment: read the WASM file from node_modules, pass an ArrayBuffer.\n * - Development: direct ESM import, Vite resolves node_modules. The WASM file is loaded from public/wasm/occt-wasm.wasm.\n * - Production: dynamically import the whole module + WASM from a CDN.\n *\n * @returns the initialized raw OCCT kernel (native L2 face — NOT BrepEngineApi;\n * the L1 contract face is built separately by createOcctPrimitives()\n * in brep/engine/adapters/occt.ts as an explicit object literal).\n */\nexport async function initOcctWasm(): Promise<OcctKernel> {\n const s = getOcctKernelSharedState()\n if (s.kernelInstance) return s.kernelInstance\n if (s.initPromise) return s.initPromise\n\n s.initPromise = (async () => {\n // 优先使用浏览器 host 注入的初始化函数(共享挂点,跨实例生效)\n if (s.customInitFn) {\n s.kernelInstance = await s.customInitFn()\n return s.kernelInstance\n }\n\n // Node.js 环境:从 node_modules 读取 WASM 文件\n if (typeof process !== 'undefined' && process.versions?.node) {\n const { OcctKernel: Ctor } = await import('occt-wasm')\n const { readFileSync } = await import('node:fs')\n const wasmPath = resolveOcctWasmPath()\n const buf = readFileSync(wasmPath)\n const wasmBinary = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer\n s.kernelInstance = await Ctor.init({ wasm: wasmBinary })\n return s.kernelInstance\n }\n\n throw new Error('initOcctWasm: no init function set and not in Node environment. Call setOcctWasmInitFn() first.')\n })()\n\n return s.initPromise\n}\n\n/**\n * Get the initialized kernel instance (initOcctWasm must be called first).\n *\n * @returns the initialized OCCT kernel\n */\nexport function getKernel(): OcctKernel {\n const k = getOcctKernelSharedState().kernelInstance\n if (!k) throw new Error('occt-wasm kernel not initialized — call initOcctWasm() first')\n return k\n}\n\n/**\n * Native-face alias for getKernel() (plan D3 ②): symmetric with\n * getBrepkitKernel() on the brepkit side. Platform-specific code uses this\n * name; the original getKernel() stays for existing host code.\n */\nexport const getOcctKernel = getKernel\n\n/** Release the kernel instance (call when the app unloads). */\nexport function disposeOcctWasm(): void {\n const s = getOcctKernelSharedState()\n if (s.kernelInstance) {\n s.kernelInstance[Symbol.dispose]()\n s.kernelInstance = null\n s.initPromise = null\n }\n}\n\n/** Options controlling mesh deflection during tessellation. */\nexport interface MeshDeflectionOptions {\n linearDeflection?: number\n angularDeflection?: number\n /** If true, linearDeflection is multiplied by the bounding-box diagonal\n * (matches OCCT's BRepMesh_IncrementalMesh relative=true behaviour). */\n relative?: boolean\n}\n\n/**\n * Compute the effective linear deflection, applying relative scaling\n * exactly as OCCT does internally: `linDefl * bboxDiagonal` when relative=true.\n *\n * @param kernel - the OCCT kernel\n * @param shape - the shape whose bounding box drives relative scaling\n * @param options - the requested deflection options\n * @returns the effective linear and angular deflection values\n */\nexport function computeEffectiveDeflection<S>(\n kernel: { getBoundingBox(shape: S, useTriangulation?: boolean): BoundingBox },\n shape: S,\n options: MeshDeflectionOptions = {},\n): { linearDeflection: number; angularDeflection: number } {\n const ld = options.linearDeflection ?? 0.1\n const ad = options.angularDeflection ?? 0.5\n const relative = options.relative ?? false\n if (!relative) return { linearDeflection: ld, angularDeflection: ad }\n\n // OCCT: BRepBndLib::AddOptimal → bbox diagonal → deflection *= diagonal\n // 浏览器 WASM 中 compound shape 可能使 getBoundingBox 抛异常,需要容错\n let bb: BoundingBox\n try {\n bb = kernel.getBoundingBox(shape, false)\n } catch {\n try {\n bb = kernel.getBoundingBox(shape, true)\n } catch {\n // 无法获取 bbox → 返回非相对模式的默认值\n return { linearDeflection: ld, angularDeflection: ad }\n }\n }\n const dx = bb.xmax - bb.xmin\n const dy = bb.ymax - bb.ymin\n const dz = bb.zmax - bb.zmin\n const diag = Math.sqrt(dx * dx + dy * dy + dz * dz)\n return { linearDeflection: ld * diag, angularDeflection: ad }\n}\n\n/**\n * Import a STEP file to ShapeHandle + Mesh (including face groups).\n *\n * Uses meshShape() rather than tessellate(), so face-group data is available\n * (each face's triangle range [triStart, triCount, faceHash]).\n *\n * @param stepData - STEP file bytes\n * @param deflection - optional mesh deflection options\n * @returns the import result with shape handle and meshes\n */\nexport async function importStepToMesh(\n stepData: ArrayBuffer | Uint8Array,\n deflection?: MeshDeflectionOptions,\n): Promise<WasmImportResult> {\n const kernel = await initOcctWasm()\n const ab = stepData instanceof Uint8Array ? stepData.buffer.slice(stepData.byteOffset, stepData.byteOffset + stepData.byteLength) as ArrayBuffer : stepData\n const shape = kernel.importStep(ab)\n\n const eff = computeEffectiveDeflection(kernel, shape, deflection)\n const mesh = kernel.meshShape(shape, { linearDeflection: eff.linearDeflection, angularDeflection: eff.angularDeflection })\n\n return {\n shapeHandle: shape,\n meshes: [{\n positions: mesh.positions,\n normals: mesh.normals,\n indices: mesh.indices,\n }],\n meshWithGroups: mesh,\n }\n}\n\n// ── Assembly (XCAF) support ──\n\n/** A node in the XCAF assembly tree (mirrors the STEP_T occurrence concept). */\nexport interface AssemblyPartNode {\n /** Dotted path: 'o0', 'o0.o1', 'o0.o1.o2' — matches STEP_T occurrence path. */\n labelPath: string\n /** XCAF label name (falls back to labelPath if empty). */\n name: string\n /** True if this node is an assembly (has child components). */\n isAssembly: boolean\n /** True if this node is a component reference (leaf part with location). */\n isComponent: boolean\n /** Shape handle with location baked in (for components) or raw shape (for roots).\n * Caller must release this handle. Null for empty assembly labels. */\n shapeHandle: ShapeHandle | null\n /** Deduplication key for prototype shapes (same geometry = same key).\n * Only set for leaf (non-assembly) nodes. */\n prototypeKey: string | null\n /** RGB color [r,g,b] in 0..1 range, or null if no color. */\n color: [number, number, number] | null\n /** Child nodes (recursive). Empty for leaf parts. */\n children: AssemblyPartNode[]\n}\n\nconst IDENTITY_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]\n\n/**\n * Import a STEP file via XCAF (STEPCAFControl_Reader), preserving the assembly\n * tree, names, colors, and per-component locations.\n *\n * Unlike `importStepToMesh` (which flattens via STEPControl_Reader.OneShape()),\n * this function returns the full assembly tree structure.\n *\n * The caller is responsible for releasing all shapeHandle values in the tree\n * (use `releaseAssemblyTree`).\n *\n * @param stepData - STEP file bytes\n * @returns the root assembly-part nodes\n */\nexport async function importAssemblyFromStep(\n stepData: ArrayBuffer | Uint8Array,\n): Promise<AssemblyPartNode[]> {\n const kernel = (await initOcctWasm()) as unknown as OcctKernel\n const stepText = stepData instanceof Uint8Array\n ? new TextDecoder().decode(stepData)\n : new TextDecoder().decode(new Uint8Array(stepData))\n\n // Parse sub-shape colors from STEP text (fallback for when XCAF label has no color).\n // STEP files from NX/SolidWorks often store colors as STYLED_ITEM on individual\n // solids rather than on XCAF labels, so getLabelInfo returns hasColor=false.\n const solidColors = getSolidColorsOrdered(stepText)\n\n // XCAFDocument.fromSTEP uses STEPCAFControl_Reader with color/name/layer modes\n const doc = kernel.importXCAFFromSTEP(stepText)\n try {\n const rootTags = doc.getRoots()\n const nodes: AssemblyPartNode[] = []\n const cursor = { idx: 0 }\n for (let i = 0; i < rootTags.length; i++) {\n nodes.push(walkLabel(kernel, doc, rootTags[i], `o${i}`, solidColors, cursor))\n }\n return nodes\n } finally {\n doc.close()\n }\n}\n\n/** Walk-context: tracks the current position in the solidColors array across\n * recursive walkLabel calls so that multi-solid compounds consume colors in order. */\ninterface WalkCtx {\n idx: number\n}\n\nfunction walkLabel(\n kernel: OcctKernel,\n doc: XCAFDocument,\n tag: LabelTag,\n path: string,\n solidColors: ([number, number, number] | null)[],\n ctx: WalkCtx,\n): AssemblyPartNode {\n const info: LabelInfo = doc.getLabelInfo(tag)\n const name = info.name || path\n const color = info.hasColor ? [info.color[0], info.color[1], info.color[2]] as [number, number, number] : null\n\n if (info.isAssembly) {\n const childTags = doc.getChildren(tag)\n const children: AssemblyPartNode[] = []\n for (let i = 0; i < childTags.length; i++) {\n children.push(walkLabel(kernel, doc, childTags[i], `${path}.o${i}`, solidColors, ctx))\n }\n // Assembly nodes have no shape of their own; release if one was returned\n if (info.shapeHandle) kernel.release(info.shapeHandle)\n return {\n labelPath: path,\n name,\n isAssembly: true,\n isComponent: false,\n shapeHandle: null,\n prototypeKey: null,\n color,\n children,\n }\n }\n\n // Leaf component — shapeHandle has location baked in (OCCT GetShape semantics)\n // Check if this is a compound with multiple solids — if so, split into\n // separate child nodes so each solid becomes its own mesh/scene-tree node.\n // This handles STEP files that have a single PRODUCT containing a compound\n // of multiple MANIFOLD_SOLID_BREP entities (common in NX/SolidWorks exports).\n if (info.shapeHandle) {\n const solids = kernel.getSubShapes(info.shapeHandle, 'solid')\n if (solids.length > 1) {\n // Multi-solid compound → split into virtual assembly children\n // Each child gets its own solid shapeHandle (already at correct location\n // because getSubShapes extracts from the located compound).\n const children: AssemblyPartNode[] = []\n for (let si = 0; si < solids.length; si++) {\n const childPath = `${path}.s${si}`\n const childName = `${name} [${si + 1}]`\n let childProtoKey: string | null\n try {\n childProtoKey = String(kernel.hashCode(solids[si], 2147483647))\n } catch {\n childProtoKey = null\n }\n // Use STEP-parsed color if label has no color (sub-shape level color)\n const childColor = color ?? (solidColors[ctx.idx] ?? null)\n ctx.idx++\n children.push({\n labelPath: childPath,\n name: childName,\n isAssembly: false,\n isComponent: true,\n shapeHandle: solids[si],\n prototypeKey: childProtoKey,\n color: childColor,\n children: [],\n })\n }\n // Release the compound shape itself — children hold individual solids\n kernel.release(info.shapeHandle)\n return {\n labelPath: path,\n name,\n isAssembly: true, // mark as assembly so it becomes a group node\n isComponent: false,\n shapeHandle: null,\n prototypeKey: null,\n color,\n children,\n }\n }\n // Single solid (or non-solid) — release the extra sub-shape handles\n for (const s of solids) kernel.release(s)\n }\n\n let prototypeKey: string | null = null\n if (info.shapeHandle) {\n // Get un-located shape for dedup (located with identity = remove location)\n try {\n const unlocated = kernel.located(info.shapeHandle, IDENTITY_MATRIX)\n prototypeKey = String(kernel.hashCode(unlocated, 2147483647))\n kernel.release(unlocated)\n } catch {\n // Fallback: use shapeHandle hash directly (less accurate dedup)\n prototypeKey = String(kernel.hashCode(info.shapeHandle, 2147483647))\n }\n }\n\n // For single-solid leaf parts, also try STEP-parsed color as fallback\n const finalColor = color ?? (solidColors[ctx.idx] ?? null)\n ctx.idx++\n\n return {\n labelPath: path,\n name,\n isAssembly: false,\n isComponent: info.isComponent,\n shapeHandle: info.shapeHandle,\n prototypeKey,\n color: finalColor,\n children: [],\n }\n}\n\n/** Recursively release all shapeHandle values in an assembly tree.\n *\n * @param kernel - the OCCT kernel used to release handles\n * @param nodes - the assembly tree nodes to release\n */\nexport function releaseAssemblyTree(kernel: OcctKernel, nodes: AssemblyPartNode[]): void {\n for (const node of nodes) {\n if (node.shapeHandle) kernel.release(node.shapeHandle)\n if (node.children.length > 0) releaseAssemblyTree(kernel, node.children)\n }\n}\n\n/** Collect all leaf (non-assembly) nodes from an assembly tree.\n *\n * @param nodes - the assembly tree nodes to traverse\n * @returns the collected leaf part nodes in depth-first order\n */\nexport function collectLeafParts(nodes: AssemblyPartNode[]): AssemblyPartNode[] {\n const leaves: AssemblyPartNode[] = []\n function walk(node: AssemblyPartNode) {\n if (node.isAssembly) {\n for (const child of node.children) walk(child)\n } else {\n leaves.push(node)\n }\n }\n for (const node of nodes) walk(node)\n return leaves\n}\n\n/**\n * Import BREP text to ShapeHandle + Mesh.\n *\n * @param brepData - BREP file content as a string\n * @param deflection - optional mesh deflection options\n * @returns the import result with shape handle and meshes\n */\nexport async function importBrepToMesh(\n brepData: string,\n deflection?: MeshDeflectionOptions,\n): Promise<WasmImportResult> {\n const kernel = await initOcctWasm()\n const shape = kernel.fromBREP(brepData)\n\n const eff = computeEffectiveDeflection(kernel, shape, deflection)\n const mesh = kernel.meshShape(shape, { linearDeflection: eff.linearDeflection, angularDeflection: eff.angularDeflection })\n\n return {\n shapeHandle: shape,\n meshes: [{\n positions: mesh.positions,\n normals: mesh.normals,\n indices: mesh.indices,\n }],\n meshWithGroups: mesh,\n }\n}\n\n/**\n * Mesh → STEP export: rebuild a STEP solid from a triangle mesh.\n * Follows the pattern from occt-wasm/examples/stl2step.mjs.\n *\n * @param positions - vertex array [x0,y0,z0, x1,y1,z1, ...]\n * @param indices - index array [i0,i1,i2, i3,i4,i5, ...]\n * @param tolerance - sewing tolerance in mm (default 0.01)\n * @returns the STEP file content string\n */\nexport function meshesToStep(\n positions: Float32Array,\n indices: Uint32Array,\n tolerance = 0.01,\n): string {\n const kernel = getKernel()\n const faces: ShapeHandle[] = []\n let solid: ShapeHandle | null = null\n\n try {\n for (let i = 0; i < indices.length; i += 3) {\n const ia = indices[i] * 3\n const ib = indices[i + 1] * 3\n const ic = indices[i + 2] * 3\n const face = kernel.buildTriFace(\n { x: positions[ia], y: positions[ia + 1], z: positions[ia + 2] },\n { x: positions[ib], y: positions[ib + 1], z: positions[ib + 2] },\n { x: positions[ic], y: positions[ic + 1], z: positions[ic + 2] },\n )\n faces.push(face)\n }\n\n solid = kernel.sewAndSolidify(faces, tolerance)\n const unified = kernel.unifySameDomain(solid)\n kernel.release(solid)\n solid = unified\n return kernel.exportStep(solid)\n } finally {\n if (solid !== null) kernel.release(solid)\n for (const f of faces) kernel.release(f)\n }\n}\n\n/**\n * Release a shape handle.\n *\n * @param handle - the shape handle to release\n */\nexport function releaseShape(handle: ShapeHandle): void {\n const k = getOcctKernelSharedState().kernelInstance\n if (k) k.release(handle)\n}\n\n/** 导出类型供外部使用 */\nexport type { OcctKernel, ShapeHandle, Mesh, EdgeData, SurfaceKind, CurveKind, BoundingBox, Vec3, XCAFDocument, LabelInfo, LabelTag }\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faicad/faijs",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"main": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
|
-
|
|
13
|
-
"exports": {
|
|
12
|
+
"exports": {
|
|
14
13
|
".": {
|
|
15
14
|
"types": "./dist/index.d.ts",
|
|
16
15
|
"default": "./dist/index.js"
|