@faicad/faijs 0.35.2 → 0.35.8

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.
Files changed (58) hide show
  1. package/dist/api/assembly/entities.js +14 -4
  2. package/dist/api/assembly/entities.js.map +1 -1
  3. package/dist/api/extrude.d.ts.map +1 -1
  4. package/dist/api/extrude.js +4 -1
  5. package/dist/api/extrude.js.map +1 -1
  6. package/dist/api/fillet.d.ts.map +1 -1
  7. package/dist/api/fillet.js +47 -0
  8. package/dist/api/fillet.js.map +1 -1
  9. package/dist/api/generated/script-face-manifest.js +1 -1
  10. package/dist/api/generated/script-face-manifest.js.map +1 -1
  11. package/dist/api/generated/topology.js +1 -1
  12. package/dist/api/generated/topology.js.map +1 -1
  13. package/dist/api/primitives.d.ts +3 -2
  14. package/dist/api/primitives.d.ts.map +1 -1
  15. package/dist/api/primitives.js +3 -2
  16. package/dist/api/primitives.js.map +1 -1
  17. package/dist/api/surface/arg-spec.d.ts.map +1 -1
  18. package/dist/api/surface/arg-spec.js +5 -6
  19. package/dist/api/surface/arg-spec.js.map +1 -1
  20. package/dist/api/topo-resolve.d.ts.map +1 -1
  21. package/dist/api/topo-resolve.js +24 -5
  22. package/dist/api/topo-resolve.js.map +1 -1
  23. package/dist/brepkit-kernel/brepkitKernel.d.ts.map +1 -1
  24. package/dist/brepkit-kernel/brepkitKernel.js +13 -2
  25. package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
  26. package/dist/brepkit-kernel/brepkitWasm.d.ts +3 -1
  27. package/dist/brepkit-kernel/brepkitWasm.d.ts.map +1 -1
  28. package/dist/brepkit-kernel/brepkitWasm.js +6 -2
  29. package/dist/brepkit-kernel/brepkitWasm.js.map +1 -1
  30. package/dist/cad-runtime/runtime.d.ts.map +1 -1
  31. package/dist/cad-runtime/runtime.js +15 -2
  32. package/dist/cad-runtime/runtime.js.map +1 -1
  33. package/dist/extra/ops/load.d.ts.map +1 -1
  34. package/dist/extra/ops/load.js +40 -2
  35. package/dist/extra/ops/load.js.map +1 -1
  36. package/dist/mesh/primitives.d.ts +6 -4
  37. package/dist/mesh/primitives.d.ts.map +1 -1
  38. package/dist/mesh/primitives.js +15 -8
  39. package/dist/mesh/primitives.js.map +1 -1
  40. package/dist/mesh/types.d.ts +4 -3
  41. package/dist/mesh/types.d.ts.map +1 -1
  42. package/dist/mesh/types.js.map +1 -1
  43. package/dist/primitives/brep-primitives.d.ts.map +1 -1
  44. package/dist/primitives/brep-primitives.js +39 -12
  45. package/dist/primitives/brep-primitives.js.map +1 -1
  46. package/dist/topology/naming/geom-hint.d.ts +12 -0
  47. package/dist/topology/naming/geom-hint.d.ts.map +1 -1
  48. package/dist/topology/naming/geom-hint.js +1 -1
  49. package/dist/topology/naming/geom-hint.js.map +1 -1
  50. package/dist/topology/naming/lineage.d.ts +1 -0
  51. package/dist/topology/naming/lineage.d.ts.map +1 -1
  52. package/dist/topology/naming/lineage.js +37 -1
  53. package/dist/topology/naming/lineage.js.map +1 -1
  54. package/dist/topology/naming/resolve-face.d.ts +13 -2
  55. package/dist/topology/naming/resolve-face.d.ts.map +1 -1
  56. package/dist/topology/naming/resolve-face.js +18 -2
  57. package/dist/topology/naming/resolve-face.js.map +1 -1
  58. package/package.json +1 -1
@@ -14,7 +14,7 @@
14
14
  * E_TOPO_NOT_FOUND(方案 R4/P0 验收③:绝不静默降级)。
15
15
  */
16
16
  import { TopoRefError } from '../../topology/naming/index.js';
17
- import { resolveTopoRef, captureEdgeAxis, captureFaceHint } from '../../topology/naming/index.js';
17
+ import { resolveTopoRef, captureEdgeAxis, captureFaceHint, captureCylinderFaceAxis } from '../../topology/naming/index.js';
18
18
  import { buildShapeResolutionContext, buildEdgeResolutionContext, resolveFaceGeometry, } from '../topo-resolve.js';
19
19
  /** 无轴可用的报错信息后缀(E_TOPO_NOT_FOUND)。 */
20
20
  function missingAxisReason(geom) {
@@ -132,10 +132,13 @@ export function resolveSolverEntity(ref, env) {
132
132
  }
133
133
  /**
134
134
  * faceIndex 简写的面几何(从解析上下文候选行直接取):
135
- * BREP 候选(活句柄)→ captureFaceHint 现场;行快照候选 → 行几何。
135
+ * **行快照优先**(与 `toScoringCandidate` / `resolveFaceGeometry` 同口径——
136
+ * brepkit 的 kernel 现场 center 会退化:uvBounds ±1e6 哨兵 → surfaceCenterOfMass
137
+ * 在参数原点求值,非居中模型上 center 全错 → 装配解算拿错面几何);无行快照才
138
+ * captureFaceHint 现场。
136
139
  */
137
140
  function faceGeometryFromContextEntry(env, entry, part) {
138
- if (entry.handle && env.kernel) {
141
+ if (entry.row === undefined && entry.handle && env.kernel) {
139
142
  const hint = captureFaceHint(env.kernel, entry.handle);
140
143
  return {
141
144
  surfaceType: hint.surfaceType,
@@ -148,11 +151,18 @@ function faceGeometryFromContextEntry(env, entry, part) {
148
151
  if (!hint?.center || !hint?.normal) {
149
152
  throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `faceIndex row has no geometry (part=${part})`);
150
153
  }
154
+ // 圆柱轴补采(同 resolveFaceGeometry):行快照不带 axis,圆柱轴一向现场采,
155
+ // 由 pointOnSurface + getFaceCylinderData 量出——不经过退化的
156
+ // surfaceCenterOfMass,现场值可靠。守住 P0①。
157
+ let axis = hint.axis;
158
+ if (axis === undefined && hint.surfaceType === 'cylinder' && entry.handle && env.kernel) {
159
+ axis = captureCylinderFaceAxis(env.kernel, entry.handle);
160
+ }
151
161
  return {
152
162
  surfaceType: hint.surfaceType,
153
163
  center: hint.center,
154
164
  normal: hint.normal,
155
- ...(hint.axis ? { axis: hint.axis } : {}),
165
+ ...(axis ? { axis } : {}),
156
166
  };
157
167
  }
158
168
  //# sourceMappingURL=entities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../../src/api/assembly/entities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api/assembly/entities — EntityRef → SolverEntity 实体抽取(P1,方案 §3.6 / §5.2)\n *\n * 三要素范式(与 chamfer 同构,方案 §3.5):\n * 1. 引用可序列化:EntityRef 进 .fai.js;\n * 2. 解析在执行期:TopoRef 复用 topo-resolve 的解析通道(BREP 现场 / mesh 行快照);\n * 3. 句柄是瞬态:解析出的几何立即转成纯数据 SolverEntity,句柄不跨语句。\n *\n * 输出契约对齐约束求解器的 SolverEntity:{ type: 'plane'|'axis'|'point',\n * origin, normal?/direction? }。dependent 侧填本地坐标(部件自身坐标系),\n * reference 侧世界变换由 solverAdapter 的 transformEntity 负责——本层不做。\n *\n * 缺轴(mesh 行快照无宿主注入的 axis、sphere 等无轴面类型)→ 抛\n * E_TOPO_NOT_FOUND(方案 R4/P0 验收③:绝不静默降级)。\n */\n\nimport type { Shape } from '../../mesh/types'\nimport type { BrepEngineApi } from '../../brep/engine/primitives'\nimport type { PartName } from '../../identity'\nimport type { SolverEntity } from './solvers/types'\nimport { TopoRefError, type AxisHint } from '../../topology/naming'\nimport { resolveTopoRef, captureEdgeAxis, captureFaceHint } from '../../topology/naming'\nimport {\n buildShapeResolutionContext,\n buildEdgeResolutionContext,\n resolveFaceGeometry,\n type ResolvedFaceGeometry,\n} from '../topo-resolve'\nimport type { EntityRef, AssemblyVec3 } from './types'\n\n/** 实体抽取环境:内核(null = mesh/primitive 行快照路径)+ 成员查找。 */\nexport interface EntityResolutionEnv {\n kernel: BrepEngineApi | null\n /** part 变量名 → 成员 Shape。 */\n memberOf: (part: string) => Shape | undefined\n}\n\n/** 无轴可用的报错信息后缀(E_TOPO_NOT_FOUND)。 */\nfunction missingAxisReason(geom: ResolvedFaceGeometry): string {\n return `face needs an axis (surfaceType=${geom.surfaceType ?? 'unknown'}) but none is available ` +\n `(BREP path: captureFaceHint; mesh path: host must inject axis into the naming row)`\n}\n\n/**\n * 面几何 → SolverEntity(方案 §5.2 抽取规则表)。\n * 平面 → plane;圆柱/圆锥(有轴)→ axis;其它/缺轴 → E_TOPO_NOT_FOUND。\n *\n * @param geom - the resolved face geometry (live BREP or mesh row snapshot).\n * @returns the solver entity (plane or axis).\n * @throws TopoRefError with code E_TOPO_NOT_FOUND when the surface has no usable axis or is unsupported.\n */\nexport function faceGeometryToSolverEntity(geom: ResolvedFaceGeometry): SolverEntity {\n const st = geom.surfaceType\n if (st === 'cylinder' || st === 'cone') {\n if (!geom.axis) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', missingAxisReason(geom))\n }\n return { type: 'axis', origin: geom.axis.origin, direction: geom.axis.direction }\n }\n if (!st || st === 'plane') {\n return { type: 'plane', origin: geom.center, normal: geom.normal }\n }\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `unsupported surface type for an assembly entity: ${st}` +\n (geom.axis ? '' : ` (${missingAxisReason(geom)})`),\n )\n}\n\n/** 快照形态 FaceRef → ResolvedFaceGeometry(无 axis——贴合语义只需 center+normal)。 */\nfunction snapshotFaceGeometry(face: { surfaceType?: string; center: AssemblyVec3; normal: AssemblyVec3 }): ResolvedFaceGeometry {\n return { surfaceType: face.surfaceType, center: face.center, normal: face.normal }\n}\n\n/**\n * 解析一个 EntityRef 的面几何(mate/align 降级路径用:只需 center + normal,\n * 圆柱面轴不参与贴合——方案 §3.7.4 适用边界)。\n *\n * @param ref - the entity reference (must be a face reference).\n * @param env - the resolution environment.\n * @returns the resolved face geometry.\n * @throws Error when the ref is not a face reference or the part is unknown.\n */\nexport function resolveFaceGeometryOfRef(ref: EntityRef, env: EntityResolutionEnv): ResolvedFaceGeometry {\n if (!('face' in ref)) {\n throw new Error('[assembly] mate/align constraints require face references on both sides')\n }\n const shape = env.memberOf(ref.part)\n if (!shape) throw new Error(`[assembly] constraint references unknown part: ${ref.part}`)\n const face = ref.face as { topoRef?: unknown; center?: AssemblyVec3; normal?: AssemblyVec3; surfaceType?: string }\n if (face.topoRef) {\n return resolveFaceGeometry(env.kernel, shape, face.topoRef as Parameters<typeof resolveFaceGeometry>[2])\n }\n if (!face.center || !face.normal) {\n throw new Error('[assembly] face snapshot requires center and normal')\n }\n return snapshotFaceGeometry(face as { surfaceType?: string; center: AssemblyVec3; normal: AssemblyVec3 })\n}\n\n/** 边 TopoRef → 轴(BREP 现场;mesh 无邻接能力 → E_TOPO_NOT_FOUND)。 */\nfunction resolveEdgeAxisFromTopoRef(\n kernel: BrepEngineApi,\n shape: Shape,\n edgeTopoRef: Parameters<typeof resolveTopoRef>[0],\n part: PartName,\n): AxisHint {\n const ctx = buildEdgeResolutionContext(kernel, shape)\n if (!ctx) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `edge entity needs a BREP naming context (part=${part}); mesh parts have no edge adjacency`,\n )\n }\n const resolved = resolveTopoRef(edgeTopoRef, ctx)\n const entry = ctx.edges?.[resolved.ordinal - 1]\n if (!entry?.handle) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `resolved edge ordinal ${resolved.ordinal} has no live handle (part=${part})`,\n )\n }\n const axis = captureEdgeAxis(kernel, entry.handle)\n if (!axis) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `edge ordinal ${resolved.ordinal} is not a line or circle — no axis entity available`,\n )\n }\n return axis\n}\n\n/**\n * 解析一个 EntityRef → SolverEntity(coincident/concentric/distance/angle 直译路径)。\n *\n * @param ref - the entity reference.\n * @param env - the resolution environment.\n * @returns the solver entity (pure data).\n * @throws TopoRefError / Error on unresolvable or unsupported references.\n */\nexport function resolveSolverEntity(ref: EntityRef, env: EntityResolutionEnv): SolverEntity {\n const shape = env.memberOf(ref.part)\n if (!shape) throw new Error(`[assembly] constraint references unknown part: ${ref.part}`)\n\n if ('point' in ref) {\n return { type: 'point', origin: ref.point }\n }\n if ('face' in ref) {\n return faceGeometryToSolverEntity(resolveFaceGeometryOfRef(ref, env))\n }\n if ('faceIndex' in ref) {\n // 序号简写(1 起,调试用):直接按 ordinal 取候选面几何(不走 TopoRef 打分)\n const ctx = buildShapeResolutionContext(env.kernel, shape)\n if (!ctx) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `faceIndex ${ref.faceIndex} has no naming context on part ${ref.part}`,\n )\n }\n const entry = ctx.faces[ref.faceIndex - 1]\n if (!entry) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `faceIndex ${ref.faceIndex} out of range (part ${ref.part} has ${ctx.faces.length} faces)`,\n )\n }\n return faceGeometryToSolverEntity(faceGeometryFromContextEntry(env, entry, ref.part))\n }\n // edge 引用\n const edge = ref.edge as { topoRef?: unknown; axis?: { origin: AssemblyVec3; direction: AssemblyVec3 } }\n if (edge.axis) {\n return { type: 'axis', origin: edge.axis.origin, direction: edge.axis.direction }\n }\n if (!env.kernel) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `edge TopoRef needs a live kernel (part=${ref.part}); mesh path supports only axis snapshots`,\n )\n }\n const axis = resolveEdgeAxisFromTopoRef(env.kernel, shape, edge.topoRef as Parameters<typeof resolveTopoRef>[0], ref.part)\n return { type: 'axis', origin: axis.origin, direction: axis.direction }\n}\n\n/**\n * faceIndex 简写的面几何(从解析上下文候选行直接取):\n * BREP 候选(活句柄)→ captureFaceHint 现场;行快照候选 → 行几何。\n */\nfunction faceGeometryFromContextEntry(\n env: EntityResolutionEnv,\n entry: { handle?: unknown; row?: unknown },\n part: PartName,\n): ResolvedFaceGeometry {\n if (entry.handle && env.kernel) {\n const hint = captureFaceHint(env.kernel, entry.handle as Parameters<typeof captureFaceHint>[1])\n return {\n surfaceType: hint.surfaceType,\n center: hint.center ?? [0, 0, 0],\n normal: hint.normal ?? [0, 0, 1],\n ...(hint.axis ? { axis: hint.axis } : {}),\n }\n }\n const hint = entry.row as\n | { surfaceType?: string; center?: AssemblyVec3; normal?: AssemblyVec3; axis?: AxisHint }\n | undefined\n if (!hint?.center || !hint?.normal) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `faceIndex row has no geometry (part=${part})`)\n }\n return {\n surfaceType: hint.surfaceType,\n center: hint.center,\n normal: hint.normal,\n ...(hint.axis ? { axis: hint.axis } : {}),\n }\n}\n"]}
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../../src/api/assembly/entities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api/assembly/entities — EntityRef → SolverEntity 实体抽取(P1,方案 §3.6 / §5.2)\n *\n * 三要素范式(与 chamfer 同构,方案 §3.5):\n * 1. 引用可序列化:EntityRef 进 .fai.js;\n * 2. 解析在执行期:TopoRef 复用 topo-resolve 的解析通道(BREP 现场 / mesh 行快照);\n * 3. 句柄是瞬态:解析出的几何立即转成纯数据 SolverEntity,句柄不跨语句。\n *\n * 输出契约对齐约束求解器的 SolverEntity:{ type: 'plane'|'axis'|'point',\n * origin, normal?/direction? }。dependent 侧填本地坐标(部件自身坐标系),\n * reference 侧世界变换由 solverAdapter 的 transformEntity 负责——本层不做。\n *\n * 缺轴(mesh 行快照无宿主注入的 axis、sphere 等无轴面类型)→ 抛\n * E_TOPO_NOT_FOUND(方案 R4/P0 验收③:绝不静默降级)。\n */\n\nimport type { Shape } from '../../mesh/types'\nimport type { BrepEngineApi } from '../../brep/engine/primitives'\nimport type { PartName } from '../../identity'\nimport type { SolverEntity } from './solvers/types'\nimport { TopoRefError, type AxisHint } from '../../topology/naming'\nimport { resolveTopoRef, captureEdgeAxis, captureFaceHint, captureCylinderFaceAxis } from '../../topology/naming'\nimport {\n buildShapeResolutionContext,\n buildEdgeResolutionContext,\n resolveFaceGeometry,\n type ResolvedFaceGeometry,\n} from '../topo-resolve'\nimport type { EntityRef, AssemblyVec3 } from './types'\n\n/** 实体抽取环境:内核(null = mesh/primitive 行快照路径)+ 成员查找。 */\nexport interface EntityResolutionEnv {\n kernel: BrepEngineApi | null\n /** part 变量名 → 成员 Shape。 */\n memberOf: (part: string) => Shape | undefined\n}\n\n/** 无轴可用的报错信息后缀(E_TOPO_NOT_FOUND)。 */\nfunction missingAxisReason(geom: ResolvedFaceGeometry): string {\n return `face needs an axis (surfaceType=${geom.surfaceType ?? 'unknown'}) but none is available ` +\n `(BREP path: captureFaceHint; mesh path: host must inject axis into the naming row)`\n}\n\n/**\n * 面几何 → SolverEntity(方案 §5.2 抽取规则表)。\n * 平面 → plane;圆柱/圆锥(有轴)→ axis;其它/缺轴 → E_TOPO_NOT_FOUND。\n *\n * @param geom - the resolved face geometry (live BREP or mesh row snapshot).\n * @returns the solver entity (plane or axis).\n * @throws TopoRefError with code E_TOPO_NOT_FOUND when the surface has no usable axis or is unsupported.\n */\nexport function faceGeometryToSolverEntity(geom: ResolvedFaceGeometry): SolverEntity {\n const st = geom.surfaceType\n if (st === 'cylinder' || st === 'cone') {\n if (!geom.axis) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', missingAxisReason(geom))\n }\n return { type: 'axis', origin: geom.axis.origin, direction: geom.axis.direction }\n }\n if (!st || st === 'plane') {\n return { type: 'plane', origin: geom.center, normal: geom.normal }\n }\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `unsupported surface type for an assembly entity: ${st}` +\n (geom.axis ? '' : ` (${missingAxisReason(geom)})`),\n )\n}\n\n/** 快照形态 FaceRef → ResolvedFaceGeometry(无 axis——贴合语义只需 center+normal)。 */\nfunction snapshotFaceGeometry(face: { surfaceType?: string; center: AssemblyVec3; normal: AssemblyVec3 }): ResolvedFaceGeometry {\n return { surfaceType: face.surfaceType, center: face.center, normal: face.normal }\n}\n\n/**\n * 解析一个 EntityRef 的面几何(mate/align 降级路径用:只需 center + normal,\n * 圆柱面轴不参与贴合——方案 §3.7.4 适用边界)。\n *\n * @param ref - the entity reference (must be a face reference).\n * @param env - the resolution environment.\n * @returns the resolved face geometry.\n * @throws Error when the ref is not a face reference or the part is unknown.\n */\nexport function resolveFaceGeometryOfRef(ref: EntityRef, env: EntityResolutionEnv): ResolvedFaceGeometry {\n if (!('face' in ref)) {\n throw new Error('[assembly] mate/align constraints require face references on both sides')\n }\n const shape = env.memberOf(ref.part)\n if (!shape) throw new Error(`[assembly] constraint references unknown part: ${ref.part}`)\n const face = ref.face as { topoRef?: unknown; center?: AssemblyVec3; normal?: AssemblyVec3; surfaceType?: string }\n if (face.topoRef) {\n return resolveFaceGeometry(env.kernel, shape, face.topoRef as Parameters<typeof resolveFaceGeometry>[2])\n }\n if (!face.center || !face.normal) {\n throw new Error('[assembly] face snapshot requires center and normal')\n }\n return snapshotFaceGeometry(face as { surfaceType?: string; center: AssemblyVec3; normal: AssemblyVec3 })\n}\n\n/** 边 TopoRef → 轴(BREP 现场;mesh 无邻接能力 → E_TOPO_NOT_FOUND)。 */\nfunction resolveEdgeAxisFromTopoRef(\n kernel: BrepEngineApi,\n shape: Shape,\n edgeTopoRef: Parameters<typeof resolveTopoRef>[0],\n part: PartName,\n): AxisHint {\n const ctx = buildEdgeResolutionContext(kernel, shape)\n if (!ctx) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `edge entity needs a BREP naming context (part=${part}); mesh parts have no edge adjacency`,\n )\n }\n const resolved = resolveTopoRef(edgeTopoRef, ctx)\n const entry = ctx.edges?.[resolved.ordinal - 1]\n if (!entry?.handle) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `resolved edge ordinal ${resolved.ordinal} has no live handle (part=${part})`,\n )\n }\n const axis = captureEdgeAxis(kernel, entry.handle)\n if (!axis) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `edge ordinal ${resolved.ordinal} is not a line or circle — no axis entity available`,\n )\n }\n return axis\n}\n\n/**\n * 解析一个 EntityRef → SolverEntity(coincident/concentric/distance/angle 直译路径)。\n *\n * @param ref - the entity reference.\n * @param env - the resolution environment.\n * @returns the solver entity (pure data).\n * @throws TopoRefError / Error on unresolvable or unsupported references.\n */\nexport function resolveSolverEntity(ref: EntityRef, env: EntityResolutionEnv): SolverEntity {\n const shape = env.memberOf(ref.part)\n if (!shape) throw new Error(`[assembly] constraint references unknown part: ${ref.part}`)\n\n if ('point' in ref) {\n return { type: 'point', origin: ref.point }\n }\n if ('face' in ref) {\n return faceGeometryToSolverEntity(resolveFaceGeometryOfRef(ref, env))\n }\n if ('faceIndex' in ref) {\n // 序号简写(1 起,调试用):直接按 ordinal 取候选面几何(不走 TopoRef 打分)\n const ctx = buildShapeResolutionContext(env.kernel, shape)\n if (!ctx) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `faceIndex ${ref.faceIndex} has no naming context on part ${ref.part}`,\n )\n }\n const entry = ctx.faces[ref.faceIndex - 1]\n if (!entry) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `faceIndex ${ref.faceIndex} out of range (part ${ref.part} has ${ctx.faces.length} faces)`,\n )\n }\n return faceGeometryToSolverEntity(faceGeometryFromContextEntry(env, entry, ref.part))\n }\n // edge 引用\n const edge = ref.edge as { topoRef?: unknown; axis?: { origin: AssemblyVec3; direction: AssemblyVec3 } }\n if (edge.axis) {\n return { type: 'axis', origin: edge.axis.origin, direction: edge.axis.direction }\n }\n if (!env.kernel) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'edge',\n `edge TopoRef needs a live kernel (part=${ref.part}); mesh path supports only axis snapshots`,\n )\n }\n const axis = resolveEdgeAxisFromTopoRef(env.kernel, shape, edge.topoRef as Parameters<typeof resolveTopoRef>[0], ref.part)\n return { type: 'axis', origin: axis.origin, direction: axis.direction }\n}\n\n/**\n * faceIndex 简写的面几何(从解析上下文候选行直接取):\n * **行快照优先**(与 `toScoringCandidate` / `resolveFaceGeometry` 同口径——\n * brepkit 的 kernel 现场 center 会退化:uvBounds ±1e6 哨兵 → surfaceCenterOfMass\n * 在参数原点求值,非居中模型上 center 全错 → 装配解算拿错面几何);无行快照才\n * captureFaceHint 现场。\n */\nfunction faceGeometryFromContextEntry(\n env: EntityResolutionEnv,\n entry: { handle?: unknown; row?: unknown },\n part: PartName,\n): ResolvedFaceGeometry {\n if (entry.row === undefined && entry.handle && env.kernel) {\n const hint = captureFaceHint(env.kernel, entry.handle as Parameters<typeof captureFaceHint>[1])\n return {\n surfaceType: hint.surfaceType,\n center: hint.center ?? [0, 0, 0],\n normal: hint.normal ?? [0, 0, 1],\n ...(hint.axis ? { axis: hint.axis } : {}),\n }\n }\n const hint = entry.row as\n | { surfaceType?: string; center?: AssemblyVec3; normal?: AssemblyVec3; axis?: AxisHint }\n | undefined\n if (!hint?.center || !hint?.normal) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `faceIndex row has no geometry (part=${part})`)\n }\n // 圆柱轴补采(同 resolveFaceGeometry):行快照不带 axis,圆柱轴一向现场采,\n // 由 pointOnSurface + getFaceCylinderData 量出——不经过退化的\n // surfaceCenterOfMass,现场值可靠。守住 P0①。\n let axis = hint.axis\n if (axis === undefined && hint.surfaceType === 'cylinder' && entry.handle && env.kernel) {\n axis = captureCylinderFaceAxis(env.kernel, entry.handle as Parameters<typeof captureCylinderFaceAxis>[1])\n }\n return {\n surfaceType: hint.surfaceType,\n center: hint.center,\n normal: hint.normal,\n ...(axis ? { axis } : {}),\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"extrude.d.ts","sourceRoot":"","sources":["../../src/api/extrude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAgBH,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;0DAC0D;AAC1D,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,GAAG,MAAM,CAAA;AAE5F,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,+GAA+G;IAC/G,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,gDAAgD;IAChD,WAAW,CAAC,EAAE,KAAK,CAAA;IACnB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA0TD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,OAAO;;CA0ElB,CAAA"}
1
+ {"version":3,"file":"extrude.d.ts","sourceRoot":"","sources":["../../src/api/extrude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAgBH,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;0DAC0D;AAC1D,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,GAAG,MAAM,CAAA;AAE5F,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,+GAA+G;IAC/G,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,gDAAgD;IAChD,WAAW,CAAC,EAAE,KAAK,CAAA;IACnB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA0TD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,OAAO;;CA6ElB,CAAA"}
@@ -559,7 +559,10 @@ export const extrude = defineOp({
559
559
  }
560
560
  catch { /* no engine -> skip role table */ }
561
561
  const solid = kernel ? brepOf(result) : undefined;
562
- if (kernel && solid) {
562
+ // D13(2026-10-15):solid 是 brepkit 0 基句柄,0 是合法实体句柄(首个实体恰为 0)。
563
+ // 必须用 `!== undefined` 判定存在性,禁止真值判断(真值判断在「草图拉伸为首个
564
+ // 实体语句」时跳过登记,下游 fillet/chamfer 的 edgeRef 报 nameless shape)。
565
+ if (kernel && solid !== undefined) {
563
566
  const origin = String(getCurrentStmt()?.id ?? '');
564
567
  const table = new Map([[origin, extrudeConstructRoles(kernel, solid, normal.toArray())]]);
565
568
  const part = getCurrentStmt()?.outputs?.[0];
@@ -1 +1 @@
1
- {"version":3,"file":"extrude.js","sourceRoot":"","sources":["../../src/api/extrude.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api extrude — `cad.extrude`(平台 parity op,BREP-only)\n *\n * 归属(分层红线):`cad.extrude` 是「面 → 棱柱」的**平台** op,FCStd Pad/Pocket\n * 链路与 TS 面共用它(原 `cad.fai_extrude` 路线废弃)。up-to(拉伸到面 / 到支持体端面)属\n * CadQuery `until=` 同族语义,因此落在**本 op**;`fai_extrude`(`fai_` 前缀的\n * faijs 扩展 op,与 fai_drill / fai_split 同族)不得承载它。\n *\n * 为什么是手写 op 而不是继续用生成投影:生成投影是一条 1:1 转发通道\n * (`defineOp({ brep: __own_* })` 直连 api/brep-operations/ 的源函数),有两个硬约束——\n * ① 参数表只有位置形参;D11 归一化只在「单 plain-object 形态」下按 params 表\n * 映射(api/internal/dual-form-args.ts),`(face, { upTo })` 属位置形态 →\n * 原样喂给 compat extrude → 报错,无法表达对象形态;\n * ② 生成投影没有 faijs 侧入参解析挂点,只把实参原样转发给源函数 → 依赖 faceRef 解析 /\n * 内核直调(extrudeUpToSolid)的 up-to 实现无法在生成投影里承载,落回本手写模块。\n * 先例:arg-spec 的 `fillet` 条目(faijs 侧由手写 dual-op 覆盖,生成模块符号为\n * 孤儿 by design)。\n *\n * 长度形态**委托给生成投影**(`projectedExtrude`):compat 投影仍是唯一拉伸引擎,\n * 既有 `cad.extrude(face, [x,y,z])` 语义零漂移。\n */\n\nimport { extrude as projectedExtrude } from './generated/operations'\nimport { Vec3 as Vec3C, Mat4 } from '../math3d'\nimport { solidToShape, matrixToArray } from '../brep/brep-ops'\nimport { getSolidBoundingBox } from '../brep/brep-utils'\nimport { getCurrentStmt, getBackends, MeshUnsupportedError } 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 { resolveSelectorToFaceRefs } from './selectors-to-refs'\nimport { meshFaceEntry, meshKernelFailure, meshSolidProductWithBackend } from './internal/mesh-solid-op'\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)端面 /\n * CadQuery 面选择器字符串(在 baseFeature 上解析,如 `\">Z\"`,多面命中抛错)。 */\nexport type ExtrudeUpTo = FaceTopoRef | 'last' | 'first' | { plane: UpToPlaneSpec } | string\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。字符串 `'last'`/`'first'` 取支持体端面;其它字符串为 CadQuery 面选择器(在 baseFeature 上解析,多面命中抛错)。 */\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 Vec3C(...faceNormal).normalize()\n const center = new Vec3C(...faceCenter)\n // 与 n 正交的正交基 u,v\n const aux = Math.abs(unit.y) < 0.9 ? new Vec3C(0, 1, 0) : new Vec3C(1, 0, 0)\n const u = new Vec3C().crossVectors(unit, aux).normalize()\n const v = new Vec3C().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 Mat4().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 Vec3C(...(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 (base === undefined || baseSolid === undefined) {\n throw new Error('E_UP_TO_NO_BASE: upTo \"last\"/\"first\"/selector requires a baseFeature shape')\n }\n if (upTo === 'last' || upTo === 'first') {\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 {\n // CadQuery 面选择器:在支持体(baseFeature)上解析为单个目标面。\n // 与 FaceTopoRef 分支同款——直接用解析出的 hint 快照(引用面在目标体上,\n // 不做二次 resolveFaceGeometry,见下 GOTCHA)。\n const refs = resolveSelectorToFaceRefs(base, upTo)\n if (refs.length === 0) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `extrude upTo: selector \"${upTo}\" matched no face on baseFeature`)\n }\n if (refs.length > 1) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `extrude upTo: selector \"${upTo}\" matched ${refs.length} faces; upTo takes exactly one face`,\n )\n }\n const ref = refs[0]\n if (ref.hint.center && ref.hint.normal) {\n targetCenter = [...ref.hint.center] as Vec3\n targetNormal = [...ref.hint.normal] as Vec3\n } else {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'extrude upTo: selector-resolved face ref hint has no center/normal snapshot')\n }\n }\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 Vec3C(...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 Vec3C(...shiftedCenter0)\n const tn0 = new Vec3C(...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 Vec3C(...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 Vec3C(\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 Vec3C(...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 Vec3C(...shiftedCenter0).distanceTo(\n new Vec3C((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 * `cad.extrude` 的**网格链**实现(方案 2026-10-01 §4 Phase 3-2)。\n *\n * 输入是一张**网格链面**(`cad.sketchOnFace` 在近似拓扑面上铺出来的那种),\n * 输出是一个**新的网格零件**——与输入网格实体同链。\n *\n * 只支持\"面 + 方向 + 距离\"这一支:`upTo` 需要精度链上的求交/裁切(`extrudeUpToSolid`\n * 走 BREP 内核),近似链没有对应能力,故如实拒绝而不是静默退化成普通拉伸——\n * \"拉伸到那张面\"和\"拉伸这么长\"是两件不同的事,悄悄替换会让用户拿到一个错的实体。\n *\n * 方向语义与 BREP 侧**完全一致**(同一份 `normalizeExtrudeOptions`):位置形态\n * `cad.extrude(face, 5)` 沿 +Z,`cad.extrude(face, [x,y,z])` 沿该向量;\n * 缺省法向 `[0,0,1]` 不自动改用面法向——那是两条链共同的行为约定,不在这里分叉。\n *\n * @param input - the mesh-chain face to extrude.\n * @param params - the same positional/object forms the BREP branch accepts.\n * @returns the new mesh solid (a new mesh part).\n * @throws {MeshUnsupportedError} on the mesh path when `upTo` is requested.\n */\nfunction extrudeMeshChain(input: Shape, params: unknown): Shape {\n if (!input) throw new Error('[api/extrude] no input geometry')\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 entry = meshFaceEntry(input, 'extrude')\n\n const o = normalizeExtrudeOptions(params)\n assertExtrudeOptions(o)\n if (o.upTo !== undefined) {\n throw new MeshUnsupportedError(\n 'E_MESH_SOLID_UNSUPPORTED: extrude upTo is not available on the mesh path — it needs the precision ' +\n 'chain to intersect and trim against a target face. Give an explicit length instead',\n getCurrentStmt(),\n )\n }\n\n const normal = new Vec3C(...(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\n let solid: BrepHandle\n try {\n solid = entry.kernel.extrude(entry.face, v[0]!, v[1]!, v[2]!)\n } catch (cause) {\n throw meshKernelFailure(\n 'extrude',\n 'E_EXTRUDE_FAILED',\n `extruding a mesh-chain face by ${o.length} along [${v.join(', ')}]`,\n cause,\n )\n }\n return meshSolidProductWithBackend(entry.backend, solid)\n}\n\n/**\n * 沿 normal 拉伸几何(面 → 棱柱)。\n *\n * up-to 模式(`upTo`)与长度模式(`length`)二选一;长度模式委托生成投影\n * (brepjs extrude 为唯一引擎),up-to 模式走半空间组合。\n *\n * **网格链**(`meshEngines: ['brepkit']`,方案 2026-10-01 §4 Phase 3):输入是\n * `cad.sketchOnFace` 在网格实体识别面上铺出的网格链面时,本 op 沿同一份方向语义\n * 拉伸出**一个新的网格零件**。网格链上不支持 `upTo`\n * (需精度链求交裁切)——会以 `E_MESH_SOLID_UNSUPPORTED` 明确拒绝,不静默当定长拉伸。\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(UI 拾取产物字面量)指定面 / 'last' 支持体远端面 / 'first' 近端面 / 面选择器字符串(如 \">Z\",在 baseFeature 上解析,多面命中抛错)。提供时忽略 length。❌ 禁止 cad.faceRef 序号。type:'last' | 'first' | FaceTopoRef | string\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 * // upTo 接受 FaceTopoRef(UI 拾取产物字面量);❌ 禁止 cad.faceRef 序号(跨内核不可移植)\n * const p = await cad.extrude(sk, { upTo: pickedUpToFace })\n * const p = await cad.extrude(sk, { upTo: 'last', baseFeature: part0 })\n * // AI 生成可写 CadQuery 面选择器字符串(在 baseFeature 上解析)\n * const p = await cad.extrude(sk, { upTo: '>Z', baseFeature: part0 })\n */\nexport const extrude = defineOp({\n name: 'extrude',\n meshEngines: ['brepkit'],\n mesh(input: Shape, params: unknown): Shape {\n return extrudeMeshChain(input, params)\n },\n brep: async (input: Shape, params: unknown): Promise<Shape> => {\n if (!input) throw new Error('[api/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 Vec3C(...(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 === undefined) throw new Error('[api/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 翻转 / adopt 收养)\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 的生成投影 op,\n // 在 brepkit 引擎下执行前即抛 E_BREP_UNSUPPORTED。brepkit 有原生 L1 extrude\n // (face→solid),直接走 kernel.extrude——与 revolve.ts 同路径(不经生成投影)。\n // occt 路径保持 projectedExtrude 不变,不回退既有几何/命名行为。\n if (getBackends().config.brepEngineId === 'brepkit') {\n const kernel = getBrepApi()\n const faceHandle = brepOf(input) as BrepHandle | undefined\n if (faceHandle === undefined) throw new Error('[api/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 paramDims: { length: 'length', offset: 'length', normal: 'plain' },\n})\n"]}
1
+ {"version":3,"file":"extrude.js","sourceRoot":"","sources":["../../src/api/extrude.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api extrude — `cad.extrude`(平台 parity op,BREP-only)\n *\n * 归属(分层红线):`cad.extrude` 是「面 → 棱柱」的**平台** op,FCStd Pad/Pocket\n * 链路与 TS 面共用它(原 `cad.fai_extrude` 路线废弃)。up-to(拉伸到面 / 到支持体端面)属\n * CadQuery `until=` 同族语义,因此落在**本 op**;`fai_extrude`(`fai_` 前缀的\n * faijs 扩展 op,与 fai_drill / fai_split 同族)不得承载它。\n *\n * 为什么是手写 op 而不是继续用生成投影:生成投影是一条 1:1 转发通道\n * (`defineOp({ brep: __own_* })` 直连 api/brep-operations/ 的源函数),有两个硬约束——\n * ① 参数表只有位置形参;D11 归一化只在「单 plain-object 形态」下按 params 表\n * 映射(api/internal/dual-form-args.ts),`(face, { upTo })` 属位置形态 →\n * 原样喂给 compat extrude → 报错,无法表达对象形态;\n * ② 生成投影没有 faijs 侧入参解析挂点,只把实参原样转发给源函数 → 依赖 faceRef 解析 /\n * 内核直调(extrudeUpToSolid)的 up-to 实现无法在生成投影里承载,落回本手写模块。\n * 先例:arg-spec 的 `fillet` 条目(faijs 侧由手写 dual-op 覆盖,生成模块符号为\n * 孤儿 by design)。\n *\n * 长度形态**委托给生成投影**(`projectedExtrude`):compat 投影仍是唯一拉伸引擎,\n * 既有 `cad.extrude(face, [x,y,z])` 语义零漂移。\n */\n\nimport { extrude as projectedExtrude } from './generated/operations'\nimport { Vec3 as Vec3C, Mat4 } from '../math3d'\nimport { solidToShape, matrixToArray } from '../brep/brep-ops'\nimport { getSolidBoundingBox } from '../brep/brep-utils'\nimport { getCurrentStmt, getBackends, MeshUnsupportedError } 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 { resolveSelectorToFaceRefs } from './selectors-to-refs'\nimport { meshFaceEntry, meshKernelFailure, meshSolidProductWithBackend } from './internal/mesh-solid-op'\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)端面 /\n * CadQuery 面选择器字符串(在 baseFeature 上解析,如 `\">Z\"`,多面命中抛错)。 */\nexport type ExtrudeUpTo = FaceTopoRef | 'last' | 'first' | { plane: UpToPlaneSpec } | string\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。字符串 `'last'`/`'first'` 取支持体端面;其它字符串为 CadQuery 面选择器(在 baseFeature 上解析,多面命中抛错)。 */\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 Vec3C(...faceNormal).normalize()\n const center = new Vec3C(...faceCenter)\n // 与 n 正交的正交基 u,v\n const aux = Math.abs(unit.y) < 0.9 ? new Vec3C(0, 1, 0) : new Vec3C(1, 0, 0)\n const u = new Vec3C().crossVectors(unit, aux).normalize()\n const v = new Vec3C().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 Mat4().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 Vec3C(...(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 (base === undefined || baseSolid === undefined) {\n throw new Error('E_UP_TO_NO_BASE: upTo \"last\"/\"first\"/selector requires a baseFeature shape')\n }\n if (upTo === 'last' || upTo === 'first') {\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 {\n // CadQuery 面选择器:在支持体(baseFeature)上解析为单个目标面。\n // 与 FaceTopoRef 分支同款——直接用解析出的 hint 快照(引用面在目标体上,\n // 不做二次 resolveFaceGeometry,见下 GOTCHA)。\n const refs = resolveSelectorToFaceRefs(base, upTo)\n if (refs.length === 0) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `extrude upTo: selector \"${upTo}\" matched no face on baseFeature`)\n }\n if (refs.length > 1) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `extrude upTo: selector \"${upTo}\" matched ${refs.length} faces; upTo takes exactly one face`,\n )\n }\n const ref = refs[0]\n if (ref.hint.center && ref.hint.normal) {\n targetCenter = [...ref.hint.center] as Vec3\n targetNormal = [...ref.hint.normal] as Vec3\n } else {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'extrude upTo: selector-resolved face ref hint has no center/normal snapshot')\n }\n }\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 Vec3C(...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 Vec3C(...shiftedCenter0)\n const tn0 = new Vec3C(...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 Vec3C(...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 Vec3C(\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 Vec3C(...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 Vec3C(...shiftedCenter0).distanceTo(\n new Vec3C((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 * `cad.extrude` 的**网格链**实现(方案 2026-10-01 §4 Phase 3-2)。\n *\n * 输入是一张**网格链面**(`cad.sketchOnFace` 在近似拓扑面上铺出来的那种),\n * 输出是一个**新的网格零件**——与输入网格实体同链。\n *\n * 只支持\"面 + 方向 + 距离\"这一支:`upTo` 需要精度链上的求交/裁切(`extrudeUpToSolid`\n * 走 BREP 内核),近似链没有对应能力,故如实拒绝而不是静默退化成普通拉伸——\n * \"拉伸到那张面\"和\"拉伸这么长\"是两件不同的事,悄悄替换会让用户拿到一个错的实体。\n *\n * 方向语义与 BREP 侧**完全一致**(同一份 `normalizeExtrudeOptions`):位置形态\n * `cad.extrude(face, 5)` 沿 +Z,`cad.extrude(face, [x,y,z])` 沿该向量;\n * 缺省法向 `[0,0,1]` 不自动改用面法向——那是两条链共同的行为约定,不在这里分叉。\n *\n * @param input - the mesh-chain face to extrude.\n * @param params - the same positional/object forms the BREP branch accepts.\n * @returns the new mesh solid (a new mesh part).\n * @throws {MeshUnsupportedError} on the mesh path when `upTo` is requested.\n */\nfunction extrudeMeshChain(input: Shape, params: unknown): Shape {\n if (!input) throw new Error('[api/extrude] no input geometry')\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 entry = meshFaceEntry(input, 'extrude')\n\n const o = normalizeExtrudeOptions(params)\n assertExtrudeOptions(o)\n if (o.upTo !== undefined) {\n throw new MeshUnsupportedError(\n 'E_MESH_SOLID_UNSUPPORTED: extrude upTo is not available on the mesh path — it needs the precision ' +\n 'chain to intersect and trim against a target face. Give an explicit length instead',\n getCurrentStmt(),\n )\n }\n\n const normal = new Vec3C(...(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\n let solid: BrepHandle\n try {\n solid = entry.kernel.extrude(entry.face, v[0]!, v[1]!, v[2]!)\n } catch (cause) {\n throw meshKernelFailure(\n 'extrude',\n 'E_EXTRUDE_FAILED',\n `extruding a mesh-chain face by ${o.length} along [${v.join(', ')}]`,\n cause,\n )\n }\n return meshSolidProductWithBackend(entry.backend, solid)\n}\n\n/**\n * 沿 normal 拉伸几何(面 → 棱柱)。\n *\n * up-to 模式(`upTo`)与长度模式(`length`)二选一;长度模式委托生成投影\n * (brepjs extrude 为唯一引擎),up-to 模式走半空间组合。\n *\n * **网格链**(`meshEngines: ['brepkit']`,方案 2026-10-01 §4 Phase 3):输入是\n * `cad.sketchOnFace` 在网格实体识别面上铺出的网格链面时,本 op 沿同一份方向语义\n * 拉伸出**一个新的网格零件**。网格链上不支持 `upTo`\n * (需精度链求交裁切)——会以 `E_MESH_SOLID_UNSUPPORTED` 明确拒绝,不静默当定长拉伸。\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(UI 拾取产物字面量)指定面 / 'last' 支持体远端面 / 'first' 近端面 / 面选择器字符串(如 \">Z\",在 baseFeature 上解析,多面命中抛错)。提供时忽略 length。❌ 禁止 cad.faceRef 序号。type:'last' | 'first' | FaceTopoRef | string\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 * // upTo 接受 FaceTopoRef(UI 拾取产物字面量);❌ 禁止 cad.faceRef 序号(跨内核不可移植)\n * const p = await cad.extrude(sk, { upTo: pickedUpToFace })\n * const p = await cad.extrude(sk, { upTo: 'last', baseFeature: part0 })\n * // AI 生成可写 CadQuery 面选择器字符串(在 baseFeature 上解析)\n * const p = await cad.extrude(sk, { upTo: '>Z', baseFeature: part0 })\n */\nexport const extrude = defineOp({\n name: 'extrude',\n meshEngines: ['brepkit'],\n mesh(input: Shape, params: unknown): Shape {\n return extrudeMeshChain(input, params)\n },\n brep: async (input: Shape, params: unknown): Promise<Shape> => {\n if (!input) throw new Error('[api/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 Vec3C(...(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 === undefined) throw new Error('[api/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 翻转 / adopt 收养)\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 的生成投影 op,\n // 在 brepkit 引擎下执行前即抛 E_BREP_UNSUPPORTED。brepkit 有原生 L1 extrude\n // (face→solid),直接走 kernel.extrude——与 revolve.ts 同路径(不经生成投影)。\n // occt 路径保持 projectedExtrude 不变,不回退既有几何/命名行为。\n if (getBackends().config.brepEngineId === 'brepkit') {\n const kernel = getBrepApi()\n const faceHandle = brepOf(input) as BrepHandle | undefined\n if (faceHandle === undefined) throw new Error('[api/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 // D13(2026-10-15):solid 是 brepkit 0 基句柄,0 是合法实体句柄(首个实体恰为 0)。\n // 必须用 `!== undefined` 判定存在性,禁止真值判断(真值判断在「草图拉伸为首个\n // 实体语句」时跳过登记,下游 fillet/chamfer 的 edgeRef 报 nameless shape)。\n if (kernel && solid !== undefined) {\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 paramDims: { length: 'length', offset: 'length', normal: 'plain' },\n})\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"fillet.d.ts","sourceRoot":"","sources":["../../src/api/fillet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAiB1C,gBAAgB;AAChB,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,CAAC,EAAE,wBAAwB,GAC9B,IAAI,CA+BN;AAgID;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,MAAM;;CAmBjB,CAAA"}
1
+ {"version":3,"file":"fillet.d.ts","sourceRoot":"","sources":["../../src/api/fillet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAiB1C,gBAAgB;AAChB,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,CAAC,EAAE,wBAAwB,GAC9B,IAAI,CA+BN;AAoLD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,MAAM;;CAmBjB,CAAA"}
@@ -117,8 +117,55 @@ function filletBrep(input, params) {
117
117
  catch (err) {
118
118
  throw translateOcctError(err);
119
119
  }
120
+ // 如实报告内核缺陷(2026-10-16 主线 bug「圆角预览模型消失」):brepkit
121
+ // FilletBuilder 对部分边输出**退化几何**(显示 deflection 下 bbox 压扁成线/单点、
122
+ // 顶点骤减)——execute 若放行,结果 mesh 不可见 =「物体消失」且 UI 无报错。
123
+ // 这里用与显示三角化同口径的 meshShape 度量(线性 deflection 0.1),退化即抛错,
124
+ // 绝不静默放行;失败时场景保持操作前状态(物体不消失)。
125
+ assertNonDegenerateFilletMesh(kernel, resultSolid, radius, edgeHandles.length);
120
126
  return fromBrep(solidToShape(kernel, resultSolid), { solid: resultSolid, faceEvolution, roleTable });
121
127
  }
128
+ /**
129
+ * 检查 fillet 结果是否退化(mesh 层面,与显示三角化同口径)。
130
+ *
131
+ * 内核失败(E_FILLET_RADIUS_TOO_LARGE 等)走 translateOcctError 已如实报错;
132
+ * 但「execute 成功 + 退化几何」此前是静默的——mesh bbox 压扁成线/单点时渲染
133
+ * 不可见,UI 无任何提示。本检查把它变成显式错误:报错好于掩盖(项目红线)。
134
+ *
135
+ * 阈值:顶点数 < 4(连一个完整三角形都构不成),或 bbox 任一维跨度 ≤ 1e-6
136
+ * (压扁成线/单点)。OCCT 内核同场景 12 边全健康(fillet-per-edge-occt.test.ts),
137
+ * 本检查不会误伤健康结果。
138
+ */
139
+ function assertNonDegenerateFilletMesh(kernel, solid, radius, edgeCount) {
140
+ const mesh = kernel.meshShape(solid);
141
+ const pos = mesh.positions;
142
+ if (pos.length < 12) {
143
+ throw new Error(`E_FILLET_DEGENERATE_MESH: brepkit FilletBuilder produced degenerate geometry for fillet(r=${radius}, edges=${edgeCount}): vertices=${pos.length / 3} (< 4). Refusing to render a vanished solid.`);
144
+ }
145
+ let minX = Infinity, minY = Infinity, minZ = Infinity;
146
+ let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
147
+ for (let i = 0; i < pos.length; i += 3) {
148
+ const x = pos[i], y = pos[i + 1], z = pos[i + 2];
149
+ if (x < minX)
150
+ minX = x;
151
+ if (y < minY)
152
+ minY = y;
153
+ if (z < minZ)
154
+ minZ = z;
155
+ if (x > maxX)
156
+ maxX = x;
157
+ if (y > maxY)
158
+ maxY = y;
159
+ if (z > maxZ)
160
+ maxZ = z;
161
+ }
162
+ const sx = maxX - minX;
163
+ const sy = maxY - minY;
164
+ const sz = maxZ - minZ;
165
+ if (!(sx > 1e-6 && sy > 1e-6 && sz > 1e-6)) {
166
+ throw new Error(`E_FILLET_DEGENERATE_MESH: brepkit FilletBuilder produced degenerate geometry for fillet(r=${radius}, edges=${edgeCount}): bbox=${sx.toFixed(4)}×${sy.toFixed(4)}×${sz.toFixed(4)} (flattened). Refusing to render a vanished solid.`);
167
+ }
168
+ }
122
169
  /**
123
170
  * 网格实体路径:对近似拓扑里识别出的边倒圆角(方案 2026-10-01 §3.5 三明治)。
124
171
  *
@@ -1 +1 @@
1
- {"version":3,"file":"fillet.js","sourceRoot":"","sources":["../../src/api/fillet.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api fillet — fillet 圆角库函数(BREP-only)\n *\n * 平台分层(narrowing plan Phase 5,D11):**中立 op**——`filletWithHistory` 是\n * L1 核心面(brepkit 亦实现),实现全程走 L1(getBrepApi,D12),不声明 engines。\n * 实现体内不分支:两引擎都走 `filletWithRoleTable` 历史轨(见 `filletBrep`)。\n *\n * 与 chamfer 的差异:fillet 用 filletWithHistory 走面演化 + roleTable 传播\n * (chamfer 在 P5 同步改造)。M1 只支持等半径;M2 计划支持变半径与几何限定符。\n *\n * M1:\n * - 等半径(number):kernel.filletWithHistory(solid, edges, radius, hashes, bound)\n * - 走 filletWithRoleTable 传播 roleTable\n * - BREP-only:mesh 输入 → E_MESH_UNSUPPORTED(backend-dispatch 静态判定)\n *\n * M2(未实施):\n * - 变半径 [r1, r2]:filletVariable + 单边 + 不产 roleTable(降级)\n * - 几何限定符字符串选边(§4)\n */\n\nimport type { Shape } from '../mesh/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { filletWithRoleTable } from '../brep/face-evolution'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { getCurrentStmt, nameOf, setName } from '../runtime-state'\nimport { fromBrep, brepOf, inputRoleTable, resolveCompoundMember, replaceCompoundMember } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { BrepHandle } from '../brep/engine/types'\nimport type { EdgeTopoRef, ResolutionContext } from '../topology/naming'\nimport { resolveTopoRef, TopoRefError } from '../topology/naming'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { buildEdgeResolutionContext } from './topo-resolve'\nimport { expandEdgeSelectorRefs } from './selectors-to-refs'\nimport { meshKernelFailure, meshSolidEntry, meshSolidProduct, resolveMeshEdges } from './internal/mesh-solid-op'\n\n// ── 参数自校验(api 层被直接 import 时的防御层)──\n\n/** 边选择器校验选项。 */\nexport interface EdgeSelectorCheckOptions {\n /**\n * 允许 1 起序号作为边选择器(**仅网格链**;见 `resolveMeshEdges`)。\n *\n * 精度链不开这个口子:BREP 的边身份是 role 线路(两个相邻面的 `(origin, role)`),\n * 序号只是内核枚举序,跨特征会漂移——让精度链接受序号等于把\"可重放的引用\"降级成\n * \"这次跑出来的下标\"。近似链相反:没有 role 层,序号是唯一无歧义的指认方式。\n */\n readonly allowOrdinals?: boolean\n}\n\n/**\n * Validate fillet parameters: `edges` must be a non-empty array of EdgeTopoRef\n * (each with kind key \"edge\" and a two-entry faces pair) or of selector strings\n * (`\"|Z\"` etc., precision chain) — or of 1-based ordinals when\n * `opts.allowOrdinals` (the mesh chain); a top-level selector string is also\n * accepted. `radius` must be a positive number (M1: uniform radius only).\n * @param params the raw fillet operation parameters.\n * @param opts the selector-form options (mesh chain allows ordinals).\n */\nexport function assertFilletParams(\n params: Record<string, unknown>,\n opts?: EdgeSelectorCheckOptions,\n): void {\n const edges = params.edges\n const list = typeof edges === 'string' ? [edges] : (edges as unknown[])\n if (typeof edges !== 'string' && (!Array.isArray(edges) || list.length === 0)) {\n throw new Error('E_FILLET_NO_EDGES: fillet requires at least one edge')\n }\n for (const e of list) {\n if (opts?.allowOrdinals && typeof e === 'number') {\n if (!Number.isInteger(e) || e < 1) {\n throw new Error('E_FILLET_BAD_EDGE_REF: an edge ordinal must be a positive integer (1-based)')\n }\n continue\n }\n if (typeof e === 'string') {\n if (e.trim() === '') {\n throw new Error('E_FILLET_BAD_EDGE_REF: an edge selector string must not be empty')\n }\n continue\n }\n if (!e || typeof e !== 'object' || (e as { kind?: unknown }).kind !== 'edge') {\n throw new Error('E_FILLET_BAD_EDGE_REF: every edge entry must be an EdgeTopoRef with kind:\"edge\"')\n }\n const faces = (e as { faces?: unknown }).faces\n if (!Array.isArray(faces) || faces.length !== 2) {\n throw new Error('E_FILLET_BAD_EDGE_REF: EdgeTopoRef.faces must be a two-entry RoleQualifier pair')\n }\n }\n const radius = params.radius\n if (typeof radius !== 'number' || !Number.isFinite(radius) || radius <= 0) {\n throw new Error('E_FILLET_BAD_RADIUS: fillet.radius must be a positive number')\n }\n}\n\n/** 解析一条 EdgeTopoRef → handle;失败抛命名层 TopoRefError。 */\nfunction resolveEdge(ctx: ResolutionContext, ref: EdgeTopoRef): BrepHandle {\n const r = resolveTopoRef(ref, ctx)\n if (r.handle === undefined) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'edge', `fillet: edge resolved without handle (${ref.faces[0].role}/${ref.faces[1].role})`)\n }\n return r.handle as BrepHandle\n}\n\n/**\n * 把内核 OcctError 回译成 E_FILLET_RADIUS_TOO_LARGE。\n *\n * OCCT 在半径超过邻面尺寸、三面交汇处、凹边处都可能失败。\n * 绝不可静默返回未圆角的原形状(§9 风险)。\n */\nfunction translateOcctError(err: unknown): Error {\n const msg = err instanceof Error ? err.message : String(err)\n if (msg.includes('E_FILLET') || msg.includes('E_TOPO')) return err as Error\n return new Error(`E_FILLET_RADIUS_TOO_LARGE: OCCT fillet failed — ${msg}`)\n}\n\n/** BREP-only 主入口(M1 等半径 flow)。 */\nfunction filletBrep(input: Shape, params: Record<string, unknown>): Shape {\n // L1 面(D12):filletWithHistory 是 L1,全链路中立。\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (solid === undefined) throw new Error('E_FILLET_NO_BREP: fillet input is not BREP')\n\n const edges = params.edges as EdgeTopoRef[] | string\n const radius = params.radius as number\n const ctx = buildEdgeResolutionContext(kernel, input as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'edge', 'fillet: input has no edge naming context')\n\n // 展开选择器字符串 → EdgeTopoRef[],再解析所有 EdgeTopoRef → Edge 句柄\n const edgeHandles = expandEdgeSelectorRefs(input, edges).map((e) => resolveEdge(ctx, e))\n\n // 输入 roleTable + outStmt(与 boolean.ts 同构)。Phase 1.6:origin=本次语句 StmtId。\n const inputTable = inputRoleTable(input) as ReadonlyMap<unknown, unknown> | undefined\n const outStmt = String(getCurrentStmt()?.id ?? '')\n\n let resultSolid: BrepHandle\n let faceEvolution: Map<number, number[]> | undefined\n let roleTable: ReadonlyMap<unknown, unknown> | undefined\n\n try {\n const r = filletWithRoleTable(\n kernel,\n solid,\n edgeHandles,\n radius,\n inputTable ?? new Map(),\n outStmt,\n )\n resultSolid = r.result\n faceEvolution = r.faceEvolution\n roleTable = r.roleTable\n } catch (err) {\n throw translateOcctError(err)\n }\n\n return fromBrep(\n solidToShape(kernel, resultSolid),\n { solid: resultSolid, faceEvolution, roleTable },\n )\n}\n\n/**\n * 网格实体路径:对近似拓扑里识别出的边倒圆角(方案 2026-10-01 §3.5 三明治)。\n *\n * 与 BREP 路径的三处实质差异,都是\"网格零件没有精度链、没有 role 层\"的直接后果:\n * - 边**按几何或序号**解析:`EdgeTopoRef` 走长度/中点匹配;**1 起序号**直接取自近似\n * 拓扑的 `edges` 数组下标 + 1(等长边靠几何分不开,序号是唯一无歧义的指认方式);\n * - 内核调用走 L1 裸 `kernel.fillet`(`filletWithHistory` 是精度链的面演化能力,\n * 近似拓扑没有 hash 演化可言,故**不产生** faceEvolution / roleTable,如实缺席\n * 而不是造一张恒等映射);\n * - 产物句柄写回后由 `meshSolidProduct` 重新三角化并重建近似拓扑。\n *\n * GOTCHA(2026-10-01 实测,见 `brepkit-kernel/mesh-solid-topology.test.ts`):\n * 一次请求里的**全部**边交给内核,会在共面边链(如圆柱端盖的 64 条边)上被整体\n * 拒绝——不是单条边不可倒,而是\"一次性磨圆会自交\"。故本实现**如实失败**,不自动\n * 拆成逐边重试(运行时换策略会掩盖几何事实)。要给共面边链倒角,请分多次调用,\n * 每次一组几何上互不相邻的边。\n *\n * @param input - the mesh-solid input.\n * @param params - the validated fillet parameters.\n * @returns the filleted mesh solid as a new mesh part.\n */\nfunction filletMeshSolid(input: Shape, params: Record<string, unknown>): Shape {\n const edges = params.edges as (number | string | EdgeTopoRef)[] | string\n const radius = params.radius as number\n const entry = meshSolidEntry(input, 'fillet')\n // 选择器字符串只服务精度链(网格零件无 role 层);网格链用 1 起序号。\n if (typeof edges === 'string' || edges.some((e) => typeof e === 'string')) {\n throw new TopoRefError(\n 'E_TOPO_MESH_UNSUPPORTED',\n 'edge',\n 'fillet: selector strings are precision-chain only — mesh solids have no role layer (use 1-based edge ordinals)',\n )\n }\n const edgeHandles = resolveMeshEdges(entry, edges as (number | EdgeTopoRef)[], 'fillet')\n\n let result: BrepHandle\n try {\n result = entry.kernel.fillet(entry.solid, edgeHandles, radius)\n } catch (cause) {\n throw meshKernelFailure(\n 'fillet',\n 'E_FILLET_RADIUS_TOO_LARGE',\n `fillet(r=${radius}) on ${edgeHandles.length} edge(s)`,\n cause,\n )\n }\n return meshSolidProduct(entry, result)\n}\n\n/**\n * 多零件 compound 成员是匿名 Shape:其面 role 记录在父 compound 的 part 键合并表里。\n * 解包后让 target 继承父 part 名,buildEdgeResolutionContext/inputRoleTable 才能按\n * part 名查到表(与 chamfer 同构)。单零件路径 input===target 已命名,守卫使其空转。\n */\nfunction adoptParentPartName(input: Shape, target: Shape): void {\n if (target === input) return\n const parentName = nameOf(input)\n if (parentName && !nameOf(target)) setName(target, parentName)\n}\n\n/**\n * 在几何体上做圆角(等半径)。\n *\n * 两条路径都以 `meshEngines: ['brepkit']` 之外的事实为界:BREP 输入走\n * `filletWithHistory`(带面演化与 roleTable 传播);**网格实体**输入走网格后端\n * (近似拓扑无 role,边按几何或序号解析,无面演化)。非 BREP 的**裸网格**输入仍抛\n * `E_MESH_SOLID_UNSUPPORTED`——裸网格没有近似拓扑,没有边可选。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name fillet\n * @note `radius` 为正数(mm)。BREP 路径圆角后 roleTable 经 filletWithHistory 传播,\n * 后续特征仍可按 role 选面/选边;网格实体路径没有 role 层,边只能按几何或\n * 序号(近似拓扑 `edges` 数组下标 + 1)指认。\n * @returns Shape 圆角后的几何。\n * @param input - 目标几何。type:Shape required:true\n * @param params.edges - 参与圆角的边:CadQuery 边选择器字符串(如 \"|Z\",AI 生成)或相邻两面的 role 线路(EdgeTopoRef[],UI 拾取产物);网格链上也可用 1 起序号。type:EdgeTopoRef[]|string required:true\n * @param params.radius - 圆角半径(mm,>0)。type:number required:true\n * @example\n * const p = await cad.fillet(part0, { edges: [{ kind:'edge', faces:[{ origin:'box', role:'box:top' }, { origin:'box', role:'box:front' }], hint:{ kind:'edge' } }], radius:2 })\n * // AI 生成可写 CadQuery 边选择器字符串(\"|Z\" 切线平行 Z 的边)\n * const p2 = await cad.fillet(part0, { edges: \"|Z\", radius: 2 })\n * const q = await cad.fillet(meshPart, { edges: [3], radius: 1 })\n */\nexport const fillet = defineOp({\n meshEngines: ['brepkit'],\n mesh(input: Shape, params: Record<string, unknown>) {\n assertFilletParams(params, { allowOrdinals: true })\n // P2:多零件 load 输入是 compound——按 partIndex 取目标成员,结果替换回原 compound。\n const target = resolveCompoundMember(input, params.partIndex, 'fillet')\n const result = filletMeshSolid(target, params)\n return replaceCompoundMember(input, params.partIndex, result)\n },\n brep(input: Shape, params: Record<string, unknown>) {\n assertFilletParams(params)\n const target = resolveCompoundMember(input, params.partIndex, 'fillet')\n adoptParentPartName(input, target)\n const result = filletBrep(target, params)\n return replaceCompoundMember(input, params.partIndex, result)\n },\n naming: { kind: 'kernel', newFaces: { via: 'byAdjacency' } } as Provenance,\n\n paramDims: {},\n})\n"]}
1
+ {"version":3,"file":"fillet.js","sourceRoot":"","sources":["../../src/api/fillet.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api fillet — fillet 圆角库函数(BREP-only)\n *\n * 平台分层(narrowing plan Phase 5,D11):**中立 op**——`filletWithHistory` 是\n * L1 核心面(brepkit 亦实现),实现全程走 L1(getBrepApi,D12),不声明 engines。\n * 实现体内不分支:两引擎都走 `filletWithRoleTable` 历史轨(见 `filletBrep`)。\n *\n * 与 chamfer 的差异:fillet 用 filletWithHistory 走面演化 + roleTable 传播\n * (chamfer 在 P5 同步改造)。M1 只支持等半径;M2 计划支持变半径与几何限定符。\n *\n * M1:\n * - 等半径(number):kernel.filletWithHistory(solid, edges, radius, hashes, bound)\n * - 走 filletWithRoleTable 传播 roleTable\n * - BREP-only:mesh 输入 → E_MESH_UNSUPPORTED(backend-dispatch 静态判定)\n *\n * M2(未实施):\n * - 变半径 [r1, r2]:filletVariable + 单边 + 不产 roleTable(降级)\n * - 几何限定符字符串选边(§4)\n */\n\nimport type { Shape } from '../mesh/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { filletWithRoleTable } from '../brep/face-evolution'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { getCurrentStmt, nameOf, setName } from '../runtime-state'\nimport { fromBrep, brepOf, inputRoleTable, resolveCompoundMember, replaceCompoundMember } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { BrepHandle } from '../brep/engine/types'\nimport type { EdgeTopoRef, ResolutionContext } from '../topology/naming'\nimport { resolveTopoRef, TopoRefError } from '../topology/naming'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { buildEdgeResolutionContext } from './topo-resolve'\nimport { expandEdgeSelectorRefs } from './selectors-to-refs'\nimport { meshKernelFailure, meshSolidEntry, meshSolidProduct, resolveMeshEdges } from './internal/mesh-solid-op'\n\n// ── 参数自校验(api 层被直接 import 时的防御层)──\n\n/** 边选择器校验选项。 */\nexport interface EdgeSelectorCheckOptions {\n /**\n * 允许 1 起序号作为边选择器(**仅网格链**;见 `resolveMeshEdges`)。\n *\n * 精度链不开这个口子:BREP 的边身份是 role 线路(两个相邻面的 `(origin, role)`),\n * 序号只是内核枚举序,跨特征会漂移——让精度链接受序号等于把\"可重放的引用\"降级成\n * \"这次跑出来的下标\"。近似链相反:没有 role 层,序号是唯一无歧义的指认方式。\n */\n readonly allowOrdinals?: boolean\n}\n\n/**\n * Validate fillet parameters: `edges` must be a non-empty array of EdgeTopoRef\n * (each with kind key \"edge\" and a two-entry faces pair) or of selector strings\n * (`\"|Z\"` etc., precision chain) — or of 1-based ordinals when\n * `opts.allowOrdinals` (the mesh chain); a top-level selector string is also\n * accepted. `radius` must be a positive number (M1: uniform radius only).\n * @param params the raw fillet operation parameters.\n * @param opts the selector-form options (mesh chain allows ordinals).\n */\nexport function assertFilletParams(\n params: Record<string, unknown>,\n opts?: EdgeSelectorCheckOptions,\n): void {\n const edges = params.edges\n const list = typeof edges === 'string' ? [edges] : (edges as unknown[])\n if (typeof edges !== 'string' && (!Array.isArray(edges) || list.length === 0)) {\n throw new Error('E_FILLET_NO_EDGES: fillet requires at least one edge')\n }\n for (const e of list) {\n if (opts?.allowOrdinals && typeof e === 'number') {\n if (!Number.isInteger(e) || e < 1) {\n throw new Error('E_FILLET_BAD_EDGE_REF: an edge ordinal must be a positive integer (1-based)')\n }\n continue\n }\n if (typeof e === 'string') {\n if (e.trim() === '') {\n throw new Error('E_FILLET_BAD_EDGE_REF: an edge selector string must not be empty')\n }\n continue\n }\n if (!e || typeof e !== 'object' || (e as { kind?: unknown }).kind !== 'edge') {\n throw new Error('E_FILLET_BAD_EDGE_REF: every edge entry must be an EdgeTopoRef with kind:\"edge\"')\n }\n const faces = (e as { faces?: unknown }).faces\n if (!Array.isArray(faces) || faces.length !== 2) {\n throw new Error('E_FILLET_BAD_EDGE_REF: EdgeTopoRef.faces must be a two-entry RoleQualifier pair')\n }\n }\n const radius = params.radius\n if (typeof radius !== 'number' || !Number.isFinite(radius) || radius <= 0) {\n throw new Error('E_FILLET_BAD_RADIUS: fillet.radius must be a positive number')\n }\n}\n\n/** 解析一条 EdgeTopoRef → handle;失败抛命名层 TopoRefError。 */\nfunction resolveEdge(ctx: ResolutionContext, ref: EdgeTopoRef): BrepHandle {\n const r = resolveTopoRef(ref, ctx)\n if (r.handle === undefined) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'edge', `fillet: edge resolved without handle (${ref.faces[0].role}/${ref.faces[1].role})`)\n }\n return r.handle as BrepHandle\n}\n\n/**\n * 把内核 OcctError 回译成 E_FILLET_RADIUS_TOO_LARGE。\n *\n * OCCT 在半径超过邻面尺寸、三面交汇处、凹边处都可能失败。\n * 绝不可静默返回未圆角的原形状(§9 风险)。\n */\nfunction translateOcctError(err: unknown): Error {\n const msg = err instanceof Error ? err.message : String(err)\n if (msg.includes('E_FILLET') || msg.includes('E_TOPO')) return err as Error\n return new Error(`E_FILLET_RADIUS_TOO_LARGE: OCCT fillet failed — ${msg}`)\n}\n\n/** BREP-only 主入口(M1 等半径 flow)。 */\nfunction filletBrep(input: Shape, params: Record<string, unknown>): Shape {\n // L1 面(D12):filletWithHistory 是 L1,全链路中立。\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (solid === undefined) throw new Error('E_FILLET_NO_BREP: fillet input is not BREP')\n\n const edges = params.edges as EdgeTopoRef[] | string\n const radius = params.radius as number\n const ctx = buildEdgeResolutionContext(kernel, input as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'edge', 'fillet: input has no edge naming context')\n\n // 展开选择器字符串 → EdgeTopoRef[],再解析所有 EdgeTopoRef → Edge 句柄\n const edgeHandles = expandEdgeSelectorRefs(input, edges).map((e) => resolveEdge(ctx, e))\n\n // 输入 roleTable + outStmt(与 boolean.ts 同构)。Phase 1.6:origin=本次语句 StmtId。\n const inputTable = inputRoleTable(input) as ReadonlyMap<unknown, unknown> | undefined\n const outStmt = String(getCurrentStmt()?.id ?? '')\n\n let resultSolid: BrepHandle\n let faceEvolution: Map<number, number[]> | undefined\n let roleTable: ReadonlyMap<unknown, unknown> | undefined\n\n try {\n const r = filletWithRoleTable(\n kernel,\n solid,\n edgeHandles,\n radius,\n inputTable ?? new Map(),\n outStmt,\n )\n resultSolid = r.result\n faceEvolution = r.faceEvolution\n roleTable = r.roleTable\n } catch (err) {\n throw translateOcctError(err)\n }\n\n // 如实报告内核缺陷(2026-10-16 主线 bug「圆角预览模型消失」):brepkit\n // FilletBuilder 对部分边输出**退化几何**(显示 deflection 下 bbox 压扁成线/单点、\n // 顶点骤减)——execute 若放行,结果 mesh 不可见 =「物体消失」且 UI 无报错。\n // 这里用与显示三角化同口径的 meshShape 度量(线性 deflection 0.1),退化即抛错,\n // 绝不静默放行;失败时场景保持操作前状态(物体不消失)。\n assertNonDegenerateFilletMesh(kernel, resultSolid, radius, edgeHandles.length)\n\n return fromBrep(\n solidToShape(kernel, resultSolid),\n { solid: resultSolid, faceEvolution, roleTable },\n )\n}\n\n/**\n * 检查 fillet 结果是否退化(mesh 层面,与显示三角化同口径)。\n *\n * 内核失败(E_FILLET_RADIUS_TOO_LARGE 等)走 translateOcctError 已如实报错;\n * 但「execute 成功 + 退化几何」此前是静默的——mesh bbox 压扁成线/单点时渲染\n * 不可见,UI 无任何提示。本检查把它变成显式错误:报错好于掩盖(项目红线)。\n *\n * 阈值:顶点数 < 4(连一个完整三角形都构不成),或 bbox 任一维跨度 ≤ 1e-6\n * (压扁成线/单点)。OCCT 内核同场景 12 边全健康(fillet-per-edge-occt.test.ts),\n * 本检查不会误伤健康结果。\n */\nfunction assertNonDegenerateFilletMesh(\n kernel: ReturnType<typeof getBrepApi>,\n solid: BrepHandle,\n radius: number,\n edgeCount: number,\n): void {\n const mesh = kernel.meshShape(solid)\n const pos = mesh.positions\n if (pos.length < 12) {\n throw new Error(\n `E_FILLET_DEGENERATE_MESH: brepkit FilletBuilder produced degenerate geometry for fillet(r=${radius}, edges=${edgeCount}): vertices=${pos.length / 3} (< 4). Refusing to render a vanished solid.`,\n )\n }\n let minX = Infinity, minY = Infinity, minZ = Infinity\n let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity\n for (let i = 0; i < pos.length; i += 3) {\n const x = pos[i]!, y = pos[i + 1]!, z = pos[i + 2]!\n if (x < minX) minX = x\n if (y < minY) minY = y\n if (z < minZ) minZ = z\n if (x > maxX) maxX = x\n if (y > maxY) maxY = y\n if (z > maxZ) maxZ = z\n }\n const sx = maxX - minX\n const sy = maxY - minY\n const sz = maxZ - minZ\n if (!(sx > 1e-6 && sy > 1e-6 && sz > 1e-6)) {\n throw new Error(\n `E_FILLET_DEGENERATE_MESH: brepkit FilletBuilder produced degenerate geometry for fillet(r=${radius}, edges=${edgeCount}): bbox=${sx.toFixed(4)}×${sy.toFixed(4)}×${sz.toFixed(4)} (flattened). Refusing to render a vanished solid.`,\n )\n }\n}\n\n/**\n * 网格实体路径:对近似拓扑里识别出的边倒圆角(方案 2026-10-01 §3.5 三明治)。\n *\n * 与 BREP 路径的三处实质差异,都是\"网格零件没有精度链、没有 role 层\"的直接后果:\n * - 边**按几何或序号**解析:`EdgeTopoRef` 走长度/中点匹配;**1 起序号**直接取自近似\n * 拓扑的 `edges` 数组下标 + 1(等长边靠几何分不开,序号是唯一无歧义的指认方式);\n * - 内核调用走 L1 裸 `kernel.fillet`(`filletWithHistory` 是精度链的面演化能力,\n * 近似拓扑没有 hash 演化可言,故**不产生** faceEvolution / roleTable,如实缺席\n * 而不是造一张恒等映射);\n * - 产物句柄写回后由 `meshSolidProduct` 重新三角化并重建近似拓扑。\n *\n * GOTCHA(2026-10-01 实测,见 `brepkit-kernel/mesh-solid-topology.test.ts`):\n * 一次请求里的**全部**边交给内核,会在共面边链(如圆柱端盖的 64 条边)上被整体\n * 拒绝——不是单条边不可倒,而是\"一次性磨圆会自交\"。故本实现**如实失败**,不自动\n * 拆成逐边重试(运行时换策略会掩盖几何事实)。要给共面边链倒角,请分多次调用,\n * 每次一组几何上互不相邻的边。\n *\n * @param input - the mesh-solid input.\n * @param params - the validated fillet parameters.\n * @returns the filleted mesh solid as a new mesh part.\n */\nfunction filletMeshSolid(input: Shape, params: Record<string, unknown>): Shape {\n const edges = params.edges as (number | string | EdgeTopoRef)[] | string\n const radius = params.radius as number\n const entry = meshSolidEntry(input, 'fillet')\n // 选择器字符串只服务精度链(网格零件无 role 层);网格链用 1 起序号。\n if (typeof edges === 'string' || edges.some((e) => typeof e === 'string')) {\n throw new TopoRefError(\n 'E_TOPO_MESH_UNSUPPORTED',\n 'edge',\n 'fillet: selector strings are precision-chain only — mesh solids have no role layer (use 1-based edge ordinals)',\n )\n }\n const edgeHandles = resolveMeshEdges(entry, edges as (number | EdgeTopoRef)[], 'fillet')\n\n let result: BrepHandle\n try {\n result = entry.kernel.fillet(entry.solid, edgeHandles, radius)\n } catch (cause) {\n throw meshKernelFailure(\n 'fillet',\n 'E_FILLET_RADIUS_TOO_LARGE',\n `fillet(r=${radius}) on ${edgeHandles.length} edge(s)`,\n cause,\n )\n }\n return meshSolidProduct(entry, result)\n}\n\n/**\n * 多零件 compound 成员是匿名 Shape:其面 role 记录在父 compound 的 part 键合并表里。\n * 解包后让 target 继承父 part 名,buildEdgeResolutionContext/inputRoleTable 才能按\n * part 名查到表(与 chamfer 同构)。单零件路径 input===target 已命名,守卫使其空转。\n */\nfunction adoptParentPartName(input: Shape, target: Shape): void {\n if (target === input) return\n const parentName = nameOf(input)\n if (parentName && !nameOf(target)) setName(target, parentName)\n}\n\n/**\n * 在几何体上做圆角(等半径)。\n *\n * 两条路径都以 `meshEngines: ['brepkit']` 之外的事实为界:BREP 输入走\n * `filletWithHistory`(带面演化与 roleTable 传播);**网格实体**输入走网格后端\n * (近似拓扑无 role,边按几何或序号解析,无面演化)。非 BREP 的**裸网格**输入仍抛\n * `E_MESH_SOLID_UNSUPPORTED`——裸网格没有近似拓扑,没有边可选。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name fillet\n * @note `radius` 为正数(mm)。BREP 路径圆角后 roleTable 经 filletWithHistory 传播,\n * 后续特征仍可按 role 选面/选边;网格实体路径没有 role 层,边只能按几何或\n * 序号(近似拓扑 `edges` 数组下标 + 1)指认。\n * @returns Shape 圆角后的几何。\n * @param input - 目标几何。type:Shape required:true\n * @param params.edges - 参与圆角的边:CadQuery 边选择器字符串(如 \"|Z\",AI 生成)或相邻两面的 role 线路(EdgeTopoRef[],UI 拾取产物);网格链上也可用 1 起序号。type:EdgeTopoRef[]|string required:true\n * @param params.radius - 圆角半径(mm,>0)。type:number required:true\n * @example\n * const p = await cad.fillet(part0, { edges: [{ kind:'edge', faces:[{ origin:'box', role:'box:top' }, { origin:'box', role:'box:front' }], hint:{ kind:'edge' } }], radius:2 })\n * // AI 生成可写 CadQuery 边选择器字符串(\"|Z\" 切线平行 Z 的边)\n * const p2 = await cad.fillet(part0, { edges: \"|Z\", radius: 2 })\n * const q = await cad.fillet(meshPart, { edges: [3], radius: 1 })\n */\nexport const fillet = defineOp({\n meshEngines: ['brepkit'],\n mesh(input: Shape, params: Record<string, unknown>) {\n assertFilletParams(params, { allowOrdinals: true })\n // P2:多零件 load 输入是 compound——按 partIndex 取目标成员,结果替换回原 compound。\n const target = resolveCompoundMember(input, params.partIndex, 'fillet')\n const result = filletMeshSolid(target, params)\n return replaceCompoundMember(input, params.partIndex, result)\n },\n brep(input: Shape, params: Record<string, unknown>) {\n assertFilletParams(params)\n const target = resolveCompoundMember(input, params.partIndex, 'fillet')\n adoptParentPartName(input, target)\n const result = filletBrep(target, params)\n return replaceCompoundMember(input, params.partIndex, result)\n },\n naming: { kind: 'kernel', newFaces: { via: 'byAdjacency' } } as Provenance,\n\n paramDims: {},\n})\n"]}
@@ -6,7 +6,7 @@
6
6
  /** Cad script-face op manifest (B1: single source for cad namespace, check() symbol table). */
7
7
  export const SCRIPT_FACE_OPS = [
8
8
  { name: 'torus', module: 'topology' },
9
- { name: 'fuse', module: 'topology', engines: ["occt"] },
9
+ { name: 'fuse', module: 'topology' },
10
10
  { name: 'inspectMassProps', module: 'measurement', engines: ["occt"] },
11
11
  { name: 'area', module: 'measurement' },
12
12
  { name: 'length', module: 'measurement' },