@faicad/faijs 0.32.3 → 0.33.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/api-namespace.js +3 -3
- package/dist/api/api-namespace.js.map +1 -1
- package/dist/api/assembly/index.d.ts +1 -0
- package/dist/api/assembly/index.d.ts.map +1 -1
- package/dist/api/assembly/index.js +1 -0
- package/dist/api/assembly/index.js.map +1 -1
- package/dist/api/assembly/lower.d.ts.map +1 -1
- package/dist/api/assembly/lower.js +7 -0
- package/dist/api/assembly/lower.js.map +1 -1
- package/dist/api/assembly/normalize.d.ts.map +1 -1
- package/dist/api/assembly/normalize.js +2 -1
- package/dist/api/assembly/normalize.js.map +1 -1
- package/dist/api/assembly/solve-tree.d.ts +46 -0
- package/dist/api/assembly/solve-tree.d.ts.map +1 -0
- package/dist/api/assembly/solve-tree.js +135 -0
- package/dist/api/assembly/solve-tree.js.map +1 -0
- package/dist/api/assembly/solve.d.ts +7 -0
- package/dist/api/assembly/solve.d.ts.map +1 -1
- package/dist/api/assembly/solve.js.map +1 -1
- package/dist/api/assembly/solvers/assembly-tree.d.ts +4 -0
- package/dist/api/assembly/solvers/assembly-tree.d.ts.map +1 -1
- package/dist/api/assembly/solvers/assembly-tree.js +1 -0
- package/dist/api/assembly/solvers/assembly-tree.js.map +1 -1
- package/dist/api/assembly/solvers/global-solver.d.ts.map +1 -1
- package/dist/api/assembly/solvers/global-solver.js +210 -3
- package/dist/api/assembly/solvers/global-solver.js.map +1 -1
- package/dist/api/assembly/solvers/types.d.ts +8 -0
- package/dist/api/assembly/solvers/types.d.ts.map +1 -1
- package/dist/api/assembly/solvers/types.js.map +1 -1
- package/dist/api/assembly/types.d.ts +32 -1
- package/dist/api/assembly/types.d.ts.map +1 -1
- package/dist/api/assembly/types.js.map +1 -1
- package/dist/api/assembly/validate.d.ts +2 -2
- package/dist/api/assembly/validate.d.ts.map +1 -1
- package/dist/api/assembly/validate.js +24 -2
- package/dist/api/assembly/validate.js.map +1 -1
- package/dist/api/cadquery-selectors/edge.js +1 -1
- package/dist/api/cadquery-selectors/edge.js.map +1 -1
- package/dist/api/cadquery-selectors/entity.d.ts +29 -20
- package/dist/api/cadquery-selectors/entity.d.ts.map +1 -1
- package/dist/api/cadquery-selectors/entity.js +26 -20
- package/dist/api/cadquery-selectors/entity.js.map +1 -1
- package/dist/api/cadquery-selectors/face.d.ts +4 -2
- package/dist/api/cadquery-selectors/face.d.ts.map +1 -1
- package/dist/api/cadquery-selectors/face.js +8 -7
- package/dist/api/cadquery-selectors/face.js.map +1 -1
- package/dist/api/cadquery-selectors/resolve.d.ts +7 -6
- package/dist/api/cadquery-selectors/resolve.d.ts.map +1 -1
- package/dist/api/cadquery-selectors/resolve.js +13 -11
- package/dist/api/cadquery-selectors/resolve.js.map +1 -1
- package/dist/api/chamfer.js +1 -1
- package/dist/api/chamfer.js.map +1 -1
- package/dist/api/draft.js +1 -1
- package/dist/api/draft.js.map +1 -1
- package/dist/api/edge-ref.js +2 -2
- package/dist/api/edge-ref.js.map +1 -1
- package/dist/api/engrave.js +1 -1
- package/dist/api/engrave.js.map +1 -1
- package/dist/api/export-3mf.js +1 -1
- package/dist/api/export-3mf.js.map +1 -1
- package/dist/api/export-brep.js +1 -1
- package/dist/api/export-brep.js.map +1 -1
- package/dist/api/export-step.d.ts +12 -13
- package/dist/api/export-step.d.ts.map +1 -1
- package/dist/api/export-step.js +19 -17
- package/dist/api/export-step.js.map +1 -1
- package/dist/api/extrude.js +3 -3
- package/dist/api/extrude.js.map +1 -1
- package/dist/api/face-ref.js +2 -2
- package/dist/api/face-ref.js.map +1 -1
- package/dist/api/feature-repair.js +2 -2
- package/dist/api/feature-repair.js.map +1 -1
- package/dist/api/fillet-variable.js +1 -1
- package/dist/api/fillet-variable.js.map +1 -1
- package/dist/api/fillet.js +1 -1
- package/dist/api/fillet.js.map +1 -1
- package/dist/api/geom.js +1 -1
- package/dist/api/geom.js.map +1 -1
- package/dist/api/import-step.d.ts.map +1 -1
- package/dist/api/import-step.js +4 -2
- package/dist/api/import-step.js.map +1 -1
- package/dist/api/pattern.js +1 -1
- package/dist/api/pattern.js.map +1 -1
- package/dist/api/place.js +1 -1
- package/dist/api/place.js.map +1 -1
- package/dist/api/punch-hole.js +1 -1
- package/dist/api/punch-hole.js.map +1 -1
- package/dist/api/replicate.js +1 -1
- package/dist/api/replicate.js.map +1 -1
- package/dist/api/revolve.js +1 -1
- package/dist/api/revolve.js.map +1 -1
- package/dist/api/section-by-plane.js +1 -1
- package/dist/api/section-by-plane.js.map +1 -1
- package/dist/api/shell.js +1 -1
- package/dist/api/shell.js.map +1 -1
- package/dist/api/sketch-on-face.js +1 -1
- package/dist/api/sketch-on-face.js.map +1 -1
- package/dist/api/split-by-plane.js +1 -1
- package/dist/api/split-by-plane.js.map +1 -1
- package/dist/api/split.js +1 -1
- package/dist/api/split.js.map +1 -1
- package/dist/api/thicken.js +1 -1
- package/dist/api/thicken.js.map +1 -1
- package/dist/api/topo-resolve.js +4 -4
- package/dist/api/topo-resolve.js.map +1 -1
- package/dist/api/transform.js +1 -1
- package/dist/api/transform.js.map +1 -1
- package/dist/api/view/view-projection.d.ts +8 -0
- package/dist/api/view/view-projection.d.ts.map +1 -1
- package/dist/api/view/view-projection.js +73 -2
- package/dist/api/view/view-projection.js.map +1 -1
- package/dist/boolean/cross-section.d.ts.map +1 -1
- package/dist/boolean/cross-section.js +3 -1
- package/dist/boolean/cross-section.js.map +1 -1
- package/dist/brep/brep-ops.d.ts +26 -0
- package/dist/brep/brep-ops.d.ts.map +1 -1
- package/dist/brep/brep-ops.js +45 -0
- package/dist/brep/brep-ops.js.map +1 -1
- package/dist/brep/brep-topology.js +1 -1
- package/dist/brep/brep-topology.js.map +1 -1
- package/dist/brep/engine/adapters/brep-mock.d.ts.map +1 -1
- package/dist/brep/engine/adapters/brep-mock.js +2 -0
- package/dist/brep/engine/adapters/brep-mock.js.map +1 -1
- package/dist/brep/engine/primitives.d.ts +17 -0
- package/dist/brep/engine/primitives.d.ts.map +1 -1
- package/dist/brep/engine/primitives.js.map +1 -1
- package/dist/brep/export/export-model.d.ts.map +1 -1
- package/dist/brep/export/export-model.js +36 -11
- package/dist/brep/export/export-model.js.map +1 -1
- package/dist/brep/export/step-flat.d.ts +35 -0
- package/dist/brep/export/step-flat.d.ts.map +1 -0
- package/dist/brep/export/step-flat.js +94 -0
- package/dist/brep/export/step-flat.js.map +1 -0
- package/dist/brep/export/step.d.ts.map +1 -1
- package/dist/brep/export/step.js +42 -21
- package/dist/brep/export/step.js.map +1 -1
- package/dist/brep/handle-bridge.d.ts +4 -4
- package/dist/brep/handle-bridge.js +4 -4
- package/dist/brep/handle-bridge.js.map +1 -1
- package/dist/brep/svg/svg-to-solid.d.ts +3 -17
- package/dist/brep/svg/svg-to-solid.d.ts.map +1 -1
- package/dist/brep/svg/svg-to-solid.js +16 -317
- package/dist/brep/svg/svg-to-solid.js.map +1 -1
- package/dist/brepkit-kernel/brepkitKernel.d.ts.map +1 -1
- package/dist/brepkit-kernel/brepkitKernel.js +10 -0
- package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
- package/dist/brepkit-kernel/brepkitWasm.d.ts +1 -1
- package/dist/brepkit-kernel/brepkitWasm.d.ts.map +1 -1
- package/dist/brepkit-kernel/brepkitWasm.js +7 -6
- package/dist/brepkit-kernel/brepkitWasm.js.map +1 -1
- package/dist/brepkit-kernel/multi-version-test.d.ts +7 -7
- package/dist/brepkit-kernel/multi-version-test.d.ts.map +1 -1
- package/dist/brepkit-kernel/multi-version-test.js +10 -10
- package/dist/brepkit-kernel/multi-version-test.js.map +1 -1
- package/dist/cad-runtime/direct-executor.js +2 -2
- package/dist/cad-runtime/direct-executor.js.map +1 -1
- package/dist/cad-runtime/runtime.js +5 -5
- package/dist/cad-runtime/runtime.js.map +1 -1
- package/dist/geometry2d/curve2d.js +42 -1
- package/dist/geometry2d/curve2d.js.map +1 -1
- package/dist/math3d/Box3.js.map +1 -1
- package/dist/math3d/Euler.js.map +1 -1
- package/dist/math3d/Mat4.js.map +1 -1
- package/dist/math3d/Plane.js.map +1 -1
- package/dist/math3d/Quat.js.map +1 -1
- package/dist/math3d/Vec2.js.map +1 -1
- package/dist/math3d/Vec3.js.map +1 -1
- package/dist/math3d/index.js.map +1 -1
- package/dist/mesh/meshdata/contour.d.ts +34 -0
- package/dist/mesh/meshdata/contour.d.ts.map +1 -0
- package/dist/mesh/meshdata/contour.js +39 -0
- package/dist/mesh/meshdata/contour.js.map +1 -0
- package/dist/mesh/meshdata/extrude.d.ts +25 -0
- package/dist/mesh/meshdata/extrude.d.ts.map +1 -0
- package/dist/mesh/meshdata/extrude.js +198 -0
- package/dist/mesh/meshdata/extrude.js.map +1 -0
- package/dist/mesh/meshdata/index.js.map +1 -1
- package/dist/mesh/meshdata/ops.js.map +1 -1
- package/dist/mesh/meshdata/parametric.js.map +1 -1
- package/dist/mesh/meshdata/weld.js.map +1 -1
- package/dist/node-host/cli.d.ts +9 -0
- package/dist/node-host/cli.d.ts.map +1 -1
- package/dist/node-host/cli.js +34 -9
- package/dist/node-host/cli.js.map +1 -1
- package/dist/occt-kernel/occt-primitives.d.ts.map +1 -1
- package/dist/occt-kernel/occt-primitives.js +2 -0
- package/dist/occt-kernel/occt-primitives.js.map +1 -1
- package/dist/primitives/svg/svg-path.d.ts +192 -0
- package/dist/primitives/svg/svg-path.d.ts.map +1 -0
- package/dist/primitives/svg/svg-path.js +776 -0
- package/dist/primitives/svg/svg-path.js.map +1 -0
- package/dist/third-party/earcut.d.ts +19 -0
- package/dist/third-party/earcut.d.ts.map +1 -0
- package/dist/third-party/earcut.js +575 -0
- package/dist/third-party/earcut.js.map +1 -0
- package/package.json +10 -3
|
@@ -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 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}"]}
|
|
1
|
+
{"version":3,"file":"step.js","sourceRoot":"","sources":["../../../src/brep/export/step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAA;AAC5E,OAAO,EAAE,aAAa,EAAoB,MAAM,8BAA8B,CAAA;AAE9E,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAsB,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AA6BvC;;;;;;;;;GASG;AACH,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,EAAE,GAAG,CAAC,CAAA;AACtE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,CAAW;IAClC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAA;IACvD,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,GAAG,QAAQ,EAAE,CAAC;QAC/B,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,EAAE,CAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,EAAE,CAAA;IAC5E,CAAC;IACD,0CAA0C;IAC1C,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAA;AACrD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,CAA4B;IACvD,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAA;IACxB,MAAM,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAA;IAChC,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAAA;IACzC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAA;AACzD,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAC5B,IAAuB,EACvB,OAAwB,EACxB,GAA+E;IAE/E,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAC/D,MAAM,KAAK,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAA;QACvE,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxC,qBAAqB,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;QAC/C,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC,CAAA;QACzD,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,CAAW;IAC/B,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;WACtD,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;WACpD,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;AAC7D,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,sBAAsB,CAC7B,MAAqB,EACrB,GAAiB,EACjB,KAAsB,EACtB,YAA0B;IAE1B,MAAM,YAAY,GAAG,CAAC,CAAkB,EAAc,EAAE;QACtD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,CAAC,KAAK,CAAA;QACzC,IAAI,CAAC,CAAC,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACjF,MAAM,CAAC,GAAG,wBAAwB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5E,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACpB,OAAO,CAAC,CAAA;IACV,CAAC,CAAA;IACD,MAAM,IAAI,GAAG,CAAC,CAAkB,EAAE,CAAkB,EAAc,EAAE;QAClE,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CAAA;QAC5B,IAAI,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QACtC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QACxC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACzB,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IACD,MAAM,MAAM,GAAG,CAAC,CAAuC,EAAwC,EAAE,CAC/F,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAE9E,MAAM,MAAM,GAA+E,EAAE,CAAA;IAC7F,qBAAqB,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IACzD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAA;IAEnF,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAA;IAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAM,CAAC,KAAK,EAAE,KAAM,CAAC,KAAK,CAA2B,EAAE;QACrF,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,KAAM,CAAC,KAAK,CAAC,IAAI;QACrC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,KAAM,CAAC,KAAK,CAAC,KAAK,CAAC;KACjD,CAAC,CAAA;IACF,uCAAuC;IACvC,0CAA0C;IAC1C,wCAAwC;IACxC,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,IAAI,KAAiB,CAAA;QACrB,IAAI,QAAgG,CAAA;QACpG,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,oCAAoC;YACpC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QACtC,CAAC;aAAM,CAAC;YACN,wDAAwD;YACxD,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAChC,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QACtD,CAAC;QACD,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,KAA+B,EAAE;YACnD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACnE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9E,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;SAChD,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,IAAI,KAAM,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,KAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxE,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QACrC,IAAI,GAAG,EAAE,CAAC;YACR,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,KAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACnC,MAAM,KAAK,GAAG,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;YACvC,IAAI,KAAK;gBAAE,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAClD,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAqB,EACrB,OAA0B,EAC1B,QAAmB;IAEnB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;IAC3C,CAAC;IAED,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC,kBAAkB,EAAE,CAAA;IAChD,gDAAgD;IAChD,0CAA0C;IAC1C,MAAM,YAAY,GAAiB,EAAE,CAAA;IAErC,IAAI,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,wDAAwD;YACxD,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChD,sBAAsB,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,CAAC,CAAA;gBACxD,SAAQ;YACV,CAAC;YACD,oCAAoC;YACpC,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;YACvB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;oBAChB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;gBACtE,CAAC;gBACD,KAAK,GAAG,wBAAwB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;gBAClF,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAC1B,CAAC;YAED,uDAAuD;YACvD,8CAA8C;YAC9C,iDAAiD;YACjD,sDAAsD;YACtD,+DAA+D;YAC/D,8BAA8B;YAC9B,EAAE;YACF,sCAAsC;YACtC,qDAAqD;YACrD,+DAA+D;YAC/D,uCAAuC;YACvC,MAAM,MAAM,GAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;YACrE,IAAI,IAAI,GAAiB,EAAE,CAAA;YAC3B,IAAI,WAAW,GAAG,CAAC,CAAC,CAAA;YACpB,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;gBAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;gBACpD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrB,IAAI,GAAG,KAAK,CAAA;oBACZ,WAAW,GAAG,EAAE,CAAA;oBAChB,MAAK;gBACP,CAAC;YACH,CAAC;YACD,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;gBACpB,+CAA+C;gBAC/C,yDAAyD;gBACzD,8DAA8D;gBAC9D,UAAU;gBACV,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;YAChB,CAAC;iBAAM,CAAC;gBACN,kCAAkC;gBAClC,8BAA8B;gBAC9B,KAAK,IAAI,EAAE,GAAG,WAAW,GAAG,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;oBACxD,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;oBACpD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;wBAAE,SAAQ;oBAChC,MAAM,OAAO,GAAiB,EAAE,CAAA;oBAChC,KAAK,MAAM,CAAC,IAAI,IAAI;wBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;oBACzE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;wBACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;4BAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACnE,CAAC;gBACH,CAAC;YACH,CAAC;YACD,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;gBACxC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;gBACpB,mCAAmC;gBACnC,IAAI,KAAK,KAAK,GAAG;oBAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBACzC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC;oBAC1B,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI,MAAM,KAAK,EAAE,GAAG,CAAC,GAAG;oBACvC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAA;gBACd,yDAAyD;gBACzD,6CAA6C;gBAC7C,MAAM,KAAK,GAAyC,KAAK,CAAC,KAAK;oBAC7D,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC5F,CAAC,CAAC,SAAS,CAAA;gBACb,4DAA4D;gBAC5D,sBAAsB;gBACtB,GAAG,CAAC,QAAQ,CAAC,GAA6B,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YAC9D,CAAC;QACH,CAAC;QAED,uCAAuC;QACvC,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,EAAE,CAAA;QACjC,mEAAmE;QACnE,wDAAwD;QACxD,MAAM,GAAG,GAAG,QAAQ,IAAI,uBAAuB,CAAC,QAAQ,CAAC;YACvD,CAAC,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC;YACvC,CAAC,CAAC,QAAQ,CAAA;QACZ,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAA;IAC7C,CAAC;YAAS,CAAC;QACT,2BAA2B;QAC3B,GAAG,CAAC,KAAK,EAAE,CAAA;QACX,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,mBAAmB;YACrB,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAiB,EACjB,MAAqB;IAErB,OAAO,oBAAoB,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;AAClD,CAAC","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, BrepSubShapeType } 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 !== undefined) 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 === undefined) {\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,取首个非空层级。\n // ref 侧(cadquery Shape.exportStep)可导出任意类型的形状,面/壳/边\n // compound(如 Shape.faces('>Z') / shape.edges('>Z') 的结果)同样要能\n // 写进 STEP(U22,2026-09-09)。\n //\n // ⚠️ 级联只选一个层级会丢掉平级的更低层级形状:sew 产物常是\n // 「一个 shell + 一张游离 face」的混合 compound,命中 shell 后那张\n // 游离 face 拿不到 XCAF label、导出丢面(cq-compat test_sewing__res3)。\n // 故选中某层级后,再补齐**未被任何已选形状覆盖**的更低层级形状。\n const levels: BrepSubShapeType[] = ['solid', 'shell', 'face', 'edge']\n let subs: BrepHandle[] = []\n let chosenLevel = -1\n for (let li = 0; li < levels.length; li++) {\n const found = kernel.getSubShapes(solid, levels[li])\n if (found.length > 0) {\n subs = found\n chosenLevel = li\n break\n }\n }\n if (chosenLevel < 0) {\n // 各层全空:wasm getSubShapes 未展开该 compound(如个别纯面/边\n // compound)。整体作为单个 XCAF label 写入(STEP writer 支持 compound\n // 形状,与 ref 侧 cadquery Shape.exportStep 对 faces/edges compound\n // 的行为一致)。\n subs = [solid]\n } else {\n // 补齐游离的更低层级形状:候选与「已选形状(含此前补入者)的同类\n // 子形状」做 isSame 覆盖判定,不被覆盖者才追加。\n for (let li = chosenLevel + 1; li < levels.length; li++) {\n const lower = kernel.getSubShapes(solid, levels[li])\n if (lower.length === 0) continue\n const covered: BrepHandle[] = []\n for (const s of subs) covered.push(...kernel.getSubShapes(s, levels[li]))\n for (const cand of lower) {\n if (!covered.some((c) => kernel.isSame(c, cand))) subs.push(cand)\n }\n }\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}"]}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* handle-bridge — OCCT 句柄 → faijs Shape 的 SDK 桥接(B1)
|
|
3
3
|
*
|
|
4
4
|
*
|
|
5
|
-
* 第三方库要造 BREP 产物,需要"OCCT handle → mesh"
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* 第三方库要造 BREP 产物,需要"OCCT handle → mesh"。SDK 绝不能 import
|
|
6
|
+
* heavy 几何库(会把它们拉进 SDK bundle,违反 `src/sdk.test.ts` 的零 heavy
|
|
7
|
+
* 依赖守卫)。本文件在**新建的零依赖模块**中实现,直接调运行时注入的
|
|
8
|
+
* `kernel.meshShape`。
|
|
9
9
|
*
|
|
10
10
|
* 依赖约束(硬性):只允许 import `runtime-state`、`api/shape`、type-only
|
|
11
11
|
* `mesh/types`——任何新增依赖必须保持 dist/sdk.js 零 heavy 依赖守卫通过。
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* handle-bridge — OCCT 句柄 → faijs Shape 的 SDK 桥接(B1)
|
|
3
3
|
*
|
|
4
4
|
*
|
|
5
|
-
* 第三方库要造 BREP 产物,需要"OCCT handle → mesh"
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* 第三方库要造 BREP 产物,需要"OCCT handle → mesh"。SDK 绝不能 import
|
|
6
|
+
* heavy 几何库(会把它们拉进 SDK bundle,违反 `src/sdk.test.ts` 的零 heavy
|
|
7
|
+
* 依赖守卫)。本文件在**新建的零依赖模块**中实现,直接调运行时注入的
|
|
8
|
+
* `kernel.meshShape`。
|
|
9
9
|
*
|
|
10
10
|
* 依赖约束(硬性):只允许 import `runtime-state`、`api/shape`、type-only
|
|
11
11
|
* `mesh/types`——任何新增依赖必须保持 dist/sdk.js 零 heavy 依赖守卫通过。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-bridge.js","sourceRoot":"","sources":["../../src/brep/handle-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAWnC;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAA;IAC5G,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAA;IACH,CAAC;IACD,OAAO,MAAuB,CAAA;AAChC,CAAC;AAYD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAA8B,CAAC,UAAU,KAAK,IAAI,CAAA;AACnG,CAAC;AAED;0CAC0C;AAC1C,SAAS,YAAY,CAAC,CAAU;IAC9B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,CAAA;AACjD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,MAAe,EAAE,MAAc;IACpD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,kBAAkB,MAAM,yDAAyD;YAC/E,kCAAkC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,IAAI;YAC9E,wDAAwD,CAC3D,CAAA;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe,EAAE,IAAwB;IAClE,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACnC,MAAM,MAAM,GAAG,SAAS,EAAoB,CAAA;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QACpC,gBAAgB,EAAE,IAAI,EAAE,gBAAgB,IAAI,GAAG;QAC/C,iBAAiB,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;KACrE,CAAC,CAAA;IACF,OAAO;QACL,SAAS,EAAE,IAAI,YAAY,CAAC,IAAI,CAAC,SAA8B,CAAC;QAChE,OAAO,EAAE,IAAI,WAAW,CAAC,IAAI,CAAC,OAA4B,CAAC;KAC5D,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe,EAAE,IAAwB;IAClE,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACnC,OAAO,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;AAC9D,CAAC","sourcesContent":["/**\n * handle-bridge — OCCT 句柄 → faijs Shape 的 SDK 桥接(B1)\n *\n *\n * 第三方库要造 BREP 产物,需要\"OCCT handle → mesh\"
|
|
1
|
+
{"version":3,"file":"handle-bridge.js","sourceRoot":"","sources":["../../src/brep/handle-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAWnC;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAA;IAC5G,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAA;IACH,CAAC;IACD,OAAO,MAAuB,CAAA;AAChC,CAAC;AAYD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAA8B,CAAC,UAAU,KAAK,IAAI,CAAA;AACnG,CAAC;AAED;0CAC0C;AAC1C,SAAS,YAAY,CAAC,CAAU;IAC9B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,CAAA;AACjD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,MAAe,EAAE,MAAc;IACpD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,kBAAkB,MAAM,yDAAyD;YAC/E,kCAAkC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,IAAI;YAC9E,wDAAwD,CAC3D,CAAA;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe,EAAE,IAAwB;IAClE,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACnC,MAAM,MAAM,GAAG,SAAS,EAAoB,CAAA;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QACpC,gBAAgB,EAAE,IAAI,EAAE,gBAAgB,IAAI,GAAG;QAC/C,iBAAiB,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;KACrE,CAAC,CAAA;IACF,OAAO;QACL,SAAS,EAAE,IAAI,YAAY,CAAC,IAAI,CAAC,SAA8B,CAAC;QAChE,OAAO,EAAE,IAAI,WAAW,CAAC,IAAI,CAAC,OAA4B,CAAC;KAC5D,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe,EAAE,IAAwB;IAClE,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACnC,OAAO,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;AAC9D,CAAC","sourcesContent":["/**\n * handle-bridge — OCCT 句柄 → faijs Shape 的 SDK 桥接(B1)\n *\n *\n * 第三方库要造 BREP 产物,需要\"OCCT handle → mesh\"。SDK 绝不能 import\n * heavy 几何库(会把它们拉进 SDK bundle,违反 `src/sdk.test.ts` 的零 heavy\n * 依赖守卫)。本文件在**新建的零依赖模块**中实现,直接调运行时注入的\n * `kernel.meshShape`。\n *\n * 依赖约束(硬性):只允许 import `runtime-state`、`api/shape`、type-only\n * `mesh/types`——任何新增依赖必须保持 dist/sdk.js 零 heavy 依赖守卫通过。\n */\n\nimport type { Shape } from '../mesh/types'\nimport { getBackends } from '../runtime-state'\nimport { fromBrep } from '../shape'\nimport type { BrepEngineApi } from './engine/primitives'\n\n/** 结构化内核接口(不 import occt-wasm,只做结构匹配,避免 heavy 依赖) */\ninterface MeshableKernel {\n meshShape(\n handle: unknown,\n opts: { linearDeflection: number; angularDeflection: number },\n ): { positions: ArrayLike<number>; indices: ArrayLike<number> }\n}\n\n/**\n * Get the currently injected BREP kernel instance (untyped escape hatch).\n * @deprecated D12: this exit predates the engine-neutral L1 surface and its name\n * (\"OCCT kernel\") no longer matches what it returns — the current engine's raw\n * kernel, whatever engine that is. Prefer `getBrepApi()` (typed, engine-neutral,\n * same file) for neutral ops; platform-only code (L2 face) should use\n * `getOcctKernel()` (D3). Kept for compat-face bridging and existing tests.\n * @returns the current BREP engine's kernel instance.\n * @throws when the kernel is not ready (mesh mode or uninitialized) — never silently returns null.\n */\nexport function getKernel(): unknown {\n const kernel = getBackends().kernel.brep\n if (!kernel) {\n throw new Error('[faijs/bridge] OCCT kernel not available: BREP operations require an initialized kernel')\n }\n return kernel\n}\n\n/**\n * Get the current BREP engine as the **L1 core surface** (`BrepEngineApi`).\n *\n * 2026-09-24 narrowing plan(D12):中立 op 的取内核唯一出口——取代\n * `getBackends().kernel.brep as BrepEngineApi` 的逐点断言。本文件是 L1 契约\n * 的注册点(`registerBrepEngine` 注入的就是 `BrepEngineApi` 实例),此处断言\n * 诚实;平台面(L1 之外的 occt 独有能力)仍走 `getOcctKernel()`(D3)。\n *\n * @returns the current engine's L1 contract surface (`BrepEngineApi`) — typed, engine-neutral.\n * @throws when no BREP engine is registered (uninitialized) — never silently returns null.\n */\nexport function getBrepApi(): BrepEngineApi {\n const kernel = getBackends().kernel.brep\n if (!kernel) {\n throw new Error(\n '[faijs/bridge] BREP engine API not available: BREP operations require an initialized engine',\n )\n }\n return kernel as BrepEngineApi\n}\n\n/**\n * Options controlling how a handle is tessellated.\n */\nexport interface MeshHandleOptions {\n /** linear deflection (mm), default 0.1 — matches the built-in ops (src/brep/brep-ops.ts:27). */\n linearDeflection?: number\n /** angular segment count, default 32 — angularDeflection = 2π / segments. */\n segments?: number\n}\n\n/**\n * OCC 句柄身份契约判别(单一真源)。\n *\n * occt-wasm 的 `handle()` 工厂(occtWasm helpers.ts:25)构造的句柄对象\n * 统一携带 `__occtWasm: true`。本叶子是 faijs 侧唯一允许的判据:\n * define-op 的 `wrapBrepOne` 用它区分\"几何句柄\"与\"纯数据记录\",本文件\n * `fromHandle`/`meshHandle` 入口用它做形态断言。禁止在其他文件硬编码\n * `'__occtWasm' in v` 字符串直查——句柄/数据判定的任何新消费点都必须引用本叶子。\n * @param v - 待判别的值。\n * @returns `true` 当且仅当 `v` 是携带 `__occtWasm: true` 标记的句柄对象。\n */\nexport function isOcctHandle(v: unknown): boolean {\n return typeof v === 'object' && v !== null && (v as { __occtWasm?: unknown }).__occtWasm === true\n}\n\n/** 合法句柄输入:branded numeric id(occt-wasm 的 `ShapeHandle` 运行时形态)或\n * `__occtWasm` 标记对象(`handle()` 工厂产物)。 */\nfunction isHandleLike(v: unknown): boolean {\n return typeof v === 'number' || isOcctHandle(v)\n}\n\n/**\n * 入口形态断言:拒绝把纯数据记录当句柄三角化。\n * 纯数据记录(无 `__occtWasm` 标记的 plain object,如 sheetmetal part 数据)\n * 必须在 SDK 边界被显式拒绝(E_BAD_HANDLE),而不是在 kernel 深处以\n * `OcctError: meshShape: Invalid shape ID 0` 崩溃——define-op 的 wrapBrepOne\n * 已在第一道透传过滤,本断言是第二道防线(防未来未带标记的新句柄形态漏网)。\n */\nfunction requireHandle(handle: unknown, caller: string): void {\n if (!isHandleLike(handle)) {\n throw new Error(\n `[faijs/bridge] ${caller}: E_BAD_HANDLE: expected an OCCT handle (numeric id or ` +\n `__occtWasm-tagged object); got ${handle === null ? 'null' : typeof handle}. ` +\n `Plain data records must not be tessellated as handles.`,\n )\n }\n}\n\n/**\n * Tessellate an OCCT handle into a Shape (without registering a BREP slot).\n * @param handle - the OCCT solid handle to tessellate (numeric id or `__occtWasm`-tagged object).\n * @param opts - optional tessellation options.\n * @returns the tessellated Shape.\n */\nexport function meshHandle(handle: unknown, opts?: MeshHandleOptions): Shape {\n requireHandle(handle, 'meshHandle')\n const kernel = getKernel() as MeshableKernel\n const mesh = kernel.meshShape(handle, {\n linearDeflection: opts?.linearDeflection ?? 0.1,\n angularDeflection: (2 * Math.PI) / Math.max(3, opts?.segments ?? 32),\n })\n return {\n positions: new Float32Array(mesh.positions as ArrayLike<number>),\n indices: new Uint32Array(mesh.indices as ArrayLike<number>),\n }\n}\n\n/**\n * Default path (recommended): tessellate + register the BREP slot in one step.\n * Equivalent to meshHandle(h) + fromBrep(sh, { solid: h }).\n * Library authors only fall back to the meshHandle + fromBrep combination when\n * they need custom tessellation precision.\n * @param handle - the OCCT solid handle to bridge (numeric id or `__occtWasm`-tagged object).\n * @param opts - optional tessellation options.\n * @returns the registered Shape, as returned by fromBrep.\n */\nexport function fromHandle(handle: unknown, opts?: MeshHandleOptions): ReturnType<typeof fromBrep> {\n requireHandle(handle, 'fromHandle')\n return fromBrep(meshHandle(handle, opts), { solid: handle })\n}\n"]}
|
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
import type { BrepHandle } from '../engine/types.js';
|
|
26
26
|
import type { BrepEngineApi } from '../engine/primitives.js';
|
|
27
27
|
import { parseSvgNaturalSize } from '../../primitives/parse-svg-size.js';
|
|
28
|
+
import { extractSvgPaths } from '../../primitives/svg/svg-path.js';
|
|
28
29
|
export { parseSvgNaturalSize };
|
|
30
|
+
export { extractSvgPaths };
|
|
29
31
|
/** 3D 点(OCCT Vec3 格式) */
|
|
30
32
|
interface Pt {
|
|
31
33
|
x: number;
|
|
@@ -48,16 +50,6 @@ interface ClosedSubpath {
|
|
|
48
50
|
/** 对应的 OCCT wire */
|
|
49
51
|
wire: BrepHandle;
|
|
50
52
|
}
|
|
51
|
-
/**
|
|
52
|
-
* 从 SVG 字符串中提取所有图形元素的 path `d` 属性。
|
|
53
|
-
*
|
|
54
|
-
* 支持:<path>, <rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>。
|
|
55
|
-
* 使用正则提取,无 DOMParser 依赖(可在 Web Worker / Node 中运行)。
|
|
56
|
-
*
|
|
57
|
-
* @param svgString - the raw SVG markup to extract paths from.
|
|
58
|
-
* @returns an array of path `d` strings.
|
|
59
|
-
*/
|
|
60
|
-
export declare function extractSvgPaths(svgString: string): string[];
|
|
61
53
|
/**
|
|
62
54
|
* 解析 SVG path `d` 字符串,构建 OCCT wire 列表。
|
|
63
55
|
*
|
|
@@ -76,13 +68,7 @@ interface ContourGroup {
|
|
|
76
68
|
holes: BrepHandle[];
|
|
77
69
|
}
|
|
78
70
|
/**
|
|
79
|
-
* 将闭合子路径分类为外轮廓 +
|
|
80
|
-
*
|
|
81
|
-
* 算法:
|
|
82
|
-
* 1. 按有符号面积绝对值降序排(大轮廓优先)
|
|
83
|
-
* 2. 对每条子路径 A,找到直接包含它的最小外轮廓 B
|
|
84
|
-
* 3. A 是 B 的孔洞
|
|
85
|
-
* 4. 没有外轮廓的子路径是独立外轮廓
|
|
71
|
+
* 将闭合子路径分类为外轮廓 + 孔洞的分组(复用共享 classifyNested,支持多层嵌套岛)。
|
|
86
72
|
*
|
|
87
73
|
* @param subpaths - the closed subpaths to classify.
|
|
88
74
|
* @returns the contour groups of outer wires and their hole wires.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"svg-to-solid.d.ts","sourceRoot":"","sources":["../../../src/brep/svg/svg-to-solid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;
|
|
1
|
+
{"version":3,"file":"svg-to-solid.d.ts","sourceRoot":"","sources":["../../../src/brep/svg/svg-to-solid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,EACL,eAAe,EAMhB,MAAM,+BAA+B,CAAA;AAGtC,OAAO,EAAE,mBAAmB,EAAE,CAAA;AAG9B,OAAO,EAAE,eAAe,EAAE,CAAA;AAI1B,yBAAyB;AACzB,UAAU,EAAE;IAAG,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;AAEhD,sBAAsB;AACtB,UAAU,aAAa;IACrB,sBAAsB;IACtB,MAAM,EAAE,EAAE,EAAE,CAAA;IACZ,UAAU;IACV,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAChE,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,oBAAoB;IACpB,IAAI,EAAE,UAAU,CAAA;CACjB;AAwFD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,aAAa,EAAE,CA+TjB;AAOD,UAAU,YAAY;IACpB,eAAe;IACf,KAAK,EAAE,UAAU,CAAA;IACjB,iBAAiB;IACjB,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,YAAY,EAAE,CAMvE;AAID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IACP,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,GACA,UAAU,CAqGZ"}
|
|
@@ -24,180 +24,16 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { getSolidBoundingBox } from '../brep-utils.js';
|
|
26
26
|
import { parseSvgNaturalSize } from '../../primitives/parse-svg-size.js';
|
|
27
|
+
import { extractSvgPaths, tokenizeSvgPath, extractGroupTransform, classifyNested, computeBBox, computeSignedArea, } from '../../primitives/svg/svg-path.js';
|
|
27
28
|
export { parseSvgNaturalSize };
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* @returns an array of path `d` strings.
|
|
37
|
-
*/
|
|
38
|
-
export function extractSvgPaths(svgString) {
|
|
39
|
-
const paths = [];
|
|
40
|
-
// <path d="...">
|
|
41
|
-
extractPathDAttributes(svgString, paths);
|
|
42
|
-
// <rect>
|
|
43
|
-
extractRectPaths(svgString, paths);
|
|
44
|
-
// <circle>
|
|
45
|
-
extractCirclePaths(svgString, paths);
|
|
46
|
-
// <ellipse>
|
|
47
|
-
extractEllipsePaths(svgString, paths);
|
|
48
|
-
// <line>
|
|
49
|
-
extractLinePaths(svgString, paths);
|
|
50
|
-
// <polyline>
|
|
51
|
-
extractPolylinePaths(svgString, paths, false);
|
|
52
|
-
// <polygon>
|
|
53
|
-
extractPolylinePaths(svgString, paths, true);
|
|
54
|
-
return paths;
|
|
55
|
-
}
|
|
56
|
-
function extractPathDAttributes(svg, out) {
|
|
57
|
-
const re = /<path\b[^>]*\bd\s*=\s*(?:"([^"]*)"|'([^']*)')[^>]*>/gi;
|
|
58
|
-
let m;
|
|
59
|
-
while ((m = re.exec(svg)) !== null) {
|
|
60
|
-
const d = m[1] ?? m[2];
|
|
61
|
-
if (d)
|
|
62
|
-
out.push(d);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function extractRectPaths(svg, out) {
|
|
66
|
-
const re = /<rect\b([^>]*)\/?>/gi;
|
|
67
|
-
let m;
|
|
68
|
-
while ((m = re.exec(svg)) !== null) {
|
|
69
|
-
const attrs = parseAttrs(m[1] ?? '');
|
|
70
|
-
const x = parseFloat(attrs.x ?? '0');
|
|
71
|
-
const y = parseFloat(attrs.y ?? '0');
|
|
72
|
-
const w = parseFloat(attrs.width ?? '0');
|
|
73
|
-
const h = parseFloat(attrs.height ?? '0');
|
|
74
|
-
const rx = parseFloat(attrs.rx ?? '0');
|
|
75
|
-
const ry = parseFloat(attrs.ry ?? '0');
|
|
76
|
-
if (w <= 0 || h <= 0)
|
|
77
|
-
continue;
|
|
78
|
-
// 如果 rect 有 transform 属性,将变换应用到四个角点后输出为 path d
|
|
79
|
-
const rectTransform = attrs.transform ? parseSvgTransform(attrs.transform) : undefined;
|
|
80
|
-
if (rectTransform) {
|
|
81
|
-
const corners = [
|
|
82
|
-
[x, y], [x + w, y], [x + w, y + h], [x, y + h],
|
|
83
|
-
];
|
|
84
|
-
const tc = corners.map(([cx, cy]) => [
|
|
85
|
-
rectTransform[0] * cx + rectTransform[2] * cy + rectTransform[4],
|
|
86
|
-
rectTransform[1] * cx + rectTransform[3] * cy + rectTransform[5],
|
|
87
|
-
]);
|
|
88
|
-
let d = `M ${tc[0][0]},${tc[0][1]}`;
|
|
89
|
-
for (let i = 1; i < tc.length; i++)
|
|
90
|
-
d += ` L ${tc[i][0]},${tc[i][1]}`;
|
|
91
|
-
out.push(d + ' Z');
|
|
92
|
-
}
|
|
93
|
-
else if (rx > 0 || ry > 0) {
|
|
94
|
-
const r = Math.min(rx || ry, w / 2, h / 2);
|
|
95
|
-
out.push(rectToPathRounded(x, y, w, h, r));
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
out.push(`M ${x},${y} h ${w} v ${h} h ${-w} Z`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function rectToPathRounded(x, y, w, h, r) {
|
|
103
|
-
return [
|
|
104
|
-
`M ${x + r},${y}`,
|
|
105
|
-
`h ${w - 2 * r}`,
|
|
106
|
-
`A ${r},${r} 0 0 1 ${x + w},${y + r}`,
|
|
107
|
-
`v ${h - 2 * r}`,
|
|
108
|
-
`A ${r},${r} 0 0 1 ${x + w - r},${y + h}`,
|
|
109
|
-
`h ${-(w - 2 * r)}`,
|
|
110
|
-
`A ${r},${r} 0 0 1 ${x},${y + h - r}`,
|
|
111
|
-
`v ${-(h - 2 * r)}`,
|
|
112
|
-
`A ${r},${r} 0 0 1 ${x + r},${y}`,
|
|
113
|
-
'Z',
|
|
114
|
-
].join(' ');
|
|
115
|
-
}
|
|
116
|
-
function extractCirclePaths(svg, out) {
|
|
117
|
-
const re = /<circle\b([^>]*)\/?>/gi;
|
|
118
|
-
let m;
|
|
119
|
-
while ((m = re.exec(svg)) !== null) {
|
|
120
|
-
const attrs = parseAttrs(m[1] ?? '');
|
|
121
|
-
const cx = parseFloat(attrs.cx ?? '0');
|
|
122
|
-
const cy = parseFloat(attrs.cy ?? '0');
|
|
123
|
-
const r = parseFloat(attrs.r ?? '0');
|
|
124
|
-
if (r <= 0)
|
|
125
|
-
continue;
|
|
126
|
-
// 两个半圆弧组成完整圆
|
|
127
|
-
out.push(`M ${cx - r},${cy} A ${r},${r} 0 1 0 ${cx + r},${cy} A ${r},${r} 0 1 0 ${cx - r},${cy} Z`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function extractEllipsePaths(svg, out) {
|
|
131
|
-
const re = /<ellipse\b([^>]*)\/?>/gi;
|
|
132
|
-
let m;
|
|
133
|
-
while ((m = re.exec(svg)) !== null) {
|
|
134
|
-
const attrs = parseAttrs(m[1] ?? '');
|
|
135
|
-
const cx = parseFloat(attrs.cx ?? '0');
|
|
136
|
-
const cy = parseFloat(attrs.cy ?? '0');
|
|
137
|
-
const rx = parseFloat(attrs.rx ?? '0');
|
|
138
|
-
const ry = parseFloat(attrs.ry ?? '0');
|
|
139
|
-
if (rx <= 0 || ry <= 0)
|
|
140
|
-
continue;
|
|
141
|
-
out.push(`M ${cx - rx},${cy} A ${rx},${ry} 0 1 0 ${cx + rx},${cy} A ${rx},${ry} 0 1 0 ${cx - rx},${cy} Z`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
function extractLinePaths(svg, out) {
|
|
145
|
-
const re = /<line\b([^>]*)\/?>/gi;
|
|
146
|
-
let m;
|
|
147
|
-
while ((m = re.exec(svg)) !== null) {
|
|
148
|
-
const attrs = parseAttrs(m[1] ?? '');
|
|
149
|
-
const x1 = parseFloat(attrs.x1 ?? '0');
|
|
150
|
-
const y1 = parseFloat(attrs.y1 ?? '0');
|
|
151
|
-
const x2 = parseFloat(attrs.x2 ?? '0');
|
|
152
|
-
const y2 = parseFloat(attrs.y2 ?? '0');
|
|
153
|
-
out.push(`M ${x1},${y1} L ${x2},${y2}`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function extractPolylinePaths(svg, out, closed) {
|
|
157
|
-
const tag = closed ? 'polygon' : 'polyline';
|
|
158
|
-
const re = new RegExp(`<${tag}\\b([^>]*)\\/??>`, 'gi');
|
|
159
|
-
let m;
|
|
160
|
-
while ((m = re.exec(svg)) !== null) {
|
|
161
|
-
const attrs = parseAttrs(m[1] ?? '');
|
|
162
|
-
const pointsStr = attrs.points ?? '';
|
|
163
|
-
const nums = pointsStr.split(/[\s,]+/).filter(s => s.length > 0).map(Number);
|
|
164
|
-
if (nums.length < 4)
|
|
165
|
-
continue;
|
|
166
|
-
let d = `M ${nums[0]},${nums[1]}`;
|
|
167
|
-
for (let i = 2; i + 1 < nums.length; i += 2) {
|
|
168
|
-
d += ` L ${nums[i]},${nums[i + 1]}`;
|
|
169
|
-
}
|
|
170
|
-
if (closed)
|
|
171
|
-
d += ' Z';
|
|
172
|
-
out.push(d);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
function parseAttrs(attrStr) {
|
|
176
|
-
const attrs = {};
|
|
177
|
-
const re = /(\w[\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
178
|
-
let m;
|
|
179
|
-
while ((m = re.exec(attrStr)) !== null) {
|
|
180
|
-
attrs[m[1].toLowerCase()] = m[2] ?? m[3] ?? '';
|
|
181
|
-
}
|
|
182
|
-
return attrs;
|
|
183
|
-
}
|
|
184
|
-
/** 解析 SVG transform 字符串(仅支持 matrix 形式) */
|
|
185
|
-
function parseSvgTransform(transformStr) {
|
|
186
|
-
const m = transformStr.match(/matrix\s*\(\s*([-\d.eE+]+)\s*,\s*([-\d.eE+]+)\s*,\s*([-\d.eE+]+)\s*,\s*([-\d.eE+]+)\s*,\s*([-\d.eE+]+)\s*,\s*([-\d.eE+]+)\s*\)/);
|
|
187
|
-
if (m) {
|
|
188
|
-
return [parseFloat(m[1]), parseFloat(m[2]), parseFloat(m[3]), parseFloat(m[4]), parseFloat(m[5]), parseFloat(m[6])];
|
|
189
|
-
}
|
|
190
|
-
return undefined;
|
|
191
|
-
}
|
|
192
|
-
/** 从 SVG 字符串中提取 <g transform="matrix(...)"> 的变换矩阵 */
|
|
193
|
-
function extractGroupTransform(svgString) {
|
|
194
|
-
const m = svgString.match(/<g\b[^>]*\btransform\s*=\s*(?:"([^"]*)"|'([^']*)')[^>]*>/i);
|
|
195
|
-
if (m) {
|
|
196
|
-
return parseSvgTransform(m[1] ?? m[2] ?? '');
|
|
197
|
-
}
|
|
198
|
-
return undefined;
|
|
199
|
-
}
|
|
200
|
-
/** 将 SVG 2D 变换转为 OCCT 3x4 row-major 仿射矩阵(考虑 Y 翻转) */
|
|
29
|
+
// 兼容导出:语法层共享实现(extractSvgPaths 等的历史入口仍在 brep 面可用)
|
|
30
|
+
export { extractSvgPaths };
|
|
31
|
+
// ─── SVG 元素提取 / transform / tokenizer ───
|
|
32
|
+
// 与 mesh 链共享同一实现(primitives/svg/svg-path.ts):
|
|
33
|
+
// extractSvgPaths / parseSvgTransform / extractGroupTransform / tokenizeSvgPath。
|
|
34
|
+
// brep 链保留精确几何语义(Bezier→OCCT 曲线、Arc→三点圆弧),
|
|
35
|
+
// 语法层(元素提取、矩阵解析、tokenizer)不重复实现。
|
|
36
|
+
// ─── SVG transform 处理(OCCT 矩阵适配,brep 链特有) ───
|
|
201
37
|
function svgTransformToOcctMatrix(gt) {
|
|
202
38
|
// SVG: x' = a*x + c*y + e, y' = b*x + d*y + f
|
|
203
39
|
// OCCT 点的 Y 已翻转 (y_occt = -y_svg),所以:
|
|
@@ -220,25 +56,6 @@ function applySvgTransformToOcctPoint(p, gt) {
|
|
|
220
56
|
return { x: tx, y: -ty, z: 0 };
|
|
221
57
|
}
|
|
222
58
|
// ─── SVG path tokenizer ───
|
|
223
|
-
function tokenizeSVGPath(d) {
|
|
224
|
-
const tokens = [];
|
|
225
|
-
const re = /([MmLlHhVvCcSsQqTtAaZz])([^MmLlHhVvCcSsQqTtAaZz]*)/g;
|
|
226
|
-
let match;
|
|
227
|
-
while ((match = re.exec(d)) !== null) {
|
|
228
|
-
const command = match[1] ?? '';
|
|
229
|
-
const argStr = (match[2] ?? '').trim();
|
|
230
|
-
const args = [];
|
|
231
|
-
if (argStr) {
|
|
232
|
-
const numRe = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;
|
|
233
|
-
let numMatch;
|
|
234
|
-
while ((numMatch = numRe.exec(argStr)) !== null) {
|
|
235
|
-
args.push(parseFloat(numMatch[0]));
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
tokens.push({ command, args });
|
|
239
|
-
}
|
|
240
|
-
return tokens;
|
|
241
|
-
}
|
|
242
59
|
// ─── SVG path → OCCT edges ───
|
|
243
60
|
/** Y 轴翻转:SVG Y-down → OCCT Y-up */
|
|
244
61
|
function flipY(x, y) {
|
|
@@ -285,7 +102,7 @@ function arcMidPoint(fromX, fromY, toX, toY, rx, ry, largeArc, sweepFlag) {
|
|
|
285
102
|
* @returns 闭合子路径列表(含 wire + 几何信息)
|
|
286
103
|
*/
|
|
287
104
|
export function parseSVGPathToWires(kernel, pathD) {
|
|
288
|
-
const tokens =
|
|
105
|
+
const tokens = tokenizeSvgPath(pathD);
|
|
289
106
|
const subpaths = [];
|
|
290
107
|
const cursor = {
|
|
291
108
|
cx: 0, cy: 0, sx: 0, sy: 0,
|
|
@@ -598,136 +415,18 @@ export function parseSVGPathToWires(kernel, pathD) {
|
|
|
598
415
|
}
|
|
599
416
|
return subpaths;
|
|
600
417
|
}
|
|
601
|
-
// ─── 几何工具 ───
|
|
602
|
-
function computeBBox(points) {
|
|
603
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
604
|
-
for (const p of points) {
|
|
605
|
-
if (p.x < minX)
|
|
606
|
-
minX = p.x;
|
|
607
|
-
if (p.y < minY)
|
|
608
|
-
minY = p.y;
|
|
609
|
-
if (p.x > maxX)
|
|
610
|
-
maxX = p.x;
|
|
611
|
-
if (p.y > maxY)
|
|
612
|
-
maxY = p.y;
|
|
613
|
-
}
|
|
614
|
-
return { minX, minY, maxX, maxY };
|
|
615
|
-
}
|
|
616
|
-
/** Shoelace 公式计算有符号面积(正=CCW,负=CW) */
|
|
617
|
-
function computeSignedArea(points) {
|
|
618
|
-
let area = 0;
|
|
619
|
-
const n = points.length;
|
|
620
|
-
if (n < 3)
|
|
621
|
-
return 0;
|
|
622
|
-
for (let i = 0; i < n; i++) {
|
|
623
|
-
const j = (i + 1) % n;
|
|
624
|
-
area += points[i].x * points[j].y;
|
|
625
|
-
area -= points[j].x * points[i].y;
|
|
626
|
-
}
|
|
627
|
-
return area / 2;
|
|
628
|
-
}
|
|
629
|
-
/** 点在多边形内(ray casting) */
|
|
630
|
-
function pointInPolygon(pt, polygon) {
|
|
631
|
-
const n = polygon.length;
|
|
632
|
-
if (n < 3)
|
|
633
|
-
return false;
|
|
634
|
-
let inside = false;
|
|
635
|
-
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
636
|
-
const xi = polygon[i].x, yi = polygon[i].y;
|
|
637
|
-
const xj = polygon[j].x, yj = polygon[j].y;
|
|
638
|
-
const intersect = ((yi > pt.y) !== (yj > pt.y)) &&
|
|
639
|
-
(pt.x < (xj - xi) * (pt.y - yi) / (yj - yi + 1e-12) + xi);
|
|
640
|
-
if (intersect)
|
|
641
|
-
inside = !inside;
|
|
642
|
-
}
|
|
643
|
-
return inside;
|
|
644
|
-
}
|
|
645
|
-
/** 计算多边形质心 */
|
|
646
|
-
function polygonCentroid(points) {
|
|
647
|
-
let cx = 0, cy = 0;
|
|
648
|
-
for (const p of points) {
|
|
649
|
-
cx += p.x;
|
|
650
|
-
cy += p.y;
|
|
651
|
-
}
|
|
652
|
-
return { x: cx / points.length, y: cy / points.length, z: 0 };
|
|
653
|
-
}
|
|
654
|
-
function bboxContains(outer, inner) {
|
|
655
|
-
return inner.minX >= outer.minX && inner.maxX <= outer.maxX &&
|
|
656
|
-
inner.minY >= outer.minY && inner.maxY <= outer.maxY;
|
|
657
|
-
}
|
|
658
418
|
/**
|
|
659
|
-
* 将闭合子路径分类为外轮廓 +
|
|
660
|
-
*
|
|
661
|
-
* 算法:
|
|
662
|
-
* 1. 按有符号面积绝对值降序排(大轮廓优先)
|
|
663
|
-
* 2. 对每条子路径 A,找到直接包含它的最小外轮廓 B
|
|
664
|
-
* 3. A 是 B 的孔洞
|
|
665
|
-
* 4. 没有外轮廓的子路径是独立外轮廓
|
|
419
|
+
* 将闭合子路径分类为外轮廓 + 孔洞的分组(复用共享 classifyNested,支持多层嵌套岛)。
|
|
666
420
|
*
|
|
667
421
|
* @param subpaths - the closed subpaths to classify.
|
|
668
422
|
* @returns the contour groups of outer wires and their hole wires.
|
|
669
423
|
*/
|
|
670
424
|
export function classifyHoles(subpaths) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
let bestParent = -1;
|
|
677
|
-
let bestParentArea = Infinity;
|
|
678
|
-
const centroid = polygonCentroid(sorted[i].points);
|
|
679
|
-
for (let j = 0; j < sorted.length; j++) {
|
|
680
|
-
if (i === j)
|
|
681
|
-
continue;
|
|
682
|
-
// j 的面积必须大于 i
|
|
683
|
-
if (Math.abs(sorted[j].signedArea) <= Math.abs(sorted[i].signedArea))
|
|
684
|
-
continue;
|
|
685
|
-
// j 的 bbox 必须包含 i 的 bbox
|
|
686
|
-
if (!bboxContains(sorted[j].bbox, sorted[i].bbox))
|
|
687
|
-
continue;
|
|
688
|
-
// i 的质心必须在 j 的多边形内
|
|
689
|
-
if (!pointInPolygon(centroid, sorted[j].points))
|
|
690
|
-
continue;
|
|
691
|
-
// 选面积最小的外轮廓(直接包含者)
|
|
692
|
-
if (Math.abs(sorted[j].signedArea) < bestParentArea) {
|
|
693
|
-
bestParentArea = Math.abs(sorted[j].signedArea);
|
|
694
|
-
bestParent = j;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
parentOf[i] = bestParent;
|
|
698
|
-
}
|
|
699
|
-
// 构建分组
|
|
700
|
-
const groups = [];
|
|
701
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
702
|
-
if (parentOf[i] === -1) {
|
|
703
|
-
// 外轮廓
|
|
704
|
-
groups.push({ outer: sorted[i].wire, holes: [] });
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
// 为每个孔洞找到对应的外轮廓
|
|
708
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
709
|
-
if (parentOf[i] !== -1) {
|
|
710
|
-
// 找到 groups 中对应的外轮廓
|
|
711
|
-
const parentIdx = parentOf[i];
|
|
712
|
-
// parentIdx 是 sorted 中的索引,需要找到对应的 group
|
|
713
|
-
// 外轮廓在 sorted 中的索引 → groups 中的索引
|
|
714
|
-
let groupIdx = -1;
|
|
715
|
-
let outerCount = 0;
|
|
716
|
-
for (let k = 0; k < sorted.length; k++) {
|
|
717
|
-
if (parentOf[k] === -1) {
|
|
718
|
-
if (k === parentIdx) {
|
|
719
|
-
groupIdx = outerCount;
|
|
720
|
-
break;
|
|
721
|
-
}
|
|
722
|
-
outerCount++;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
if (groupIdx >= 0 && groupIdx < groups.length) {
|
|
726
|
-
groups[groupIdx].holes.push(sorted[i].wire);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
return groups;
|
|
425
|
+
const groups = classifyNested(subpaths);
|
|
426
|
+
return groups.map((g) => ({
|
|
427
|
+
outer: subpaths[g.outerIdx].wire,
|
|
428
|
+
holes: g.holes.map((h) => subpaths[h].wire),
|
|
429
|
+
}));
|
|
731
430
|
}
|
|
732
431
|
// ─── SVG → Solid ───
|
|
733
432
|
/**
|