@faicad/faijs 0.18.0 → 0.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"extrude.d.ts","sourceRoot":"","sources":["../../src/api/extrude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAcH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAGhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAsGrD,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,+BAA+B;IAC/B,KAAK,EAAE,IAAI,CAAA;IACX,gDAAgD;IAChD,MAAM,EAAE,IAAI,CAAA;CACb;AAED,6DAA6D;AAC7D,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,CAAA;AAEnF,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6BAA6B;IAC7B,MAAM,CAAC,EAAE,IAAI,CAAA;IACb,uDAAuD;IACvD,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,CAAA;IAC7B,kCAAkC;IAClC,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,gDAAgD;IAChD,WAAW,CAAC,EAAE,KAAK,CAAA;IACnB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA2OD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,OAAO;;CAkElB,CAAA"}
1
+ {"version":3,"file":"extrude.d.ts","sourceRoot":"","sources":["../../src/api/extrude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAcH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAGhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AA2IrD,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,+BAA+B;IAC/B,KAAK,EAAE,IAAI,CAAA;IACX,gDAAgD;IAChD,MAAM,EAAE,IAAI,CAAA;CACb;AAED,6DAA6D;AAC7D,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,CAAA;AAEnF,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6BAA6B;IAC7B,MAAM,CAAC,EAAE,IAAI,CAAA;IACb,uDAAuD;IACvD,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,CAAA;IAC7B,kCAAkC;IAClC,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,gDAAgD;IAChD,WAAW,CAAC,EAAE,KAAK,CAAA;IACnB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA2OD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,OAAO;;CAoElB,CAAA"}
@@ -39,28 +39,35 @@ import { TopoRefError } from '../topology/naming/index.js';
39
39
  * extrude 的 construct 词汇表(计划 §4.4):`cap:bottom` / `cap:top` / `wall:<i>`。
40
40
  *
41
41
  * 判定口径:
42
- * - 拉伸方向取自一对平面端面法向的**带符号差**(两法向指向相反)——不依赖脚本
43
- * 怎么写方向,现场几何是唯一真值;
42
+ * - 拉伸方向**优先由 op 给出**(`hintAxis`),但必须由现场几何裁定:只有当至少
43
+ * 两张面的法向与该方向 |cos| ≈ 1 时才采信,否则回退到纯几何判定(A2,见下);
44
44
  * - 面法向与拉伸轴 |cos| ≈ 1 → 端面:中心沿轴分量小者 `cap:bottom`、大者 `cap:top`;
45
45
  * - 面法向与拉伸轴 |cos| ≈ 0 → 侧面 `wall:<i>`,i = 该面在侧面枚举中的相对序
46
46
  * (OCCT prism 的侧面顺序 = profile wire 的边序——makeWire 按构造顺序收边,
47
47
  * prism 不重排;G3-L1 改参重放依赖该稳定性);
48
- * - 非平面/无法向的面不进表:缺身份是显式状态(naming 行 role=null),不伪造。
48
+ * - 侧面含**非平面**(profile 里的弧段扫出圆柱面)同样进表:圆柱面 uv 中点法向
49
+ * 是径向,与拉伸轴垂直,判定与平面侧面同式。
49
50
  *
50
51
  * @param kernel - the OCCT kernel.
51
52
  * @param solid - the extruded solid.
53
+ * @param hintAxis - 拉伸方向(op 已知;仅作候选,由现场几何确认)。
52
54
  * @returns role(线格式串)→ hash 子表。
53
55
  */
54
- function extrudeConstructRoles(kernel, solid) {
56
+ function extrudeConstructRoles(kernel, solid, hintAxis) {
55
57
  const roles = new Map();
56
58
  const faceHandles = kernel.getSubShapes(solid, 'face');
57
59
  try {
58
60
  const hashes = kernel.subShapeHashes(solid, 'face', 2147483647);
59
- // 每面的法向(仅平面;侧面可能是圆柱/其它——wall 只挂平面侧面的前提是
60
- // 直拉伸,斜面/圆柱侧面留待 3.1 的完整枚举器)
61
+ // A2 (2026-09-26, Beds.FCStd): 侧面可能是**圆柱面**——profile 含弧段时,该弧
62
+ // 沿拉伸方向扫出的就是一个圆柱侧面。旧实现只在 `surfaceType === 'plane'` 时
63
+ // 取法向,于是圆柱侧面 normals[i] === undefined:既拿不到 `wall:<i>`,还让
64
+ // `wallIdx` 跳过它们 —— wall 序号因此不再是 profile 边序(违反 role-name.ts
65
+ // 的契约「wall:3 = 由 profile 第 3 条边扫出」)。实测 Beds part28(轮廓含 4
66
+ // 段弧)10 面只覆盖 6 个(缺序号 2/4/6/8),下游 fillet 的 `cad.edgeRef`
67
+ // 遂抛 `edgeRef: adjacent face ordinal 2 has no role lineage`。
68
+ // 圆柱面 uv 中点的法向是径向、与拉伸轴垂直(|cos| ≈ 0),可直接用于侧面判定。
69
+ const planar = faceHandles.map((f) => kernel.surfaceType(f) === 'plane');
61
70
  const normals = faceHandles.map((f) => {
62
- if (kernel.surfaceType(f) !== 'plane')
63
- return undefined;
64
71
  const uv = kernel.uvBounds(f);
65
72
  const n = kernel.surfaceNormal(f, (uv.uMin + uv.uMax) / 2, (uv.vMin + uv.vMax) / 2);
66
73
  return [n.x, n.y, n.z];
@@ -69,13 +76,40 @@ function extrudeConstructRoles(kernel, solid) {
69
76
  const c = kernel.surfaceCenterOfMass(f);
70
77
  return [c.x, c.y, c.z];
71
78
  });
72
- // 拉伸轴:找一对 |cos| ≈ 1 的反平行平面法向 → 带符号单位方向
79
+ // 拉伸轴:op 已知的拉伸方向优先(须由现场几何确认),否则找一对
80
+ // |cos| ≈ 1 的反平行平面法向 → 带符号单位方向。
81
+ //
82
+ // A2 (2026-09-26, Beds.FCStd part28): 纯几何判定取「第一对反平行的平面法向」,
83
+ // 但方形类轮廓的**一对对侧平面侧壁同样反平行**——Beds 的柱子于是把 X 向侧壁
84
+ // 判成端盖、把真正的 Z 向端盖当成 wall,4 个圆柱侧面彻底无名(10 面只覆盖 6,
85
+ // 缺序号 2/4/6/8),下游 fillet 的 `cad.edgeRef` 抛
86
+ // `edgeRef: adjacent face ordinal 2 has no role lineage`。方向由 op 给出、
87
+ // 由几何裁定(至少两张面法向与该方向 |cos| ≈ 1 才采信),两者缺一不可。
73
88
  let axis;
89
+ const hintLen = hintAxis ? Math.hypot(hintAxis[0], hintAxis[1], hintAxis[2]) : 0;
90
+ if (hintAxis && hintLen > 1e-9) {
91
+ const d = [
92
+ hintAxis[0] / hintLen, hintAxis[1] / hintLen, hintAxis[2] / hintLen,
93
+ ];
94
+ const aligned = normals.filter((n) => {
95
+ const len = Math.hypot(n[0], n[1], n[2]);
96
+ if (len < 1e-9)
97
+ return false;
98
+ return Math.abs((n[0] * d[0] + n[1] * d[1] + n[2] * d[2]) / len) > 0.999;
99
+ });
100
+ if (aligned.length >= 2)
101
+ axis = d;
102
+ }
74
103
  for (let i = 0; i < normals.length && !axis; i++) {
104
+ // 端面必须是一对反平行的**平面**法向(棱柱的端盖恒为平面)。
105
+ if (!planar[i])
106
+ continue;
75
107
  const a = normals[i];
76
108
  if (!a)
77
109
  continue;
78
110
  for (let j = i + 1; j < normals.length; j++) {
111
+ if (!planar[j])
112
+ continue;
79
113
  const b = normals[j];
80
114
  if (!b)
81
115
  continue;
@@ -97,6 +131,8 @@ function extrudeConstructRoles(kernel, solid) {
97
131
  // 端面:沿轴投影最小者 = cap:bottom,最大者 = cap:top
98
132
  const capCandidates = [];
99
133
  for (let i = 0; i < normals.length; i++) {
134
+ if (!planar[i])
135
+ continue;
100
136
  const n = normals[i];
101
137
  if (!n)
102
138
  continue;
@@ -118,7 +154,8 @@ function extrudeConstructRoles(kernel, solid) {
118
154
  if (hashes[top.idx] !== undefined)
119
155
  roles.set(formatRoleName(semantic('top')), [hashes[top.idx]]);
120
156
  }
121
- // 侧面:|cos| ≈ 0,按枚举序编号(= profile 边序)
157
+ // 侧面:|cos| ≈ 0,按枚举序编号(= profile 边序)。平面/圆柱侧面一律进表
158
+ // (A2)——跳过圆柱侧面会让 wall 序号脱离 profile 边序。
122
159
  let wallIdx = 0;
123
160
  for (let i = 0; i < normals.length; i++) {
124
161
  const n = normals[i];
@@ -396,6 +433,9 @@ export const extrude = defineOp({
396
433
  }
397
434
  const o = normalizeExtrudeOptions(params);
398
435
  assertExtrudeOptions(o);
436
+ // A2: 拉伸方向同时是链根建表的候选轴(extrudeConstructRoles 的 hintAxis),
437
+ // 两条路径(定长 / up-to)都要用,故提到分支之前。
438
+ const normal = new THREE.Vector3(...(o.normal ?? [0, 0, 1])).normalize();
399
439
  if (o.upTo !== undefined) {
400
440
  const kernel = getBrepApi();
401
441
  const inputSolid = brepOf(input);
@@ -409,11 +449,10 @@ export const extrude = defineOp({
409
449
  const origin = String(getCurrentStmt()?.id ?? '');
410
450
  return fromBrep(solidToShape(kernel, clipped), {
411
451
  solid: clipped,
412
- roleTable: new Map([[origin, extrudeConstructRoles(kernel, clipped)]]),
452
+ roleTable: new Map([[origin, extrudeConstructRoles(kernel, clipped, normal.toArray())]]),
413
453
  });
414
454
  }
415
455
  // 长度形态:委托生成投影(生成投影自带借入 / Result 翻转 / 收养)
416
- const normal = new THREE.Vector3(...(o.normal ?? [0, 0, 1])).normalize();
417
456
  const sign = o.mode === 'backward' ? -1 : 1;
418
457
  const v = [normal.x * o.length * sign, normal.y * o.length * sign, normal.z * o.length * sign];
419
458
  let result;
@@ -446,7 +485,7 @@ export const extrude = defineOp({
446
485
  const solid = kernel ? brepOf(result) : undefined;
447
486
  if (kernel && solid) {
448
487
  const origin = String(getCurrentStmt()?.id ?? '');
449
- const table = new Map([[origin, extrudeConstructRoles(kernel, solid)]]);
488
+ const table = new Map([[origin, extrudeConstructRoles(kernel, solid, normal.toArray())]]);
450
489
  const part = getCurrentStmt()?.outputs?.[0];
451
490
  runtimeLineage.recordOutput(origin, table, solid, part);
452
491
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extrude.js","sourceRoot":"","sources":["../../src/api/extrude.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * stdlib extrude — `cad.extrude`(平台 parity op,BREP-only)\n *\n * 归属(分层红线):`cad.extrude` 是「面 → 棱柱」的**平台** op,FCStd Pad/Pocket\n * 链路与 TS 面共用它(docs/plans/2026-09-15-fcstd-to-faijs-port-plan.md M4.6:\n * 「原 `cad.fai_extrude` 路线废弃」)。up-to(拉伸到面 / 到支持体端面)属\n * CadQuery `until=` 同族语义,因此落在**本 op**;`fai_extrude`(`fai_` 前缀的\n * faijs 扩展 op,与 fai_drill / fai_split 同族)不得承载它。\n *\n * 为什么是手写 op 而不是继续用生成投影:生成投影\n * `compatOp(projectBrepOp('extrude', ['face','height'], 'A', vendoredExtrude))`\n * 有两个硬约束——\n * ① 参数表只有位置形参;D11 归一化只在「单 plain-object 形态」下按 params 表\n * 映射(api/internal/dual-form-args.ts),`(face, { upTo })` 属位置形态 →\n * 原样喂给 vendored extrude → 报错,无法表达对象形态;\n * ② compatOp 的入参先经 `borrowDeep` 借成 brepjs 视图(api/internal/compat-op.ts),\n * 在那一层 `brepOf()` 取不到 → 依赖 faceRef / brepOf / 内核直调的 up-to 实现\n * 必然走空(同 AGENTS.md 记录的「compatOp 自动提升边界」)。\n * 先例:arg-spec 的 `fillet` 条目(faijs 侧由手写 dual-op 覆盖,生成模块符号为\n * 孤儿 by design)。\n *\n * 长度形态**委托给生成投影**(`projectedExtrude`):vendored 仍是唯一拉伸引擎,\n * 既有 `cad.extrude(face, [x,y,z])` 语义零漂移。\n */\n\nimport { extrude as projectedExtrude } from './generated/operations'\nimport * as THREE from 'three'\nimport { solidToShape, matrixToArray } from '../brep/brep-ops'\nimport { getSolidBoundingBox } from '../brep/brep-utils'\nimport { getCurrentStmt, getBackends } from '../runtime-state'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { fromBrep, brepOf, isCurveShape } from '../shape'\nimport { runtimeLineage } from '../topology/naming/lineage'\nimport { formatRoleName, semantic, wall } from '../topology/naming/role-name'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { defineOp } from '../sdk'\nimport { assertPositiveNumber } from './assert'\nimport type { Shape, Vec3 } from '../mesh/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { FaceTopoRef } from '../topology/naming'\nimport { TopoRefError } from '../topology/naming'\n\n/**\n\n * extrude 的 construct 词汇表(计划 §4.4):`cap:bottom` / `cap:top` / `wall:<i>`。\n *\n * 判定口径:\n * - 拉伸方向取自一对平面端面法向的**带符号差**(两法向指向相反)——不依赖脚本\n * 怎么写方向,现场几何是唯一真值;\n * - 面法向与拉伸轴 |cos| ≈ 1 → 端面:中心沿轴分量小者 `cap:bottom`、大者 `cap:top`;\n * - 面法向与拉伸轴 |cos| ≈ 0 → 侧面 `wall:<i>`,i = 该面在侧面枚举中的相对序\n * (OCCT prism 的侧面顺序 = profile wire 的边序——makeWire 按构造顺序收边,\n * prism 不重排;G3-L1 改参重放依赖该稳定性);\n * - 非平面/无法向的面不进表:缺身份是显式状态(naming 行 role=null),不伪造。\n *\n * @param kernel - the OCCT kernel.\n * @param solid - the extruded solid.\n * @returns role(线格式串)→ hash 子表。\n */\nfunction extrudeConstructRoles(kernel: BrepEngineApi, solid: BrepHandle): Map<string, number[]> {\n const roles = new Map<string, number[]>()\n const faceHandles = kernel.getSubShapes(solid, 'face')\n try {\n const hashes = kernel.subShapeHashes(solid, 'face', 2147483647)\n // 每面的法向(仅平面;侧面可能是圆柱/其它——wall 只挂平面侧面的前提是\n // 直拉伸,斜面/圆柱侧面留待 3.1 的完整枚举器)\n const normals: (readonly [number, number, number] | undefined)[] = faceHandles.map((f) => {\n if (kernel.surfaceType(f) !== 'plane') return undefined\n const uv = kernel.uvBounds(f)\n const n = kernel.surfaceNormal(f, (uv.uMin + uv.uMax) / 2, (uv.vMin + uv.vMax) / 2)\n return [n.x, n.y, n.z] as const\n })\n const centers = faceHandles.map((f) => {\n const c = kernel.surfaceCenterOfMass(f)\n return [c.x, c.y, c.z] as const\n })\n\n // 拉伸轴:找一对 |cos| ≈ 1 的反平行平面法向 → 带符号单位方向\n let axis: readonly [number, number, number] | undefined\n for (let i = 0; i < normals.length && !axis; i++) {\n const a = normals[i]\n if (!a) continue\n for (let j = i + 1; j < normals.length; j++) {\n const b = normals[j]\n if (!b) continue\n const lenA = Math.hypot(a[0], a[1], a[2])\n const lenB = Math.hypot(b[0], b[1], b[2])\n if (lenA < 1e-9 || lenB < 1e-9) continue\n const cos = (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]) / (lenA * lenB)\n if (Math.abs(cos) > 0.999) {\n // b 反向即「从 i 面指向 j 面」的方向\n axis = [b[0] - a[0], b[1] - a[1], b[2] - a[2]] as const\n break\n }\n }\n }\n const axisLen = axis ? Math.hypot(axis[0], axis[1], axis[2]) : 0\n if (axis && axisLen > 1e-9) {\n const ax: readonly [number, number, number] = [axis[0] / axisLen, axis[1] / axisLen, axis[2] / axisLen]\n // 端面:沿轴投影最小者 = cap:bottom,最大者 = cap:top\n const capCandidates: { idx: number; t: number }[] = []\n for (let i = 0; i < normals.length; i++) {\n const n = normals[i]\n if (!n) continue\n const len = Math.hypot(n[0], n[1], n[2]) || 1\n const cos = Math.abs(n[0] * ax[0] + n[1] * ax[1] + n[2] * ax[2]) / len\n if (cos > 0.999) {\n capCandidates.push({ idx: i, t: centers[i]![0] * ax[0] + centers[i]![1] * ax[1] + centers[i]![2] * ax[2] })\n }\n }\n if (capCandidates.length >= 2) {\n capCandidates.sort((p, q) => p.t - q.t)\n const bottom = capCandidates[0]!\n const top = capCandidates[capCandidates.length - 1]!\n // 端面词汇用 semantic('bottom'/'top'——与 box 同形)。计划 §4.4 写的\n // `cap:<name>` 前缀形态与 RoleName 契约冲突(semantic 名不允许 ':',\n // role-name.ts SEMANTIC_NAME_RE),落地以契约为准:semantic 直名。\n if (hashes[bottom.idx] !== undefined) roles.set(formatRoleName(semantic('bottom')), [hashes[bottom.idx]!])\n if (hashes[top.idx] !== undefined) roles.set(formatRoleName(semantic('top')), [hashes[top.idx]!])\n }\n // 侧面:|cos| ≈ 0,按枚举序编号(= profile 边序)\n let wallIdx = 0\n for (let i = 0; i < normals.length; i++) {\n const n = normals[i]\n if (!n || hashes[i] === undefined) continue\n const len = Math.hypot(n[0], n[1], n[2]) || 1\n const cos = Math.abs(n[0] * ax[0] + n[1] * ax[1] + n[2] * ax[2]) / len\n if (cos < 0.001) {\n roles.set(formatRoleName(wall(wallIdx)), [hashes[i]!])\n wallIdx++\n }\n }\n }\n return roles\n } finally {\n for (const h of faceHandles) {\n try { kernel.release(h) } catch { /* 已释放 */ }\n }\n }\n}\n\n/** 显式平面目标(up-to 截断面):点 + 法向(点在平面上即可,无需共面于面边界)。 */\nexport interface UpToPlaneSpec {\n /** 平面上一点(世界坐标)。type:[x,y,z] */\n point: Vec3\n /** 平面法向(世界坐标,方向仅定半空间语义,截断取两侧之一)。type:[x,y,z] */\n normal: Vec3\n}\n\n/** up-to 目标:显式面引用 / 显式平面 / 支持体沿拉伸方向的远端(last)/近端(first)端面。 */\nexport type ExtrudeUpTo = FaceTopoRef | 'last' | 'first' | { plane: UpToPlaneSpec }\n\n/** `cad.extrude` 的对象形态参数(位置形态由 {@link normalizeExtrudeOptions} 归一到这里)。 */\nexport interface ExtrudeOptions {\n /** 拉伸量(mm,>0)。up-to 模式下无需给。 */\n length?: number\n /** 拉伸方向(世界坐标);缺省 [0,0,1]。 */\n normal?: Vec3\n /** 沿 normal 的前进方向;缺省 'forward'('backward' 等价于方向取反)。 */\n mode?: 'forward' | 'backward'\n /** 拉伸到面 / 到支持体端面;提供时忽略 length。 */\n upTo?: ExtrudeUpTo\n /** upTo 'last'/'first' 的支持体(累积支持体,由转换器显式传入)。 */\n baseFeature?: Shape\n /** 截断面沿其法向的偏移(mm);缺省 0。 */\n offset?: number\n}\n\n/** 判定是否为朴素对象(对象形态判别器,避免误吞 Shape / 数组)。 */\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype\n}\n\n/**\n * 归一化 `cad.extrude` 的两种调用形态。\n *\n * - 位置形态(与生成投影同款,历史产物在用):`cad.extrude(face, 10)` /\n * `cad.extrude(face, [x,y,z])`——向量即「方向 × 长度」,恒为沿该方向前进。\n * - 对象形态(faijs 面):`cad.extrude(face, { length, normal, mode, upTo, ... })`。\n *\n * @param params - 调用点第二实参(数字 / 向量 / 对象)。\n * @returns 归一化后的对象形态参数。\n * @throws Error 参数既非数字 / 向量 / 朴素对象时(不猜)。\n */\nfunction normalizeExtrudeOptions(params: unknown): ExtrudeOptions {\n if (isPlainObject(params)) return params as ExtrudeOptions\n if (Array.isArray(params)) {\n const v = params as Vec3\n const len = Math.hypot(v[0] ?? 0, v[1] ?? 0, v[2] ?? 0)\n if (!Number.isFinite(len) || len < 1e-12) {\n throw new Error('E_EXTRUDE_ZERO_VECTOR: extrude direction vector has zero length')\n }\n return { length: len, normal: [v[0] / len, v[1] / len, v[2] / len], mode: 'forward' }\n }\n if (typeof params === 'number') return { length: params, normal: [0, 0, 1], mode: 'forward' }\n throw new Error('E_EXTRUDE_BAD_PARAMS: extrude expects (face, number | [x,y,z] | { length | upTo, ... })')\n}\n\n/**\n * 参数自校验:up-to 模式下无需 length;否则 length 必须为正数。\n *\n * @param o - 归一化后的参数。\n */\nfunction assertExtrudeOptions(o: ExtrudeOptions): void {\n if (o.upTo !== undefined) return // up-to 模式:长度由目标面决定\n assertPositiveNumber(o.length, 'extrude.length')\n}\n\n/** bbox 对角线长度(长试探的余量基数)。 */\nfunction bboxDiag(bbox: { min: Vec3; max: Vec3 }): number {\n return Math.hypot(bbox.max[0] - bbox.min[0], bbox.max[1] - bbox.min[1], bbox.max[2] - bbox.min[2])\n}\n\n/**\n * 目标面法向负侧的半空间覆盖盒(截断长拉伸用)——定向版。\n *\n * GOTCHA(PadTest Pad001 斜置基准面实测):轴对齐盒的边界不是斜面本身,\n * 截断呈台阶而非精确斜平面(体积 7076 vs 真值 4860)。正确做法:在局部基\n * (u, v, n) 里构造盒(深度沿 −n 伸出 R、横向 ±R),再用 kernel.transform\n * 旋到世界系——盒的 n=R 端面就是精确的目标平面。\n *\n * @param kernel - BREP 内核。\n * @param faceCenter - 目标面中心(世界坐标,盒的 n=0 端面过此点)。\n * @param faceNormal - 目标面法向(半空间保留 −n 侧…即盒占据 −n 侧深度)。\n * @param reach - 覆盖半径 R。\n * @returns 定向半空间盒的 brep 句柄。\n */\nfunction farSideBox(kernel: BrepEngineApi, faceCenter: Vec3, faceNormal: Vec3, reach: number): BrepHandle {\n const unit = new THREE.Vector3(...faceNormal).normalize()\n const center = new THREE.Vector3(...faceCenter)\n // 与 n 正交的正交基 u,v\n const aux = Math.abs(unit.y) < 0.9 ? new THREE.Vector3(0, 1, 0) : new THREE.Vector3(1, 0, 0)\n const u = new THREE.Vector3().crossVectors(unit, aux).normalize()\n const v = new THREE.Vector3().crossVectors(unit, u).normalize()\n const R = reach\n // 局部系盒:x∈[−R,R] (u), y∈[−R,R] (v), z∈[−R,0] (n 深度侧)\n const box = kernel.makeBoxFromCorners(\n { x: -R, y: -R, z: -R },\n { x: R, y: R, z: 0 },\n )\n // 旋转矩阵:列 = u, v, n;平移 = center(局部 z=0 端面落到过 center 的目标平面)\n const m = new THREE.Matrix4().makeBasis(u, v, unit).setPosition(center)\n return kernel.transform(box, matrixToArray(m))\n}\n\n/**\n * up-to 拉伸:occt-wasm 未暴露 `BRepFeat_MakePrism`,用「长拉伸 → 与目标面侧\n * 半空间盒求交」等价组合;长试探上限只作布尔构造辅助,不进入结果几何。\n *\n * @param kernel - BREP 内核。\n * @param inputSolid - 轮廓实体句柄。\n * @param o - 归一化参数(需带 upTo)。\n * @returns 截断后的实体句柄。\n * @throws Error E_UP_TO_* 系列:缺支持体 / 方向与目标面平行 / 不相交(禁静默)。\n */\nfunction extrudeUpToSolid(kernel: BrepEngineApi, inputSolid: BrepHandle, o: ExtrudeOptions): BrepHandle {\n const normal = new THREE.Vector3(...(o.normal ?? [0, 0, 1])).normalize()\n const offset = o.offset ?? 0\n const upTo = o.upTo as ExtrudeUpTo\n\n // 1) 方向目标:FaceTopoRef → 现场面几何;'last'/'first' → baseFeature 支持体\n // 沿拉伸方向的端面(远端 last / 近端 first)。\n let targetCenter: Vec3\n let targetNormal: Vec3\n if (typeof upTo === 'string') {\n const base = o.baseFeature\n const baseSolid = base ? (brepOf(base) as BrepHandle | undefined) : undefined\n if (!baseSolid) {\n throw new Error('E_UP_TO_NO_BASE: upTo \"last\"/\"first\" requires a baseFeature shape')\n }\n // 支持体 bbox 沿拉伸方向的远端/近端平面(R-C:baseFeature 显式传参)\n const bbox = getSolidBoundingBox(kernel, baseSolid)\n const pick = (which: 'max' | 'min'): Vec3 => {\n const c: Vec3 = [\n (bbox.min[0] + bbox.max[0]) / 2,\n (bbox.min[1] + bbox.max[1]) / 2,\n (bbox.min[2] + bbox.max[2]) / 2,\n ]\n const extent = which === 'max' ? 1 : -1\n const proj =\n (bbox.max[0] - bbox.min[0]) * Math.abs(normal.x) +\n (bbox.max[1] - bbox.min[1]) * Math.abs(normal.y) +\n (bbox.max[2] - bbox.min[2]) * Math.abs(normal.z)\n return [\n c[0] + ((normal.x * proj) / 2) * extent,\n c[1] + ((normal.y * proj) / 2) * extent,\n c[2] + ((normal.z * proj) / 2) * extent,\n ]\n }\n targetCenter = pick(upTo === 'last' ? 'max' : 'min')\n targetNormal = [normal.x, normal.y, normal.z]\n } else if (upTo && typeof upTo === 'object' && 'plane' in upTo) {\n // 显式平面目标(datum plane 等无限平面:截断面由平面方程决定,不受\n // 目标面边界限制——斜置基准面时顶面是斜的,定长路径语义错误)。\n targetCenter = [...upTo.plane.point] as Vec3\n targetNormal = [...upTo.plane.normal] as Vec3\n } else {\n // GOTCHA(探针实测):FaceTopoRef 携带的 hint 是 faceRef 现场捕获的\n // center/normal 快照,直接使用;不要对拉伸轮廓(sketch 面)做二次\n // resolveFaceGeometry——引用的面在目标体上、不在轮廓上,会 E_TOPO_NOT_FOUND。\n if (upTo.hint.center && upTo.hint.normal) {\n targetCenter = [...upTo.hint.center] as Vec3\n targetNormal = [...upTo.hint.normal] as Vec3\n } else {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'extrude upTo: face ref hint has no center/normal snapshot')\n }\n }\n\n // 2) 长试探长度:GOTCHA(探针实测)——不能按轮廓自身 bbox 对角线算(小轮廓够不到\n // 远处目标面);必须取「截断平面沿拉伸方向的投影距离」加余量,保证长拉伸至\n // 少穿过目标面。\n const bbox = getSolidBoundingBox(kernel, inputSolid)\n const shiftedCenter0: Vec3 = [\n targetCenter[0] + targetNormal[0] * offset,\n targetCenter[1] + targetNormal[1] * offset,\n targetCenter[2] + targetNormal[2] * offset,\n ]\n // 自动定向(仅 { plane } 目标):FreeCAD UpToFace 朝目标面所在方向拉伸。\n // GOTCHA(PadTest Pad001 实测,probe 探针):斜置基准面在草图局部坐标里\n // 位于轮廓的反方向(圆心处平面交点 t=−26 而非 +26),固定沿 +Z 拉截不到\n // 平面(体积 597 ≠ 真值 4860)。轮廓 bbox 中心到截断平面的有向投影与\n // 拉伸方向异号 → 翻转拉伸方向(就地翻转 Vector3,后续 tMax/keepSide 自洽)。\n if (upTo && typeof upTo === 'object' && 'plane' in upTo) {\n const c0: Vec3 = [\n (bbox.min[0] + bbox.max[0]) / 2,\n (bbox.min[1] + bbox.max[1]) / 2,\n (bbox.min[2] + bbox.max[2]) / 2,\n ]\n const tnAuto = new THREE.Vector3(...targetNormal).normalize()\n const dAuto =\n (c0[0] - shiftedCenter0[0]) * tnAuto.x +\n (c0[1] - shiftedCenter0[1]) * tnAuto.y +\n (c0[2] - shiftedCenter0[2]) * tnAuto.z\n // 沿拉伸方向到平面的有向交点参数:t = −((c0−plane)·n) / (dir·n)。\n // t<0 → 平面在行进反方向 → 翻转(FreeCAD UpToFace 朝平面拉伸)。\n const tAuto = -dAuto / normal.dot(tnAuto)\n if (tAuto < 0) {\n normal.multiplyScalar(-1)\n }\n }\n const cornerProjections = [\n [bbox.min[0], bbox.min[1], bbox.min[2]],\n [bbox.max[0], bbox.min[1], bbox.min[2]],\n [bbox.min[0], bbox.max[1], bbox.min[2]],\n [bbox.min[0], bbox.min[1], bbox.max[2]],\n [bbox.max[0], bbox.max[1], bbox.min[2]],\n [bbox.max[0], bbox.min[1], bbox.max[2]],\n [bbox.min[0], bbox.max[1], bbox.max[2]],\n [bbox.max[0], bbox.max[1], bbox.max[2]],\n ] as Vec3[]\n const planePoint0 = new THREE.Vector3(...shiftedCenter0)\n const tn0 = new THREE.Vector3(...targetNormal).normalize()\n const planeD = planePoint0.dot(tn0)\n const dirD = normal.dot(tn0)\n if (Math.abs(dirD) < 1e-9) {\n throw new Error('E_UP_TO_PARALLEL: extrude direction is parallel to the target face plane')\n }\n const tMax = cornerProjections\n .map((p) => (planeD - new THREE.Vector3(...p).dot(tn0)) / dirD)\n .reduce((a, b) => Math.max(a, b), 0)\n const L = tMax + Math.abs(offset) + bboxDiag(bbox) * 0.1 + 1\n const longSolid = kernel.extrude(inputSolid, normal.x * L, normal.y * L, normal.z * L)\n\n // 3) 截断平面 = 目标面 + offset(沿面法向)。GOTCHA(探针实测):半空间侧不能固定\n // 取「法向负侧」——目标面法向朝向与拉伸方向无必然关系。正确语义:保留「包含\n // 拉伸起点」的那一侧。farSideBox 覆盖「传入法向的反侧」,因此传 -keepSide。\n const tn = tn0\n const startCenter = new THREE.Vector3(\n (bbox.min[0] + bbox.max[0]) / 2,\n (bbox.min[1] + bbox.max[1]) / 2,\n (bbox.min[2] + bbox.max[2]) / 2,\n )\n const planePoint = new THREE.Vector3(...shiftedCenter0)\n const d = startCenter.sub(planePoint).dot(tn)\n // 保留侧法向:起点所在侧(d 与 tn 同号 → 正侧;异号 → 负侧);d≈0 时退化为\n // 拉伸方向(起点在面上时按拉伸方向截)\n const keepSide = Math.abs(d) < 1e-9 ? normal.clone() : tn.clone().multiplyScalar(Math.sign(d))\n // GOTCHA(PadTest Pad001 斜置基准面实测):targetCenter 可以是平面上离轮廓\n // 很远的任意一点(基准面 point 在草图局部坐标里横向偏 ~100),reach 若只取\n // L(按轮廓到截断面的投影距离)则半空间盒横向盖不住长拉伸体 → common 为空。\n // reach 必须 ≥ 平面点到拉伸体的距离 + 体对角线(全方向覆盖)。\n const reach =\n new THREE.Vector3(...shiftedCenter0).distanceTo(\n new THREE.Vector3((bbox.min[0] + bbox.max[0]) / 2, (bbox.min[1] + bbox.max[1]) / 2, (bbox.min[2] + bbox.max[2]) / 2),\n ) + bboxDiag(bbox) + Math.abs(offset) + 1\n const halfspace = farSideBox(kernel, shiftedCenter0, [-keepSide.x, -keepSide.y, -keepSide.z], reach)\n\n // 4) 求交:长拉伸 ∩ 半空间盒\n const clipped = kernel.common(longSolid, halfspace)\n kernel.release(longSolid)\n kernel.release(halfspace)\n\n // 预检:方向上不相交 → 显式报错(禁静默)\n const clippedBBox = getSolidBoundingBox(kernel, clipped)\n if (!Number.isFinite(bboxDiag(clippedBBox)) || bboxDiag(clippedBBox) < 1e-9) {\n kernel.release(clipped)\n throw new Error('E_UP_TO_NO_INTERSECTION: extrude direction does not reach the target face')\n }\n return clipped\n}\n\n/**\n * 沿 normal 拉伸几何(面 → 棱柱)。\n *\n * up-to 模式(`upTo`)与长度模式(`length`)二选一;长度模式委托生成投影\n * (vendored extrude 为唯一引擎),up-to 模式走半空间组合。\n *\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name extrude\n * @returns Shape 拉伸后的几何。\n * @param input - 目标几何(平面面)。type:Shape required:true\n * @param params.length - 总拉伸量(mm,>0)。up-to 模式下无需给。type:number\n * @param params.normal - 拉伸方向(世界坐标)。type:[x,y,z] 默认 [0,0,1]\n * @param params.mode - 沿 normal 的前进方向:forward 正向 / backward 反向。type:'forward' | 'backward' 默认 'forward'\n * @param params.upTo - 拉伸到面:FaceTopoRef 指定面 / 'last' 支持体远端面 / 'first' 近端面(需 baseFeature)。提供时忽略 length。type:'last' | 'first' | FaceTopoRef\n * @param params.baseFeature - upTo 'last'/'first' 的支持体几何(累积支持体)。type:Shape\n * @param params.offset - 截断面沿其法向的偏移(mm)。type:number 默认 0\n * @example\n * const p = await cad.extrude(part0, [0, 0, 10])\n * const p = await cad.extrude(part0, { length: 10 })\n * const p = await cad.extrude(sk, { upTo: cad.faceRef(part0, 3) })\n * const p = await cad.extrude(sk, { upTo: 'last', baseFeature: part0 })\n */\nexport const extrude = defineOp({\n name: 'extrude',\n brep: async (input: Shape, params: unknown): Promise<Shape> => {\n if (!input) throw new Error('[stdlib/extrude] no input geometry')\n // Phase 3 执行前预检:1D 曲线(wire / helix / sketch as:'wire')不是面,不得落进\n // 内核深层才报「操作失败」——维度不符在调用内核前明确拒绝(1D 判别位 kind='curve')。\n if (isCurveShape(input)) {\n throw new Error(\n 'E_EXTRUDE_NEEDS_FACE: extrude requires a 2D face input, got a 1D curve (kind=\"curve\")',\n )\n }\n const o = normalizeExtrudeOptions(params)\n assertExtrudeOptions(o)\n\n if (o.upTo !== undefined) {\n const kernel = getBrepApi()\n const inputSolid = brepOf(input) as BrepHandle | undefined\n if (!inputSolid) throw new Error('[stdlib/extrude] input is not BREP')\n const clipped = extrudeUpToSolid(kernel, inputSolid, o)\n // E3-b(2026-09-23,同 revolve 修法):不能对投影产物再走一次 fromBrep——\n // 同语句 registeredStmtId 去重会吞掉第二次登记(part 键保持空表,下游\n // fillet/chamfer 的 edgeRef 报 nameless shape)。upTo 路径不经过投影/\n // adoptOut,fromBrep 只在此走一次,roleTable 直挂 + solid 句柄登记。\n const origin = String(getCurrentStmt()?.id ?? '')\n return fromBrep(solidToShape(kernel, clipped), {\n solid: clipped,\n roleTable: new Map([[origin, extrudeConstructRoles(kernel, clipped)]]),\n })\n }\n\n // 长度形态:委托生成投影(生成投影自带借入 / Result 翻转 / 收养)\n const normal = new THREE.Vector3(...(o.normal ?? [0, 0, 1])).normalize()\n const sign = o.mode === 'backward' ? -1 : 1\n const v: Vec3 = [normal.x * o.length! * sign, normal.y * o.length! * sign, normal.z * o.length! * sign]\n let result: Shape\n // GOTCHA:projectedExtrude 是 occt-gated 的 compat op(vendored brepjs 借入),\n // 在 brepkit 引擎下执行前即抛 E_BREP_UNSUPPORTED。brepkit 有原生 L1 extrude\n // (face→solid),直接走 kernel.extrude——与 revolve.ts 同路径(不经过 vendored 借入)。\n // occt 路径保持 projectedExtrude 不变,不回退既有几何/命名行为。\n if (getBackends().config.brepEngineId === 'brepkit') {\n const kernel = getBrepApi()\n const faceHandle = brepOf(input) as BrepHandle | undefined\n if (!faceHandle) throw new Error('[stdlib/extrude] brepkit path: input is not BREP')\n const solid = kernel.extrude(faceHandle, v[0]!, v[1]!, v[2]!)\n result = fromBrep(solidToShape(kernel, solid), { solid })\n } else {\n result = (await projectedExtrude(input, v)) as Shape\n }\n // E3 后续:委托路径同样要建链根表(投影只给几何,命名是 faijs 语义)。\n // E3-b(2026-09-23,同 revolve 修法):不能对投影产物再走一次 fromBrep——\n // 同语句 registeredStmtId 去重会吞掉第二次登记(part 键保持空表,下游\n // fillet/chamfer 的 edgeRef 报 `input shape has no role table`)。改经\n // runtimeLineage.recordOutput 落语句键表 + part 键权威位。\n // D12(2026-09-24):取内核走 getBrepApi;引擎未初始化 → 跳过建表(纯 mesh 结果)。\n let kernel: BrepEngineApi | null = null\n try { kernel = getBrepApi() } catch { /* no engine -> skip role table */ }\n const solid = kernel ? (brepOf(result) as BrepHandle | undefined) : undefined\n if (kernel && solid) {\n const origin = String(getCurrentStmt()?.id ?? '')\n const table = new Map([[origin, extrudeConstructRoles(kernel, solid)]])\n const part = getCurrentStmt()?.outputs?.[0]\n runtimeLineage.recordOutput(origin as never, table, solid as never, part as never)\n }\n return result\n },\n naming: { kind: 'construct', newFaces: { via: 'explicit', vocab: [{ kind: 'semantic', name: 'top' }, { kind: 'semantic', name: 'bottom' }, { kind: 'wall', index: 0 }] } } as Provenance,\n})\n"]}
1
+ {"version":3,"file":"extrude.js","sourceRoot":"","sources":["../../src/api/extrude.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * stdlib extrude — `cad.extrude`(平台 parity op,BREP-only)\n *\n * 归属(分层红线):`cad.extrude` 是「面 → 棱柱」的**平台** op,FCStd Pad/Pocket\n * 链路与 TS 面共用它(docs/plans/2026-09-15-fcstd-to-faijs-port-plan.md M4.6:\n * 「原 `cad.fai_extrude` 路线废弃」)。up-to(拉伸到面 / 到支持体端面)属\n * CadQuery `until=` 同族语义,因此落在**本 op**;`fai_extrude`(`fai_` 前缀的\n * faijs 扩展 op,与 fai_drill / fai_split 同族)不得承载它。\n *\n * 为什么是手写 op 而不是继续用生成投影:生成投影\n * `compatOp(projectBrepOp('extrude', ['face','height'], 'A', vendoredExtrude))`\n * 有两个硬约束——\n * ① 参数表只有位置形参;D11 归一化只在「单 plain-object 形态」下按 params 表\n * 映射(api/internal/dual-form-args.ts),`(face, { upTo })` 属位置形态 →\n * 原样喂给 vendored extrude → 报错,无法表达对象形态;\n * ② compatOp 的入参先经 `borrowDeep` 借成 brepjs 视图(api/internal/compat-op.ts),\n * 在那一层 `brepOf()` 取不到 → 依赖 faceRef / brepOf / 内核直调的 up-to 实现\n * 必然走空(同 AGENTS.md 记录的「compatOp 自动提升边界」)。\n * 先例:arg-spec 的 `fillet` 条目(faijs 侧由手写 dual-op 覆盖,生成模块符号为\n * 孤儿 by design)。\n *\n * 长度形态**委托给生成投影**(`projectedExtrude`):vendored 仍是唯一拉伸引擎,\n * 既有 `cad.extrude(face, [x,y,z])` 语义零漂移。\n */\n\nimport { extrude as projectedExtrude } from './generated/operations'\nimport * as THREE from 'three'\nimport { solidToShape, matrixToArray } from '../brep/brep-ops'\nimport { getSolidBoundingBox } from '../brep/brep-utils'\nimport { getCurrentStmt, getBackends } from '../runtime-state'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { fromBrep, brepOf, isCurveShape } from '../shape'\nimport { runtimeLineage } from '../topology/naming/lineage'\nimport { formatRoleName, semantic, wall } from '../topology/naming/role-name'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { defineOp } from '../sdk'\nimport { assertPositiveNumber } from './assert'\nimport type { Shape, Vec3 } from '../mesh/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { FaceTopoRef } from '../topology/naming'\nimport { TopoRefError } from '../topology/naming'\n\n/**\n\n * extrude 的 construct 词汇表(计划 §4.4):`cap:bottom` / `cap:top` / `wall:<i>`。\n *\n * 判定口径:\n * - 拉伸方向**优先由 op 给出**(`hintAxis`),但必须由现场几何裁定:只有当至少\n * 两张面的法向与该方向 |cos| ≈ 1 时才采信,否则回退到纯几何判定(A2,见下);\n * - 面法向与拉伸轴 |cos| ≈ 1 → 端面:中心沿轴分量小者 `cap:bottom`、大者 `cap:top`;\n * - 面法向与拉伸轴 |cos| ≈ 0 → 侧面 `wall:<i>`,i = 该面在侧面枚举中的相对序\n * (OCCT prism 的侧面顺序 = profile wire 的边序——makeWire 按构造顺序收边,\n * prism 不重排;G3-L1 改参重放依赖该稳定性);\n * - 侧面含**非平面**(profile 里的弧段扫出圆柱面)同样进表:圆柱面 uv 中点法向\n * 是径向,与拉伸轴垂直,判定与平面侧面同式。\n *\n * @param kernel - the OCCT kernel.\n * @param solid - the extruded solid.\n * @param hintAxis - 拉伸方向(op 已知;仅作候选,由现场几何确认)。\n * @returns role(线格式串)→ hash 子表。\n */\nfunction extrudeConstructRoles(\n kernel: BrepEngineApi,\n solid: BrepHandle,\n hintAxis?: readonly [number, number, number],\n): Map<string, number[]> {\n const roles = new Map<string, number[]>()\n const faceHandles = kernel.getSubShapes(solid, 'face')\n try {\n const hashes = kernel.subShapeHashes(solid, 'face', 2147483647)\n // A2 (2026-09-26, Beds.FCStd): 侧面可能是**圆柱面**——profile 含弧段时,该弧\n // 沿拉伸方向扫出的就是一个圆柱侧面。旧实现只在 `surfaceType === 'plane'` 时\n // 取法向,于是圆柱侧面 normals[i] === undefined:既拿不到 `wall:<i>`,还让\n // `wallIdx` 跳过它们 —— wall 序号因此不再是 profile 边序(违反 role-name.ts\n // 的契约「wall:3 = 由 profile 第 3 条边扫出」)。实测 Beds part28(轮廓含 4\n // 段弧)10 面只覆盖 6 个(缺序号 2/4/6/8),下游 fillet 的 `cad.edgeRef`\n // 遂抛 `edgeRef: adjacent face ordinal 2 has no role lineage`。\n // 圆柱面 uv 中点的法向是径向、与拉伸轴垂直(|cos| ≈ 0),可直接用于侧面判定。\n const planar = faceHandles.map((f) => kernel.surfaceType(f) === 'plane')\n const normals: (readonly [number, number, number])[] = faceHandles.map((f) => {\n const uv = kernel.uvBounds(f)\n const n = kernel.surfaceNormal(f, (uv.uMin + uv.uMax) / 2, (uv.vMin + uv.vMax) / 2)\n return [n.x, n.y, n.z] as const\n })\n const centers = faceHandles.map((f) => {\n const c = kernel.surfaceCenterOfMass(f)\n return [c.x, c.y, c.z] as const\n })\n\n // 拉伸轴:op 已知的拉伸方向优先(须由现场几何确认),否则找一对\n // |cos| ≈ 1 的反平行平面法向 → 带符号单位方向。\n //\n // A2 (2026-09-26, Beds.FCStd part28): 纯几何判定取「第一对反平行的平面法向」,\n // 但方形类轮廓的**一对对侧平面侧壁同样反平行**——Beds 的柱子于是把 X 向侧壁\n // 判成端盖、把真正的 Z 向端盖当成 wall,4 个圆柱侧面彻底无名(10 面只覆盖 6,\n // 缺序号 2/4/6/8),下游 fillet 的 `cad.edgeRef` 抛\n // `edgeRef: adjacent face ordinal 2 has no role lineage`。方向由 op 给出、\n // 由几何裁定(至少两张面法向与该方向 |cos| ≈ 1 才采信),两者缺一不可。\n let axis: readonly [number, number, number] | undefined\n const hintLen = hintAxis ? Math.hypot(hintAxis[0], hintAxis[1], hintAxis[2]) : 0\n if (hintAxis && hintLen > 1e-9) {\n const d: readonly [number, number, number] = [\n hintAxis[0] / hintLen, hintAxis[1] / hintLen, hintAxis[2] / hintLen,\n ]\n const aligned = normals.filter((n) => {\n const len = Math.hypot(n[0], n[1], n[2])\n if (len < 1e-9) return false\n return Math.abs((n[0] * d[0] + n[1] * d[1] + n[2] * d[2]) / len) > 0.999\n })\n if (aligned.length >= 2) axis = d\n }\n for (let i = 0; i < normals.length && !axis; i++) {\n // 端面必须是一对反平行的**平面**法向(棱柱的端盖恒为平面)。\n if (!planar[i]) continue\n const a = normals[i]\n if (!a) continue\n for (let j = i + 1; j < normals.length; j++) {\n if (!planar[j]) continue\n const b = normals[j]\n if (!b) continue\n const lenA = Math.hypot(a[0], a[1], a[2])\n const lenB = Math.hypot(b[0], b[1], b[2])\n if (lenA < 1e-9 || lenB < 1e-9) continue\n const cos = (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]) / (lenA * lenB)\n if (Math.abs(cos) > 0.999) {\n // b 反向即「从 i 面指向 j 面」的方向\n axis = [b[0] - a[0], b[1] - a[1], b[2] - a[2]] as const\n break\n }\n }\n }\n const axisLen = axis ? Math.hypot(axis[0], axis[1], axis[2]) : 0\n if (axis && axisLen > 1e-9) {\n const ax: readonly [number, number, number] = [axis[0] / axisLen, axis[1] / axisLen, axis[2] / axisLen]\n // 端面:沿轴投影最小者 = cap:bottom,最大者 = cap:top\n const capCandidates: { idx: number; t: number }[] = []\n for (let i = 0; i < normals.length; i++) {\n if (!planar[i]) continue\n const n = normals[i]\n if (!n) continue\n const len = Math.hypot(n[0], n[1], n[2]) || 1\n const cos = Math.abs(n[0] * ax[0] + n[1] * ax[1] + n[2] * ax[2]) / len\n if (cos > 0.999) {\n capCandidates.push({ idx: i, t: centers[i]![0] * ax[0] + centers[i]![1] * ax[1] + centers[i]![2] * ax[2] })\n }\n }\n if (capCandidates.length >= 2) {\n capCandidates.sort((p, q) => p.t - q.t)\n const bottom = capCandidates[0]!\n const top = capCandidates[capCandidates.length - 1]!\n // 端面词汇用 semantic('bottom'/'top'——与 box 同形)。计划 §4.4 写的\n // `cap:<name>` 前缀形态与 RoleName 契约冲突(semantic 名不允许 ':',\n // role-name.ts SEMANTIC_NAME_RE),落地以契约为准:semantic 直名。\n if (hashes[bottom.idx] !== undefined) roles.set(formatRoleName(semantic('bottom')), [hashes[bottom.idx]!])\n if (hashes[top.idx] !== undefined) roles.set(formatRoleName(semantic('top')), [hashes[top.idx]!])\n }\n // 侧面:|cos| ≈ 0,按枚举序编号(= profile 边序)。平面/圆柱侧面一律进表\n // (A2)——跳过圆柱侧面会让 wall 序号脱离 profile 边序。\n let wallIdx = 0\n for (let i = 0; i < normals.length; i++) {\n const n = normals[i]\n if (!n || hashes[i] === undefined) continue\n const len = Math.hypot(n[0], n[1], n[2]) || 1\n const cos = Math.abs(n[0] * ax[0] + n[1] * ax[1] + n[2] * ax[2]) / len\n if (cos < 0.001) {\n roles.set(formatRoleName(wall(wallIdx)), [hashes[i]!])\n wallIdx++\n }\n }\n }\n return roles\n } finally {\n for (const h of faceHandles) {\n try { kernel.release(h) } catch { /* 已释放 */ }\n }\n }\n}\n\n/** 显式平面目标(up-to 截断面):点 + 法向(点在平面上即可,无需共面于面边界)。 */\nexport interface UpToPlaneSpec {\n /** 平面上一点(世界坐标)。type:[x,y,z] */\n point: Vec3\n /** 平面法向(世界坐标,方向仅定半空间语义,截断取两侧之一)。type:[x,y,z] */\n normal: Vec3\n}\n\n/** up-to 目标:显式面引用 / 显式平面 / 支持体沿拉伸方向的远端(last)/近端(first)端面。 */\nexport type ExtrudeUpTo = FaceTopoRef | 'last' | 'first' | { plane: UpToPlaneSpec }\n\n/** `cad.extrude` 的对象形态参数(位置形态由 {@link normalizeExtrudeOptions} 归一到这里)。 */\nexport interface ExtrudeOptions {\n /** 拉伸量(mm,>0)。up-to 模式下无需给。 */\n length?: number\n /** 拉伸方向(世界坐标);缺省 [0,0,1]。 */\n normal?: Vec3\n /** 沿 normal 的前进方向;缺省 'forward'('backward' 等价于方向取反)。 */\n mode?: 'forward' | 'backward'\n /** 拉伸到面 / 到支持体端面;提供时忽略 length。 */\n upTo?: ExtrudeUpTo\n /** upTo 'last'/'first' 的支持体(累积支持体,由转换器显式传入)。 */\n baseFeature?: Shape\n /** 截断面沿其法向的偏移(mm);缺省 0。 */\n offset?: number\n}\n\n/** 判定是否为朴素对象(对象形态判别器,避免误吞 Shape / 数组)。 */\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype\n}\n\n/**\n * 归一化 `cad.extrude` 的两种调用形态。\n *\n * - 位置形态(与生成投影同款,历史产物在用):`cad.extrude(face, 10)` /\n * `cad.extrude(face, [x,y,z])`——向量即「方向 × 长度」,恒为沿该方向前进。\n * - 对象形态(faijs 面):`cad.extrude(face, { length, normal, mode, upTo, ... })`。\n *\n * @param params - 调用点第二实参(数字 / 向量 / 对象)。\n * @returns 归一化后的对象形态参数。\n * @throws Error 参数既非数字 / 向量 / 朴素对象时(不猜)。\n */\nfunction normalizeExtrudeOptions(params: unknown): ExtrudeOptions {\n if (isPlainObject(params)) return params as ExtrudeOptions\n if (Array.isArray(params)) {\n const v = params as Vec3\n const len = Math.hypot(v[0] ?? 0, v[1] ?? 0, v[2] ?? 0)\n if (!Number.isFinite(len) || len < 1e-12) {\n throw new Error('E_EXTRUDE_ZERO_VECTOR: extrude direction vector has zero length')\n }\n return { length: len, normal: [v[0] / len, v[1] / len, v[2] / len], mode: 'forward' }\n }\n if (typeof params === 'number') return { length: params, normal: [0, 0, 1], mode: 'forward' }\n throw new Error('E_EXTRUDE_BAD_PARAMS: extrude expects (face, number | [x,y,z] | { length | upTo, ... })')\n}\n\n/**\n * 参数自校验:up-to 模式下无需 length;否则 length 必须为正数。\n *\n * @param o - 归一化后的参数。\n */\nfunction assertExtrudeOptions(o: ExtrudeOptions): void {\n if (o.upTo !== undefined) return // up-to 模式:长度由目标面决定\n assertPositiveNumber(o.length, 'extrude.length')\n}\n\n/** bbox 对角线长度(长试探的余量基数)。 */\nfunction bboxDiag(bbox: { min: Vec3; max: Vec3 }): number {\n return Math.hypot(bbox.max[0] - bbox.min[0], bbox.max[1] - bbox.min[1], bbox.max[2] - bbox.min[2])\n}\n\n/**\n * 目标面法向负侧的半空间覆盖盒(截断长拉伸用)——定向版。\n *\n * GOTCHA(PadTest Pad001 斜置基准面实测):轴对齐盒的边界不是斜面本身,\n * 截断呈台阶而非精确斜平面(体积 7076 vs 真值 4860)。正确做法:在局部基\n * (u, v, n) 里构造盒(深度沿 −n 伸出 R、横向 ±R),再用 kernel.transform\n * 旋到世界系——盒的 n=R 端面就是精确的目标平面。\n *\n * @param kernel - BREP 内核。\n * @param faceCenter - 目标面中心(世界坐标,盒的 n=0 端面过此点)。\n * @param faceNormal - 目标面法向(半空间保留 −n 侧…即盒占据 −n 侧深度)。\n * @param reach - 覆盖半径 R。\n * @returns 定向半空间盒的 brep 句柄。\n */\nfunction farSideBox(kernel: BrepEngineApi, faceCenter: Vec3, faceNormal: Vec3, reach: number): BrepHandle {\n const unit = new THREE.Vector3(...faceNormal).normalize()\n const center = new THREE.Vector3(...faceCenter)\n // 与 n 正交的正交基 u,v\n const aux = Math.abs(unit.y) < 0.9 ? new THREE.Vector3(0, 1, 0) : new THREE.Vector3(1, 0, 0)\n const u = new THREE.Vector3().crossVectors(unit, aux).normalize()\n const v = new THREE.Vector3().crossVectors(unit, u).normalize()\n const R = reach\n // 局部系盒:x∈[−R,R] (u), y∈[−R,R] (v), z∈[−R,0] (n 深度侧)\n const box = kernel.makeBoxFromCorners(\n { x: -R, y: -R, z: -R },\n { x: R, y: R, z: 0 },\n )\n // 旋转矩阵:列 = u, v, n;平移 = center(局部 z=0 端面落到过 center 的目标平面)\n const m = new THREE.Matrix4().makeBasis(u, v, unit).setPosition(center)\n return kernel.transform(box, matrixToArray(m))\n}\n\n/**\n * up-to 拉伸:occt-wasm 未暴露 `BRepFeat_MakePrism`,用「长拉伸 → 与目标面侧\n * 半空间盒求交」等价组合;长试探上限只作布尔构造辅助,不进入结果几何。\n *\n * @param kernel - BREP 内核。\n * @param inputSolid - 轮廓实体句柄。\n * @param o - 归一化参数(需带 upTo)。\n * @returns 截断后的实体句柄。\n * @throws Error E_UP_TO_* 系列:缺支持体 / 方向与目标面平行 / 不相交(禁静默)。\n */\nfunction extrudeUpToSolid(kernel: BrepEngineApi, inputSolid: BrepHandle, o: ExtrudeOptions): BrepHandle {\n const normal = new THREE.Vector3(...(o.normal ?? [0, 0, 1])).normalize()\n const offset = o.offset ?? 0\n const upTo = o.upTo as ExtrudeUpTo\n\n // 1) 方向目标:FaceTopoRef → 现场面几何;'last'/'first' → baseFeature 支持体\n // 沿拉伸方向的端面(远端 last / 近端 first)。\n let targetCenter: Vec3\n let targetNormal: Vec3\n if (typeof upTo === 'string') {\n const base = o.baseFeature\n const baseSolid = base ? (brepOf(base) as BrepHandle | undefined) : undefined\n if (!baseSolid) {\n throw new Error('E_UP_TO_NO_BASE: upTo \"last\"/\"first\" requires a baseFeature shape')\n }\n // 支持体 bbox 沿拉伸方向的远端/近端平面(R-C:baseFeature 显式传参)\n const bbox = getSolidBoundingBox(kernel, baseSolid)\n const pick = (which: 'max' | 'min'): Vec3 => {\n const c: Vec3 = [\n (bbox.min[0] + bbox.max[0]) / 2,\n (bbox.min[1] + bbox.max[1]) / 2,\n (bbox.min[2] + bbox.max[2]) / 2,\n ]\n const extent = which === 'max' ? 1 : -1\n const proj =\n (bbox.max[0] - bbox.min[0]) * Math.abs(normal.x) +\n (bbox.max[1] - bbox.min[1]) * Math.abs(normal.y) +\n (bbox.max[2] - bbox.min[2]) * Math.abs(normal.z)\n return [\n c[0] + ((normal.x * proj) / 2) * extent,\n c[1] + ((normal.y * proj) / 2) * extent,\n c[2] + ((normal.z * proj) / 2) * extent,\n ]\n }\n targetCenter = pick(upTo === 'last' ? 'max' : 'min')\n targetNormal = [normal.x, normal.y, normal.z]\n } else if (upTo && typeof upTo === 'object' && 'plane' in upTo) {\n // 显式平面目标(datum plane 等无限平面:截断面由平面方程决定,不受\n // 目标面边界限制——斜置基准面时顶面是斜的,定长路径语义错误)。\n targetCenter = [...upTo.plane.point] as Vec3\n targetNormal = [...upTo.plane.normal] as Vec3\n } else {\n // GOTCHA(探针实测):FaceTopoRef 携带的 hint 是 faceRef 现场捕获的\n // center/normal 快照,直接使用;不要对拉伸轮廓(sketch 面)做二次\n // resolveFaceGeometry——引用的面在目标体上、不在轮廓上,会 E_TOPO_NOT_FOUND。\n if (upTo.hint.center && upTo.hint.normal) {\n targetCenter = [...upTo.hint.center] as Vec3\n targetNormal = [...upTo.hint.normal] as Vec3\n } else {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'extrude upTo: face ref hint has no center/normal snapshot')\n }\n }\n\n // 2) 长试探长度:GOTCHA(探针实测)——不能按轮廓自身 bbox 对角线算(小轮廓够不到\n // 远处目标面);必须取「截断平面沿拉伸方向的投影距离」加余量,保证长拉伸至\n // 少穿过目标面。\n const bbox = getSolidBoundingBox(kernel, inputSolid)\n const shiftedCenter0: Vec3 = [\n targetCenter[0] + targetNormal[0] * offset,\n targetCenter[1] + targetNormal[1] * offset,\n targetCenter[2] + targetNormal[2] * offset,\n ]\n // 自动定向(仅 { plane } 目标):FreeCAD UpToFace 朝目标面所在方向拉伸。\n // GOTCHA(PadTest Pad001 实测,probe 探针):斜置基准面在草图局部坐标里\n // 位于轮廓的反方向(圆心处平面交点 t=−26 而非 +26),固定沿 +Z 拉截不到\n // 平面(体积 597 ≠ 真值 4860)。轮廓 bbox 中心到截断平面的有向投影与\n // 拉伸方向异号 → 翻转拉伸方向(就地翻转 Vector3,后续 tMax/keepSide 自洽)。\n if (upTo && typeof upTo === 'object' && 'plane' in upTo) {\n const c0: Vec3 = [\n (bbox.min[0] + bbox.max[0]) / 2,\n (bbox.min[1] + bbox.max[1]) / 2,\n (bbox.min[2] + bbox.max[2]) / 2,\n ]\n const tnAuto = new THREE.Vector3(...targetNormal).normalize()\n const dAuto =\n (c0[0] - shiftedCenter0[0]) * tnAuto.x +\n (c0[1] - shiftedCenter0[1]) * tnAuto.y +\n (c0[2] - shiftedCenter0[2]) * tnAuto.z\n // 沿拉伸方向到平面的有向交点参数:t = −((c0−plane)·n) / (dir·n)。\n // t<0 → 平面在行进反方向 → 翻转(FreeCAD UpToFace 朝平面拉伸)。\n const tAuto = -dAuto / normal.dot(tnAuto)\n if (tAuto < 0) {\n normal.multiplyScalar(-1)\n }\n }\n const cornerProjections = [\n [bbox.min[0], bbox.min[1], bbox.min[2]],\n [bbox.max[0], bbox.min[1], bbox.min[2]],\n [bbox.min[0], bbox.max[1], bbox.min[2]],\n [bbox.min[0], bbox.min[1], bbox.max[2]],\n [bbox.max[0], bbox.max[1], bbox.min[2]],\n [bbox.max[0], bbox.min[1], bbox.max[2]],\n [bbox.min[0], bbox.max[1], bbox.max[2]],\n [bbox.max[0], bbox.max[1], bbox.max[2]],\n ] as Vec3[]\n const planePoint0 = new THREE.Vector3(...shiftedCenter0)\n const tn0 = new THREE.Vector3(...targetNormal).normalize()\n const planeD = planePoint0.dot(tn0)\n const dirD = normal.dot(tn0)\n if (Math.abs(dirD) < 1e-9) {\n throw new Error('E_UP_TO_PARALLEL: extrude direction is parallel to the target face plane')\n }\n const tMax = cornerProjections\n .map((p) => (planeD - new THREE.Vector3(...p).dot(tn0)) / dirD)\n .reduce((a, b) => Math.max(a, b), 0)\n const L = tMax + Math.abs(offset) + bboxDiag(bbox) * 0.1 + 1\n const longSolid = kernel.extrude(inputSolid, normal.x * L, normal.y * L, normal.z * L)\n\n // 3) 截断平面 = 目标面 + offset(沿面法向)。GOTCHA(探针实测):半空间侧不能固定\n // 取「法向负侧」——目标面法向朝向与拉伸方向无必然关系。正确语义:保留「包含\n // 拉伸起点」的那一侧。farSideBox 覆盖「传入法向的反侧」,因此传 -keepSide。\n const tn = tn0\n const startCenter = new THREE.Vector3(\n (bbox.min[0] + bbox.max[0]) / 2,\n (bbox.min[1] + bbox.max[1]) / 2,\n (bbox.min[2] + bbox.max[2]) / 2,\n )\n const planePoint = new THREE.Vector3(...shiftedCenter0)\n const d = startCenter.sub(planePoint).dot(tn)\n // 保留侧法向:起点所在侧(d 与 tn 同号 → 正侧;异号 → 负侧);d≈0 时退化为\n // 拉伸方向(起点在面上时按拉伸方向截)\n const keepSide = Math.abs(d) < 1e-9 ? normal.clone() : tn.clone().multiplyScalar(Math.sign(d))\n // GOTCHA(PadTest Pad001 斜置基准面实测):targetCenter 可以是平面上离轮廓\n // 很远的任意一点(基准面 point 在草图局部坐标里横向偏 ~100),reach 若只取\n // L(按轮廓到截断面的投影距离)则半空间盒横向盖不住长拉伸体 → common 为空。\n // reach 必须 ≥ 平面点到拉伸体的距离 + 体对角线(全方向覆盖)。\n const reach =\n new THREE.Vector3(...shiftedCenter0).distanceTo(\n new THREE.Vector3((bbox.min[0] + bbox.max[0]) / 2, (bbox.min[1] + bbox.max[1]) / 2, (bbox.min[2] + bbox.max[2]) / 2),\n ) + bboxDiag(bbox) + Math.abs(offset) + 1\n const halfspace = farSideBox(kernel, shiftedCenter0, [-keepSide.x, -keepSide.y, -keepSide.z], reach)\n\n // 4) 求交:长拉伸 ∩ 半空间盒\n const clipped = kernel.common(longSolid, halfspace)\n kernel.release(longSolid)\n kernel.release(halfspace)\n\n // 预检:方向上不相交 → 显式报错(禁静默)\n const clippedBBox = getSolidBoundingBox(kernel, clipped)\n if (!Number.isFinite(bboxDiag(clippedBBox)) || bboxDiag(clippedBBox) < 1e-9) {\n kernel.release(clipped)\n throw new Error('E_UP_TO_NO_INTERSECTION: extrude direction does not reach the target face')\n }\n return clipped\n}\n\n/**\n * 沿 normal 拉伸几何(面 → 棱柱)。\n *\n * up-to 模式(`upTo`)与长度模式(`length`)二选一;长度模式委托生成投影\n * (vendored extrude 为唯一引擎),up-to 模式走半空间组合。\n *\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name extrude\n * @returns Shape 拉伸后的几何。\n * @param input - 目标几何(平面面)。type:Shape required:true\n * @param params.length - 总拉伸量(mm,>0)。up-to 模式下无需给。type:number\n * @param params.normal - 拉伸方向(世界坐标)。type:[x,y,z] 默认 [0,0,1]\n * @param params.mode - 沿 normal 的前进方向:forward 正向 / backward 反向。type:'forward' | 'backward' 默认 'forward'\n * @param params.upTo - 拉伸到面:FaceTopoRef 指定面 / 'last' 支持体远端面 / 'first' 近端面(需 baseFeature)。提供时忽略 length。type:'last' | 'first' | FaceTopoRef\n * @param params.baseFeature - upTo 'last'/'first' 的支持体几何(累积支持体)。type:Shape\n * @param params.offset - 截断面沿其法向的偏移(mm)。type:number 默认 0\n * @example\n * const p = await cad.extrude(part0, [0, 0, 10])\n * const p = await cad.extrude(part0, { length: 10 })\n * const p = await cad.extrude(sk, { upTo: cad.faceRef(part0, 3) })\n * const p = await cad.extrude(sk, { upTo: 'last', baseFeature: part0 })\n */\nexport const extrude = defineOp({\n name: 'extrude',\n brep: async (input: Shape, params: unknown): Promise<Shape> => {\n if (!input) throw new Error('[stdlib/extrude] no input geometry')\n // Phase 3 执行前预检:1D 曲线(wire / helix / sketch as:'wire')不是面,不得落进\n // 内核深层才报「操作失败」——维度不符在调用内核前明确拒绝(1D 判别位 kind='curve')。\n if (isCurveShape(input)) {\n throw new Error(\n 'E_EXTRUDE_NEEDS_FACE: extrude requires a 2D face input, got a 1D curve (kind=\"curve\")',\n )\n }\n const o = normalizeExtrudeOptions(params)\n assertExtrudeOptions(o)\n // A2: 拉伸方向同时是链根建表的候选轴(extrudeConstructRoles 的 hintAxis),\n // 两条路径(定长 / up-to)都要用,故提到分支之前。\n const normal = new THREE.Vector3(...(o.normal ?? [0, 0, 1])).normalize()\n\n if (o.upTo !== undefined) {\n const kernel = getBrepApi()\n const inputSolid = brepOf(input) as BrepHandle | undefined\n if (!inputSolid) throw new Error('[stdlib/extrude] input is not BREP')\n const clipped = extrudeUpToSolid(kernel, inputSolid, o)\n // E3-b(2026-09-23,同 revolve 修法):不能对投影产物再走一次 fromBrep——\n // 同语句 registeredStmtId 去重会吞掉第二次登记(part 键保持空表,下游\n // fillet/chamfer 的 edgeRef 报 nameless shape)。upTo 路径不经过投影/\n // adoptOut,fromBrep 只在此走一次,roleTable 直挂 + solid 句柄登记。\n const origin = String(getCurrentStmt()?.id ?? '')\n return fromBrep(solidToShape(kernel, clipped), {\n solid: clipped,\n roleTable: new Map([[origin, extrudeConstructRoles(kernel, clipped, normal.toArray() as Vec3)]]),\n })\n }\n\n // 长度形态:委托生成投影(生成投影自带借入 / Result 翻转 / 收养)\n const sign = o.mode === 'backward' ? -1 : 1\n const v: Vec3 = [normal.x * o.length! * sign, normal.y * o.length! * sign, normal.z * o.length! * sign]\n let result: Shape\n // GOTCHA:projectedExtrude 是 occt-gated 的 compat op(vendored brepjs 借入),\n // 在 brepkit 引擎下执行前即抛 E_BREP_UNSUPPORTED。brepkit 有原生 L1 extrude\n // (face→solid),直接走 kernel.extrude——与 revolve.ts 同路径(不经过 vendored 借入)。\n // occt 路径保持 projectedExtrude 不变,不回退既有几何/命名行为。\n if (getBackends().config.brepEngineId === 'brepkit') {\n const kernel = getBrepApi()\n const faceHandle = brepOf(input) as BrepHandle | undefined\n if (!faceHandle) throw new Error('[stdlib/extrude] brepkit path: input is not BREP')\n const solid = kernel.extrude(faceHandle, v[0]!, v[1]!, v[2]!)\n result = fromBrep(solidToShape(kernel, solid), { solid })\n } else {\n result = (await projectedExtrude(input, v)) as Shape\n }\n // E3 后续:委托路径同样要建链根表(投影只给几何,命名是 faijs 语义)。\n // E3-b(2026-09-23,同 revolve 修法):不能对投影产物再走一次 fromBrep——\n // 同语句 registeredStmtId 去重会吞掉第二次登记(part 键保持空表,下游\n // fillet/chamfer 的 edgeRef 报 `input shape has no role table`)。改经\n // runtimeLineage.recordOutput 落语句键表 + part 键权威位。\n // D12(2026-09-24):取内核走 getBrepApi;引擎未初始化 → 跳过建表(纯 mesh 结果)。\n let kernel: BrepEngineApi | null = null\n try { kernel = getBrepApi() } catch { /* no engine -> skip role table */ }\n const solid = kernel ? (brepOf(result) as BrepHandle | undefined) : undefined\n if (kernel && solid) {\n const origin = String(getCurrentStmt()?.id ?? '')\n const table = new Map([[origin, extrudeConstructRoles(kernel, solid, normal.toArray() as Vec3)]])\n const part = getCurrentStmt()?.outputs?.[0]\n runtimeLineage.recordOutput(origin as never, table, solid as never, part as never)\n }\n return result\n },\n naming: { kind: 'construct', newFaces: { via: 'explicit', vocab: [{ kind: 'semantic', name: 'top' }, { kind: 'semantic', name: 'bottom' }, { kind: 'wall', index: 0 }] } } as Provenance,\n})\n"]}
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * 设计:
9
9
  * - 输入:{ contours: ProfileLoop[] },每个 loop = 有序 segments(line / arc,z=0)。
10
- * - 外环 = 面积(shoelace)绝对值最大的 loop;其余 loop 作为孔。
10
+ * - 环语义:嵌套 = 孔,不相交 = 独立岛(多岛 → face 的 compound),见 buildProfileShape。
11
11
  * - 圆弧:用起点/中点/终点三点的 makeArcEdge;整圆(sweep≈2π)拆成两段弧。
12
12
  * - 输出:Shape(mesh 三角化 + 句柄登记)。brep-only(下游 extrude/revolve 亦 brep-only)。
13
13
  *
@@ -45,11 +45,11 @@ export type ProfileSeg = ProfileLineSeg | ProfileArcSeg;
45
45
  export interface ProfileLoop {
46
46
  segments: ProfileSeg[];
47
47
  }
48
- /** `cad.profile` 参数:轮廓环集合(第一个为外环,其余为孔)。 */
48
+ /** `cad.profile` 参数:轮廓环集合。语义见 {@link buildProfileShape}(嵌套=孔,不相交=独立岛)。 */
49
49
  export interface ProfileParams {
50
50
  contours: ProfileLoop[];
51
51
  /**
52
- * 产物形态:'face'(默认)构面;'wire' 只交出不带面的外环 wire(1D 曲线),
52
+ * 产物形态:'face'(默认)构面;'wire' 只交出不带面的最大岛外环 wire(1D 曲线),
53
53
  * 供扫掠族(sweep/loft/…)直接作 spine。'wire' 形态丢弃孔环(扫掠脊柱为单闭合轮廓)。
54
54
  */
55
55
  as?: 'face' | 'wire';
@@ -61,12 +61,16 @@ export interface ProfileParams {
61
61
  */
62
62
  export declare function assertProfileParams(params: Record<string, unknown>): void;
63
63
  /**
64
- * BREP 路径:2D 轮廓 → planar face(外环 + 孔),或 'wire' 形态仅交外环 wire。
64
+ * BREP 路径:2D 轮廓集合 → planar face 集合。
65
65
  *
66
- * 导出供 `@faicad/faijs-sketch` 等下游库复用——构面实现单点,草图求解结果经此构面。
66
+ * GOTCHA(2026-09-26,Beds part16/39):环语义是「**嵌套 = 孔,不相交 = 独立岛**」
67
+ * (与 FreeCAD 草图一致),不是「面积最大者当外环、其余全当孔」。旧实现把双岛轮廓的
68
+ * 第二个岛判成「洞外面」→ `addHolesInFace` 收到与外环不相交的 wire → 构面退化
69
+ * (7 面、volume 0)。多岛产物 = compound(每岛一面、各带自己的孔),下游
70
+ * `cad.extrude` 对 compound 输入逐面挤出(kernel MakePrism 收复合形状)。
67
71
  *
68
72
  * @param params - 轮廓环集合与产物形态。
69
- * @returns Shape(`as:'wire'` 时为 1D 曲线)。
73
+ * @returns Shape(单岛为 face;多岛为 face 的 compound;`as:'wire'` 为 1D 曲线)。
70
74
  */
71
75
  export declare function buildProfileShape(params: ProfileParams): Shape;
72
76
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/api/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAU1C,2BAA2B;AAC3B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;CACX;AAED,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,KAAK,CAAA;IACX,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,cAAc;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,OAAO,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;CACX;AAED,0BAA0B;AAC1B,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,aAAa,CAAA;AAEvD,yBAAyB;AACzB,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,UAAU,EAAE,CAAA;CACvB;AAED,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACrB;AAID;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAazE;AA4FD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,KAAK,CAiC9D;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO;;CAOlB,CAAA"}
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/api/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAU1C,2BAA2B;AAC3B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;CACX;AAED,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,KAAK,CAAA;IACX,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,cAAc;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,OAAO,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;CACX;AAED,0BAA0B;AAC1B,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,aAAa,CAAA;AAEvD,yBAAyB;AACzB,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,UAAU,EAAE,CAAA;CACvB;AAED,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACrB;AAID;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAazE;AA6HD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,KAAK,CAuE9D;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO;;CAOlB,CAAA"}
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * 设计:
9
9
  * - 输入:{ contours: ProfileLoop[] },每个 loop = 有序 segments(line / arc,z=0)。
10
- * - 外环 = 面积(shoelace)绝对值最大的 loop;其余 loop 作为孔。
10
+ * - 环语义:嵌套 = 孔,不相交 = 独立岛(多岛 → face 的 compound),见 buildProfileShape。
11
11
  * - 圆弧:用起点/中点/终点三点的 makeArcEdge;整圆(sweep≈2π)拆成两段弧。
12
12
  * - 输出:Shape(mesh 三角化 + 句柄登记)。brep-only(下游 extrude/revolve 亦 brep-only)。
13
13
  *
@@ -131,43 +131,125 @@ function loopToWire(kernel, loop) {
131
131
  }
132
132
  return kernel.makeWire(edges);
133
133
  }
134
+ /** 环 → 2D 采样多边形(直线取端点、弧等角采样),用于环间包含性判定。 */
135
+ function loopToPolygon(loop) {
136
+ const pts = [];
137
+ for (const seg of loop.segments) {
138
+ if (seg.kind === 'line') {
139
+ pts.push({ x: seg.x1, y: seg.y1 });
140
+ continue;
141
+ }
142
+ let sweep = seg.endAngle - seg.startAngle;
143
+ while (sweep <= 0)
144
+ sweep += 2 * Math.PI;
145
+ while (sweep > 2 * Math.PI)
146
+ sweep -= 2 * Math.PI;
147
+ const n = Math.max(8, Math.ceil((sweep / (2 * Math.PI)) * 32));
148
+ for (let k = 0; k < n; k++) {
149
+ const a = seg.startAngle + (sweep * k) / n;
150
+ pts.push({ x: seg.cx + seg.radius * Math.cos(a), y: seg.cy + seg.radius * Math.sin(a) });
151
+ }
152
+ }
153
+ return pts;
154
+ }
155
+ /** 射线法:点是否在多边形内(采样多边形足够密,顶点/边界角落情形由环间不共边保证不出现)。 */
156
+ function polygonContains(poly, px, py) {
157
+ let inside = false;
158
+ for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
159
+ const xi = poly[i].x;
160
+ const yi = poly[i].y;
161
+ const xj = poly[j].x;
162
+ const yj = poly[j].y;
163
+ if (yi > py !== yj > py && px < ((xj - xi) * (py - yi)) / (yj - yi) + xi)
164
+ inside = !inside;
165
+ }
166
+ return inside;
167
+ }
134
168
  /**
135
- * BREP 路径:2D 轮廓 → planar face(外环 + 孔),或 'wire' 形态仅交外环 wire。
169
+ * BREP 路径:2D 轮廓集合 → planar face 集合。
136
170
  *
137
- * 导出供 `@faicad/faijs-sketch` 等下游库复用——构面实现单点,草图求解结果经此构面。
171
+ * GOTCHA(2026-09-26,Beds part16/39):环语义是「**嵌套 = 孔,不相交 = 独立岛**」
172
+ * (与 FreeCAD 草图一致),不是「面积最大者当外环、其余全当孔」。旧实现把双岛轮廓的
173
+ * 第二个岛判成「洞外面」→ `addHolesInFace` 收到与外环不相交的 wire → 构面退化
174
+ * (7 面、volume 0)。多岛产物 = compound(每岛一面、各带自己的孔),下游
175
+ * `cad.extrude` 对 compound 输入逐面挤出(kernel MakePrism 收复合形状)。
138
176
  *
139
177
  * @param params - 轮廓环集合与产物形态。
140
- * @returns Shape(`as:'wire'` 时为 1D 曲线)。
178
+ * @returns Shape(单岛为 face;多岛为 face 的 compound;`as:'wire'` 为 1D 曲线)。
141
179
  */
142
180
  export function buildProfileShape(params) {
143
181
  const kernel = getBrepApi();
144
182
  const as = params.as ?? 'face';
145
183
  const loops = params.contours.map((loop) => ({ loop, area: Math.abs(loopSignedArea(loop)) }));
146
- // 面积最大者为外环,其余为孔
147
- let outerIdx = 0;
148
- for (let i = 1; i < loops.length; i++) {
149
- if (loops[i].area > loops[outerIdx].area)
150
- outerIdx = i;
184
+ // 包含分类:每个环的直接父环 = 面积最小的严格包含它的环;深度偶数 = 岛,奇数 = 父环的孔。
185
+ const polys = loops.map((l) => loopToPolygon(l.loop));
186
+ const parent = new Array(loops.length).fill(-1);
187
+ for (let i = 0; i < loops.length; i++) {
188
+ const probe = polys[i][0];
189
+ for (let j = 0; j < loops.length; j++) {
190
+ if (i === j || loops[j].area <= loops[i].area)
191
+ continue;
192
+ if (!polygonContains(polys[j], probe.x, probe.y))
193
+ continue;
194
+ if (parent[i] === -1 || loops[j].area < loops[parent[i]].area)
195
+ parent[i] = j;
196
+ }
197
+ }
198
+ const depthOf = (i) => {
199
+ let d = 0;
200
+ let cur = i;
201
+ while (parent[cur] !== -1) {
202
+ d++;
203
+ cur = parent[cur];
204
+ }
205
+ return d;
206
+ };
207
+ const islands = [];
208
+ const holesOf = new Map();
209
+ for (let i = 0; i < loops.length; i++) {
210
+ if (depthOf(i) % 2 === 0)
211
+ islands.push(i);
212
+ else {
213
+ const p = parent[i];
214
+ const arr = holesOf.get(p);
215
+ if (arr)
216
+ arr.push(i);
217
+ else
218
+ holesOf.set(p, [i]);
219
+ }
151
220
  }
152
- const outerWire = loopToWire(kernel, loops[outerIdx].loop);
153
- // 'wire' 形态:只交外环 wire(1D 曲线),不构面 —— 供扫掠族作 spine。
221
+ // 'wire' 形态:只交最大岛的外环 wire(1D 曲线),不构面 —— 供扫掠族作 spine。
154
222
  if (as === 'wire') {
155
- return fromBrepCurve(solidToShape(kernel, outerWire), { solid: outerWire });
223
+ let outer = islands[0];
224
+ for (const isl of islands) {
225
+ if (loops[isl].area > loops[outer].area)
226
+ outer = isl;
227
+ }
228
+ const wire = loopToWire(kernel, loops[outer].loop);
229
+ return fromBrepCurve(solidToShape(kernel, wire), { solid: wire });
156
230
  }
157
- const face = kernel.makeFace(outerWire);
158
- const holeWires = [];
159
- for (let i = 0; i < loops.length; i++) {
160
- if (i === outerIdx)
161
- continue;
162
- holeWires.push(loopToWire(kernel, loops[i].loop));
231
+ // 每岛一面(岛环为外环,直接子环为孔)
232
+ const faces = [];
233
+ for (const isl of islands) {
234
+ const wire = loopToWire(kernel, loops[isl].loop);
235
+ const face = kernel.makeFace(wire);
236
+ const holes = holesOf.get(isl);
237
+ if (holes && holes.length > 0) {
238
+ const holeWires = holes.map((h) => loopToWire(kernel, loops[h].loop));
239
+ const faced = kernel.addHolesInFace(face, holeWires);
240
+ kernel.release(face);
241
+ faces.push(faced);
242
+ }
243
+ else {
244
+ faces.push(face);
245
+ }
163
246
  }
164
- if (holeWires.length > 0) {
165
- const faced = kernel.addHolesInFace(face, holeWires);
166
- kernel.release(face);
167
- return fromBrep(solidToShape(kernel, faced), { solid: faced });
247
+ if (faces.length === 1) {
248
+ const f = faces[0];
249
+ return fromBrep(solidToShape(kernel, f), { solid: f });
168
250
  }
169
- kernel.release(outerWire);
170
- return fromBrep(solidToShape(kernel, face), { solid: face });
251
+ const compound = kernel.makeCompound(faces);
252
+ return fromBrep(solidToShape(kernel, compound), { solid: compound });
171
253
  }
172
254
  /**
173
255
  * 从 2D 轮廓构造平面(creator,无输入)。仅 BREP 可用。