@faicad/faijs 0.32.2 → 0.33.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/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.map +1 -1
- package/dist/node-host/cli.js +3 -2
- 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 +22 -3
package/dist/api/draft.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/api/draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AAoB3D,qCAAqC;AACrC,SAAS,SAAS,CAAC,KAAY,EAAE,MAAmB;IAClD,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAK,EAAkB,CAAA;IACzD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;IACvF,CAAC;IACD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACjF,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAA;IAC1F,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;IACrD,IAAI,
|
|
1
|
+
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/api/draft.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api draft — 拔模:对选定面施加拔模斜度(手写**平台** op,Phase 5)\n *\n * `engines: ['occt']` —— 但**不是平台依赖**(实现只经 L1 契约面 `getBrepApi()`,\n * 见下选面口径),而是**实证收窄**:brepkit 的 L1 `draft` 实测产出错误几何,\n * 必须让静态门在执行前拒绝(红线:BREP 链可用性执行前静态判定,不允许\n * 「门说能、内核说不能」;也不允许静默产出错几何)。\n *\n * 实证(同脚本、box(20,20,10)、angleDeg=3,两引擎同一次运行):\n * - occt:4 个侧面(faceRef ordinal 1–4)**都给一致 delta = −52.4078**\n * —— 对称输入 ⇒ 对称输出,几何自洽;⊥ pull 的端面由内核报 KERNEL_ERROR。\n * - brepkit:ordinal 1–5 给 +60.29 / 0 / 0 / +17.47 / 0 —— **对称性被破坏**\n * (对称 box 上对单个侧面拔模,四个侧面不可能给出不同结果),其中三个是\n * **静默无操作**(体积与 bbox 均不变),而 ⊥ pull 的端面反而把 x/y 凸包各撑大\n * 0.524(= 10·tan 3°)。序号错配不能解释:破坏对称性与序号无关。\n *\n * 选面口径(设计原则 4):`faces: FaceTopoRef[]`(`cad.faceRef` 产物)。\n * `neutral` 用 `{point, normal}` 平面参数——不给 face 引用(方案 §7 建议 2)。\n *\n * 回归守卫:`api/feature-family.test.ts` 的「brepkit 下 draft 静态拒绝」用例。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { BrepHandle, BrepVec3 } from '../brep/engine/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { brepOf, fromBrep } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { FaceTopoRef } from '../topology/naming'\nimport { resolveTopoRef, TopoRefError } from '../topology/naming'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { buildEdgeResolutionContext } from './topo-resolve'\n\ntype Vec3 = [number, number, number]\n\n/** `cad.draft` 参数。 */\nexport interface DraftParams {\n /** 拔模面(FaceTopoRef[],`cad.faceRef` 产物)。 */\n faces: FaceTopoRef[]\n /** 拔模角(度)。 */\n angleDeg: number\n /** 拔模方向(默认 +Z)。 */\n pull?: Vec3\n /**\n * 中性平面:**只有点的语义**(内核把该点所在高度视为 0° 基准;省略取原点)。\n * `normal` 无任何内核消费者(occt-wasm 原生 `draft` 无 neutral 形参,brepkit\n * 只收点),传了会显式报 `E_DRAFT_NEUTRAL_NORMAL_UNUSED`。\n */\n neutral?: { point: Vec3; normal?: Vec3 }\n}\n\n/** BREP 路径:解析面引用 → L1 draft → 收养。 */\nfunction draftBrep(input: Shape, params: DraftParams): Shape {\n const { faces, angleDeg } = params ?? ({} as DraftParams)\n if (!Array.isArray(faces) || faces.length === 0) {\n throw new Error('E_DRAFT_NO_FACES: draft requires at least one FaceTopoRef in faces')\n }\n if (typeof angleDeg !== 'number' || !Number.isFinite(angleDeg) || angleDeg === 0) {\n throw new Error('E_DRAFT_BAD_ANGLE: draft.angleDeg must be a non-zero number (degrees)')\n }\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (solid === undefined) throw new Error('E_DRAFT_NO_BREP: draft input is not BREP')\n\n const ctx = buildEdgeResolutionContext(kernel, input as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'draft: input has no BREP naming context')\n const faceHandles = faces.map((ref) => {\n if (!ref || (ref as { kind?: unknown }).kind !== 'face') {\n throw new Error('E_DRAFT_BAD_FACE_REF: every faces entry must be a FaceTopoRef (cad.faceRef)')\n }\n const r = resolveTopoRef(ref, ctx)\n if (r.handle === undefined) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `draft: face resolved without handle (${ref.origin}/${ref.role})`)\n }\n return r.handle as BrepHandle\n })\n\n // GOTCHA-1(Phase 5 实测):L1 的向量形参是 `BrepVec3 = {x,y,z}` **对象**,不是元组。\n // 传 `[0,0,1]` 会被内核读成零向量 ⇒ occt 抛空文案 KERNEL_ERROR、brepkit 抛\n // \"cannot normalize zero vector\",两边都不可用。必须显式转对象(同 pattern.ts:41)。\n const [px, py, pz] = params.pull ?? [0, 0, 1]\n const pullVec: BrepVec3 = { x: px, y: py, z: pz }\n\n // 中性面缺省:中性点取原点(内核语义为「该点处角度为 0」)。\n const nPoint = params.neutral?.point\n const neutralVec: BrepVec3 = nPoint\n ? { x: nPoint[0], y: nPoint[1], z: nPoint[2] }\n : { x: 0, y: 0, z: 0 }\n\n // GOTCHA-2(Phase 5 实测):L1 draft 的 neutral 只有**点**语义(occt-wasm 原生\n // `draft(shape, face, angleRad, direction)` 根本没有 neutral 形参,brepkit 也只收\n // 点:brepkitKernel.ts:669-676)。故 `neutral.normal` 无任何消费者——按项目约定\n // (occt-primitives.ts interpolatePoints「拒绝而非静默产出错几何」)显式报错,\n // 不静默丢弃。\n if (params.neutral?.normal) {\n throw new Error(\n 'E_DRAFT_NEUTRAL_NORMAL_UNUSED: L1 draft takes a neutral POINT only (no plane normal) — ' +\n 'drop neutral.normal, or model the plane via pull + a point on it',\n )\n }\n\n const result = kernel.draft(solid, faceHandles, pullVec, neutralVec, angleDeg)\n return fromBrep(solidToShape(kernel, result), { solid: result })\n}\n\n/**\n * 拔模:对选定面施加拔模斜度(铸造/注塑出模角)。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name draft\n * @note **occt-only**(`engines: ['occt']`;实证收窄,非平台依赖——见文件头实证:\n * brepkit 破坏对称性且部分 ordinal 静默无操作)。`neutral`(中性点)**只支持\n * 原点**:occt-wasm 原生 `draft(shape, face, angleRad, direction)` 没有 neutral\n * 形参,传非原点中性点会显式报错(不静默产出错几何);brepkit 已被静态拒绝,\n * 故非原点 `neutral` 当前**没有任何可用引擎**——需要该语义时请改用\n * `pull` + 面上一点建模。仅 BREP 可用(mesh 输入执行前报错)。\n * @returns Shape 拔模后的几何。\n * @param input - 目标几何。type:Shape required:true\n * @param params.faces - 拔模面(FaceTopoRef[],cad.faceRef 产物)。type:FaceTopoRef[] required:true\n * @param params.angleDeg - 拔模角(度,≠0)。type:number required:true\n * @param params.pull - 拔模方向(默认 +Z)。type:Vec3 required:false\n * @param params.neutral - 中性面**点**({point};normal 无消费者)。type:{point:Vec3,normal?:Vec3} required:false\n * @example\n * const d = await cad.draft(part0, { faces: [cad.faceRef(part0, 3)], angleDeg: 3 })\n */\nexport const draft = defineOp({\n // 实证收窄(2026-09-24):brepkit 的 L1 draft 产出错误几何(破坏对称性 / 静默无操作),\n // 声明 occt 白名单让静态门在执行前拒绝(引擎身份是 op 侧唯一的收窄轴)。\n engines: ['occt'],\n brep(input: Shape, params: DraftParams) {\n return draftBrep(input, params)\n },\n naming: { kind: 'kernel', newFaces: { via: 'byAdjacency' } } as Provenance,\n\n paramDims: {},\n})\n"]}
|
package/dist/api/edge-ref.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* 绝不静默拿序号硬取。
|
|
22
22
|
*/
|
|
23
23
|
import { getBrepApi } from '../brep/handle-bridge.js';
|
|
24
|
-
import {
|
|
24
|
+
import { hasBrep } from '../shape.js';
|
|
25
25
|
import { nameOf } from '../runtime-state.js';
|
|
26
26
|
import { captureEdgeHint, findOriginRole, TopoRefError } from '../topology/naming/index.js';
|
|
27
27
|
import { buildEdgeResolutionContext, recoverEdgeFaceRole } from './topo-resolve.js';
|
|
@@ -51,7 +51,7 @@ export function edgeRef(of, edgeOrdinal) {
|
|
|
51
51
|
if (!kernel) {
|
|
52
52
|
throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'edge', 'edgeRef: topology references are BREP-only (mesh path unsupported)');
|
|
53
53
|
}
|
|
54
|
-
if (!
|
|
54
|
+
if (!hasBrep(of)) {
|
|
55
55
|
throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'edge', 'edgeRef: input shape has no BREP solid (mesh shapes do not carry topology identity)');
|
|
56
56
|
}
|
|
57
57
|
const ctx = buildEdgeResolutionContext(kernel, of);
|
package/dist/api/edge-ref.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge-ref.js","sourceRoot":"","sources":["../../src/api/edge-ref.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"edge-ref.js","sourceRoot":"","sources":["../../src/api/edge-ref.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api edge-ref — 边引用查询:把「边序号」解析成 `EdgeTopoRef`(1 起)\n *\n * fillet / chamfer 的选边参数是 `EdgeTopoRef`(相邻两面的 role 对,见\n * `topology/naming`);而外部格式(FCStd / STEP 编辑器 / 上游特征文件)通常只给\n * 「第 N 条边」。本模块是两者之间的桥:在内核现场枚举里取第 N 条边,再由输入\n * Shape 的命名槽(RoleTable + subShapeHashes)反查它两邻面的 `{origin, role}`,\n * 合成可直接喂给 `cad.fillet` / `cad.chamfer` 的 `EdgeTopoRef`。\n *\n * 序号语义(GOTCHA):faijs 的 `getSubShapes(solid,'edge')` 与 `wireframe()` 同用\n * `TopExp::MapShapes` + `NCollection_IndexedMap`(`occt-kernel/topologyExt.ts` 已\n * 记档),故第 N 条边 == FreeCAD 的 `EdgeN`(fcstd-port/test/FreeCAD 的\n * external-geo corpus 测试实测 `wireframe.edgeGroups[k] == FreeCAD \"Edge(k+1)\"`;\n * 2026-09-20 随语料依赖测试迁至 fcstd-port)。这是 FCStd 移植选边的锚点。\n *\n * 与 `faceNormal`(`api/geom.ts`)的差异:`faceNormal` 的 `ordinal` 是 0 起\n * (直接下标 `faces[ordinal]`),而本函数是 1 起——与命名层 `TopoRef` 序号\n * (`ordinal` 1 起,见 `topology/naming/types.ts`)及 FreeCAD `EdgeN` 一致。\n *\n * 纯查询:同步、无副作用、不改 Shape;定不了案一律抛 `TopoRefError`(三态错误码),\n * 绝不静默拿序号硬取。\n */\n\nimport type { Shape } from '../mesh/types'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { hasBrep } from '../shape'\nimport { nameOf } from '../runtime-state'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { captureEdgeHint, findOriginRole, TopoRefError } from '../topology/naming'\nimport type { EdgeTopoRef, RoleQualifier } from '../topology/naming'\nimport { buildEdgeResolutionContext, recoverEdgeFaceRole } from './topo-resolve'\n\n/**\n * 查询几何体第 N 条边的 `EdgeTopoRef`,供 `cad.fillet` / `cad.chamfer` 的 `edges` 使用:\n * `cad.fillet(base, { edges: [cad.edgeRef(base, 17)], radius: 2 })`。\n *\n * 定不了案(无 BREP / 序号越界 / 邻面不足两面 / 邻面无 role 血统)抛 `TopoRefError`。\n * @group 查询\n * @inputs 1\n * @async false\n * @qual ok\n * @name edgeRef\n * @returns EdgeTopoRef 该边的拓扑引用(相邻两面 role 对 + length/midpoint hint)。\n * @param of - 目标几何(BREP 实体所在的 Shape)。type:Shape required:true\n * @param edgeOrdinal - 边序号(1 起;等于 FreeCAD 的 `EdgeN`)。type:number required:true\n * @note 序号 1 起,与命名层 `TopoRef.ordinal` 及 FreeCAD `EdgeN` 同序(`getSubShapes(solid,'edge')` 用 TopExp::MapShapes + IndexedMap 枚举)。\n * @example\n * const part1 = cad.fillet(part0, { edges: [cad.edgeRef(part0, 1)], radius: 2 })\n */\nexport function edgeRef(of: Shape, edgeOrdinal: number): EdgeTopoRef {\n if (!Number.isInteger(edgeOrdinal) || edgeOrdinal < 1) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'edge', `edgeRef: edgeOrdinal must be an integer >= 1 (got ${edgeOrdinal})`)\n }\n const kernel = getBrepApi()\n // Phase 1.9(D6/§3):mesh 路径不支持拓扑引用——静态判定、显式抛错,不静默降级\n if (!kernel) {\n throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'edge', 'edgeRef: topology references are BREP-only (mesh path unsupported)')\n }\n if (!hasBrep(of)) {\n throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'edge', 'edgeRef: input shape has no BREP solid (mesh shapes do not carry topology identity)')\n }\n\n const ctx = buildEdgeResolutionContext(kernel, of as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'edge', 'edgeRef: input has no BREP naming context')\n\n const edge = ctx.edges?.find((e) => e.ordinal === edgeOrdinal)\n if (!edge || edge.handle === undefined) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `edgeRef: edge ordinal ${edgeOrdinal} out of range [1, ${ctx.edges?.length ?? 0}]`,\n )\n }\n const handle = edge.handle\n\n // 该边的相邻面(现场:逐面枚举其边,与 handle 同体者即邻面)——不依赖\n // ctx.edgeFaceAdjacency 的数组下标对齐(那里按 liveEdges 过滤后建表)。\n const faceOrdinals: number[] = []\n for (const f of ctx.faces) {\n if (f.handle === undefined) continue\n if (kernel.getSubShapes(f.handle, 'edge').some((eh) => kernel.isSame(eh, handle))) {\n faceOrdinals.push(f.ordinal)\n }\n }\n if (faceOrdinals.length < 2) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `edgeRef: edge ${edgeOrdinal} has ${faceOrdinals.length} adjacent face(s); an EdgeTopoRef needs a two-face pair`,\n )\n }\n\n const table = ctx.roleTable\n if (!table) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'edge', 'edgeRef: input shape has no role table (nameless shape)')\n }\n const ordinalToHash = ctx.faces.map((f) => f.hash ?? 0)\n const quals: RoleQualifier[] = []\n for (const fo of faceOrdinals.slice(0, 2)) {\n let found = findOriginRole(table, ordinalToHash, fo)\n if (!found) {\n // 1.10 前置③ 推广到边路径:目标 part 的 roleTable 缓存残缺,但根节点(名字\n // 诞生处)的表完整、血缘可回走 → 遍历候选 (origin,role) 重算 hash 集找回身份。\n const part = nameOf(of as object)\n if (part) found = recoverEdgeFaceRole(kernel, part, fo, ordinalToHash)\n }\n if (!found) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'edge', `edgeRef: adjacent face ordinal ${fo} has no role lineage`)\n }\n quals.push({ origin: found.origin, role: found.role })\n }\n\n const [qa, qb] = quals as [RoleQualifier, RoleQualifier]\n return { kind: 'edge', faces: [qa, qb], hint: captureEdgeHint(kernel, handle as BrepHandle) }\n}\n"]}
|
package/dist/api/engrave.js
CHANGED
|
@@ -72,7 +72,7 @@ function centerSolidAtOrigin(kernel, solid) {
|
|
|
72
72
|
async function engraveBrepPath(input, params, svgText) {
|
|
73
73
|
const kernel = getBrepApi();
|
|
74
74
|
const inputSolid = brepOf(input);
|
|
75
|
-
if (
|
|
75
|
+
if (inputSolid === undefined)
|
|
76
76
|
throw new Error('[api/engrave] input is not BREP');
|
|
77
77
|
const depth = params.depth;
|
|
78
78
|
const mode = params.mode ?? 'concave';
|
package/dist/api/engrave.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engrave.js","sourceRoot":"","sources":["../../src/api/engrave.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api engrave — 雕刻库函数\n *\n *\n * dispatchPath 静态判定 brep/mesh。\n * BREP 路径:textToSolid/svgToSolid + boolean(cut/fuse)。\n */\n\nimport type { Shape, Vec3 } from '../mesh/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { AssetResolver } from '../cad-runtime/ports'\n// B3 correction (2026-10-06): no library-face cad aggregate — import the impl from its own module.\nimport * as meshEngrave from '../mesh/engrave'\nimport { parseSvgNaturalSize } from '../primitives/parse-svg-size'\nimport { textToSolid } from '../brep/text/text-to-solid'\nimport { svgToSolid } from '../brep/svg/svg-to-solid'\nimport { ensureDefaultFont } from '../brep/text/fontRegistry'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getSolidBoundingBox } from '../brep/brep-utils'\nimport { resolveSvgArg } from './internal/svg-asset-resolver'\nimport { getBackends } from '../runtime-state'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { fromBrep, brepOf } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { assertPositiveNumber } from './assert'\n\n// ── per-op 参数自校验(Phase 2.2;api 层被直接 import 时的防御层) ──\n\n/**\n * Validate engrave parameters: at least one of `text` or `svg` must be\n * provided, and `depth` (if provided) must be a positive number.\n * @param params - the raw engrave operation parameters.\n */\nexport function assertEngraveParams(params: Record<string, unknown>): void {\n const hasText = params.text !== undefined && params.text !== null && params.text !== ''\n const hasSvg = params.svg !== undefined && params.svg !== null\n if (!hasText && !hasSvg) {\n throw new Error('[api/engrave] requires either \"text\" or \"svg\"')\n }\n if (params.depth !== undefined && params.depth !== null) {\n assertPositiveNumber(params.depth, 'engrave.depth')\n }\n}\n\n/** 计算将 +Z 轴对齐到目标法向量的 3x4 仿射变换矩阵(仅旋转,无平移,Rodrigues 公式)。 */\nfunction alignZToNormal(normal: Vec3): number[] {\n const [nx, ny, nz] = normal\n const len = Math.sqrt(nx * nx + ny * ny + nz * nz)\n if (len < 1e-10) {\n return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]\n }\n const ux = nx / len, uy = ny / len, uz = nz / len\n\n if (Math.abs(ux) < 1e-10 && Math.abs(uy) < 1e-10) {\n if (uz > 0) return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]\n return [1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0]\n }\n\n const s = Math.sqrt(ux * ux + uy * uy)\n const c = uz\n const t = 1 - c\n const ax = -uy / s, ay = ux / s\n\n return [\n c + ax * ax * t, ax * ay * t, ay * s, 0,\n ax * ay * t, c + ay * ay * t, -ax * s, 0,\n -ay * s, ax * s, c, 0,\n ]\n}\n\n/** 将 solid 居中到原点(基于包围盒中心,与 mesh 路径语义一致)。 */\nfunction centerSolidAtOrigin(kernel: BrepEngineApi, solid: BrepHandle): BrepHandle {\n const bb = getSolidBoundingBox(kernel, solid)\n const cx = (bb.min[0] + bb.max[0]) / 2\n const cy = (bb.min[1] + bb.max[1]) / 2\n const cz = (bb.min[2] + bb.max[2]) / 2\n const centered = kernel.translate(solid, -cx, -cy, -cz)\n kernel.release(solid)\n return centered\n}\n\n/** BREP 路径:textToSolid/svgToSolid + boolean(cut/fuse)。 */\nasync function engraveBrepPath(input: Shape, params: Record<string, unknown>, svgText?: string): Promise<Shape> {\n const kernel = getBrepApi()\n const inputSolid = brepOf(input) as BrepHandle | undefined\n if (!inputSolid) throw new Error('[api/engrave] input is not BREP')\n\n const depth = params.depth as number\n const mode = (params.mode as 'convex' | 'concave' | undefined) ?? 'concave'\n const text = params.text as string | undefined\n\n // 1. 创建装饰 solid(文字或 SVG),depth 与 mesh 路径一致\n let decorationSolid: BrepHandle\n if (text) {\n await ensureDefaultFont()\n const textSize = (params.textSize as number) ?? 10\n decorationSolid = textToSolid(kernel, text, { fontSize: textSize, depth })\n } else if (svgText) {\n decorationSolid = svgToSolid(kernel, svgText, {\n depth,\n targetLongSide: (params.svgSize as number) ?? 20,\n })\n } else {\n throw new Error('[api/engrave] neither text nor svg provided')\n }\n\n // 2. 居中到原点\n decorationSolid = centerSolidAtOrigin(kernel, decorationSolid)\n\n // 3. 旋转 +Z 对齐面法向\n const worldFaceNormal: Vec3 = (params.faceNormal as Vec3) ?? [0, 0, 1]\n const rotMatrix = alignZToNormal(worldFaceNormal)\n const rotated = kernel.transform(decorationSolid, rotMatrix)\n kernel.release(decorationSolid)\n\n // 4. 沿法向偏移 + 平移到面中心(凸 offset=+depth/2,凹 offset=-depth/2;加 0.01 避免共面)\n // faceCenter/faceNormal 契约(方向 A):模型局部空间值(宿主生成脚本时已把交互\n // 世界坐标转换好),本 op 不做 world→local(无 partTransform 依赖)。\n const localFaceCenter: Vec3 = (params.faceCenter as Vec3) ?? [0, 0, 0]\n const offset = mode === 'convex' ? depth / 2 : -depth / 2\n const epsilon = 0.01\n const tx = localFaceCenter[0] + worldFaceNormal[0] * (offset + epsilon)\n const ty = localFaceCenter[1] + worldFaceNormal[1] * (offset + epsilon)\n const tz = localFaceCenter[2] + worldFaceNormal[2] * (offset + epsilon)\n const positioned = kernel.translate(rotated, tx, ty, tz)\n kernel.release(rotated)\n\n // 5. 布尔运算(凸=fuse,凹=cut)\n const isRaised = mode === 'convex'\n let result: BrepHandle\n if (isRaised) {\n result = kernel.fuse(inputSolid, positioned)\n } else {\n result = kernel.cut(inputSolid, positioned)\n }\n kernel.release(positioned)\n\n // 5b. cut 可能分裂成多个 solid(compound),用 fuseAll 合并回单一 solid\n try {\n const subSolids = kernel.getSubShapes(result, 'solid')\n if (subSolids.length > 1) {\n const fused = kernel.fuseAll(subSolids)\n for (const s of subSolids) {\n try { kernel.release(s) } catch { /* 已释放 */ }\n }\n try { kernel.release(result) } catch { /* 已释放 */ }\n result = fused\n }\n } catch {\n // getSubShapes 失败说明结果不是 compound,无需处理\n }\n\n return fromBrep(solidToShape(kernel, result), { solid: result })\n}\n\n/** 解析 SVG 资产引用(AssetRef → SVG 文本),两条路径共享;无 svg 返回 undefined。 */\nasync function resolveEngraveSvg(params: Record<string, unknown>): Promise<string | undefined> {\n return params.svg !== undefined && params.svg !== null\n ? await resolveSvgArg(params.svg, { assets: getBackends().assets as AssetResolver | undefined })\n : undefined\n}\n\n/**\n * 在几何表面雕刻文字或 SVG(文字分支与 logo 分支都可用)。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name engrave\n * @returns Shape 雕刻后的几何。\n * @param input - 目标几何。type:Shape required:true\n * @param params.mode - 雕刻方式:concave 凹陷(减法)/ convex 凸出(加法)。type:'concave' | 'convex' 默认 'concave'\n * @param params.depth - 深度 / 凸出高度(mm)。type:number 默认 0\n * @param params.text - 文字内容(与 svg 二选一)。type:string\n * @param params.textSize - 字号(mm)。type:number 默认 10\n * @param params.svg - SVG 资产引用(与 text 二选一;经 cad.asset 解析)。type:string\n * @param params.svgSize - SVG 长边目标尺寸。type:number\n * @param params.faceCenter - 面位置(绝对坐标)。type:[x,y,z] 默认 [0,0,0]\n * @param params.faceNormal - 面法向。type:[x,y,z] 默认 [0,0,1]\n * @note 早期 logo 分支用 `svgText`(整份 XML 拷贝 + `svgSize` 文本导出丢失,往返失真);现已改为 `svg` 资产引用,`engravingType` 冗余键已移除。faceCenter/faceNormal 目前是绝对坐标快照。\n * @example\n * const p = await cad.engrave(part0, { mode: 'concave', depth: 2, text: 'Hello', textSize: 10, faceCenter: [0, 0, 0], faceNormal: [0, 0, -1] })\n */\nexport const engrave = defineOp({\n mesh: async (input: Shape, params: Record<string, unknown>) => {\n if (!input) throw new Error('[api/engrave] no input geometry')\n assertEngraveParams(params)\n const svgText = await resolveEngraveSvg(params)\n // mesh 路径\n const faceCenter: Vec3 = (params.faceCenter as Vec3) ?? [0, 0, 0]\n const faceNormal: Vec3 = (params.faceNormal as Vec3) ?? [0, 0, 1]\n const { naturalWidth, naturalHeight } = svgText ? parseSvgNaturalSize(svgText) : { naturalWidth: 0, naturalHeight: 0 }\n const hasText = !!(params.text as string | undefined)\n\n return meshEngrave.engrave(input, {\n mode: (params.mode as 'convex' | 'concave' | undefined) ?? 'concave',\n depth: params.depth as number,\n face: { center: faceCenter, normal: faceNormal },\n text: hasText ? (params.text as string) : undefined,\n textSize: params.textSize as number | undefined,\n svg: svgText,\n svgNaturalWidth: naturalWidth,\n svgNaturalHeight: naturalHeight,\n svgSize: params.svgSize as number | undefined,\n })\n },\n brep: async (input: Shape, params: Record<string, unknown>) => {\n if (!input) throw new Error('[api/engrave] no input geometry')\n assertEngraveParams(params)\n const svgText = await resolveEngraveSvg(params)\n return engraveBrepPath(input, params, svgText)\n },\n naming: { kind: 'kernel', newFaces: { via: 'byAdjacency' } } as Provenance,\n\n paramDims: {},\n})\n"]}
|
|
1
|
+
{"version":3,"file":"engrave.js","sourceRoot":"","sources":["../../src/api/engrave.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api engrave — 雕刻库函数\n *\n *\n * dispatchPath 静态判定 brep/mesh。\n * BREP 路径:textToSolid/svgToSolid + boolean(cut/fuse)。\n */\n\nimport type { Shape, Vec3 } from '../mesh/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { AssetResolver } from '../cad-runtime/ports'\n// B3 correction (2026-10-06): no library-face cad aggregate — import the impl from its own module.\nimport * as meshEngrave from '../mesh/engrave'\nimport { parseSvgNaturalSize } from '../primitives/parse-svg-size'\nimport { textToSolid } from '../brep/text/text-to-solid'\nimport { svgToSolid } from '../brep/svg/svg-to-solid'\nimport { ensureDefaultFont } from '../brep/text/fontRegistry'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getSolidBoundingBox } from '../brep/brep-utils'\nimport { resolveSvgArg } from './internal/svg-asset-resolver'\nimport { getBackends } from '../runtime-state'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { fromBrep, brepOf } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { assertPositiveNumber } from './assert'\n\n// ── per-op 参数自校验(Phase 2.2;api 层被直接 import 时的防御层) ──\n\n/**\n * Validate engrave parameters: at least one of `text` or `svg` must be\n * provided, and `depth` (if provided) must be a positive number.\n * @param params - the raw engrave operation parameters.\n */\nexport function assertEngraveParams(params: Record<string, unknown>): void {\n const hasText = params.text !== undefined && params.text !== null && params.text !== ''\n const hasSvg = params.svg !== undefined && params.svg !== null\n if (!hasText && !hasSvg) {\n throw new Error('[api/engrave] requires either \"text\" or \"svg\"')\n }\n if (params.depth !== undefined && params.depth !== null) {\n assertPositiveNumber(params.depth, 'engrave.depth')\n }\n}\n\n/** 计算将 +Z 轴对齐到目标法向量的 3x4 仿射变换矩阵(仅旋转,无平移,Rodrigues 公式)。 */\nfunction alignZToNormal(normal: Vec3): number[] {\n const [nx, ny, nz] = normal\n const len = Math.sqrt(nx * nx + ny * ny + nz * nz)\n if (len < 1e-10) {\n return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]\n }\n const ux = nx / len, uy = ny / len, uz = nz / len\n\n if (Math.abs(ux) < 1e-10 && Math.abs(uy) < 1e-10) {\n if (uz > 0) return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]\n return [1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0]\n }\n\n const s = Math.sqrt(ux * ux + uy * uy)\n const c = uz\n const t = 1 - c\n const ax = -uy / s, ay = ux / s\n\n return [\n c + ax * ax * t, ax * ay * t, ay * s, 0,\n ax * ay * t, c + ay * ay * t, -ax * s, 0,\n -ay * s, ax * s, c, 0,\n ]\n}\n\n/** 将 solid 居中到原点(基于包围盒中心,与 mesh 路径语义一致)。 */\nfunction centerSolidAtOrigin(kernel: BrepEngineApi, solid: BrepHandle): BrepHandle {\n const bb = getSolidBoundingBox(kernel, solid)\n const cx = (bb.min[0] + bb.max[0]) / 2\n const cy = (bb.min[1] + bb.max[1]) / 2\n const cz = (bb.min[2] + bb.max[2]) / 2\n const centered = kernel.translate(solid, -cx, -cy, -cz)\n kernel.release(solid)\n return centered\n}\n\n/** BREP 路径:textToSolid/svgToSolid + boolean(cut/fuse)。 */\nasync function engraveBrepPath(input: Shape, params: Record<string, unknown>, svgText?: string): Promise<Shape> {\n const kernel = getBrepApi()\n const inputSolid = brepOf(input) as BrepHandle | undefined\n if (inputSolid === undefined) throw new Error('[api/engrave] input is not BREP')\n\n const depth = params.depth as number\n const mode = (params.mode as 'convex' | 'concave' | undefined) ?? 'concave'\n const text = params.text as string | undefined\n\n // 1. 创建装饰 solid(文字或 SVG),depth 与 mesh 路径一致\n let decorationSolid: BrepHandle\n if (text) {\n await ensureDefaultFont()\n const textSize = (params.textSize as number) ?? 10\n decorationSolid = textToSolid(kernel, text, { fontSize: textSize, depth })\n } else if (svgText) {\n decorationSolid = svgToSolid(kernel, svgText, {\n depth,\n targetLongSide: (params.svgSize as number) ?? 20,\n })\n } else {\n throw new Error('[api/engrave] neither text nor svg provided')\n }\n\n // 2. 居中到原点\n decorationSolid = centerSolidAtOrigin(kernel, decorationSolid)\n\n // 3. 旋转 +Z 对齐面法向\n const worldFaceNormal: Vec3 = (params.faceNormal as Vec3) ?? [0, 0, 1]\n const rotMatrix = alignZToNormal(worldFaceNormal)\n const rotated = kernel.transform(decorationSolid, rotMatrix)\n kernel.release(decorationSolid)\n\n // 4. 沿法向偏移 + 平移到面中心(凸 offset=+depth/2,凹 offset=-depth/2;加 0.01 避免共面)\n // faceCenter/faceNormal 契约(方向 A):模型局部空间值(宿主生成脚本时已把交互\n // 世界坐标转换好),本 op 不做 world→local(无 partTransform 依赖)。\n const localFaceCenter: Vec3 = (params.faceCenter as Vec3) ?? [0, 0, 0]\n const offset = mode === 'convex' ? depth / 2 : -depth / 2\n const epsilon = 0.01\n const tx = localFaceCenter[0] + worldFaceNormal[0] * (offset + epsilon)\n const ty = localFaceCenter[1] + worldFaceNormal[1] * (offset + epsilon)\n const tz = localFaceCenter[2] + worldFaceNormal[2] * (offset + epsilon)\n const positioned = kernel.translate(rotated, tx, ty, tz)\n kernel.release(rotated)\n\n // 5. 布尔运算(凸=fuse,凹=cut)\n const isRaised = mode === 'convex'\n let result: BrepHandle\n if (isRaised) {\n result = kernel.fuse(inputSolid, positioned)\n } else {\n result = kernel.cut(inputSolid, positioned)\n }\n kernel.release(positioned)\n\n // 5b. cut 可能分裂成多个 solid(compound),用 fuseAll 合并回单一 solid\n try {\n const subSolids = kernel.getSubShapes(result, 'solid')\n if (subSolids.length > 1) {\n const fused = kernel.fuseAll(subSolids)\n for (const s of subSolids) {\n try { kernel.release(s) } catch { /* 已释放 */ }\n }\n try { kernel.release(result) } catch { /* 已释放 */ }\n result = fused\n }\n } catch {\n // getSubShapes 失败说明结果不是 compound,无需处理\n }\n\n return fromBrep(solidToShape(kernel, result), { solid: result })\n}\n\n/** 解析 SVG 资产引用(AssetRef → SVG 文本),两条路径共享;无 svg 返回 undefined。 */\nasync function resolveEngraveSvg(params: Record<string, unknown>): Promise<string | undefined> {\n return params.svg !== undefined && params.svg !== null\n ? await resolveSvgArg(params.svg, { assets: getBackends().assets as AssetResolver | undefined })\n : undefined\n}\n\n/**\n * 在几何表面雕刻文字或 SVG(文字分支与 logo 分支都可用)。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name engrave\n * @returns Shape 雕刻后的几何。\n * @param input - 目标几何。type:Shape required:true\n * @param params.mode - 雕刻方式:concave 凹陷(减法)/ convex 凸出(加法)。type:'concave' | 'convex' 默认 'concave'\n * @param params.depth - 深度 / 凸出高度(mm)。type:number 默认 0\n * @param params.text - 文字内容(与 svg 二选一)。type:string\n * @param params.textSize - 字号(mm)。type:number 默认 10\n * @param params.svg - SVG 资产引用(与 text 二选一;经 cad.asset 解析)。type:string\n * @param params.svgSize - SVG 长边目标尺寸。type:number\n * @param params.faceCenter - 面位置(绝对坐标)。type:[x,y,z] 默认 [0,0,0]\n * @param params.faceNormal - 面法向。type:[x,y,z] 默认 [0,0,1]\n * @note 早期 logo 分支用 `svgText`(整份 XML 拷贝 + `svgSize` 文本导出丢失,往返失真);现已改为 `svg` 资产引用,`engravingType` 冗余键已移除。faceCenter/faceNormal 目前是绝对坐标快照。\n * @example\n * const p = await cad.engrave(part0, { mode: 'concave', depth: 2, text: 'Hello', textSize: 10, faceCenter: [0, 0, 0], faceNormal: [0, 0, -1] })\n */\nexport const engrave = defineOp({\n mesh: async (input: Shape, params: Record<string, unknown>) => {\n if (!input) throw new Error('[api/engrave] no input geometry')\n assertEngraveParams(params)\n const svgText = await resolveEngraveSvg(params)\n // mesh 路径\n const faceCenter: Vec3 = (params.faceCenter as Vec3) ?? [0, 0, 0]\n const faceNormal: Vec3 = (params.faceNormal as Vec3) ?? [0, 0, 1]\n const { naturalWidth, naturalHeight } = svgText ? parseSvgNaturalSize(svgText) : { naturalWidth: 0, naturalHeight: 0 }\n const hasText = !!(params.text as string | undefined)\n\n return meshEngrave.engrave(input, {\n mode: (params.mode as 'convex' | 'concave' | undefined) ?? 'concave',\n depth: params.depth as number,\n face: { center: faceCenter, normal: faceNormal },\n text: hasText ? (params.text as string) : undefined,\n textSize: params.textSize as number | undefined,\n svg: svgText,\n svgNaturalWidth: naturalWidth,\n svgNaturalHeight: naturalHeight,\n svgSize: params.svgSize as number | undefined,\n })\n },\n brep: async (input: Shape, params: Record<string, unknown>) => {\n if (!input) throw new Error('[api/engrave] no input geometry')\n assertEngraveParams(params)\n const svgText = await resolveEngraveSvg(params)\n return engraveBrepPath(input, params, svgText)\n },\n naming: { kind: 'kernel', newFaces: { via: 'byAdjacency' } } as Provenance,\n\n paramDims: {},\n})\n"]}
|
package/dist/api/export-3mf.js
CHANGED
|
@@ -58,7 +58,7 @@ function entryOfNode(node) {
|
|
|
58
58
|
if (!mesh) {
|
|
59
59
|
// 兜底:Shape 只有 BREP 槽(跨库产物)时现场三角化一次;引擎身份中立。
|
|
60
60
|
const handle = brepOf(node.shape);
|
|
61
|
-
if (handle) {
|
|
61
|
+
if (handle !== undefined) {
|
|
62
62
|
const tri = solidToShape(getBrepApi(), handle);
|
|
63
63
|
if (tri.indices.length > 0)
|
|
64
64
|
mesh = { positions: tri.positions, indices: tri.indices };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-3mf.js","sourceRoot":"","sources":["../../src/api/export-3mf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAMH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAoB,MAAM,6BAA6B,CAAA;AAC/E,OAAO,EAAE,YAAY,EAAyB,MAAM,2BAA2B,CAAA;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAYpD,wBAAwB;AACxB,SAAS,SAAS,CAAC,IAAsB;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,SAAS,CAAA;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,CAAA;IACnC,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAC5C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IACpE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAA;AAC/B,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,IAAsB;IACzC,IAAI,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,2CAA2C;QAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAA2B,CAAA;QAC3D,IAAI,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"export-3mf.js","sourceRoot":"","sources":["../../src/api/export-3mf.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api export3mf — 3MF 导出(脚本面薄壳:宿主门 + 库面序列化器)\n *\n * §2.6 定式 1「薄壳 + 门禁,不重写格式逻辑」:本体只做三件事——宿主门\n * (`assertHostFor`)→ 把入参转成 `ExportEntry[]` → 调库面序列化器\n * `exportModelSync(entries, '3mf', { unit })`。因此脚本面与库面**天然逐字节同源**\n * (矩阵 B14)。\n *\n * **无引擎门**(与 `exportStep` 的差别):faijs 的 3MF 写出器是纯 XML + ZIP\n * (`build3mfModelXml` + `writeZipEntries`),与引擎身份无关;只有「活句柄 → 三角\n * 载荷」这一条兜底路径需要内核,且它对 occt / brepkit 一视同仁(走 `getBrepApi()`\n * 的当前引擎,不做身份断言)。\n *\n * **宿主门**:与其余导出命令同轴(§2.5),仅 `'node'` 宿主开放。理由:`.fai.js` 可\n * 由 AI 生成任意代码、属不受信输入,导出把「往哪写、写几份、写什么」的决定权交给\n * 脚本文本;浏览器 / 小程序里要导出模型必须由宿主入口发起。断言写在函数体第一行,\n * 所以脚本面 `cad.*` 与 TS 直连同一函数都被同一门禁覆盖。\n *\n * **条目必须是 `mesh`,且不得是空 mesh**(定式 4 / P8):`build3mfModelXml` 对无\n * `mesh` 的条目**静默跳过**(`export-model.ts:330` 的 `if (!e.mesh) return`),只给\n * `solid` 会产出「ZIP 合法但对象为空」的假成功。故本 op 只交 `mesh` 条目,且三角数\n * 为 0 的成员视为不可导出;全体不可导出时**显式报错**,不落空文件。\n *\n * **三角载荷取自 Shape 自身,而非二次三角化**(对 §2.6 定式 4 措辞的按证据修正):\n * 定式 4 原话是「3mf 先 `solidToShape` 三角化再给 `mesh`」,但其依据是\n * `cli.ts#writeOutput` 的 `brepSolid` 分支——那里 solid 来自独立快照表、`shape` 可能\n * 只有 mesh,故必须先三角化。本 op 的入参 Shape 由执行链产出时**已带当前显示 mesh**\n * (mesh 链由构造器填、brep 链由 `fromHandle` → `meshHandle` 填)。再走一遍\n * `solidToShape` 会**二次三角化**并换掉默认精度:`meshHandle` 缺省 segments=32\n * (`brep/handle-bridge.ts:125`),`solidToShape` 缺省 2π/64(`brep/brep-ops.ts:68`),\n * 两者产出的是**两份不同的 mesh**——违反「导出所用 mesh 必须是用户看到的 mesh」\n * (规则 1),与 `api/export-stl.ts` 拒绝走原生二次三角化是同一取舍。故只在 Shape\n * 自带的三角载荷缺席 / 为空时(例如只有 BREP 槽的跨库产物)才用 `solidToShape` 兜底。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { CompoundShape } from '../shape'\nimport type { BrepHandle } from '../brep/engine/types'\nimport type { UnitName } from '../units'\nimport { brepOf } from '../shape'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { solidToShape } from '../brep/brep-ops'\nimport { exportModelSync, type ExportEntry } from '../brep/export/export-model'\nimport { exportTreeOf, type ExportMemberNode } from './internal/export-members'\nimport { assertHostFor } from './internal/l3-bridge'\n\n/** `cad.export3mf` 选项。 */\nexport interface Export3mfOptions {\n /**\n * 文件里声明的长度单位(缺省 `'mm'`)。语义与库面 `ExportOptions.unit` 一致:\n * 实现负责把坐标换算到该刻度,并与 `<model unit>` 成对写出(§10.3 规则 3);\n * `yard` 无 3MF 枚举值,库面成对回落到 mm(§10.3 规则 2)。\n */\n unit?: UnitName\n}\n\n/** 节点自带的三角载荷(非空才算数)。 */\nfunction ownMeshOf(node: ExportMemberNode): { positions: Float32Array; indices: Uint32Array } | undefined {\n const positions = node.shape?.positions\n const indices = node.shape?.indices\n if (!positions || !indices) return undefined\n if (positions.length === 0 || indices.length === 0) return undefined\n return { positions, indices }\n}\n\n/**\n * 一个成员树节点 → 3mf 条目:递归携带 children(装配层级 → `<components>`)。\n * 只产出带非空 `mesh` 的节点,或带 children 的容器(无 mesh 的节点会被写出器静默\n * 跳过,故容器节点若无 mesh 仅以 `<components>` 表达层级,符合 3MF Core 规范)。\n */\nfunction entryOfNode(node: ExportMemberNode): ExportEntry {\n let mesh = ownMeshOf(node)\n if (!mesh) {\n // 兜底:Shape 只有 BREP 槽(跨库产物)时现场三角化一次;引擎身份中立。\n const handle = brepOf(node.shape) as BrepHandle | undefined\n if (handle !== undefined) {\n const tri = solidToShape(getBrepApi(), handle)\n if (tri.indices.length > 0) mesh = { positions: tri.positions, indices: tri.indices }\n }\n }\n const entry: ExportEntry = {}\n if (node.name !== undefined) entry.name = node.name\n if (node.color !== undefined) entry.color = node.color\n if (node.transform) entry.transform = node.transform\n if (mesh) entry.mesh = mesh\n if (node.children?.length) entry.children = node.children.map(entryOfNode)\n return entry\n}\n\n/**\n * 导出 3MF(ZIP 容器字节)。\n * @group 导出\n * @inputs 1\n * @async false\n * @qual ok\n * @name export3mf\n * @note 薄壳 op:条目构造后一律交库面 `exportModelSync(entries,'3mf',{unit})` 序列化,\n * 故同一入参下脚本面字节与库面**逐字节一致**(同一真源)。返回 **ZIP 字节**\n * (`Uint8Array`),由宿主写入 `.3mf` 文件——内核不 import fs。`unit` 只声明单位\n * (写 `<model unit>`),坐标随之换算,两者同源(缺省 mm)。\n * 入参可为单件 Shape 或装配 compound;compound 逐成员导出并带上成员名与\n * `memberColors`(basematerials)。条目**只交 mesh**:无 mesh 的条目会被写出器\n * 静默跳过、产出「ZIP 合法但对象为空」的假成功,故三角数为 0 的成员视为不可导出,\n * 全体不可导出时显式报 E_EXPORT_3MF_EMPTY。\n * **宿主门**:仅 `'node'` 宿主可执行;browser / weapp / 未声明宿主报\n * E_HOST_UNSUPPORTED(门在读取载荷之前)。无引擎门——写出器是纯 XML + ZIP,\n * 与引擎身份无关。\n * @returns Uint8Array 3MF(ZIP)字节。\n * @param shape - 目标几何(单件 Shape 或装配 compound)。type:Shape|CompoundShape required:true\n * @param options.unit - 文件里声明的长度单位,缺省 mm(坐标随之换算)。type:UnitName\n * @example\n * const bytes = cad.export3mf(part0)\n * const asmBytes = cad.export3mf(asm1)\n */\nexport function export3mf(shape: Shape | CompoundShape, options?: Export3mfOptions): Uint8Array {\n // 宿主门在实现体最前:非 node 宿主先报 E_HOST_UNSUPPORTED,不读取载荷、不产字节。\n assertHostFor('export3mf', ['node'])\n const entries = exportTreeOf(shape)\n .map(entryOfNode)\n .filter((e) => e.mesh !== undefined || (e.children?.length ?? 0) > 0)\n if (entries.length === 0) {\n throw new Error('E_EXPORT_3MF_EMPTY: export3mf input has no exportable member (empty assembly or members without triangles)')\n }\n const buffer = exportModelSync(entries, '3mf', options?.unit === undefined ? undefined : { unit: options.unit })\n const bytes = new Uint8Array(buffer)\n if (bytes.length === 0) {\n throw new Error('E_EXPORT_3MF_EMPTY: 3MF writer produced no bytes')\n }\n return bytes\n}\n"]}
|
package/dist/api/export-brep.js
CHANGED
|
@@ -34,7 +34,7 @@ function exportBrepNative(shape) {
|
|
|
34
34
|
assertHostFor('exportBrep', ['node']);
|
|
35
35
|
assertEngineFor('exportBrep', ['occt']);
|
|
36
36
|
const handle = brepOf(shape);
|
|
37
|
-
if (
|
|
37
|
+
if (handle === undefined) {
|
|
38
38
|
throw new Error('E_EXPORT_BREP_NO_BREP: exportBrep input has no BREP handle (mesh-only shape)');
|
|
39
39
|
}
|
|
40
40
|
const text = getOcctKernel().toBREP(handle);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-brep.js","sourceRoot":"","sources":["../../src/api/export-brep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAErE,2DAA2D;AAC3D,SAAS,gBAAgB,CAAC,KAAY;IACpC,mDAAmD;IACnD,8BAA8B;IAC9B,aAAa,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IACrC,eAAe,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;IACtD,IAAI,
|
|
1
|
+
{"version":3,"file":"export-brep.js","sourceRoot":"","sources":["../../src/api/export-brep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAErE,2DAA2D;AAC3D,SAAS,gBAAgB,CAAC,KAAY;IACpC,mDAAmD;IACnD,8BAA8B;IAC9B,aAAa,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IACrC,eAAe,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;IACtD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAA;IACjG,CAAC;IACD,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC,MAAM,CAAC,MAAe,CAAC,CAAA;IACpD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACtE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAA;AAChC,CAAC","sourcesContent":["/**\n * api exportBrep — BREP 文本导出(平台:occt 专属,普通函数形态)\n *\n * @platform occt — 实现直调 occt 原生 `toBREP(shape)`(occt-wasm 有,L1 契约\n * `brep/engine/primitives.ts` **只有 `fromBREP` 导入侧、没有导出成员**)。\n *\n * 为什么不是 `defineOp`:本 op 返回**纯文本**(非 Shape)。`defineOp` 的 brep 包装\n * (`define-op.ts#wrapBrepOne`)把返回值一律收养为 Shape——字符串会落到\n * `fromHandle` 并抛 `E_BAD_HANDLE: ... got string`(实测)。数据型 op 走普通函数是\n * 本仓既有口径(先例:`api/view/view-projection.ts#projectView` 返回 SVG 字符串)。\n *\n * 引擎身份**不能**靠 dispatchPath 门控(普通函数不走那条路)⇒ 按\n * `api/internal/l3-bridge.ts#assertEngineFor` 的既定做法:函数体第一行断言当前\n * 引擎,触碰内核之前报出与 D11-4 同构的 `E_BREP_UNSUPPORTED`,不补桩、不回退。\n * (与 dispatchPath 的差异:`assertEngineFor` **不含** D11-3 的 `brep_mock` 豁免——\n * mock 句柄不是 occt 句柄,放行只会拿到错形状,故这里如实拦截。)\n *\n * 宿主门在引擎门之前(`assertHostFor('exportBrep', ['node'])`):导出命令只在 node\n * 宿主开放,browser / weapp / 未声明宿主先报 `E_HOST_UNSUPPORTED`。理由见\n * `cad-runtime/ports.ts#HostEnv`。\n *\n * §9.5 降级路径(记录在案):brepkit 的 wasm 面**自带 `toBREP`**\n * (`api/surface/brepkit-wasm-surface.json` 的 `methods` 含 `toBREP`),只是尚未接进\n * L1 契约。一旦契约新增导出成员且两侧适配器同时落地,本函数删掉 `assertEngineFor`\n * 并改走 `getBrepApi()`,该方法即从 C2 转入 C1。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { brepOf } from '../shape'\nimport { getOcctKernel } from '../occt-kernel/occtKernel'\nimport { assertEngineFor, assertHostFor } from './internal/l3-bridge'\n\n/** BREP 路径:先宿主门 → 引擎门 → 借出 brep 句柄 → occt 原生 toBREP 文本。 */\nfunction exportBrepNative(shape: Shape): string {\n // 门序固定:宿主门先于引擎门。门在 `brepOf(shape)` 之前,所以非 node 宿主下\n // 不读取 Shape 的 BREP 槽、不触碰任何内核。\n assertHostFor('exportBrep', ['node'])\n assertEngineFor('exportBrep', ['occt'])\n const handle = brepOf(shape) as BrepHandle | undefined\n if (handle === undefined) {\n throw new Error('E_EXPORT_BREP_NO_BREP: exportBrep input has no BREP handle (mesh-only shape)')\n }\n const text = getOcctKernel().toBREP(handle as never)\n if (typeof text !== 'string' || text.length === 0) {\n throw new Error('E_EXPORT_BREP_EMPTY: occt toBREP produced no text')\n }\n return text\n}\n\n/**\n * 导出 BREP 文本(与 `import_brep` 成对的导出侧)。\n * @group 导出\n * @inputs 1\n * @async false\n * @qual ok\n * @name exportBrep\n * @note 平台 op:仅 occt 引擎(原生 toBREP,L1 契约无对应导出成员)。非 occt 引擎\n * 执行前报 E_BREP_UNSUPPORTED;brep_mock 不拦截(引擎身份判定读\n * `config.brepEngineId`)。返回**纯文本**,由宿主写入 `.brep` 文件;\n * 经 `import_brep` / L1 `fromBREP` 可无损回读(精确 BREP,非 mesh 回填)。\n * **宿主门**:仅 `'node'` 宿主可执行,且宿主门先于引擎门——browser / weapp /\n * 未声明宿主报 E_HOST_UNSUPPORTED(不读 BREP 槽、不触碰内核)。\n * @returns string OCCT BREP 文本。\n * @param shape - 目标几何(必须带 BREP 槽;mesh-only 输入报 E_EXPORT_BREP_NO_BREP)。type:Shape required:true\n * @example\n * const text = cad.exportBrep(part0)\n */\nexport function exportBrep(shape: Shape): string {\n return exportBrepNative(shape)\n}\n"]}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* api exportStep — STEP 导出(脚本面薄壳:宿主门 +
|
|
2
|
+
* api exportStep — STEP 导出(脚本面薄壳:宿主门 + 库面序列化器)
|
|
3
3
|
*
|
|
4
|
-
* §2.6 定式 1「薄壳 +
|
|
5
|
-
* (`assertHostFor`)→
|
|
4
|
+
* §2.6 定式 1「薄壳 + 门禁,不重写格式逻辑」:本体只做三件事——宿主门
|
|
5
|
+
* (`assertHostFor`)→ 把入参转成 `ExportEntry[]`
|
|
6
6
|
* (`brep/export/export-model.ts#ExportEntry`)→ 调库面序列化器
|
|
7
7
|
* `exportModelSync(entries, 'step', { unit })`。因此脚本面与库面**天然逐字节同源**
|
|
8
8
|
* (矩阵 B12),单位声明与坐标刻度的成对不变式(§10.3 规则 3:OCCT 恒写
|
|
9
9
|
* SI_UNIT(.MILLI.,.METRE.),坐标预缩放后声明必须跟上)由库面统一保证,op 里不复刻。
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* 异常,也不得把 brepkit 句柄交给 occt 内核(句柄不得跨引擎传递)。
|
|
11
|
+
* **引擎通道(无门禁)**:引擎分派在库面(`export-model.ts` step 分支)按当前引擎
|
|
12
|
+
* 静态选择——brepkit → 内核原生通道(`step-flat.ts`,平铺多实体,unit/fileMeta 不
|
|
13
|
+
* 生效),其余(含 occt)→ XCAF 装配通道。不因 brepkit 缺特性报错
|
|
14
|
+
* (方案 docs/plans/2026-10-11-brepkit-step-io.md)。
|
|
16
15
|
*
|
|
17
|
-
* **宿主门**:与其余导出命令同轴(§2.5),仅 `'node'`
|
|
18
|
-
*
|
|
16
|
+
* **宿主门**:与其余导出命令同轴(§2.5),仅 `'node'` 宿主开放。理由:`.fai.js`
|
|
17
|
+
* 可由 AI 生成任意代码、属不受信输入,导出把「往哪写、写几份、
|
|
19
18
|
* 写什么」的决定权交给脚本文本;浏览器 / 小程序里要导出模型必须由宿主入口发起。
|
|
20
19
|
* 断言写在函数体第一行,所以脚本面 `cad.*` 与 TS 直连同一函数都被同一门禁覆盖。
|
|
21
20
|
*
|
|
@@ -53,9 +52,9 @@ export interface ExportStepOptions {
|
|
|
53
52
|
* 入参可为单件 Shape 或装配 compound(`cad.assembly` / `cad.group` 产物),
|
|
54
53
|
* compound 逐成员导出并带上成员名与 `memberColors`;空装配显式报
|
|
55
54
|
* E_EXPORT_STEP_EMPTY,不落空文件。
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
55
|
+
* **宿主门**:仅 `'node'`(browser / weapp / 未声明报 E_HOST_UNSUPPORTED)。
|
|
56
|
+
* 引擎通道无门禁:库面按当前引擎分派——brepkit → 内核原生通道(unit/fileMeta
|
|
57
|
+
* 不生效),occt → XCAF 装配通道。
|
|
59
58
|
* @returns string STEP(ISO-10303-21)文本。
|
|
60
59
|
* @param shape - 目标几何(单件 Shape 或装配 compound)。type:Shape|CompoundShape required:true
|
|
61
60
|
* @param options.unit - 文件里声明的长度单位,缺省 mm(坐标随之换算)。type:UnitName
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-step.d.ts","sourceRoot":"","sources":["../../src/api/export-step.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"export-step.d.ts","sourceRoot":"","sources":["../../src/api/export-step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAE7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAMxC,2BAA2B;AAC3B,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAA;CAChB;AAuBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,aAAa,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAmB5F"}
|
package/dist/api/export-step.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* api exportStep — STEP 导出(脚本面薄壳:宿主门 +
|
|
2
|
+
* api exportStep — STEP 导出(脚本面薄壳:宿主门 + 库面序列化器)
|
|
3
3
|
*
|
|
4
|
-
* §2.6 定式 1「薄壳 +
|
|
5
|
-
* (`assertHostFor`)→
|
|
4
|
+
* §2.6 定式 1「薄壳 + 门禁,不重写格式逻辑」:本体只做三件事——宿主门
|
|
5
|
+
* (`assertHostFor`)→ 把入参转成 `ExportEntry[]`
|
|
6
6
|
* (`brep/export/export-model.ts#ExportEntry`)→ 调库面序列化器
|
|
7
7
|
* `exportModelSync(entries, 'step', { unit })`。因此脚本面与库面**天然逐字节同源**
|
|
8
8
|
* (矩阵 B12),单位声明与坐标刻度的成对不变式(§10.3 规则 3:OCCT 恒写
|
|
9
9
|
* SI_UNIT(.MILLI.,.METRE.),坐标预缩放后声明必须跟上)由库面统一保证,op 里不复刻。
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* 异常,也不得把 brepkit 句柄交给 occt 内核(句柄不得跨引擎传递)。
|
|
11
|
+
* **引擎通道(无门禁)**:引擎分派在库面(`export-model.ts` step 分支)按当前引擎
|
|
12
|
+
* 静态选择——brepkit → 内核原生通道(`step-flat.ts`,平铺多实体,unit/fileMeta 不
|
|
13
|
+
* 生效),其余(含 occt)→ XCAF 装配通道。不因 brepkit 缺特性报错
|
|
14
|
+
* (方案 docs/plans/2026-10-11-brepkit-step-io.md)。
|
|
16
15
|
*
|
|
17
|
-
* **宿主门**:与其余导出命令同轴(§2.5),仅 `'node'`
|
|
18
|
-
*
|
|
16
|
+
* **宿主门**:与其余导出命令同轴(§2.5),仅 `'node'` 宿主开放。理由:`.fai.js`
|
|
17
|
+
* 可由 AI 生成任意代码、属不受信输入,导出把「往哪写、写几份、
|
|
19
18
|
* 写什么」的决定权交给脚本文本;浏览器 / 小程序里要导出模型必须由宿主入口发起。
|
|
20
19
|
* 断言写在函数体第一行,所以脚本面 `cad.*` 与 TS 直连同一函数都被同一门禁覆盖。
|
|
21
20
|
*
|
|
@@ -31,14 +30,16 @@
|
|
|
31
30
|
import { brepOf } from '../shape.js';
|
|
32
31
|
import { exportModelSync } from '../brep/export/export-model.js';
|
|
33
32
|
import { exportMembersOf } from './internal/export-members.js';
|
|
34
|
-
import {
|
|
33
|
+
import { assertHostFor } from './internal/l3-bridge.js';
|
|
35
34
|
/** 一个成员 → step 条目:有活句柄给精确 BREP,没有才退回自带三角载荷。 */
|
|
36
35
|
function stepEntryOf(member) {
|
|
37
36
|
const handle = brepOf(member.shape);
|
|
38
37
|
const positions = member.shape?.positions;
|
|
39
38
|
const indices = member.shape?.indices;
|
|
40
39
|
const hasMesh = !!positions && !!indices && positions.length > 0 && indices.length > 0;
|
|
41
|
-
|
|
40
|
+
// 句柄存在性 = `!== undefined`:brepkit 的句柄空间是 0 基(0 是合法 solid 句柄),
|
|
41
|
+
// 真值判断会把 0 误判成「无句柄」而退回 mesh 通道(丢精确拓扑)。
|
|
42
|
+
const payload = handle !== undefined
|
|
42
43
|
? { solid: handle }
|
|
43
44
|
: hasMesh
|
|
44
45
|
? { mesh: { positions, indices } }
|
|
@@ -65,9 +66,9 @@ function stepEntryOf(member) {
|
|
|
65
66
|
* 入参可为单件 Shape 或装配 compound(`cad.assembly` / `cad.group` 产物),
|
|
66
67
|
* compound 逐成员导出并带上成员名与 `memberColors`;空装配显式报
|
|
67
68
|
* E_EXPORT_STEP_EMPTY,不落空文件。
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
69
|
+
* **宿主门**:仅 `'node'`(browser / weapp / 未声明报 E_HOST_UNSUPPORTED)。
|
|
70
|
+
* 引擎通道无门禁:库面按当前引擎分派——brepkit → 内核原生通道(unit/fileMeta
|
|
71
|
+
* 不生效),occt → XCAF 装配通道。
|
|
71
72
|
* @returns string STEP(ISO-10303-21)文本。
|
|
72
73
|
* @param shape - 目标几何(单件 Shape 或装配 compound)。type:Shape|CompoundShape required:true
|
|
73
74
|
* @param options.unit - 文件里声明的长度单位,缺省 mm(坐标随之换算)。type:UnitName
|
|
@@ -76,9 +77,10 @@ function stepEntryOf(member) {
|
|
|
76
77
|
* const asm = cad.exportStep(asm1, { unit: 'inch' })
|
|
77
78
|
*/
|
|
78
79
|
export function exportStep(shape, options) {
|
|
79
|
-
//
|
|
80
|
+
// 门序固定:宿主门(在触碰任何句柄之前)。引擎门已移除(方案
|
|
81
|
+
// docs/plans/2026-10-11-brepkit-step-io.md):通道按引擎在库面分派——
|
|
82
|
+
// brepkit → 内核原生通道,occt → XCAF 装配通道,不因缺特性报错。
|
|
80
83
|
assertHostFor('exportStep', ['node']);
|
|
81
|
-
assertEngineFor('exportStep', ['occt']);
|
|
82
84
|
const entries = [];
|
|
83
85
|
for (const member of exportMembersOf(shape)) {
|
|
84
86
|
const entry = stepEntryOf(member);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-step.js","sourceRoot":"","sources":["../../src/api/export-step.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"export-step.js","sourceRoot":"","sources":["../../src/api/export-step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAMH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,eAAe,EAAoB,MAAM,6BAA6B,CAAA;AAC/E,OAAO,EAAE,eAAe,EAAqB,MAAM,2BAA2B,CAAA;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAWpD,+CAA+C;AAC/C,SAAS,WAAW,CAAC,MAAoB;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAA2B,CAAA;IAC7D,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,SAAS,CAAA;IACzC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,CAAA;IACrC,MAAM,OAAO,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;IACtF,8DAA8D;IAC9D,uCAAuC;IACvC,MAAM,OAAO,GAA4B,MAAM,KAAK,SAAS;QAC3D,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE;QACnB,CAAC,CAAC,OAAO;YACP,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;YAClC,CAAC,CAAC,SAAS,CAAA;IACf,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAC9B,OAAO;QACL,GAAG,OAAO;QACV,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QAC3D,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;KAC/D,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,UAAU,CAAC,KAA4B,EAAE,OAA2B;IAClF,gCAAgC;IAChC,yDAAyD;IACzD,6CAA6C;IAC7C,aAAa,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IACrC,MAAM,OAAO,GAAkB,EAAE,CAAA;IACjC,KAAK,MAAM,MAAM,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;QACjC,IAAI,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,yGAAyG,CAAC,CAAA;IAC5H,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;IACjH,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;IACpE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["/**\n * api exportStep — STEP 导出(脚本面薄壳:宿主门 + 库面序列化器)\n *\n * §2.6 定式 1「薄壳 + 门禁,不重写格式逻辑」:本体只做三件事——宿主门\n * (`assertHostFor`)→ 把入参转成 `ExportEntry[]`\n * (`brep/export/export-model.ts#ExportEntry`)→ 调库面序列化器\n * `exportModelSync(entries, 'step', { unit })`。因此脚本面与库面**天然逐字节同源**\n * (矩阵 B12),单位声明与坐标刻度的成对不变式(§10.3 规则 3:OCCT 恒写\n * SI_UNIT(.MILLI.,.METRE.),坐标预缩放后声明必须跟上)由库面统一保证,op 里不复刻。\n *\n * **引擎通道(无门禁)**:引擎分派在库面(`export-model.ts` step 分支)按当前引擎\n * 静态选择——brepkit → 内核原生通道(`step-flat.ts`,平铺多实体,unit/fileMeta 不\n * 生效),其余(含 occt)→ XCAF 装配通道。不因 brepkit 缺特性报错\n * (方案 docs/plans/2026-10-11-brepkit-step-io.md)。\n *\n * **宿主门**:与其余导出命令同轴(§2.5),仅 `'node'` 宿主开放。理由:`.fai.js`\n * 可由 AI 生成任意代码、属不受信输入,导出把「往哪写、写几份、\n * 写什么」的决定权交给脚本文本;浏览器 / 小程序里要导出模型必须由宿主入口发起。\n * 断言写在函数体第一行,所以脚本面 `cad.*` 与 TS 直连同一函数都被同一门禁覆盖。\n *\n * **层级入口**:入参是 `Shape` 或 `CompoundShape`(§2.1——`cad.assembly` / `cad.group`\n * 的产物),`cad.exportStep(compound)` 即脚本面的层级导出入口;成员拆解口径见\n * `api/internal/export-members.ts`(对齐 `node-host/cli.ts#writeAssemblyStep`)。\n *\n * **条目按格式分派**(定式 4):step 吃 `solid`(保留精确 BREP 拓扑,缺则库面用\n * `mesh` 重建,`brep/export/step.ts:83`),所以活句柄存在就给 `solid`,没有才给\n * `mesh`——与 `cli.ts#writeOutput`(`:744-758`)同口径。空装配**显式报错**,不落\n * 空文件(对齐 `api/export-stl.ts` 的 `E_EXPORT_STL_EMPTY` 口径)。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { CompoundShape } from '../shape'\nimport type { BrepHandle } from '../brep/engine/types'\nimport type { UnitName } from '../units'\nimport { brepOf } from '../shape'\nimport { exportModelSync, type ExportEntry } from '../brep/export/export-model'\nimport { exportMembersOf, type ExportMember } from './internal/export-members'\nimport { assertHostFor } from './internal/l3-bridge'\n\n/** `cad.exportStep` 选项。 */\nexport interface ExportStepOptions {\n /**\n * 文件里声明的长度单位(缺省 `'mm'`)。语义与库面 `ExportOptions.unit` 一致:\n * 实现负责把坐标换算到该刻度,并与声明**成对**写出(`export-model.ts` §10.3)。\n */\n unit?: UnitName\n}\n\n/** 一个成员 → step 条目:有活句柄给精确 BREP,没有才退回自带三角载荷。 */\nfunction stepEntryOf(member: ExportMember): ExportEntry | undefined {\n const handle = brepOf(member.shape) as BrepHandle | undefined\n const positions = member.shape?.positions\n const indices = member.shape?.indices\n const hasMesh = !!positions && !!indices && positions.length > 0 && indices.length > 0\n // 句柄存在性 = `!== undefined`:brepkit 的句柄空间是 0 基(0 是合法 solid 句柄),\n // 真值判断会把 0 误判成「无句柄」而退回 mesh 通道(丢精确拓扑)。\n const payload: ExportEntry | undefined = handle !== undefined\n ? { solid: handle }\n : hasMesh\n ? { mesh: { positions, indices } }\n : undefined\n if (!payload) return undefined\n return {\n ...payload,\n ...(member.name === undefined ? {} : { name: member.name }),\n ...(member.color === undefined ? {} : { color: member.color }),\n }\n}\n\n/**\n * 导出 STEP 文本(与 `cad.import_step` 成对的导出侧)。\n * @group 导出\n * @inputs 1\n * @async false\n * @qual ok\n * @name exportStep\n * @note 薄壳 op:条目构造后一律交库面 `exportModelSync(entries,'step',{unit})` 序列化,\n * 故同一入参下脚本面文本与库面**逐字节一致**(同一真源,不另起写出通道)。\n * 返回**纯文本**(ISO-10303-21),由宿主写入 `.step` / `.stp` 文件——内核不 import\n * fs。`unit` 只声明单位,坐标换算与声明由库面成对完成(缺省 mm)。\n * 入参可为单件 Shape 或装配 compound(`cad.assembly` / `cad.group` 产物),\n * compound 逐成员导出并带上成员名与 `memberColors`;空装配显式报\n * E_EXPORT_STEP_EMPTY,不落空文件。\n * **宿主门**:仅 `'node'`(browser / weapp / 未声明报 E_HOST_UNSUPPORTED)。\n * 引擎通道无门禁:库面按当前引擎分派——brepkit → 内核原生通道(unit/fileMeta\n * 不生效),occt → XCAF 装配通道。\n * @returns string STEP(ISO-10303-21)文本。\n * @param shape - 目标几何(单件 Shape 或装配 compound)。type:Shape|CompoundShape required:true\n * @param options.unit - 文件里声明的长度单位,缺省 mm(坐标随之换算)。type:UnitName\n * @example\n * const text = cad.exportStep(part0)\n * const asm = cad.exportStep(asm1, { unit: 'inch' })\n */\nexport function exportStep(shape: Shape | CompoundShape, options?: ExportStepOptions): string {\n // 门序固定:宿主门(在触碰任何句柄之前)。引擎门已移除(方案\n // docs/plans/2026-10-11-brepkit-step-io.md):通道按引擎在库面分派——\n // brepkit → 内核原生通道,occt → XCAF 装配通道,不因缺特性报错。\n assertHostFor('exportStep', ['node'])\n const entries: ExportEntry[] = []\n for (const member of exportMembersOf(shape)) {\n const entry = stepEntryOf(member)\n if (entry) entries.push(entry)\n }\n if (entries.length === 0) {\n throw new Error('E_EXPORT_STEP_EMPTY: exportStep input has no exportable member (empty assembly or payload-less members)')\n }\n const buffer = exportModelSync(entries, 'step', options?.unit === undefined ? undefined : { unit: options.unit })\n const text = new TextDecoder().decode(new Uint8Array(buffer))\n if (text.length === 0) {\n throw new Error('E_EXPORT_STEP_EMPTY: occt produced no STEP text')\n }\n return text\n}\n"]}
|
package/dist/api/extrude.js
CHANGED
|
@@ -271,7 +271,7 @@ function extrudeUpToSolid(kernel, inputSolid, o) {
|
|
|
271
271
|
if (typeof upTo === 'string') {
|
|
272
272
|
const base = o.baseFeature;
|
|
273
273
|
const baseSolid = base ? brepOf(base) : undefined;
|
|
274
|
-
if (
|
|
274
|
+
if (baseSolid === undefined) {
|
|
275
275
|
throw new Error('E_UP_TO_NO_BASE: upTo "last"/"first" requires a baseFeature shape');
|
|
276
276
|
}
|
|
277
277
|
// 支持体 bbox 沿拉伸方向的远端/近端平面(R-C:baseFeature 显式传参)
|
|
@@ -490,7 +490,7 @@ export const extrude = defineOp({
|
|
|
490
490
|
if (o.upTo !== undefined) {
|
|
491
491
|
const kernel = getBrepApi();
|
|
492
492
|
const inputSolid = brepOf(input);
|
|
493
|
-
if (
|
|
493
|
+
if (inputSolid === undefined)
|
|
494
494
|
throw new Error('[api/extrude] input is not BREP');
|
|
495
495
|
const clipped = extrudeUpToSolid(kernel, inputSolid, o);
|
|
496
496
|
// E3-b(2026-09-23,同 revolve 修法):不能对投影产物再走一次 fromBrep——
|
|
@@ -514,7 +514,7 @@ export const extrude = defineOp({
|
|
|
514
514
|
if (getBackends().config.brepEngineId === 'brepkit') {
|
|
515
515
|
const kernel = getBrepApi();
|
|
516
516
|
const faceHandle = brepOf(input);
|
|
517
|
-
if (
|
|
517
|
+
if (faceHandle === undefined)
|
|
518
518
|
throw new Error('[api/extrude] brepkit path: input is not BREP');
|
|
519
519
|
const solid = kernel.extrude(faceHandle, v[0], v[1], v[2]);
|
|
520
520
|
result = fromBrep(solidToShape(kernel, solid), { solid });
|