@faicad/faijs 0.30.9 → 0.31.1
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/align/index.d.ts +64 -0
- package/dist/api/align/index.d.ts.map +1 -0
- package/dist/api/align/index.js +101 -0
- package/dist/api/align/index.js.map +1 -0
- package/dist/api/api-namespace.d.ts +5 -4
- package/dist/api/api-namespace.d.ts.map +1 -1
- package/dist/api/api-namespace.js +36 -4
- package/dist/api/api-namespace.js.map +1 -1
- package/dist/api/boolean-op/index.d.ts +65 -0
- package/dist/api/boolean-op/index.d.ts.map +1 -0
- package/dist/api/boolean-op/index.js +113 -0
- package/dist/api/boolean-op/index.js.map +1 -0
- package/dist/api/boolean.d.ts.map +1 -1
- package/dist/api/boolean.js +15 -27
- package/dist/api/boolean.js.map +1 -1
- package/dist/api/brep-operations/healingFns.d.ts +13 -0
- package/dist/api/brep-operations/healingFns.d.ts.map +1 -1
- package/dist/api/brep-operations/healingFns.js +28 -1
- package/dist/api/brep-operations/healingFns.js.map +1 -1
- package/dist/api/brep-operations/topologyFns.d.ts +27 -6
- package/dist/api/brep-operations/topologyFns.d.ts.map +1 -1
- package/dist/api/brep-operations/topologyFns.js +46 -22
- package/dist/api/brep-operations/topologyFns.js.map +1 -1
- package/dist/api/chamfer.d.ts +5 -4
- package/dist/api/chamfer.d.ts.map +1 -1
- package/dist/api/chamfer.js +12 -11
- package/dist/api/chamfer.js.map +1 -1
- package/dist/api/curve-sketch/index.d.ts.map +1 -1
- package/dist/api/curve-sketch/index.js +9 -3
- package/dist/api/curve-sketch/index.js.map +1 -1
- package/dist/api/draft.d.ts.map +1 -1
- package/dist/api/draft.js +1 -3
- package/dist/api/draft.js.map +1 -1
- package/dist/api/engrave.d.ts.map +1 -1
- package/dist/api/engrave.js +3 -13
- package/dist/api/engrave.js.map +1 -1
- package/dist/api/export-3mf.d.ts +73 -0
- package/dist/api/export-3mf.d.ts.map +1 -0
- package/dist/api/export-3mf.js +121 -0
- package/dist/api/export-3mf.js.map +1 -0
- package/dist/api/export-brep.d.ts +6 -0
- package/dist/api/export-brep.d.ts.map +1 -1
- package/dist/api/export-brep.js +11 -2
- package/dist/api/export-brep.js.map +1 -1
- package/dist/api/export-step.d.ts +67 -0
- package/dist/api/export-step.d.ts.map +1 -0
- package/dist/api/export-step.js +98 -0
- package/dist/api/export-step.js.map +1 -0
- package/dist/api/export-stl.d.ts +8 -0
- package/dist/api/export-stl.d.ts.map +1 -1
- package/dist/api/export-stl.js +23 -8
- package/dist/api/export-stl.js.map +1 -1
- package/dist/api/feature-repair.d.ts +1 -1
- package/dist/api/feature-repair.d.ts.map +1 -1
- package/dist/api/feature-repair.js +1 -7
- package/dist/api/feature-repair.js.map +1 -1
- package/dist/api/fillet-variable.d.ts.map +1 -1
- package/dist/api/fillet-variable.js +0 -1
- package/dist/api/fillet-variable.js.map +1 -1
- package/dist/api/fillet.d.ts +3 -3
- package/dist/api/fillet.d.ts.map +1 -1
- package/dist/api/fillet.js +3 -4
- package/dist/api/fillet.js.map +1 -1
- package/dist/api/generated/operations.js +6 -6
- package/dist/api/generated/operations.js.map +1 -1
- package/dist/api/generated/script-face-manifest.d.ts +1 -1
- package/dist/api/generated/script-face-manifest.js +3 -3
- package/dist/api/generated/script-face-manifest.js.map +1 -1
- package/dist/api/generated/script-face.d.ts +1 -1
- package/dist/api/generated/script-face.js +1 -1
- package/dist/api/generated/script-face.js.map +1 -1
- package/dist/api/generated/sketching.js +1 -1
- package/dist/api/generated/sketching.js.map +1 -1
- package/dist/api/generated/topology.js +10 -10
- package/dist/api/generated/topology.js.map +1 -1
- package/dist/api/helix.d.ts +1 -1
- package/dist/api/helix.js +1 -1
- package/dist/api/helix.js.map +1 -1
- package/dist/api/index.d.ts +4 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +16 -4
- package/dist/api/index.js.map +1 -1
- package/dist/api/internal/compat-op.d.ts +6 -6
- package/dist/api/internal/compat-op.d.ts.map +1 -1
- package/dist/api/internal/compat-op.js +7 -9
- package/dist/api/internal/compat-op.js.map +1 -1
- package/dist/api/internal/export-members.d.ts +80 -0
- package/dist/api/internal/export-members.d.ts.map +1 -0
- package/dist/api/internal/export-members.js +104 -0
- package/dist/api/internal/export-members.js.map +1 -0
- package/dist/api/internal/l3-bridge.d.ts +17 -0
- package/dist/api/internal/l3-bridge.d.ts.map +1 -1
- package/dist/api/internal/l3-bridge.js +23 -1
- package/dist/api/internal/l3-bridge.js.map +1 -1
- package/dist/api/measure-query/index.d.ts +262 -0
- package/dist/api/measure-query/index.d.ts.map +1 -0
- package/dist/api/measure-query/index.js +390 -0
- package/dist/api/measure-query/index.js.map +1 -0
- package/dist/api/pattern.d.ts.map +1 -1
- package/dist/api/pattern.js +0 -3
- package/dist/api/pattern.js.map +1 -1
- package/dist/api/primitives.d.ts +6 -6
- package/dist/api/primitives.d.ts.map +1 -1
- package/dist/api/primitives.js +12 -11
- package/dist/api/primitives.js.map +1 -1
- package/dist/api/profile.d.ts.map +1 -1
- package/dist/api/profile.js +0 -1
- package/dist/api/profile.js.map +1 -1
- package/dist/api/punch-hole.d.ts.map +1 -1
- package/dist/api/punch-hole.js +0 -1
- package/dist/api/punch-hole.js.map +1 -1
- package/dist/api/replicate.d.ts +5 -7
- package/dist/api/replicate.d.ts.map +1 -1
- package/dist/api/replicate.js +18 -56
- package/dist/api/replicate.js.map +1 -1
- package/dist/api/section-by-plane.d.ts.map +1 -1
- package/dist/api/section-by-plane.js +0 -1
- package/dist/api/section-by-plane.js.map +1 -1
- package/dist/api/shell.d.ts +1 -1
- package/dist/api/shell.d.ts.map +1 -1
- package/dist/api/shell.js +11 -2
- package/dist/api/shell.js.map +1 -1
- package/dist/api/sketch-on-face.d.ts +3 -3
- package/dist/api/sketch-on-face.d.ts.map +1 -1
- package/dist/api/sketch-on-face.js +6 -4
- package/dist/api/sketch-on-face.js.map +1 -1
- package/dist/api/sketch-on-plane.d.ts.map +1 -1
- package/dist/api/sketch-on-plane.js +0 -1
- package/dist/api/sketch-on-plane.js.map +1 -1
- package/dist/api/split-by-plane.d.ts.map +1 -1
- package/dist/api/split-by-plane.js +0 -1
- package/dist/api/split-by-plane.js.map +1 -1
- package/dist/api/split.d.ts +1 -1
- package/dist/api/split.js +1 -1
- package/dist/api/split.js.map +1 -1
- package/dist/api/surface/arg-spec.d.ts +3 -12
- package/dist/api/surface/arg-spec.d.ts.map +1 -1
- package/dist/api/surface/arg-spec.js +19 -26
- package/dist/api/surface/arg-spec.js.map +1 -1
- package/dist/api/sweep.d.ts +15 -3
- package/dist/api/sweep.d.ts.map +1 -1
- package/dist/api/sweep.js +5 -3
- package/dist/api/sweep.js.map +1 -1
- package/dist/api/thicken.d.ts.map +1 -1
- package/dist/api/thicken.js +11 -2
- package/dist/api/thicken.js.map +1 -1
- package/dist/api/transform.d.ts +3 -3
- package/dist/api/transform.d.ts.map +1 -1
- package/dist/api/transform.js +47 -17
- package/dist/api/transform.js.map +1 -1
- package/dist/boolean/index.d.ts +23 -0
- package/dist/boolean/index.d.ts.map +1 -0
- package/dist/boolean/index.js +27 -0
- package/dist/boolean/index.js.map +1 -0
- package/dist/brep/brep-chain.d.ts +1 -19
- package/dist/brep/brep-chain.d.ts.map +1 -1
- package/dist/brep/brep-chain.js +0 -1
- package/dist/brep/brep-chain.js.map +1 -1
- package/dist/brep/brep-ops.d.ts +4 -1
- package/dist/brep/brep-ops.d.ts.map +1 -1
- package/dist/brep/brep-ops.js +9 -5
- package/dist/brep/brep-ops.js.map +1 -1
- package/dist/brep/engine/adapters/brep-mock.d.ts.map +1 -1
- package/dist/brep/engine/adapters/brep-mock.js +0 -3
- package/dist/brep/engine/adapters/brep-mock.js.map +1 -1
- package/dist/brep/engine/adapters/brepkit.d.ts +4 -3
- package/dist/brep/engine/adapters/brepkit.d.ts.map +1 -1
- package/dist/brep/engine/adapters/brepkit.js +4 -98
- package/dist/brep/engine/adapters/brepkit.js.map +1 -1
- package/dist/brep/engine/adapters/occt.d.ts.map +1 -1
- package/dist/brep/engine/adapters/occt.js +0 -144
- package/dist/brep/engine/adapters/occt.js.map +1 -1
- package/dist/brep/engine/native-history.d.ts +27 -0
- package/dist/brep/engine/native-history.d.ts.map +1 -0
- package/dist/brep/engine/native-history.js +54 -0
- package/dist/brep/engine/native-history.js.map +1 -0
- package/dist/brep/engine/registry.d.ts +2 -4
- package/dist/brep/engine/registry.d.ts.map +1 -1
- package/dist/brep/engine/registry.js +1 -1
- package/dist/brep/engine/registry.js.map +1 -1
- package/dist/brep/engine/types.d.ts +35 -77
- package/dist/brep/engine/types.d.ts.map +1 -1
- package/dist/brep/engine/types.js.map +1 -1
- package/dist/brep/export/export-model.d.ts +21 -0
- package/dist/brep/export/export-model.d.ts.map +1 -1
- package/dist/brep/export/export-model.js +171 -73
- package/dist/brep/export/export-model.js.map +1 -1
- package/dist/brep/export/step.d.ts +17 -1
- package/dist/brep/export/step.d.ts.map +1 -1
- package/dist/brep/export/step.js +133 -0
- package/dist/brep/export/step.js.map +1 -1
- package/dist/brep/export/stl.d.ts +22 -1
- package/dist/brep/export/stl.d.ts.map +1 -1
- package/dist/brep/export/stl.js +58 -10
- package/dist/brep/export/stl.js.map +1 -1
- package/dist/brep/export/transform12.d.ts +47 -0
- package/dist/brep/export/transform12.d.ts.map +1 -0
- package/dist/brep/export/transform12.js +87 -0
- package/dist/brep/export/transform12.js.map +1 -0
- package/dist/brep/face-evolution.d.ts +53 -0
- package/dist/brep/face-evolution.d.ts.map +1 -1
- package/dist/brep/face-evolution.js +65 -0
- package/dist/brep/face-evolution.js.map +1 -1
- package/dist/browser-host/index.d.ts +4 -1
- package/dist/browser-host/index.d.ts.map +1 -1
- package/dist/browser-host/index.js +7 -1
- package/dist/browser-host/index.js.map +1 -1
- package/dist/browser.d.ts +4 -8
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +4 -26
- package/dist/browser.js.map +1 -1
- package/dist/cad-runtime/backend-dispatch.d.ts +13 -58
- package/dist/cad-runtime/backend-dispatch.d.ts.map +1 -1
- package/dist/cad-runtime/backend-dispatch.js +22 -80
- package/dist/cad-runtime/backend-dispatch.js.map +1 -1
- package/dist/cad-runtime/content-key.d.ts +3 -3
- package/dist/cad-runtime/content-key.js +3 -3
- package/dist/cad-runtime/content-key.js.map +1 -1
- package/dist/cad-runtime/ports.d.ts +28 -0
- package/dist/cad-runtime/ports.d.ts.map +1 -1
- package/dist/cad-runtime/ports.js.map +1 -1
- package/dist/cad-runtime/runtime.d.ts +33 -12
- package/dist/cad-runtime/runtime.d.ts.map +1 -1
- package/dist/cad-runtime/runtime.js +44 -48
- package/dist/cad-runtime/runtime.js.map +1 -1
- package/dist/define-op.d.ts +6 -11
- package/dist/define-op.d.ts.map +1 -1
- package/dist/define-op.js +3 -20
- package/dist/define-op.js.map +1 -1
- package/dist/env-agnostic.d.ts +2 -2
- package/dist/env-agnostic.d.ts.map +1 -1
- package/dist/env-agnostic.js +1 -1
- package/dist/env-agnostic.js.map +1 -1
- package/dist/index.d.ts +13 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -75
- package/dist/index.js.map +1 -1
- package/dist/lang/symbol-table.generated.d.ts +17 -0
- package/dist/lang/symbol-table.generated.d.ts.map +1 -1
- package/dist/lang/symbol-table.generated.js +17 -0
- package/dist/lang/symbol-table.generated.js.map +1 -1
- package/dist/mesh/index.d.ts +18 -23
- package/dist/mesh/index.d.ts.map +1 -1
- package/dist/mesh/index.js +18 -20
- package/dist/mesh/index.js.map +1 -1
- package/dist/mesh/io.d.ts +42 -14
- package/dist/mesh/io.d.ts.map +1 -1
- package/dist/mesh/io.js +77 -13
- package/dist/mesh/io.js.map +1 -1
- package/dist/mesh/threemf-loader.d.ts +38 -4
- package/dist/mesh/threemf-loader.d.ts.map +1 -1
- package/dist/mesh/threemf-loader.js +115 -34
- package/dist/mesh/threemf-loader.js.map +1 -1
- package/dist/mesh/types.d.ts +30 -0
- package/dist/mesh/types.d.ts.map +1 -1
- package/dist/mesh/types.js.map +1 -1
- package/dist/node-host/execute-script.d.ts +2 -1
- package/dist/node-host/execute-script.d.ts.map +1 -1
- package/dist/node-host/execute-script.js +5 -4
- package/dist/node-host/execute-script.js.map +1 -1
- package/dist/node-host/index.d.ts +1 -0
- package/dist/node-host/index.d.ts.map +1 -1
- package/dist/node-host/index.js +4 -0
- package/dist/node-host/index.js.map +1 -1
- package/dist/primitives/index.d.ts +19 -0
- package/dist/primitives/index.d.ts.map +1 -0
- package/dist/primitives/index.js +21 -0
- package/dist/primitives/index.js.map +1 -0
- package/dist/runtime-state.d.ts +63 -29
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +57 -0
- package/dist/runtime-state.js.map +1 -1
- package/dist/sdf/index.d.ts +12 -0
- package/dist/sdf/index.d.ts.map +1 -0
- package/dist/sdf/index.js +13 -0
- package/dist/sdf/index.js.map +1 -0
- package/dist/sdk.d.ts +0 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js.map +1 -1
- package/dist/weapp.d.ts +2 -2
- package/dist/weapp.d.ts.map +1 -1
- package/dist/weapp.js +1 -1
- package/dist/weapp.js.map +1 -1
- package/package.json +276 -35
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
import type { BrepHandle } from '../engine/types.js';
|
|
16
16
|
import type { BrepEngineApi } from '../engine/primitives.js';
|
|
17
17
|
import type { FileMeta } from '../../api/meta.js';
|
|
18
|
-
|
|
18
|
+
import { type NodeTransform } from './transform12.js';
|
|
19
|
+
/** STEP 导出条目:一个 part(精确 BREP 形状或三角网格,二选一)或一个装配容器。 */
|
|
19
20
|
export interface StepExportEntry {
|
|
20
21
|
/** 精确 BREP 形状(solid/shell/face/compound,来自宿主导出缓存)。与 mesh 二选一,solid 优先。 */
|
|
21
22
|
solid?: BrepHandle;
|
|
@@ -28,6 +29,21 @@ export interface StepExportEntry {
|
|
|
28
29
|
name?: string;
|
|
29
30
|
/** RGB 0..1 颜色(sRGB 编码,如宿主材质 base color)。写入前转 linear。 */
|
|
30
31
|
color?: [number, number, number];
|
|
32
|
+
/**
|
|
33
|
+
* 装配子节点(方案 2026-10-08 步骤 7 / P3):非空时本条目按**真装配容器**写出
|
|
34
|
+
* (XCAF assembly + component),不再展平为平级 PRODUCT。容器自身不贡献几何
|
|
35
|
+
* (compound 几何 == 成员并集,写出会重复)。
|
|
36
|
+
*
|
|
37
|
+
* 写出语义:直接子叶的完整位姿(平移 + 旋转)走 XCAF component location;
|
|
38
|
+
* 首叶位姿烘进晋升几何;嵌套容器的叶递归收集后作为顶层容器的直接组件
|
|
39
|
+
* (子装配分组待 wasm 提供空装配 label 原语后升级)。
|
|
40
|
+
*/
|
|
41
|
+
children?: StepExportEntry[];
|
|
42
|
+
/**
|
|
43
|
+
* 本节点相对父的位姿(与 `Shape.transform` 同形态,坐标已按单位刻度换算)。
|
|
44
|
+
* 直接子节点写 XCAF component location;更深层的叶子烘进几何。
|
|
45
|
+
*/
|
|
46
|
+
transform?: NodeTransform;
|
|
31
47
|
}
|
|
32
48
|
/**
|
|
33
49
|
* Export multiple BREP solids (and/or triangle meshes) to STEP, one
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step.d.ts","sourceRoot":"","sources":["../../../src/brep/export/step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"step.d.ts","sourceRoot":"","sources":["../../../src/brep/export/step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAIzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAGvE,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,0DAA0D;IAC1D,IAAI,CAAC,EAAE;QAAE,SAAS,EAAE,YAAY,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAA;IACxD,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAA;IAC5B;;;OAGG;IACH,SAAS,CAAC,EAAE,aAAa,CAAA;CAC1B;AAuJD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,eAAe,EAAE,EAC1B,QAAQ,CAAC,EAAE,QAAQ,GAClB,WAAW,CAwFb;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,aAAa,GACpB,WAAW,CAEb"}
|
package/dist/brep/export/step.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
import { reconstructSolidFromMesh } from '../../occt-kernel/meshReconstruct.js';
|
|
16
16
|
import { getOcctKernel } from '../../occt-kernel/occtKernel.js';
|
|
17
17
|
import { rewriteStepHeader, fileMetaHasHeaderFields } from './step-meta-header.js';
|
|
18
|
+
import { transformToMatrix12 } from './transform12.js';
|
|
19
|
+
import { composeMatrix12 } from './stl.js';
|
|
18
20
|
/**
|
|
19
21
|
* sRGB → linear 转换。
|
|
20
22
|
*
|
|
@@ -28,6 +30,132 @@ import { rewriteStepHeader, fileMetaHasHeaderFields } from './step-meta-header.j
|
|
|
28
30
|
function srgbToLinear(c) {
|
|
29
31
|
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
30
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* 3×3 行主序旋转矩阵 → Euler 角(弧度)。
|
|
35
|
+
*
|
|
36
|
+
* 约定 **R = Rz(rz)·Ry(ry)·Rx(rx)**(X 先转,Z 最后)——这是 occt-wasm
|
|
37
|
+
* `xcafAddComponent` 的合成约定(`trsf = rotZ * rotY * rotX`),由
|
|
38
|
+
* `xcaf-step-assembly-fidelity.test.ts`(occt-wasm 仓)的 Rz/Rx 往返测试与
|
|
39
|
+
* 本仓 step-export 装配测试共同钉住:若 wasm 侧改约定,两边测试先红。
|
|
40
|
+
*/
|
|
41
|
+
function rotationToEuler(m) {
|
|
42
|
+
const ry = -Math.asin(Math.max(-1, Math.min(1, m[8])));
|
|
43
|
+
if (Math.abs(m[8]) < 0.999999) {
|
|
44
|
+
return { rx: Math.atan2(m[9], m[10]), ry, rz: Math.atan2(m[4], m[0]) };
|
|
45
|
+
}
|
|
46
|
+
// 万向锁:m[8] = ±1 时 rx/rz 耦合,取 rz = 0 的退化解。
|
|
47
|
+
return { rx: Math.atan2(-m[6], m[5]), ry, rz: 0 };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 节点位姿 → wasm `addChild({location})` 的分量形态:平移 + Euler 角(弧度)。
|
|
51
|
+
*
|
|
52
|
+
* 完整位姿走 location(旋转分量经 occt-wasm `xcaf-step-assembly-fidelity`
|
|
53
|
+
* 往返测试证实保真,不再烘进几何)。坐标刻度由调用方保证(export-model
|
|
54
|
+
* 映射时已按单位换算平移分量)。
|
|
55
|
+
*/
|
|
56
|
+
function transformToLocation(t) {
|
|
57
|
+
if (!t)
|
|
58
|
+
return undefined;
|
|
59
|
+
const m = transformToMatrix12(t);
|
|
60
|
+
const { rx, ry, rz } = rotationToEuler(m);
|
|
61
|
+
return { tx: m[3], ty: m[7], tz: m[11], rx, ry, rz };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 装配容器的叶子收集:嵌套容器递归展开,位姿沿树复合(行主序 3×4)。
|
|
65
|
+
* `nested` = 该叶经过了嵌套容器(非顶层容器的直接子节点)。
|
|
66
|
+
*/
|
|
67
|
+
function collectAssemblyLeaves(list, parentM, out) {
|
|
68
|
+
for (const c of list) {
|
|
69
|
+
const m = c.transform ? transformToMatrix12(c.transform) : null;
|
|
70
|
+
const world = m && parentM ? composeMatrix12(parentM, m) : m ?? parentM;
|
|
71
|
+
if (c.children && c.children.length > 0) {
|
|
72
|
+
collectAssemblyLeaves(c.children, world, out);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
out.push({ entry: c, world, nested: parentM !== null });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function isIdentity12(m) {
|
|
80
|
+
return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 0
|
|
81
|
+
&& m[4] === 0 && m[5] === 1 && m[6] === 0 && m[7] === 0
|
|
82
|
+
&& m[8] === 0 && m[9] === 0 && m[10] === 1 && m[11] === 0;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 写一个装配容器为真装配(XCAF assembly + component,方案步骤 7 / P3)。
|
|
86
|
+
*
|
|
87
|
+
* XCAF 原语约束:`addChild` 只收 shape 句柄、且「part 在首个 child 加入时转为
|
|
88
|
+
* assembly,原几何移入 identity 首组件」。因此:
|
|
89
|
+
* - 首叶几何升起为容器 label 的初始 part,其位姿烘进几何(晋升强制 identity
|
|
90
|
+
* location,首叶位姿只能烘焙);
|
|
91
|
+
* - 其余**直接**子叶经 `addChild({location})` 写**完整位姿**(平移 + 旋转);
|
|
92
|
+
* - 嵌套容器的叶(`nested`)位姿复合后烘进几何、以 identity 位置加入——几何与
|
|
93
|
+
* 名色保真,子装配分组待 wasm 提供空装配 label 原语后升级。
|
|
94
|
+
*
|
|
95
|
+
* 名字保真:容器名优先升起为容器/原型名;容器名与首叶名不同时,在晋升完成后
|
|
96
|
+
* 把首叶名补写回首组件及其原型(setName 必须在 addChild 之后调用——晋升发生
|
|
97
|
+
* 在首个 addChild,此前 getChildren 为空)。组件名与原型名均过 STEP 往返
|
|
98
|
+
* (occt-wasm `xcaf-step-assembly-fidelity.test.ts` 钉住)。
|
|
99
|
+
*/
|
|
100
|
+
function writeAssemblyContainer(kernel, doc, entry, ownedHandles) {
|
|
101
|
+
const resolveSolid = (e) => {
|
|
102
|
+
if (e.solid)
|
|
103
|
+
return e.solid;
|
|
104
|
+
if (!e.mesh)
|
|
105
|
+
throw new Error('StepExportEntry must provide either solid or mesh');
|
|
106
|
+
const s = reconstructSolidFromMesh(kernel, e.mesh.positions, e.mesh.indices);
|
|
107
|
+
ownedHandles.push(s);
|
|
108
|
+
return s;
|
|
109
|
+
};
|
|
110
|
+
const bake = (e, m) => {
|
|
111
|
+
const base = resolveSolid(e);
|
|
112
|
+
if (!m || isIdentity12(m))
|
|
113
|
+
return base;
|
|
114
|
+
const placed = kernel.transform(base, m);
|
|
115
|
+
ownedHandles.push(placed);
|
|
116
|
+
return placed;
|
|
117
|
+
};
|
|
118
|
+
const linear = (c) => c ? [srgbToLinear(c[0]), srgbToLinear(c[1]), srgbToLinear(c[2])] : undefined;
|
|
119
|
+
const leaves = [];
|
|
120
|
+
collectAssemblyLeaves(entry.children ?? [], null, leaves);
|
|
121
|
+
if (leaves.length === 0)
|
|
122
|
+
throw new Error('Assembly container has no leaf geometry');
|
|
123
|
+
const [first, ...rest] = leaves;
|
|
124
|
+
const label = doc.addShape(bake(first.entry, first.world), {
|
|
125
|
+
name: entry.name ?? first.entry.name,
|
|
126
|
+
color: linear(entry.color ?? first.entry.color),
|
|
127
|
+
});
|
|
128
|
+
// 名字保真:容器名优先升起为容器/原型名;容器名与首叶名不同时,晋升完成后
|
|
129
|
+
// 把首叶名补写回首组件及其原型(必须在 addChild 之后——晋升发生在首个
|
|
130
|
+
// addChild,此前 getChildren 为空,过早调用是空操作)。
|
|
131
|
+
for (const leaf of rest) {
|
|
132
|
+
let solid;
|
|
133
|
+
let location;
|
|
134
|
+
if (leaf.nested) {
|
|
135
|
+
// 嵌套容器的叶:位姿全量复合后烘进几何,identity 位置加入。
|
|
136
|
+
solid = bake(leaf.entry, leaf.world);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// 直接子叶:完整位姿走 location(旋转经 Euler 分解,wasm 按 Rz·Ry·Rx 合成)。
|
|
140
|
+
solid = resolveSolid(leaf.entry);
|
|
141
|
+
location = transformToLocation(leaf.entry.transform);
|
|
142
|
+
}
|
|
143
|
+
doc.addChild(label, solid, {
|
|
144
|
+
...(leaf.entry.name === undefined ? {} : { name: leaf.entry.name }),
|
|
145
|
+
...(leaf.entry.color === undefined ? {} : { color: linear(leaf.entry.color) }),
|
|
146
|
+
...(location === undefined ? {} : { location }),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (entry.name && first.entry.name && entry.name !== first.entry.name) {
|
|
150
|
+
const own = doc.getChildren(label)[0];
|
|
151
|
+
if (own) {
|
|
152
|
+
doc.setName(own, first.entry.name);
|
|
153
|
+
const proto = doc.getReferredLabel(own);
|
|
154
|
+
if (proto)
|
|
155
|
+
doc.setName(proto, first.entry.name);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
31
159
|
/**
|
|
32
160
|
* Export multiple BREP solids (and/or triangle meshes) to STEP, one
|
|
33
161
|
* independent entity per entry — no fuse, no string splicing.
|
|
@@ -50,6 +178,11 @@ export function exportStepFromSolids(kernel, entries, fileMeta) {
|
|
|
50
178
|
const ownedHandles = [];
|
|
51
179
|
try {
|
|
52
180
|
for (const entry of entries) {
|
|
181
|
+
// 0. 装配容器 → 真装配写入(XCAF assembly + component,步骤 7 / P3)。
|
|
182
|
+
if (entry.children && entry.children.length > 0) {
|
|
183
|
+
writeAssemblyContainer(kernel, doc, entry, ownedHandles);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
53
186
|
// 1. 解析 solid:优先用精确 solid;无则从三角网格重建
|
|
54
187
|
let solid = entry.solid;
|
|
55
188
|
if (!solid) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step.js","sourceRoot":"","sources":["../../../src/brep/export/step.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * exportStepFromSolids — L1 STEP 导出(从 OCCT solid 句柄,多实体、零 fuse)\n *\n * @platform occt — 本文件 import occt-kernel(createXCAFDocument/XCAF label 写入是\n * occt-only 平台面,D3)。文件级标注满足守卫①(平台 import 自证身份)。\n *\n * 每个 entry 作为 STEP 中一个独立实体(独立 XCAF label / PRODUCT)导出,\n * 保留名称与颜色。多实体之间绝不 fuse —— 这是硬性约定:\n * fuse 只允许作为用户脚本里的几何布尔运算,禁止作为导出时的实体合并手段。\n *\n * 导出 → 再导入身份闭环:导入侧 importAssemblyFromStep(XCAF)会把\n * 多实体 STEP 还原为多个 part;多 solid compound 拆分出的子节点命名为\n * `name [n]`,与本节点的展平命名规则一致。\n */\n\nimport type { BrepHandle } from '../engine/types'\nimport type { BrepEngineApi } from '../engine/primitives'\nimport { reconstructSolidFromMesh } from '../../occt-kernel/meshReconstruct'\nimport { getOcctKernel, type ShapeHandle } from '../../occt-kernel/occtKernel'\nimport type { FileMeta } from '../../api/meta'\nimport { rewriteStepHeader, fileMetaHasHeaderFields } from './step-meta-header'\n\n/** STEP 导出条目:一个 part(精确 BREP 形状或三角网格,二选一)。 */\nexport interface StepExportEntry {\n /** 精确 BREP 形状(solid/shell/face/compound,来自宿主导出缓存)。与 mesh 二选一,solid 优先。 */\n solid?: BrepHandle\n /** 三角网格(世界坐标、已按单位缩放),经 reconstructSolidFromMesh 重建为实体。 */\n mesh?: { positions: Float32Array; indices: Uint32Array }\n /** 实体名称(写入 label name,导出为 PRODUCT 名称)。 */\n name?: string\n /** RGB 0..1 颜色(sRGB 编码,如宿主材质 base color)。写入前转 linear。 */\n color?: [number, number, number]\n}\n\n/**\n * sRGB → linear 转换。\n *\n * OCCT XCAF 内部按 linear RGB 存储颜色,STEP writer(xcafExportSTEP)\n * 导出时做 linear→sRGB 编码写 COLOUR_RGB。为了让\"宿主传入的 sRGB 颜色 ==\n * STEP 文件中的 COLOUR_RGB == 回读颜色\",写入前必须先转 linear。\n *\n * @param c sRGB 编码值(0..1)\n * @returns linear 值(0..1)\n */\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n}\n\n/**\n * Export multiple BREP solids (and/or triangle meshes) to STEP, one\n * independent entity per entry — no fuse, no string splicing.\n *\n * @param kernel OCCT kernel (must match the one that created the solids)\n * @param entries parts to export; each becomes its own XCAF label\n * @param fileMeta optional file-level metadata; mapped to the P21 header\n * (`FILE_NAME`/`FILE_DESCRIPTION`) via text rewrite\n * (`rewriteStepHeader`). No effect on geometry/DATA entities.\n * @returns STEP file content as ArrayBuffer\n * @throws if entries is empty, or a mesh entry fails to reconstruct\n */\nexport function exportStepFromSolids(\n kernel: BrepEngineApi,\n entries: StepExportEntry[],\n fileMeta?: FileMeta,\n): ArrayBuffer {\n if (entries.length === 0) {\n throw new Error('No exportable geometry')\n }\n\n const doc = getOcctKernel().createXCAFDocument()\n // 本函数创建的句柄(展平出的子 solid + mesh 重建的 solid),导出后释放。\n // 缓存里的原 solid(entry.solid)绝不释放,归调用方/缓存所有。\n const ownedHandles: BrepHandle[] = []\n\n try {\n for (const entry of entries) {\n // 1. 解析 solid:优先用精确 solid;无则从三角网格重建\n let solid = entry.solid\n if (!solid) {\n if (!entry.mesh) {\n throw new Error('StepExportEntry must provide either solid or mesh')\n }\n solid = reconstructSolidFromMesh(kernel, entry.mesh.positions, entry.mesh.indices)\n ownedHandles.push(solid)\n }\n\n // 2. 展平 Compound(多 solid 导入的 part 其 solid 是 Compound);\n // 纯 solid 时 getSubShapes('solid') 返回 [自身]。\n // 形状类型分派:solid → shell → face → edge。ref 侧(cadquery\n // Shape.exportStep)可导出任意类型的形状,面/壳/边 compound(如\n // Shape.faces('>Z') / shape.edges('>Z') 的结果)同样要能写进\n // STEP(U22,2026-09-09)。\n let subs = kernel.getSubShapes(solid, 'solid')\n if (subs.length === 0) {\n subs = kernel.getSubShapes(solid, 'shell')\n }\n if (subs.length === 0) {\n subs = kernel.getSubShapes(solid, 'face')\n }\n if (subs.length === 0) {\n subs = kernel.getSubShapes(solid, 'edge')\n }\n if (subs.length === 0) {\n // wasm getSubShapes 不展开 compound 层(TopExp_Explorer 默认跳过),\n // 纯面/壳/边 compound(如 Shape.siblings / faces('>Z') 结果)在这里\n // 拿不到子形状。把 compound 整体作为单个 XCAF label 写入(STEP\n // writer 支持 compound 形状,与 ref 侧 cadquery Shape.exportStep\n // 对 faces/edges compound 的行为一致)。\n subs = [solid]\n }\n for (let si = 0; si < subs.length; si++) {\n const sub = subs[si]\n // 展平出的子句柄若不属于调用方缓存,记入 ownedHandles\n if (solid !== sub) ownedHandles.push(sub)\n const name = subs.length > 1\n ? `${entry.name ?? 'part'} [${si + 1}]`\n : entry.name\n // sRGB → linear:XCAF 按 linear 存储,STEP writer 输出时线性→sRGB,\n // 转换后 STEP 文件里的 COLOUR_RGB 才是宿主传入的原始 sRGB 值。\n const color: [number, number, number] | undefined = entry.color\n ? [srgbToLinear(entry.color[0]), srgbToLinear(entry.color[1]), srgbToLinear(entry.color[2])]\n : undefined\n // 平台面(D3):XCAF label 写入是 occt-only。BrepHandle ↔ ShapeHandle\n // 运行时同构,品牌转换只发生在平台边界。\n doc.addShape(sub as unknown as ShapeHandle, { name, color })\n }\n }\n\n // 3. 导出(每个 label 一个独立 PRODUCT,保留名称/颜色)\n const stepText = doc.exportSTEP()\n // 文件级 meta → P21 header 重写(title/creationDate/createor/author/org/\n // application/description)。无 header 字段时不重写。文本层只改声明不碰几何。\n const out = fileMeta && fileMetaHasHeaderFields(fileMeta)\n ? rewriteStepHeader(stepText, fileMeta)\n : stepText\n return new TextEncoder().encode(out).buffer\n } finally {\n // 4. 释放顺序:先关文档,再释放本函数创建的句柄\n doc.close()\n for (const h of ownedHandles) {\n try {\n kernel.release(h)\n } catch {\n // already released\n }\n }\n }\n}\n\n/**\n * Export a BREP solid (OCCT native) to STEP format.\n *\n * 单实体导出 = exportStepFromSolids 的单条目特例(同一实现,无第二真源)。\n *\n * @param solid OCCT solid handle\n * @param kernel OCCT kernel (must match the one that created the solid)\n * @returns STEP file content as ArrayBuffer\n */\nexport function exportStepFromSolid(\n solid: BrepHandle,\n kernel: BrepEngineApi,\n): ArrayBuffer {\n return exportStepFromSolids(kernel, [{ solid }])\n}"]}
|
|
1
|
+
{"version":3,"file":"step.js","sourceRoot":"","sources":["../../../src/brep/export/step.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * exportStepFromSolids — L1 STEP 导出(从 OCCT solid 句柄,多实体、零 fuse)\n *\n * @platform occt — 本文件 import occt-kernel(createXCAFDocument/XCAF label 写入是\n * occt-only 平台面,D3)。文件级标注满足守卫①(平台 import 自证身份)。\n *\n * 每个 entry 作为 STEP 中一个独立实体(独立 XCAF label / PRODUCT)导出,\n * 保留名称与颜色。多实体之间绝不 fuse —— 这是硬性约定:\n * fuse 只允许作为用户脚本里的几何布尔运算,禁止作为导出时的实体合并手段。\n *\n * 导出 → 再导入身份闭环:导入侧 importAssemblyFromStep(XCAF)会把\n * 多实体 STEP 还原为多个 part;多 solid compound 拆分出的子节点命名为\n * `name [n]`,与本节点的展平命名规则一致。\n */\n\nimport type { BrepHandle } from '../engine/types'\nimport type { BrepEngineApi } from '../engine/primitives'\nimport type { XCAFDocument } from 'occt-wasm'\nimport { reconstructSolidFromMesh } from '../../occt-kernel/meshReconstruct'\nimport { getOcctKernel, type ShapeHandle } from '../../occt-kernel/occtKernel'\nimport type { FileMeta } from '../../api/meta'\nimport { rewriteStepHeader, fileMetaHasHeaderFields } from './step-meta-header'\nimport { transformToMatrix12, type NodeTransform } from './transform12'\nimport { composeMatrix12 } from './stl'\n\n/** STEP 导出条目:一个 part(精确 BREP 形状或三角网格,二选一)或一个装配容器。 */\nexport interface StepExportEntry {\n /** 精确 BREP 形状(solid/shell/face/compound,来自宿主导出缓存)。与 mesh 二选一,solid 优先。 */\n solid?: BrepHandle\n /** 三角网格(世界坐标、已按单位缩放),经 reconstructSolidFromMesh 重建为实体。 */\n mesh?: { positions: Float32Array; indices: Uint32Array }\n /** 实体名称(写入 label name,导出为 PRODUCT 名称)。 */\n name?: string\n /** RGB 0..1 颜色(sRGB 编码,如宿主材质 base color)。写入前转 linear。 */\n color?: [number, number, number]\n /**\n * 装配子节点(方案 2026-10-08 步骤 7 / P3):非空时本条目按**真装配容器**写出\n * (XCAF assembly + component),不再展平为平级 PRODUCT。容器自身不贡献几何\n * (compound 几何 == 成员并集,写出会重复)。\n *\n * 写出语义:直接子叶的完整位姿(平移 + 旋转)走 XCAF component location;\n * 首叶位姿烘进晋升几何;嵌套容器的叶递归收集后作为顶层容器的直接组件\n * (子装配分组待 wasm 提供空装配 label 原语后升级)。\n */\n children?: StepExportEntry[]\n /**\n * 本节点相对父的位姿(与 `Shape.transform` 同形态,坐标已按单位刻度换算)。\n * 直接子节点写 XCAF component location;更深层的叶子烘进几何。\n */\n transform?: NodeTransform\n}\n\n/**\n * sRGB → linear 转换。\n *\n * OCCT XCAF 内部按 linear RGB 存储颜色,STEP writer(xcafExportSTEP)\n * 导出时做 linear→sRGB 编码写 COLOUR_RGB。为了让\"宿主传入的 sRGB 颜色 ==\n * STEP 文件中的 COLOUR_RGB == 回读颜色\",写入前必须先转 linear。\n *\n * @param c sRGB 编码值(0..1)\n * @returns linear 值(0..1)\n */\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n}\n\n/**\n * 3×3 行主序旋转矩阵 → Euler 角(弧度)。\n *\n * 约定 **R = Rz(rz)·Ry(ry)·Rx(rx)**(X 先转,Z 最后)——这是 occt-wasm\n * `xcafAddComponent` 的合成约定(`trsf = rotZ * rotY * rotX`),由\n * `xcaf-step-assembly-fidelity.test.ts`(occt-wasm 仓)的 Rz/Rx 往返测试与\n * 本仓 step-export 装配测试共同钉住:若 wasm 侧改约定,两边测试先红。\n */\nfunction rotationToEuler(m: number[]): { rx: number; ry: number; rz: number } {\n const ry = -Math.asin(Math.max(-1, Math.min(1, m[8]!)))\n if (Math.abs(m[8]!) < 0.999999) {\n return { rx: Math.atan2(m[9]!, m[10]!), ry, rz: Math.atan2(m[4]!, m[0]!) }\n }\n // 万向锁:m[8] = ±1 时 rx/rz 耦合,取 rz = 0 的退化解。\n return { rx: Math.atan2(-m[6]!, m[5]!), ry, rz: 0 }\n}\n\n/**\n * 节点位姿 → wasm `addChild({location})` 的分量形态:平移 + Euler 角(弧度)。\n *\n * 完整位姿走 location(旋转分量经 occt-wasm `xcaf-step-assembly-fidelity`\n * 往返测试证实保真,不再烘进几何)。坐标刻度由调用方保证(export-model\n * 映射时已按单位换算平移分量)。\n */\nfunction transformToLocation(t: NodeTransform | undefined): { tx: number; ty: number; tz: number; rx: number; ry: number; rz: number } | undefined {\n if (!t) return undefined\n const m = transformToMatrix12(t)\n const { rx, ry, rz } = rotationToEuler(m)\n return { tx: m[3]!, ty: m[7]!, tz: m[11]!, rx, ry, rz }\n}\n\n/**\n * 装配容器的叶子收集:嵌套容器递归展开,位姿沿树复合(行主序 3×4)。\n * `nested` = 该叶经过了嵌套容器(非顶层容器的直接子节点)。\n */\nfunction collectAssemblyLeaves(\n list: StepExportEntry[],\n parentM: number[] | null,\n out: Array<{ entry: StepExportEntry; world: number[] | null; nested: boolean }>,\n): void {\n for (const c of list) {\n const m = c.transform ? transformToMatrix12(c.transform) : null\n const world = m && parentM ? composeMatrix12(parentM, m) : m ?? parentM\n if (c.children && c.children.length > 0) {\n collectAssemblyLeaves(c.children, world, out)\n } else {\n out.push({ entry: c, world, nested: parentM !== null })\n }\n }\n}\n\nfunction isIdentity12(m: number[]): boolean {\n return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 0\n && m[4] === 0 && m[5] === 1 && m[6] === 0 && m[7] === 0\n && m[8] === 0 && m[9] === 0 && m[10] === 1 && m[11] === 0\n}\n\n/**\n * 写一个装配容器为真装配(XCAF assembly + component,方案步骤 7 / P3)。\n *\n * XCAF 原语约束:`addChild` 只收 shape 句柄、且「part 在首个 child 加入时转为\n * assembly,原几何移入 identity 首组件」。因此:\n * - 首叶几何升起为容器 label 的初始 part,其位姿烘进几何(晋升强制 identity\n * location,首叶位姿只能烘焙);\n * - 其余**直接**子叶经 `addChild({location})` 写**完整位姿**(平移 + 旋转);\n * - 嵌套容器的叶(`nested`)位姿复合后烘进几何、以 identity 位置加入——几何与\n * 名色保真,子装配分组待 wasm 提供空装配 label 原语后升级。\n *\n * 名字保真:容器名优先升起为容器/原型名;容器名与首叶名不同时,在晋升完成后\n * 把首叶名补写回首组件及其原型(setName 必须在 addChild 之后调用——晋升发生\n * 在首个 addChild,此前 getChildren 为空)。组件名与原型名均过 STEP 往返\n * (occt-wasm `xcaf-step-assembly-fidelity.test.ts` 钉住)。\n */\nfunction writeAssemblyContainer(\n kernel: BrepEngineApi,\n doc: XCAFDocument,\n entry: StepExportEntry,\n ownedHandles: BrepHandle[],\n): void {\n const resolveSolid = (e: StepExportEntry): BrepHandle => {\n if (e.solid) return e.solid\n if (!e.mesh) throw new Error('StepExportEntry must provide either solid or mesh')\n const s = reconstructSolidFromMesh(kernel, e.mesh.positions, e.mesh.indices)\n ownedHandles.push(s)\n return s\n }\n const bake = (e: StepExportEntry, m: number[] | null): BrepHandle => {\n const base = resolveSolid(e)\n if (!m || isIdentity12(m)) return base\n const placed = kernel.transform(base, m)\n ownedHandles.push(placed)\n return placed\n }\n const linear = (c: [number, number, number] | undefined): [number, number, number] | undefined =>\n c ? [srgbToLinear(c[0]), srgbToLinear(c[1]), srgbToLinear(c[2])] : undefined\n\n const leaves: Array<{ entry: StepExportEntry; world: number[] | null; nested: boolean }> = []\n collectAssemblyLeaves(entry.children ?? [], null, leaves)\n if (leaves.length === 0) throw new Error('Assembly container has no leaf geometry')\n\n const [first, ...rest] = leaves\n const label = doc.addShape(bake(first!.entry, first!.world) as unknown as ShapeHandle, {\n name: entry.name ?? first!.entry.name,\n color: linear(entry.color ?? first!.entry.color),\n })\n // 名字保真:容器名优先升起为容器/原型名;容器名与首叶名不同时,晋升完成后\n // 把首叶名补写回首组件及其原型(必须在 addChild 之后——晋升发生在首个\n // addChild,此前 getChildren 为空,过早调用是空操作)。\n for (const leaf of rest) {\n let solid: BrepHandle\n let location: { tx: number; ty: number; tz: number; rx: number; ry: number; rz: number } | undefined\n if (leaf.nested) {\n // 嵌套容器的叶:位姿全量复合后烘进几何,identity 位置加入。\n solid = bake(leaf.entry, leaf.world)\n } else {\n // 直接子叶:完整位姿走 location(旋转经 Euler 分解,wasm 按 Rz·Ry·Rx 合成)。\n solid = resolveSolid(leaf.entry)\n location = transformToLocation(leaf.entry.transform)\n }\n doc.addChild(label, solid as unknown as ShapeHandle, {\n ...(leaf.entry.name === undefined ? {} : { name: leaf.entry.name }),\n ...(leaf.entry.color === undefined ? {} : { color: linear(leaf.entry.color) }),\n ...(location === undefined ? {} : { location }),\n })\n }\n if (entry.name && first!.entry.name && entry.name !== first!.entry.name) {\n const own = doc.getChildren(label)[0]\n if (own) {\n doc.setName(own, first!.entry.name)\n const proto = doc.getReferredLabel(own)\n if (proto) doc.setName(proto, first!.entry.name)\n }\n }\n}\n\n/**\n * Export multiple BREP solids (and/or triangle meshes) to STEP, one\n * independent entity per entry — no fuse, no string splicing.\n *\n * @param kernel OCCT kernel (must match the one that created the solids)\n * @param entries parts to export; each becomes its own XCAF label\n * @param fileMeta optional file-level metadata; mapped to the P21 header\n * (`FILE_NAME`/`FILE_DESCRIPTION`) via text rewrite\n * (`rewriteStepHeader`). No effect on geometry/DATA entities.\n * @returns STEP file content as ArrayBuffer\n * @throws if entries is empty, or a mesh entry fails to reconstruct\n */\nexport function exportStepFromSolids(\n kernel: BrepEngineApi,\n entries: StepExportEntry[],\n fileMeta?: FileMeta,\n): ArrayBuffer {\n if (entries.length === 0) {\n throw new Error('No exportable geometry')\n }\n\n const doc = getOcctKernel().createXCAFDocument()\n // 本函数创建的句柄(展平出的子 solid + mesh 重建的 solid),导出后释放。\n // 缓存里的原 solid(entry.solid)绝不释放,归调用方/缓存所有。\n const ownedHandles: BrepHandle[] = []\n\n try {\n for (const entry of entries) {\n // 0. 装配容器 → 真装配写入(XCAF assembly + component,步骤 7 / P3)。\n if (entry.children && entry.children.length > 0) {\n writeAssemblyContainer(kernel, doc, entry, ownedHandles)\n continue\n }\n // 1. 解析 solid:优先用精确 solid;无则从三角网格重建\n let solid = entry.solid\n if (!solid) {\n if (!entry.mesh) {\n throw new Error('StepExportEntry must provide either solid or mesh')\n }\n solid = reconstructSolidFromMesh(kernel, entry.mesh.positions, entry.mesh.indices)\n ownedHandles.push(solid)\n }\n\n // 2. 展平 Compound(多 solid 导入的 part 其 solid 是 Compound);\n // 纯 solid 时 getSubShapes('solid') 返回 [自身]。\n // 形状类型分派:solid → shell → face → edge。ref 侧(cadquery\n // Shape.exportStep)可导出任意类型的形状,面/壳/边 compound(如\n // Shape.faces('>Z') / shape.edges('>Z') 的结果)同样要能写进\n // STEP(U22,2026-09-09)。\n let subs = kernel.getSubShapes(solid, 'solid')\n if (subs.length === 0) {\n subs = kernel.getSubShapes(solid, 'shell')\n }\n if (subs.length === 0) {\n subs = kernel.getSubShapes(solid, 'face')\n }\n if (subs.length === 0) {\n subs = kernel.getSubShapes(solid, 'edge')\n }\n if (subs.length === 0) {\n // wasm getSubShapes 不展开 compound 层(TopExp_Explorer 默认跳过),\n // 纯面/壳/边 compound(如 Shape.siblings / faces('>Z') 结果)在这里\n // 拿不到子形状。把 compound 整体作为单个 XCAF label 写入(STEP\n // writer 支持 compound 形状,与 ref 侧 cadquery Shape.exportStep\n // 对 faces/edges compound 的行为一致)。\n subs = [solid]\n }\n for (let si = 0; si < subs.length; si++) {\n const sub = subs[si]\n // 展平出的子句柄若不属于调用方缓存,记入 ownedHandles\n if (solid !== sub) ownedHandles.push(sub)\n const name = subs.length > 1\n ? `${entry.name ?? 'part'} [${si + 1}]`\n : entry.name\n // sRGB → linear:XCAF 按 linear 存储,STEP writer 输出时线性→sRGB,\n // 转换后 STEP 文件里的 COLOUR_RGB 才是宿主传入的原始 sRGB 值。\n const color: [number, number, number] | undefined = entry.color\n ? [srgbToLinear(entry.color[0]), srgbToLinear(entry.color[1]), srgbToLinear(entry.color[2])]\n : undefined\n // 平台面(D3):XCAF label 写入是 occt-only。BrepHandle ↔ ShapeHandle\n // 运行时同构,品牌转换只发生在平台边界。\n doc.addShape(sub as unknown as ShapeHandle, { name, color })\n }\n }\n\n // 3. 导出(每个 label 一个独立 PRODUCT,保留名称/颜色)\n const stepText = doc.exportSTEP()\n // 文件级 meta → P21 header 重写(title/creationDate/createor/author/org/\n // application/description)。无 header 字段时不重写。文本层只改声明不碰几何。\n const out = fileMeta && fileMetaHasHeaderFields(fileMeta)\n ? rewriteStepHeader(stepText, fileMeta)\n : stepText\n return new TextEncoder().encode(out).buffer\n } finally {\n // 4. 释放顺序:先关文档,再释放本函数创建的句柄\n doc.close()\n for (const h of ownedHandles) {\n try {\n kernel.release(h)\n } catch {\n // already released\n }\n }\n }\n}\n\n/**\n * Export a BREP solid (OCCT native) to STEP format.\n *\n * 单实体导出 = exportStepFromSolids 的单条目特例(同一实现,无第二真源)。\n *\n * @param solid OCCT solid handle\n * @param kernel OCCT kernel (must match the one that created the solid)\n * @returns STEP file content as ArrayBuffer\n */\nexport function exportStepFromSolid(\n solid: BrepHandle,\n kernel: BrepEngineApi,\n): ArrayBuffer {\n return exportStepFromSolids(kernel, [{ solid }])\n}"]}
|
|
@@ -16,12 +16,33 @@
|
|
|
16
16
|
* - vertex3: 3 × float32 (12 bytes)
|
|
17
17
|
* - attr: 2 bytes (0)
|
|
18
18
|
*/
|
|
19
|
+
/**
|
|
20
|
+
* Compose two row-major 3×4 affine matrices (implicit homogeneous row [0,0,0,1]).
|
|
21
|
+
* Returns B∘A: apply A first, then B. Used to accumulate a node's local transform
|
|
22
|
+
* onto its parent's world transform during assembly flattening (方案 §2.1 / §5).
|
|
23
|
+
*
|
|
24
|
+
* @param a - 先应用的内层矩阵(子节点局部 transform)。
|
|
25
|
+
* @param b - 后应用的外层矩阵(父节点累积 transform)。
|
|
26
|
+
* @returns 复合后的行主序 3×4 仿射矩阵(12 元组)。
|
|
27
|
+
*/
|
|
28
|
+
export declare function composeMatrix12(a: number[], b: number[]): number[];
|
|
29
|
+
/**
|
|
30
|
+
* Apply a row-major 3×4 affine matrix (implicit homogeneous [0,0,0,1]) to every
|
|
31
|
+
* vertex of an interleaved position buffer, returning a new buffer (STL 顶点烘焙).
|
|
32
|
+
*
|
|
33
|
+
* @param positions - 交错的顶点坐标缓冲(每顶点 x,y,z)。
|
|
34
|
+
* @param m - 行主序 3×4 仿射矩阵(12 元组,隐式齐次行 [0,0,0,1])。
|
|
35
|
+
* @returns 烘焙后的新缓冲(不修改入参)。
|
|
36
|
+
*/
|
|
37
|
+
export declare function bakeMatrix12ToPositions(positions: Float32Array, m: number[]): Float32Array;
|
|
19
38
|
/**
|
|
20
39
|
* Build a binary STL buffer from raw positions and indices (no THREE dependency).
|
|
21
40
|
* @param positions - the interleaved vertex positions (x,y,z per vertex).
|
|
22
41
|
* @param indices - the triangle indices (3 indices per triangle).
|
|
23
42
|
* @param header - optional 80-byte header text (defaults to 'Faicad STL').
|
|
43
|
+
* @param transform - optional row-major 3×4 affine matrix (12-tuple). When given,
|
|
44
|
+
* every vertex is baked through it before writing (STL 顶点烘焙, 方案 §2.1 / §5).
|
|
24
45
|
* @returns an ArrayBuffer holding the binary STL data.
|
|
25
46
|
*/
|
|
26
|
-
export declare function buildStlBufferFromMesh(positions: Float32Array, indices: Uint32Array, header?: string): ArrayBuffer;
|
|
47
|
+
export declare function buildStlBufferFromMesh(positions: Float32Array, indices: Uint32Array, header?: string, transform?: number[]): ArrayBuffer;
|
|
27
48
|
//# sourceMappingURL=stl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stl.d.ts","sourceRoot":"","sources":["../../../src/brep/export/stl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH
|
|
1
|
+
{"version":3,"file":"stl.d.ts","sourceRoot":"","sources":["../../../src/brep/export/stl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAalE;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,YAAY,CAW1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,WAAW,CAgFb"}
|
package/dist/brep/export/stl.js
CHANGED
|
@@ -16,15 +16,63 @@
|
|
|
16
16
|
* - vertex3: 3 × float32 (12 bytes)
|
|
17
17
|
* - attr: 2 bytes (0)
|
|
18
18
|
*/
|
|
19
|
+
/**
|
|
20
|
+
* Compose two row-major 3×4 affine matrices (implicit homogeneous row [0,0,0,1]).
|
|
21
|
+
* Returns B∘A: apply A first, then B. Used to accumulate a node's local transform
|
|
22
|
+
* onto its parent's world transform during assembly flattening (方案 §2.1 / §5).
|
|
23
|
+
*
|
|
24
|
+
* @param a - 先应用的内层矩阵(子节点局部 transform)。
|
|
25
|
+
* @param b - 后应用的外层矩阵(父节点累积 transform)。
|
|
26
|
+
* @returns 复合后的行主序 3×4 仿射矩阵(12 元组)。
|
|
27
|
+
*/
|
|
28
|
+
export function composeMatrix12(a, b) {
|
|
29
|
+
const C = new Array(12).fill(0);
|
|
30
|
+
for (let r = 0; r < 3; r++) {
|
|
31
|
+
for (let c = 0; c < 3; c++) {
|
|
32
|
+
let s = 0;
|
|
33
|
+
for (let k = 0; k < 3; k++)
|
|
34
|
+
s += b[r * 4 + k] * a[k * 4 + c];
|
|
35
|
+
C[r * 4 + c] = s;
|
|
36
|
+
}
|
|
37
|
+
let t = 0;
|
|
38
|
+
for (let k = 0; k < 3; k++)
|
|
39
|
+
t += b[r * 4 + k] * a[k * 4 + 3];
|
|
40
|
+
C[r * 4 + 3] = t + b[r * 4 + 3];
|
|
41
|
+
}
|
|
42
|
+
return C;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Apply a row-major 3×4 affine matrix (implicit homogeneous [0,0,0,1]) to every
|
|
46
|
+
* vertex of an interleaved position buffer, returning a new buffer (STL 顶点烘焙).
|
|
47
|
+
*
|
|
48
|
+
* @param positions - 交错的顶点坐标缓冲(每顶点 x,y,z)。
|
|
49
|
+
* @param m - 行主序 3×4 仿射矩阵(12 元组,隐式齐次行 [0,0,0,1])。
|
|
50
|
+
* @returns 烘焙后的新缓冲(不修改入参)。
|
|
51
|
+
*/
|
|
52
|
+
export function bakeMatrix12ToPositions(positions, m) {
|
|
53
|
+
const out = new Float32Array(positions.length);
|
|
54
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
55
|
+
const x = positions[i];
|
|
56
|
+
const y = positions[i + 1];
|
|
57
|
+
const z = positions[i + 2];
|
|
58
|
+
out[i] = m[0] * x + m[1] * y + m[2] * z + m[3];
|
|
59
|
+
out[i + 1] = m[4] * x + m[5] * y + m[6] * z + m[7];
|
|
60
|
+
out[i + 2] = m[8] * x + m[9] * y + m[10] * z + m[11];
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
19
64
|
/**
|
|
20
65
|
* Build a binary STL buffer from raw positions and indices (no THREE dependency).
|
|
21
66
|
* @param positions - the interleaved vertex positions (x,y,z per vertex).
|
|
22
67
|
* @param indices - the triangle indices (3 indices per triangle).
|
|
23
68
|
* @param header - optional 80-byte header text (defaults to 'Faicad STL').
|
|
69
|
+
* @param transform - optional row-major 3×4 affine matrix (12-tuple). When given,
|
|
70
|
+
* every vertex is baked through it before writing (STL 顶点烘焙, 方案 §2.1 / §5).
|
|
24
71
|
* @returns an ArrayBuffer holding the binary STL data.
|
|
25
72
|
*/
|
|
26
|
-
export function buildStlBufferFromMesh(positions, indices, header) {
|
|
73
|
+
export function buildStlBufferFromMesh(positions, indices, header, transform) {
|
|
27
74
|
const triCount = indices.length / 3;
|
|
75
|
+
const pos = transform ? bakeMatrix12ToPositions(positions, transform) : positions;
|
|
28
76
|
const headerSize = 80;
|
|
29
77
|
const countSize = 4;
|
|
30
78
|
const triByteSize = 50; // 12 (normal) + 12*3 (vertices) + 2 (attr)
|
|
@@ -44,15 +92,15 @@ export function buildStlBufferFromMesh(positions, indices, header) {
|
|
|
44
92
|
const ia = indices[i * 3];
|
|
45
93
|
const ib = indices[i * 3 + 1];
|
|
46
94
|
const ic = indices[i * 3 + 2];
|
|
47
|
-
const ax =
|
|
48
|
-
const ay =
|
|
49
|
-
const az =
|
|
50
|
-
const bx =
|
|
51
|
-
const by =
|
|
52
|
-
const bz =
|
|
53
|
-
const cx =
|
|
54
|
-
const cy =
|
|
55
|
-
const cz =
|
|
95
|
+
const ax = pos[ia * 3];
|
|
96
|
+
const ay = pos[ia * 3 + 1];
|
|
97
|
+
const az = pos[ia * 3 + 2];
|
|
98
|
+
const bx = pos[ib * 3];
|
|
99
|
+
const by = pos[ib * 3 + 1];
|
|
100
|
+
const bz = pos[ib * 3 + 2];
|
|
101
|
+
const cx = pos[ic * 3];
|
|
102
|
+
const cy = pos[ic * 3 + 1];
|
|
103
|
+
const cz = pos[ic * 3 + 2];
|
|
56
104
|
// Compute face normal: cross(B-A, C-A)
|
|
57
105
|
const ux = bx - ax;
|
|
58
106
|
const uy = by - ay;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stl.js","sourceRoot":"","sources":["../../../src/brep/export/stl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAuB,EACvB,OAAoB,EACpB,MAAe;IAEf,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;IAEnC,MAAM,UAAU,GAAG,EAAE,CAAA;IACrB,MAAM,SAAS,GAAG,CAAC,CAAA;IACnB,MAAM,WAAW,GAAG,EAAE,CAAA,CAAC,2CAA2C;IAClE,MAAM,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAA;IAEjE,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAA;IACzC,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAA;IAE/B,oBAAoB;IACpB,MAAM,SAAS,GAAG,MAAM,IAAI,YAAY,CAAA;IACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxD,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IACzC,CAAC;IAED,iBAAiB;IACjB,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAA;IAEhC,YAAY;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAA;QACxB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAE,CAAA;QAC1B,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAE,CAAA;QAC9B,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAE,CAAA;QAE9B,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;QAC5B,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAChC,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAChC,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;QAC5B,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAChC,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAChC,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;QAC5B,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAChC,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAEhC,uCAAuC;QACvC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAClB,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAClB,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAClB,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAClB,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAClB,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAClB,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAC1B,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAC1B,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;QACnD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,EAAE,IAAI,IAAI,CAAA;YACV,EAAE,IAAI,IAAI,CAAA;YACV,EAAE,IAAI,IAAI,CAAA;QACZ,CAAC;QAED,SAAS;QACT,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QACjC,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QACjC,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAEjC,WAAW;QACX,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAClC,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAClC,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAElC,WAAW;QACX,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAClC,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAClC,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAElC,WAAW;QACX,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAClC,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAClC,EAAE,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;QAElC,iCAAiC;QACjC,EAAE,CAAC,SAAS,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAA;IAClC,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["/**\n * buildStlBufferFromMesh — L1 纯数据 STL 导出(无 THREE 依赖)\n *\n *\n * 从原始 positions + indices 构建 binary STL buffer。\n * 与 src/renderer/lib/build-stl.ts 的 buildStlBuffer() 功能相同,\n * 但不依赖 THREE.BufferGeometry,可在 Node 环境直接使用。\n *\n * Binary STL format:\n * bytes 0-79: 80-byte header\n * bytes 80-83: 4-byte uint32 triangle count N\n * bytes 84+: N × 50-byte triangles\n * - normal: 3 × float32 (12 bytes)\n * - vertex1: 3 × float32 (12 bytes)\n * - vertex2: 3 × float32 (12 bytes)\n * - vertex3: 3 × float32 (12 bytes)\n * - attr: 2 bytes (0)\n */\n\n/**\n * Build a binary STL buffer from raw positions and indices (no THREE dependency).\n * @param positions - the interleaved vertex positions (x,y,z per vertex).\n * @param indices - the triangle indices (3 indices per triangle).\n * @param header - optional 80-byte header text (defaults to 'Faicad STL').\n * @returns an ArrayBuffer holding the binary STL data.\n */\nexport function buildStlBufferFromMesh(\n positions: Float32Array,\n indices: Uint32Array,\n header?: string,\n): ArrayBuffer {\n const triCount = indices.length / 3\n\n const headerSize = 80\n const countSize = 4\n const triByteSize = 50 // 12 (normal) + 12*3 (vertices) + 2 (attr)\n const totalSize = headerSize + countSize + triCount * triByteSize\n\n const buffer = new ArrayBuffer(totalSize)\n const dv = new DataView(buffer)\n\n // Header (80 bytes)\n const headerStr = header ?? 'Faicad STL'\n for (let i = 0; i < Math.min(headerStr.length, 80); i++) {\n dv.setUint8(i, headerStr.charCodeAt(i))\n }\n\n // Triangle count\n dv.setUint32(80, triCount, true)\n\n // Triangles\n for (let i = 0; i < triCount; i++) {\n const base = 84 + i * 50\n const ia = indices[i * 3]!\n const ib = indices[i * 3 + 1]!\n const ic = indices[i * 3 + 2]!\n\n const ax = positions[ia * 3]\n const ay = positions[ia * 3 + 1]\n const az = positions[ia * 3 + 2]\n const bx = positions[ib * 3]\n const by = positions[ib * 3 + 1]\n const bz = positions[ib * 3 + 2]\n const cx = positions[ic * 3]\n const cy = positions[ic * 3 + 1]\n const cz = positions[ic * 3 + 2]\n\n // Compute face normal: cross(B-A, C-A)\n const ux = bx - ax\n const uy = by - ay\n const uz = bz - az\n const vx = cx - ax\n const vy = cy - ay\n const vz = cz - az\n let nx = uy * vz - uz * vy\n let ny = uz * vx - ux * vz\n let nz = ux * vy - uy * vx\n const nlen = Math.sqrt(nx * nx + ny * ny + nz * nz)\n if (nlen > 0) {\n nx /= nlen\n ny /= nlen\n nz /= nlen\n }\n\n // Normal\n dv.setFloat32(base + 0, nx, true)\n dv.setFloat32(base + 4, ny, true)\n dv.setFloat32(base + 8, nz, true)\n\n // Vertex A\n dv.setFloat32(base + 12, ax, true)\n dv.setFloat32(base + 16, ay, true)\n dv.setFloat32(base + 20, az, true)\n\n // Vertex B\n dv.setFloat32(base + 24, bx, true)\n dv.setFloat32(base + 28, by, true)\n dv.setFloat32(base + 32, bz, true)\n\n // Vertex C\n dv.setFloat32(base + 36, cx, true)\n dv.setFloat32(base + 40, cy, true)\n dv.setFloat32(base + 44, cz, true)\n\n // Attribute (2 bytes, usually 0)\n dv.setUint16(base + 48, 0, true)\n }\n\n return buffer\n}\n"]}
|
|
1
|
+
{"version":3,"file":"stl.js","sourceRoot":"","sources":["../../../src/brep/export/stl.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * buildStlBufferFromMesh — L1 纯数据 STL 导出(无 THREE 依赖)\n *\n *\n * 从原始 positions + indices 构建 binary STL buffer。\n * 与 src/renderer/lib/build-stl.ts 的 buildStlBuffer() 功能相同,\n * 但不依赖 THREE.BufferGeometry,可在 Node 环境直接使用。\n *\n * Binary STL format:\n * bytes 0-79: 80-byte header\n * bytes 80-83: 4-byte uint32 triangle count N\n * bytes 84+: N × 50-byte triangles\n * - normal: 3 × float32 (12 bytes)\n * - vertex1: 3 × float32 (12 bytes)\n * - vertex2: 3 × float32 (12 bytes)\n * - vertex3: 3 × float32 (12 bytes)\n * - attr: 2 bytes (0)\n */\n\n/**\n * Compose two row-major 3×4 affine matrices (implicit homogeneous row [0,0,0,1]).\n * Returns B∘A: apply A first, then B. Used to accumulate a node's local transform\n * onto its parent's world transform during assembly flattening (方案 §2.1 / §5).\n *\n * @param a - 先应用的内层矩阵(子节点局部 transform)。\n * @param b - 后应用的外层矩阵(父节点累积 transform)。\n * @returns 复合后的行主序 3×4 仿射矩阵(12 元组)。\n */\nexport function composeMatrix12(a: number[], b: number[]): number[] {\n const C = new Array<number>(12).fill(0)\n for (let r = 0; r < 3; r++) {\n for (let c = 0; c < 3; c++) {\n let s = 0\n for (let k = 0; k < 3; k++) s += b[r * 4 + k]! * a[k * 4 + c]!\n C[r * 4 + c] = s\n }\n let t = 0\n for (let k = 0; k < 3; k++) t += b[r * 4 + k]! * a[k * 4 + 3]!\n C[r * 4 + 3] = t + b[r * 4 + 3]!\n }\n return C\n}\n\n/**\n * Apply a row-major 3×4 affine matrix (implicit homogeneous [0,0,0,1]) to every\n * vertex of an interleaved position buffer, returning a new buffer (STL 顶点烘焙).\n *\n * @param positions - 交错的顶点坐标缓冲(每顶点 x,y,z)。\n * @param m - 行主序 3×4 仿射矩阵(12 元组,隐式齐次行 [0,0,0,1])。\n * @returns 烘焙后的新缓冲(不修改入参)。\n */\nexport function bakeMatrix12ToPositions(positions: Float32Array, m: number[]): Float32Array {\n const out = new Float32Array(positions.length)\n for (let i = 0; i < positions.length; i += 3) {\n const x = positions[i]!\n const y = positions[i + 1]!\n const z = positions[i + 2]!\n out[i] = m[0]! * x + m[1]! * y + m[2]! * z + m[3]!\n out[i + 1] = m[4]! * x + m[5]! * y + m[6]! * z + m[7]!\n out[i + 2] = m[8]! * x + m[9]! * y + m[10]! * z + m[11]!\n }\n return out\n}\n\n/**\n * Build a binary STL buffer from raw positions and indices (no THREE dependency).\n * @param positions - the interleaved vertex positions (x,y,z per vertex).\n * @param indices - the triangle indices (3 indices per triangle).\n * @param header - optional 80-byte header text (defaults to 'Faicad STL').\n * @param transform - optional row-major 3×4 affine matrix (12-tuple). When given,\n * every vertex is baked through it before writing (STL 顶点烘焙, 方案 §2.1 / §5).\n * @returns an ArrayBuffer holding the binary STL data.\n */\nexport function buildStlBufferFromMesh(\n positions: Float32Array,\n indices: Uint32Array,\n header?: string,\n transform?: number[],\n): ArrayBuffer {\n const triCount = indices.length / 3\n const pos = transform ? bakeMatrix12ToPositions(positions, transform) : positions\n\n const headerSize = 80\n const countSize = 4\n const triByteSize = 50 // 12 (normal) + 12*3 (vertices) + 2 (attr)\n const totalSize = headerSize + countSize + triCount * triByteSize\n\n const buffer = new ArrayBuffer(totalSize)\n const dv = new DataView(buffer)\n\n // Header (80 bytes)\n const headerStr = header ?? 'Faicad STL'\n for (let i = 0; i < Math.min(headerStr.length, 80); i++) {\n dv.setUint8(i, headerStr.charCodeAt(i))\n }\n\n // Triangle count\n dv.setUint32(80, triCount, true)\n\n // Triangles\n for (let i = 0; i < triCount; i++) {\n const base = 84 + i * 50\n const ia = indices[i * 3]!\n const ib = indices[i * 3 + 1]!\n const ic = indices[i * 3 + 2]!\n\n const ax = pos[ia * 3]\n const ay = pos[ia * 3 + 1]\n const az = pos[ia * 3 + 2]\n const bx = pos[ib * 3]\n const by = pos[ib * 3 + 1]\n const bz = pos[ib * 3 + 2]\n const cx = pos[ic * 3]\n const cy = pos[ic * 3 + 1]\n const cz = pos[ic * 3 + 2]\n\n // Compute face normal: cross(B-A, C-A)\n const ux = bx - ax\n const uy = by - ay\n const uz = bz - az\n const vx = cx - ax\n const vy = cy - ay\n const vz = cz - az\n let nx = uy * vz - uz * vy\n let ny = uz * vx - ux * vz\n let nz = ux * vy - uy * vx\n const nlen = Math.sqrt(nx * nx + ny * ny + nz * nz)\n if (nlen > 0) {\n nx /= nlen\n ny /= nlen\n nz /= nlen\n }\n\n // Normal\n dv.setFloat32(base + 0, nx, true)\n dv.setFloat32(base + 4, ny, true)\n dv.setFloat32(base + 8, nz, true)\n\n // Vertex A\n dv.setFloat32(base + 12, ax, true)\n dv.setFloat32(base + 16, ay, true)\n dv.setFloat32(base + 20, az, true)\n\n // Vertex B\n dv.setFloat32(base + 24, bx, true)\n dv.setFloat32(base + 28, by, true)\n dv.setFloat32(base + 32, bz, true)\n\n // Vertex C\n dv.setFloat32(base + 36, cx, true)\n dv.setFloat32(base + 40, cy, true)\n dv.setFloat32(base + 44, cz, true)\n\n // Attribute (2 bytes, usually 0)\n dv.setUint16(base + 48, 0, true)\n }\n\n return buffer\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* transform12 — 装配节点位姿 → 3×4 行主序矩阵(写出器共用件)
|
|
3
|
+
*
|
|
4
|
+
* `Shape.transform`(mesh/types.ts)到 12 元组的唯一换算点。消费方:
|
|
5
|
+
* - `export-model.ts`:3MF `<component transform>` / STL 顶点烘焙;
|
|
6
|
+
* - `export-step.ts`:c1 transform 非 identity 时烘进几何(首组件恒 identity);
|
|
7
|
+
* - `api/export-stl.ts`:transform 烘焙进顶点。
|
|
8
|
+
*
|
|
9
|
+
* 12 元组约定(行主序 3×4,平移在末列):`[r00,r01,r02,tx, r10,r11,r12,ty, r20,r21,r22,tz]`,
|
|
10
|
+
* 与加载器 `parseTransformAttr` / wasm `getLocation` 同约定。
|
|
11
|
+
*/
|
|
12
|
+
import type { Vec3 } from '../../mesh/types.js';
|
|
13
|
+
/**
|
|
14
|
+
* 轴角(度)→ 3×3 行主序旋转矩阵(Rodrigues)。
|
|
15
|
+
*
|
|
16
|
+
* @param axis - 旋转轴(零向量回退 z 轴)。
|
|
17
|
+
* @param angleDeg - 旋转角(度)。
|
|
18
|
+
* @returns 行主序 3×3 旋转矩阵(9 元素)。
|
|
19
|
+
*/
|
|
20
|
+
export declare function rotationMatrix3x3(axis: Vec3, angleDeg: number): number[];
|
|
21
|
+
/** 装配节点位姿(与 `Shape.transform` 同形态)。 */
|
|
22
|
+
export interface NodeTransform {
|
|
23
|
+
translate?: Vec3;
|
|
24
|
+
rotate?: {
|
|
25
|
+
angle: number;
|
|
26
|
+
axis?: Vec3;
|
|
27
|
+
};
|
|
28
|
+
matrix?: number[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 3MF `<component transform>` 的 12 元组(行主序 3×4,平移在末列),与加载器 `parseTransformAttr` 同约定。
|
|
32
|
+
*
|
|
33
|
+
* @param t - 节点位姿(`matrix` 12 元组优先,缺省时由 `rotate` / `translate` 合成)。
|
|
34
|
+
* @returns 行主序 3×4 仿射矩阵的 12 元组。
|
|
35
|
+
*/
|
|
36
|
+
export declare function transformToMatrix12(t: NodeTransform): number[];
|
|
37
|
+
/**
|
|
38
|
+
* 位姿平移分量按单位刻度换算(旋转不变)。基准单位(mm)→ 目标声明单位刻度,
|
|
39
|
+
* 与几何缩放(`scalePositions` / `kernel.scale`)同一 `scale` 因子——
|
|
40
|
+
* 「声明单位 == 坐标刻度」不变式对组件位姿同样成立。
|
|
41
|
+
*
|
|
42
|
+
* @param t - 基准单位刻度的节点位姿。
|
|
43
|
+
* @param scale - 单位换算因子(`1 / UNIT_SCALE[unit]`)。
|
|
44
|
+
* @returns 平移已换算的位姿(不修改入参)。
|
|
45
|
+
*/
|
|
46
|
+
export declare function scaleTransform(t: NodeTransform, scale: number): NodeTransform;
|
|
47
|
+
//# sourceMappingURL=transform12.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform12.d.ts","sourceRoot":"","sources":["../../../src/brep/export/transform12.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAQ5C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAWxE;AAED,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAA;KAAE,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,EAAE,CAa9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAY7E"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* transform12 — 装配节点位姿 → 3×4 行主序矩阵(写出器共用件)
|
|
3
|
+
*
|
|
4
|
+
* `Shape.transform`(mesh/types.ts)到 12 元组的唯一换算点。消费方:
|
|
5
|
+
* - `export-model.ts`:3MF `<component transform>` / STL 顶点烘焙;
|
|
6
|
+
* - `export-step.ts`:c1 transform 非 identity 时烘进几何(首组件恒 identity);
|
|
7
|
+
* - `api/export-stl.ts`:transform 烘焙进顶点。
|
|
8
|
+
*
|
|
9
|
+
* 12 元组约定(行主序 3×4,平移在末列):`[r00,r01,r02,tx, r10,r11,r12,ty, r20,r21,r22,tz]`,
|
|
10
|
+
* 与加载器 `parseTransformAttr` / wasm `getLocation` 同约定。
|
|
11
|
+
*/
|
|
12
|
+
/** 归一化向量(零向量回退 z 轴)。 */
|
|
13
|
+
function normalize3(v) {
|
|
14
|
+
const len = Math.hypot(v[0], v[1], v[2]);
|
|
15
|
+
return len === 0 ? [0, 0, 1] : [v[0] / len, v[1] / len, v[2] / len];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 轴角(度)→ 3×3 行主序旋转矩阵(Rodrigues)。
|
|
19
|
+
*
|
|
20
|
+
* @param axis - 旋转轴(零向量回退 z 轴)。
|
|
21
|
+
* @param angleDeg - 旋转角(度)。
|
|
22
|
+
* @returns 行主序 3×3 旋转矩阵(9 元素)。
|
|
23
|
+
*/
|
|
24
|
+
export function rotationMatrix3x3(axis, angleDeg) {
|
|
25
|
+
const [x, y, z] = normalize3(axis);
|
|
26
|
+
const a = (angleDeg * Math.PI) / 180;
|
|
27
|
+
const c = Math.cos(a);
|
|
28
|
+
const s = Math.sin(a);
|
|
29
|
+
const t = 1 - c;
|
|
30
|
+
return [
|
|
31
|
+
t * x * x + c, t * x * y - s * z, t * x * z + s * y,
|
|
32
|
+
t * x * y + s * z, t * y * y + c, t * y * z - s * x,
|
|
33
|
+
t * x * z - s * y, t * y * z + s * x, t * z * z + c,
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 3MF `<component transform>` 的 12 元组(行主序 3×4,平移在末列),与加载器 `parseTransformAttr` 同约定。
|
|
38
|
+
*
|
|
39
|
+
* @param t - 节点位姿(`matrix` 12 元组优先,缺省时由 `rotate` / `translate` 合成)。
|
|
40
|
+
* @returns 行主序 3×4 仿射矩阵的 12 元组。
|
|
41
|
+
*/
|
|
42
|
+
export function transformToMatrix12(t) {
|
|
43
|
+
if (t.matrix && t.matrix.length === 12)
|
|
44
|
+
return t.matrix.slice();
|
|
45
|
+
const m = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0];
|
|
46
|
+
if (t.rotate) {
|
|
47
|
+
const R = rotationMatrix3x3(t.rotate.axis ?? [0, 0, 1], t.rotate.angle);
|
|
48
|
+
m[0] = R[0];
|
|
49
|
+
m[1] = R[1];
|
|
50
|
+
m[2] = R[2];
|
|
51
|
+
m[4] = R[3];
|
|
52
|
+
m[5] = R[4];
|
|
53
|
+
m[6] = R[5];
|
|
54
|
+
m[8] = R[6];
|
|
55
|
+
m[9] = R[7];
|
|
56
|
+
m[10] = R[8];
|
|
57
|
+
}
|
|
58
|
+
if (t.translate) {
|
|
59
|
+
m[3] = t.translate[0];
|
|
60
|
+
m[7] = t.translate[1];
|
|
61
|
+
m[11] = t.translate[2];
|
|
62
|
+
}
|
|
63
|
+
return m;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 位姿平移分量按单位刻度换算(旋转不变)。基准单位(mm)→ 目标声明单位刻度,
|
|
67
|
+
* 与几何缩放(`scalePositions` / `kernel.scale`)同一 `scale` 因子——
|
|
68
|
+
* 「声明单位 == 坐标刻度」不变式对组件位姿同样成立。
|
|
69
|
+
*
|
|
70
|
+
* @param t - 基准单位刻度的节点位姿。
|
|
71
|
+
* @param scale - 单位换算因子(`1 / UNIT_SCALE[unit]`)。
|
|
72
|
+
* @returns 平移已换算的位姿(不修改入参)。
|
|
73
|
+
*/
|
|
74
|
+
export function scaleTransform(t, scale) {
|
|
75
|
+
if (t.matrix && t.matrix.length === 12) {
|
|
76
|
+
const m = t.matrix.slice();
|
|
77
|
+
m[3] = m[3] * scale;
|
|
78
|
+
m[7] = m[7] * scale;
|
|
79
|
+
m[11] = m[11] * scale;
|
|
80
|
+
return { matrix: m };
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
...(t.translate ? { translate: [t.translate[0] * scale, t.translate[1] * scale, t.translate[2] * scale] } : {}),
|
|
84
|
+
...(t.rotate ? { rotate: t.rotate } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=transform12.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform12.js","sourceRoot":"","sources":["../../../src/brep/export/transform12.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * transform12 — 装配节点位姿 → 3×4 行主序矩阵(写出器共用件)\n *\n * `Shape.transform`(mesh/types.ts)到 12 元组的唯一换算点。消费方:\n * - `export-model.ts`:3MF `<component transform>` / STL 顶点烘焙;\n * - `export-step.ts`:c1 transform 非 identity 时烘进几何(首组件恒 identity);\n * - `api/export-stl.ts`:transform 烘焙进顶点。\n *\n * 12 元组约定(行主序 3×4,平移在末列):`[r00,r01,r02,tx, r10,r11,r12,ty, r20,r21,r22,tz]`,\n * 与加载器 `parseTransformAttr` / wasm `getLocation` 同约定。\n */\n\nimport type { Vec3 } from '../../mesh/types'\n\n/** 归一化向量(零向量回退 z 轴)。 */\nfunction normalize3(v: Vec3): Vec3 {\n const len = Math.hypot(v[0], v[1], v[2])\n return len === 0 ? [0, 0, 1] : [v[0] / len, v[1] / len, v[2] / len]\n}\n\n/**\n * 轴角(度)→ 3×3 行主序旋转矩阵(Rodrigues)。\n *\n * @param axis - 旋转轴(零向量回退 z 轴)。\n * @param angleDeg - 旋转角(度)。\n * @returns 行主序 3×3 旋转矩阵(9 元素)。\n */\nexport function rotationMatrix3x3(axis: Vec3, angleDeg: number): number[] {\n const [x, y, z] = normalize3(axis)\n const a = (angleDeg * Math.PI) / 180\n const c = Math.cos(a)\n const s = Math.sin(a)\n const t = 1 - c\n return [\n t * x * x + c, t * x * y - s * z, t * x * z + s * y,\n t * x * y + s * z, t * y * y + c, t * y * z - s * x,\n t * x * z - s * y, t * y * z + s * x, t * z * z + c,\n ]\n}\n\n/** 装配节点位姿(与 `Shape.transform` 同形态)。 */\nexport interface NodeTransform {\n translate?: Vec3\n rotate?: { angle: number; axis?: Vec3 }\n matrix?: number[]\n}\n\n/**\n * 3MF `<component transform>` 的 12 元组(行主序 3×4,平移在末列),与加载器 `parseTransformAttr` 同约定。\n *\n * @param t - 节点位姿(`matrix` 12 元组优先,缺省时由 `rotate` / `translate` 合成)。\n * @returns 行主序 3×4 仿射矩阵的 12 元组。\n */\nexport function transformToMatrix12(t: NodeTransform): number[] {\n if (t.matrix && t.matrix.length === 12) return t.matrix.slice()\n const m = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]\n if (t.rotate) {\n const R = rotationMatrix3x3(t.rotate.axis ?? [0, 0, 1], t.rotate.angle)\n m[0] = R[0]; m[1] = R[1]; m[2] = R[2]\n m[4] = R[3]; m[5] = R[4]; m[6] = R[5]\n m[8] = R[6]; m[9] = R[7]; m[10] = R[8]\n }\n if (t.translate) {\n m[3] = t.translate[0]; m[7] = t.translate[1]; m[11] = t.translate[2]\n }\n return m\n}\n\n/**\n * 位姿平移分量按单位刻度换算(旋转不变)。基准单位(mm)→ 目标声明单位刻度,\n * 与几何缩放(`scalePositions` / `kernel.scale`)同一 `scale` 因子——\n * 「声明单位 == 坐标刻度」不变式对组件位姿同样成立。\n *\n * @param t - 基准单位刻度的节点位姿。\n * @param scale - 单位换算因子(`1 / UNIT_SCALE[unit]`)。\n * @returns 平移已换算的位姿(不修改入参)。\n */\nexport function scaleTransform(t: NodeTransform, scale: number): NodeTransform {\n if (t.matrix && t.matrix.length === 12) {\n const m = t.matrix.slice()\n m[3] = m[3]! * scale\n m[7] = m[7]! * scale\n m[11] = m[11]! * scale\n return { matrix: m }\n }\n return {\n ...(t.translate ? { translate: [t.translate[0] * scale, t.translate[1] * scale, t.translate[2] * scale] as Vec3 } : {}),\n ...(t.rotate ? { rotate: t.rotate } : {}),\n }\n}\n"]}
|