@faicad/faijs 0.32.2 → 0.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/dist/api/api-namespace.js +3 -3
  2. package/dist/api/api-namespace.js.map +1 -1
  3. package/dist/api/assembly/index.d.ts +1 -0
  4. package/dist/api/assembly/index.d.ts.map +1 -1
  5. package/dist/api/assembly/index.js +1 -0
  6. package/dist/api/assembly/index.js.map +1 -1
  7. package/dist/api/assembly/lower.d.ts.map +1 -1
  8. package/dist/api/assembly/lower.js +7 -0
  9. package/dist/api/assembly/lower.js.map +1 -1
  10. package/dist/api/assembly/normalize.d.ts.map +1 -1
  11. package/dist/api/assembly/normalize.js +2 -1
  12. package/dist/api/assembly/normalize.js.map +1 -1
  13. package/dist/api/assembly/solve-tree.d.ts +46 -0
  14. package/dist/api/assembly/solve-tree.d.ts.map +1 -0
  15. package/dist/api/assembly/solve-tree.js +135 -0
  16. package/dist/api/assembly/solve-tree.js.map +1 -0
  17. package/dist/api/assembly/solve.d.ts +7 -0
  18. package/dist/api/assembly/solve.d.ts.map +1 -1
  19. package/dist/api/assembly/solve.js.map +1 -1
  20. package/dist/api/assembly/solvers/assembly-tree.d.ts +4 -0
  21. package/dist/api/assembly/solvers/assembly-tree.d.ts.map +1 -1
  22. package/dist/api/assembly/solvers/assembly-tree.js +1 -0
  23. package/dist/api/assembly/solvers/assembly-tree.js.map +1 -1
  24. package/dist/api/assembly/solvers/global-solver.d.ts.map +1 -1
  25. package/dist/api/assembly/solvers/global-solver.js +210 -3
  26. package/dist/api/assembly/solvers/global-solver.js.map +1 -1
  27. package/dist/api/assembly/solvers/types.d.ts +8 -0
  28. package/dist/api/assembly/solvers/types.d.ts.map +1 -1
  29. package/dist/api/assembly/solvers/types.js.map +1 -1
  30. package/dist/api/assembly/types.d.ts +32 -1
  31. package/dist/api/assembly/types.d.ts.map +1 -1
  32. package/dist/api/assembly/types.js.map +1 -1
  33. package/dist/api/assembly/validate.d.ts +2 -2
  34. package/dist/api/assembly/validate.d.ts.map +1 -1
  35. package/dist/api/assembly/validate.js +24 -2
  36. package/dist/api/assembly/validate.js.map +1 -1
  37. package/dist/api/cadquery-selectors/edge.js +1 -1
  38. package/dist/api/cadquery-selectors/edge.js.map +1 -1
  39. package/dist/api/cadquery-selectors/entity.d.ts +29 -20
  40. package/dist/api/cadquery-selectors/entity.d.ts.map +1 -1
  41. package/dist/api/cadquery-selectors/entity.js +26 -20
  42. package/dist/api/cadquery-selectors/entity.js.map +1 -1
  43. package/dist/api/cadquery-selectors/face.d.ts +4 -2
  44. package/dist/api/cadquery-selectors/face.d.ts.map +1 -1
  45. package/dist/api/cadquery-selectors/face.js +8 -7
  46. package/dist/api/cadquery-selectors/face.js.map +1 -1
  47. package/dist/api/cadquery-selectors/resolve.d.ts +7 -6
  48. package/dist/api/cadquery-selectors/resolve.d.ts.map +1 -1
  49. package/dist/api/cadquery-selectors/resolve.js +13 -11
  50. package/dist/api/cadquery-selectors/resolve.js.map +1 -1
  51. package/dist/api/chamfer.js +1 -1
  52. package/dist/api/chamfer.js.map +1 -1
  53. package/dist/api/draft.js +1 -1
  54. package/dist/api/draft.js.map +1 -1
  55. package/dist/api/edge-ref.js +2 -2
  56. package/dist/api/edge-ref.js.map +1 -1
  57. package/dist/api/engrave.js +1 -1
  58. package/dist/api/engrave.js.map +1 -1
  59. package/dist/api/export-3mf.js +1 -1
  60. package/dist/api/export-3mf.js.map +1 -1
  61. package/dist/api/export-brep.js +1 -1
  62. package/dist/api/export-brep.js.map +1 -1
  63. package/dist/api/export-step.d.ts +12 -13
  64. package/dist/api/export-step.d.ts.map +1 -1
  65. package/dist/api/export-step.js +19 -17
  66. package/dist/api/export-step.js.map +1 -1
  67. package/dist/api/extrude.js +3 -3
  68. package/dist/api/extrude.js.map +1 -1
  69. package/dist/api/face-ref.js +2 -2
  70. package/dist/api/face-ref.js.map +1 -1
  71. package/dist/api/feature-repair.js +2 -2
  72. package/dist/api/feature-repair.js.map +1 -1
  73. package/dist/api/fillet-variable.js +1 -1
  74. package/dist/api/fillet-variable.js.map +1 -1
  75. package/dist/api/fillet.js +1 -1
  76. package/dist/api/fillet.js.map +1 -1
  77. package/dist/api/geom.js +1 -1
  78. package/dist/api/geom.js.map +1 -1
  79. package/dist/api/import-step.d.ts.map +1 -1
  80. package/dist/api/import-step.js +4 -2
  81. package/dist/api/import-step.js.map +1 -1
  82. package/dist/api/pattern.js +1 -1
  83. package/dist/api/pattern.js.map +1 -1
  84. package/dist/api/place.js +1 -1
  85. package/dist/api/place.js.map +1 -1
  86. package/dist/api/punch-hole.js +1 -1
  87. package/dist/api/punch-hole.js.map +1 -1
  88. package/dist/api/replicate.js +1 -1
  89. package/dist/api/replicate.js.map +1 -1
  90. package/dist/api/revolve.js +1 -1
  91. package/dist/api/revolve.js.map +1 -1
  92. package/dist/api/section-by-plane.js +1 -1
  93. package/dist/api/section-by-plane.js.map +1 -1
  94. package/dist/api/shell.js +1 -1
  95. package/dist/api/shell.js.map +1 -1
  96. package/dist/api/sketch-on-face.js +1 -1
  97. package/dist/api/sketch-on-face.js.map +1 -1
  98. package/dist/api/split-by-plane.js +1 -1
  99. package/dist/api/split-by-plane.js.map +1 -1
  100. package/dist/api/split.js +1 -1
  101. package/dist/api/split.js.map +1 -1
  102. package/dist/api/thicken.js +1 -1
  103. package/dist/api/thicken.js.map +1 -1
  104. package/dist/api/topo-resolve.js +4 -4
  105. package/dist/api/topo-resolve.js.map +1 -1
  106. package/dist/api/transform.js +1 -1
  107. package/dist/api/transform.js.map +1 -1
  108. package/dist/api/view/view-projection.d.ts +8 -0
  109. package/dist/api/view/view-projection.d.ts.map +1 -1
  110. package/dist/api/view/view-projection.js +73 -2
  111. package/dist/api/view/view-projection.js.map +1 -1
  112. package/dist/boolean/cross-section.d.ts.map +1 -1
  113. package/dist/boolean/cross-section.js +3 -1
  114. package/dist/boolean/cross-section.js.map +1 -1
  115. package/dist/brep/brep-ops.d.ts +26 -0
  116. package/dist/brep/brep-ops.d.ts.map +1 -1
  117. package/dist/brep/brep-ops.js +45 -0
  118. package/dist/brep/brep-ops.js.map +1 -1
  119. package/dist/brep/brep-topology.js +1 -1
  120. package/dist/brep/brep-topology.js.map +1 -1
  121. package/dist/brep/engine/adapters/brep-mock.d.ts.map +1 -1
  122. package/dist/brep/engine/adapters/brep-mock.js +2 -0
  123. package/dist/brep/engine/adapters/brep-mock.js.map +1 -1
  124. package/dist/brep/engine/primitives.d.ts +17 -0
  125. package/dist/brep/engine/primitives.d.ts.map +1 -1
  126. package/dist/brep/engine/primitives.js.map +1 -1
  127. package/dist/brep/export/export-model.d.ts.map +1 -1
  128. package/dist/brep/export/export-model.js +36 -11
  129. package/dist/brep/export/export-model.js.map +1 -1
  130. package/dist/brep/export/step-flat.d.ts +35 -0
  131. package/dist/brep/export/step-flat.d.ts.map +1 -0
  132. package/dist/brep/export/step-flat.js +94 -0
  133. package/dist/brep/export/step-flat.js.map +1 -0
  134. package/dist/brep/export/step.d.ts.map +1 -1
  135. package/dist/brep/export/step.js +42 -21
  136. package/dist/brep/export/step.js.map +1 -1
  137. package/dist/brep/handle-bridge.d.ts +4 -4
  138. package/dist/brep/handle-bridge.js +4 -4
  139. package/dist/brep/handle-bridge.js.map +1 -1
  140. package/dist/brep/svg/svg-to-solid.d.ts +3 -17
  141. package/dist/brep/svg/svg-to-solid.d.ts.map +1 -1
  142. package/dist/brep/svg/svg-to-solid.js +16 -317
  143. package/dist/brep/svg/svg-to-solid.js.map +1 -1
  144. package/dist/brepkit-kernel/brepkitKernel.d.ts.map +1 -1
  145. package/dist/brepkit-kernel/brepkitKernel.js +10 -0
  146. package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
  147. package/dist/brepkit-kernel/brepkitWasm.d.ts +1 -1
  148. package/dist/brepkit-kernel/brepkitWasm.d.ts.map +1 -1
  149. package/dist/brepkit-kernel/brepkitWasm.js +7 -6
  150. package/dist/brepkit-kernel/brepkitWasm.js.map +1 -1
  151. package/dist/brepkit-kernel/multi-version-test.d.ts +7 -7
  152. package/dist/brepkit-kernel/multi-version-test.d.ts.map +1 -1
  153. package/dist/brepkit-kernel/multi-version-test.js +10 -10
  154. package/dist/brepkit-kernel/multi-version-test.js.map +1 -1
  155. package/dist/cad-runtime/direct-executor.js +2 -2
  156. package/dist/cad-runtime/direct-executor.js.map +1 -1
  157. package/dist/cad-runtime/runtime.js +5 -5
  158. package/dist/cad-runtime/runtime.js.map +1 -1
  159. package/dist/geometry2d/curve2d.js +42 -1
  160. package/dist/geometry2d/curve2d.js.map +1 -1
  161. package/dist/math3d/Box3.js.map +1 -1
  162. package/dist/math3d/Euler.js.map +1 -1
  163. package/dist/math3d/Mat4.js.map +1 -1
  164. package/dist/math3d/Plane.js.map +1 -1
  165. package/dist/math3d/Quat.js.map +1 -1
  166. package/dist/math3d/Vec2.js.map +1 -1
  167. package/dist/math3d/Vec3.js.map +1 -1
  168. package/dist/math3d/index.js.map +1 -1
  169. package/dist/mesh/meshdata/contour.d.ts +34 -0
  170. package/dist/mesh/meshdata/contour.d.ts.map +1 -0
  171. package/dist/mesh/meshdata/contour.js +39 -0
  172. package/dist/mesh/meshdata/contour.js.map +1 -0
  173. package/dist/mesh/meshdata/extrude.d.ts +25 -0
  174. package/dist/mesh/meshdata/extrude.d.ts.map +1 -0
  175. package/dist/mesh/meshdata/extrude.js +198 -0
  176. package/dist/mesh/meshdata/extrude.js.map +1 -0
  177. package/dist/mesh/meshdata/index.js.map +1 -1
  178. package/dist/mesh/meshdata/ops.js.map +1 -1
  179. package/dist/mesh/meshdata/parametric.js.map +1 -1
  180. package/dist/mesh/meshdata/weld.js.map +1 -1
  181. package/dist/node-host/cli.d.ts.map +1 -1
  182. package/dist/node-host/cli.js +3 -2
  183. package/dist/node-host/cli.js.map +1 -1
  184. package/dist/occt-kernel/occt-primitives.d.ts.map +1 -1
  185. package/dist/occt-kernel/occt-primitives.js +2 -0
  186. package/dist/occt-kernel/occt-primitives.js.map +1 -1
  187. package/dist/primitives/svg/svg-path.d.ts +192 -0
  188. package/dist/primitives/svg/svg-path.d.ts.map +1 -0
  189. package/dist/primitives/svg/svg-path.js +776 -0
  190. package/dist/primitives/svg/svg-path.js.map +1 -0
  191. package/dist/third-party/earcut.d.ts +19 -0
  192. package/dist/third-party/earcut.d.ts.map +1 -0
  193. package/dist/third-party/earcut.js +575 -0
  194. package/dist/third-party/earcut.js.map +1 -0
  195. package/package.json +22 -3
@@ -1 +1 @@
1
- {"version":3,"file":"section-by-plane.js","sourceRoot":"","sources":["../../src/api/section-by-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAajC,8DAA8D;AAC9D,SAAS,MAAM,CAAC,CAAO;IACrB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvG,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAA;IAC5F,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACtC,CAAC;AAED,wEAAwE;AACxE,SAAS,kBAAkB,CAAC,KAAY,EAAE,MAA4B;IACpE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAK,EAA2B,CAAA;IAChE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;IACrD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAA;IAC3F,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IAC1E,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAA;IAC3F,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IAC5C,OAAO,aAAa,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;AAC3E,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC;IACrC,SAAS,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE;IAClE,IAAI,CAAC,KAAY,EAAE,MAA4B;QAC7C,OAAO,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC1C,CAAC;IACD,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,gDAAgD,EAAgB;CACtG,CAAC,CAAA","sourcesContent":["/**\n * api sectionByPlane — 平面截面:solid 与无限平面的精确交线(手写中立 op,Phase 6.2)\n *\n * 中立 op:L1 `sectionByPlane(shape, point, normal)` 在 occt 与 brepkit 两侧均有\n * 真实现 ⇒ 走 `getBrepApi()`(D12),不声明 engines。\n *\n * 产物形态(设计原则 5 + §2.4①):L1 返回交线句柄**数组**(occt 适配器把\n * compound 逐条 downcast 成 edge/wire),数组过不了单产物边界 —— 收拢为\n * **1D compound**(`makeCompound(edges)`)经 `fromBrepCurve` 登记为\n * `kind:'curve'`(Phase 3 判别位;wire/edge 载荷为空,显示走 L1 `wireframe`)。\n * 无交线(平面不与体相交)显式报错 —— 不返回假空产物(原则 9)。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { BrepHandle, BrepVec3 } from '../brep/engine/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { brepOf, fromBrepCurve } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\n\ntype Vec3 = [number, number, number]\n\n/** `cad.sectionByPlane` 参数。 */\nexport interface SectionByPlaneParams {\n /** 平面上一点(mm)。 */\n point: Vec3\n /** 平面法向(不必单位化)。 */\n normal: Vec3\n}\n\n/** 元组 → BrepVec3 对象(同 split-by-plane:L1 向量形参是 {x,y,z} 对象)。 */\nfunction toVec3(v: Vec3): BrepVec3 {\n if (!Array.isArray(v) || v.length !== 3 || v.some((n) => typeof n !== 'number' || !Number.isFinite(n))) {\n throw new Error('E_SECTION_BY_PLANE_BAD_VEC: point/normal must be [x,y,z] finite numbers')\n }\n return { x: v[0], y: v[1], z: v[2] }\n}\n\n/** BREP 路径:L1 sectionByPlane → 交线收拢为 1D compound → fromBrepCurve 登记。 */\nfunction sectionByPlaneBrep(input: Shape, params: SectionByPlaneParams): Shape {\n const { point, normal } = params ?? ({} as SectionByPlaneParams)\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (!solid) throw new Error('E_SECTION_BY_PLANE_NO_BREP: sectionByPlane input is not BREP')\n const curves = kernel.sectionByPlane(solid, toVec3(point), toVec3(normal))\n if (!curves || curves.length === 0) {\n throw new Error('E_SECTION_BY_PLANE_NO_INTERSECTION: plane does not intersect the shape')\n }\n const compound = kernel.makeCompound(curves)\n return fromBrepCurve(solidToShape(kernel, compound), { solid: compound })\n}\n\n/**\n * 求实体与无限平面的精确截面线(1D 曲线,可继续建模/导出 STEP)。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name sectionByPlane\n * @note 中立 op:L1 sectionByPlane 两引擎同实现。产物是 1D 曲线(kind:'curve',\n * 全部交线收拢为一个 compound);平面不与体相交时显式报错。\n * @returns Shape 1D 截面曲线。\n * @param input - 目标实体。type:Shape required:true\n * @param params.point - 平面上一点 [x,y,z](mm)。type:Vec3 required:true\n * @param params.normal - 平面法向 [x,y,z]。type:Vec3 required:true\n * @example\n * const sec = await cad.sectionByPlane(part0, { point: [0,0,5], normal: [0,0,1] })\n */\nexport const sectionByPlane = defineOp({\n paramDims: { 'params.point': 'length', 'params.normal': 'length' },\n brep(input: Shape, params: SectionByPlaneParams) {\n return sectionByPlaneBrep(input, params)\n },\n naming: { kind: 'unmodeled', reason: '1D section curves have no face role vocabulary' } as Provenance,\n})\n"]}
1
+ {"version":3,"file":"section-by-plane.js","sourceRoot":"","sources":["../../src/api/section-by-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAajC,8DAA8D;AAC9D,SAAS,MAAM,CAAC,CAAO;IACrB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvG,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAA;IAC5F,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACtC,CAAC;AAED,wEAAwE;AACxE,SAAS,kBAAkB,CAAC,KAAY,EAAE,MAA4B;IACpE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAK,EAA2B,CAAA;IAChE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;IACrD,IAAI,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAA;IACxG,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IAC1E,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAA;IAC3F,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IAC5C,OAAO,aAAa,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;AAC3E,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC;IACrC,SAAS,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE;IAClE,IAAI,CAAC,KAAY,EAAE,MAA4B;QAC7C,OAAO,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC1C,CAAC;IACD,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,gDAAgD,EAAgB;CACtG,CAAC,CAAA","sourcesContent":["/**\n * api sectionByPlane — 平面截面:solid 与无限平面的精确交线(手写中立 op,Phase 6.2)\n *\n * 中立 op:L1 `sectionByPlane(shape, point, normal)` 在 occt 与 brepkit 两侧均有\n * 真实现 ⇒ 走 `getBrepApi()`(D12),不声明 engines。\n *\n * 产物形态(设计原则 5 + §2.4①):L1 返回交线句柄**数组**(occt 适配器把\n * compound 逐条 downcast 成 edge/wire),数组过不了单产物边界 —— 收拢为\n * **1D compound**(`makeCompound(edges)`)经 `fromBrepCurve` 登记为\n * `kind:'curve'`(Phase 3 判别位;wire/edge 载荷为空,显示走 L1 `wireframe`)。\n * 无交线(平面不与体相交)显式报错 —— 不返回假空产物(原则 9)。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { BrepHandle, BrepVec3 } from '../brep/engine/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { brepOf, fromBrepCurve } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\n\ntype Vec3 = [number, number, number]\n\n/** `cad.sectionByPlane` 参数。 */\nexport interface SectionByPlaneParams {\n /** 平面上一点(mm)。 */\n point: Vec3\n /** 平面法向(不必单位化)。 */\n normal: Vec3\n}\n\n/** 元组 → BrepVec3 对象(同 split-by-plane:L1 向量形参是 {x,y,z} 对象)。 */\nfunction toVec3(v: Vec3): BrepVec3 {\n if (!Array.isArray(v) || v.length !== 3 || v.some((n) => typeof n !== 'number' || !Number.isFinite(n))) {\n throw new Error('E_SECTION_BY_PLANE_BAD_VEC: point/normal must be [x,y,z] finite numbers')\n }\n return { x: v[0], y: v[1], z: v[2] }\n}\n\n/** BREP 路径:L1 sectionByPlane → 交线收拢为 1D compound → fromBrepCurve 登记。 */\nfunction sectionByPlaneBrep(input: Shape, params: SectionByPlaneParams): Shape {\n const { point, normal } = params ?? ({} as SectionByPlaneParams)\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (solid === undefined) throw new Error('E_SECTION_BY_PLANE_NO_BREP: sectionByPlane input is not BREP')\n const curves = kernel.sectionByPlane(solid, toVec3(point), toVec3(normal))\n if (!curves || curves.length === 0) {\n throw new Error('E_SECTION_BY_PLANE_NO_INTERSECTION: plane does not intersect the shape')\n }\n const compound = kernel.makeCompound(curves)\n return fromBrepCurve(solidToShape(kernel, compound), { solid: compound })\n}\n\n/**\n * 求实体与无限平面的精确截面线(1D 曲线,可继续建模/导出 STEP)。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name sectionByPlane\n * @note 中立 op:L1 sectionByPlane 两引擎同实现。产物是 1D 曲线(kind:'curve',\n * 全部交线收拢为一个 compound);平面不与体相交时显式报错。\n * @returns Shape 1D 截面曲线。\n * @param input - 目标实体。type:Shape required:true\n * @param params.point - 平面上一点 [x,y,z](mm)。type:Vec3 required:true\n * @param params.normal - 平面法向 [x,y,z]。type:Vec3 required:true\n * @example\n * const sec = await cad.sectionByPlane(part0, { point: [0,0,5], normal: [0,0,1] })\n */\nexport const sectionByPlane = defineOp({\n paramDims: { 'params.point': 'length', 'params.normal': 'length' },\n brep(input: Shape, params: SectionByPlaneParams) {\n return sectionByPlaneBrep(input, params)\n },\n naming: { kind: 'unmodeled', reason: '1D section curves have no face role vocabulary' } as Provenance,\n})\n"]}
package/dist/api/shell.js CHANGED
@@ -39,7 +39,7 @@ function shellBrep(input, params) {
39
39
  assertShellParams(params);
40
40
  const kernel = getBrepApi();
41
41
  const solid = brepOf(input);
42
- if (!solid)
42
+ if (solid === undefined)
43
43
  throw new Error('E_SHELL_NO_BREP: shell input is not BREP');
44
44
  const ctx = buildEdgeResolutionContext(kernel, input);
45
45
  if (!ctx)
@@ -1 +1 @@
1
- {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/api/shell.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api shell — 抽壳:按面移除生成等厚薄壁(手写中立 op,Phase 5)\n *\n * 中立 op:L1 `shell(solid, facesToRemove, thickness, tolerance)` 在 occt 与\n * brepkit 两侧均有真实现(engine-method-map 实测 `dialect`)⇒ 走 `getBrepApi()`\n * (D12),不声明 engines(中立 op:实现只经 L1 契约面)。相对现状是\n * 覆盖面扩大——旧版是 occt 平台 op(arg-spec `engines:['occt']`,skip)。\n *\n * 选面口径(设计原则 4):`openFaces: FaceTopoRef[]`(`cad.faceRef` 产物),\n * 与 `fillet` 的 `EdgeTopoRef[]` / `extrude` 的 `upTo` 同族;不复刻旧\n * `Face[]` 句柄入参。解析在 op 内一处完成(`buildEdgeResolutionContext` 现场枚举\n * + `resolveTopoRef`)。\n *\n * 网格链(方案 2026-10-01 §4 Phase 4 / B3 批):`mesh:` 实现走网格后端,面按\n * **序号或 FaceTopoRef** 解析(近似拓扑没有 role 层),产物是新的网格零件。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { shellWithHashEvolution } from '../brep/face-evolution'\nimport { brepOf, fromBrep } from '../shape'\nimport { defineOp } from '../sdk'\nimport { hasNativeHistory } from '../brep/engine/native-history'\nimport type { FaceTopoRef } from '../topology/naming'\nimport { resolveTopoRef, TopoRefError } from '../topology/naming'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { buildEdgeResolutionContext } from './topo-resolve'\nimport { meshKernelFailure, meshSolidBasicEntry, meshSolidProduct, resolveMeshFaces } from './internal/mesh-solid-op'\n\n/** 抽壳时移除的面选择器:精度链用 `FaceTopoRef`;网格链也可用 1 起序号。 */\nexport type ShellFaceSelector = number | FaceTopoRef\n\n/** `cad.shell` 参数。 */\nexport interface ShellParams {\n /** 抽壳时移除的面(FaceTopoRef[],`cad.faceRef` 产物;空数组 = 全封闭薄壁)。 */\n openFaces: ShellFaceSelector[]\n /** 壁厚(mm,>0,向内偏置)。 */\n thickness: number\n /** 容差(mm,可选;默认走内核约定值)。 */\n tolerance?: number\n}\n\n/** 参数共校验(两条路径共用;`openFaces` 为空数组合法 = 全封闭薄壁)。 */\nfunction assertShellParams(params: ShellParams): void {\n const { openFaces, thickness } = params ?? ({} as ShellParams)\n if (!Array.isArray(openFaces)) {\n throw new Error('E_SHELL_BAD_OPEN_FACES: shell.openFaces must be an array of FaceTopoRef or ordinals')\n }\n if (typeof thickness !== 'number' || !Number.isFinite(thickness) || thickness <= 0) {\n throw new Error('E_SHELL_BAD_THICKNESS: shell.thickness must be a positive number')\n }\n}\n\n/** BREP 路径:解析面引用 → L1 shell → 收养。 */\nfunction shellBrep(input: Shape, params: ShellParams): Shape {\n const { openFaces, thickness, tolerance } = params\n assertShellParams(params)\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (!solid) throw new Error('E_SHELL_NO_BREP: shell input is not BREP')\n\n const ctx = buildEdgeResolutionContext(kernel, input as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'shell: input has no BREP naming context')\n const faceHandles = openFaces.map((ref) => {\n // 精度链不开序号口子:BREP 的面身份是 `(origin, role)`,序号只是本次枚举序。\n if (typeof ref === 'number') {\n throw new Error(\n 'E_SHELL_BAD_OPEN_FACES: an ordinal face selector is only available on the mesh chain — ' +\n 'a precision-chain face is identified by its role line (cad.faceRef)',\n )\n }\n if (!ref || (ref as { kind?: unknown }).kind !== 'face') {\n throw new Error('E_SHELL_BAD_OPEN_FACES: every openFaces entry must be a FaceTopoRef (cad.faceRef)')\n }\n const r = resolveTopoRef(ref, ctx)\n if (r.handle === undefined) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `shell: face resolved without handle (${ref.origin}/${ref.role})`)\n }\n return r.handle as BrepHandle\n })\n\n // C5 实现面接入(静态双轨,无运行时 try-catch):\n // - occt 原生有 `shellWithHistory` → 走权威面演化(血缘更准);\n // - brepkit 没有 → 显式回退裸 L1 `shell`(**不报错**),不伪造面演化\n // (抽壳是毁面重造,恒等映射不真实)。\n if (hasNativeHistory('shellWithHistory')) {\n const r = shellWithHashEvolution(kernel, solid, faceHandles, thickness, tolerance ?? 1e-6)\n return fromBrep(solidToShape(kernel, r.result), { solid: r.result, faceEvolution: r.faceEvolution })\n }\n const result = kernel.shell(solid, faceHandles, thickness, tolerance ?? 1e-6)\n return fromBrep(solidToShape(kernel, result), { solid: result })\n}\n\n/**\n * 网格实体路径:按序号或几何选出要开口的面,交网格后端抽壳。\n *\n * 与 BREP 路径同一条 L1 契约面(`shell(solid, faces, thickness, tolerance)`),\n * 差别只在选面方式:近似拓扑没有 role 层,面靠**序号**(拓扑 `faces` 数组下标 + 1)\n * 或 `FaceTopoRef` 的几何匹配(法向/中心/面积)指认。产物是新网格零件,\n * 近似拓扑由 `meshSolidProduct` 重建。\n *\n * @param input - the mesh-solid input.\n * @param params - the shell parameters.\n * @returns the shelled mesh solid as a new mesh part.\n */\nfunction shellMeshSolid(input: Shape, params: ShellParams): Shape {\n const { openFaces, thickness, tolerance } = params\n assertShellParams(params)\n const entry = meshSolidBasicEntry(input, 'shell')\n const faceHandles = resolveMeshFaces(entry.kernel, input, openFaces, 'shell')\n\n let result: BrepHandle\n try {\n result = entry.kernel.shell(entry.solid, faceHandles, thickness, tolerance ?? 1e-6)\n } catch (cause) {\n throw meshKernelFailure(\n 'shell',\n 'E_SHELL_FAILED',\n `shell(thickness=${thickness}) removing ${faceHandles.length} face(s)`,\n cause,\n )\n }\n return meshSolidProduct(entry, result)\n}\n\n/**\n * 抽壳:移除指定面并把余下面偏置成等厚薄壁。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name shell\n * @note 中立 op:L1 shell 两引擎同实现。`openFaces` 为空数组时生成全封闭薄壁。\n * 精度链按 role 线路选面(`cad.faceRef`);网格链按序号或几何选面——网格零件\n * 没有 role 层,序号是唯一无歧义的指认方式。\n * @returns Shape 抽壳后的薄壁体。\n * @param input - 目标几何。type:Shape required:true\n * @param params.openFaces - 要移除的面(FaceTopoRef[],cad.faceRef 产物;网格链可用 1 起序号)。type:FaceTopoRef[] required:true\n * @param params.thickness - 壁厚(mm,>0)。type:number required:true\n * @param params.tolerance - 容差(mm)。type:number required:false\n * @example\n * const sh = await cad.shell(part0, { openFaces: [cad.faceRef(part0, 1)], thickness: 2 })\n * const sm = await cad.shell(meshPart, { openFaces: [4], thickness: 2 })\n */\nexport const shell = defineOp({\n meshEngines: ['brepkit'],\n mesh(input: Shape, params: ShellParams) {\n return shellMeshSolid(input, params)\n },\n brep(input: Shape, params: ShellParams) {\n return shellBrep(input, params)\n },\n naming: { kind: 'kernel', newFaces: { via: 'byAdjacency' } } as Provenance,\n\n paramDims: {},\n})\n"]}
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/api/shell.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api shell — 抽壳:按面移除生成等厚薄壁(手写中立 op,Phase 5)\n *\n * 中立 op:L1 `shell(solid, facesToRemove, thickness, tolerance)` 在 occt 与\n * brepkit 两侧均有真实现(engine-method-map 实测 `dialect`)⇒ 走 `getBrepApi()`\n * (D12),不声明 engines(中立 op:实现只经 L1 契约面)。相对现状是\n * 覆盖面扩大——旧版是 occt 平台 op(arg-spec `engines:['occt']`,skip)。\n *\n * 选面口径(设计原则 4):`openFaces: FaceTopoRef[]`(`cad.faceRef` 产物),\n * 与 `fillet` 的 `EdgeTopoRef[]` / `extrude` 的 `upTo` 同族;不复刻旧\n * `Face[]` 句柄入参。解析在 op 内一处完成(`buildEdgeResolutionContext` 现场枚举\n * + `resolveTopoRef`)。\n *\n * 网格链(方案 2026-10-01 §4 Phase 4 / B3 批):`mesh:` 实现走网格后端,面按\n * **序号或 FaceTopoRef** 解析(近似拓扑没有 role 层),产物是新的网格零件。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { shellWithHashEvolution } from '../brep/face-evolution'\nimport { brepOf, fromBrep } from '../shape'\nimport { defineOp } from '../sdk'\nimport { hasNativeHistory } from '../brep/engine/native-history'\nimport type { FaceTopoRef } from '../topology/naming'\nimport { resolveTopoRef, TopoRefError } from '../topology/naming'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { buildEdgeResolutionContext } from './topo-resolve'\nimport { meshKernelFailure, meshSolidBasicEntry, meshSolidProduct, resolveMeshFaces } from './internal/mesh-solid-op'\n\n/** 抽壳时移除的面选择器:精度链用 `FaceTopoRef`;网格链也可用 1 起序号。 */\nexport type ShellFaceSelector = number | FaceTopoRef\n\n/** `cad.shell` 参数。 */\nexport interface ShellParams {\n /** 抽壳时移除的面(FaceTopoRef[],`cad.faceRef` 产物;空数组 = 全封闭薄壁)。 */\n openFaces: ShellFaceSelector[]\n /** 壁厚(mm,>0,向内偏置)。 */\n thickness: number\n /** 容差(mm,可选;默认走内核约定值)。 */\n tolerance?: number\n}\n\n/** 参数共校验(两条路径共用;`openFaces` 为空数组合法 = 全封闭薄壁)。 */\nfunction assertShellParams(params: ShellParams): void {\n const { openFaces, thickness } = params ?? ({} as ShellParams)\n if (!Array.isArray(openFaces)) {\n throw new Error('E_SHELL_BAD_OPEN_FACES: shell.openFaces must be an array of FaceTopoRef or ordinals')\n }\n if (typeof thickness !== 'number' || !Number.isFinite(thickness) || thickness <= 0) {\n throw new Error('E_SHELL_BAD_THICKNESS: shell.thickness must be a positive number')\n }\n}\n\n/** BREP 路径:解析面引用 → L1 shell → 收养。 */\nfunction shellBrep(input: Shape, params: ShellParams): Shape {\n const { openFaces, thickness, tolerance } = params\n assertShellParams(params)\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (solid === undefined) throw new Error('E_SHELL_NO_BREP: shell input is not BREP')\n\n const ctx = buildEdgeResolutionContext(kernel, input as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'shell: input has no BREP naming context')\n const faceHandles = openFaces.map((ref) => {\n // 精度链不开序号口子:BREP 的面身份是 `(origin, role)`,序号只是本次枚举序。\n if (typeof ref === 'number') {\n throw new Error(\n 'E_SHELL_BAD_OPEN_FACES: an ordinal face selector is only available on the mesh chain — ' +\n 'a precision-chain face is identified by its role line (cad.faceRef)',\n )\n }\n if (!ref || (ref as { kind?: unknown }).kind !== 'face') {\n throw new Error('E_SHELL_BAD_OPEN_FACES: every openFaces entry must be a FaceTopoRef (cad.faceRef)')\n }\n const r = resolveTopoRef(ref, ctx)\n if (r.handle === undefined) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `shell: face resolved without handle (${ref.origin}/${ref.role})`)\n }\n return r.handle as BrepHandle\n })\n\n // C5 实现面接入(静态双轨,无运行时 try-catch):\n // - occt 原生有 `shellWithHistory` → 走权威面演化(血缘更准);\n // - brepkit 没有 → 显式回退裸 L1 `shell`(**不报错**),不伪造面演化\n // (抽壳是毁面重造,恒等映射不真实)。\n if (hasNativeHistory('shellWithHistory')) {\n const r = shellWithHashEvolution(kernel, solid, faceHandles, thickness, tolerance ?? 1e-6)\n return fromBrep(solidToShape(kernel, r.result), { solid: r.result, faceEvolution: r.faceEvolution })\n }\n const result = kernel.shell(solid, faceHandles, thickness, tolerance ?? 1e-6)\n return fromBrep(solidToShape(kernel, result), { solid: result })\n}\n\n/**\n * 网格实体路径:按序号或几何选出要开口的面,交网格后端抽壳。\n *\n * 与 BREP 路径同一条 L1 契约面(`shell(solid, faces, thickness, tolerance)`),\n * 差别只在选面方式:近似拓扑没有 role 层,面靠**序号**(拓扑 `faces` 数组下标 + 1)\n * 或 `FaceTopoRef` 的几何匹配(法向/中心/面积)指认。产物是新网格零件,\n * 近似拓扑由 `meshSolidProduct` 重建。\n *\n * @param input - the mesh-solid input.\n * @param params - the shell parameters.\n * @returns the shelled mesh solid as a new mesh part.\n */\nfunction shellMeshSolid(input: Shape, params: ShellParams): Shape {\n const { openFaces, thickness, tolerance } = params\n assertShellParams(params)\n const entry = meshSolidBasicEntry(input, 'shell')\n const faceHandles = resolveMeshFaces(entry.kernel, input, openFaces, 'shell')\n\n let result: BrepHandle\n try {\n result = entry.kernel.shell(entry.solid, faceHandles, thickness, tolerance ?? 1e-6)\n } catch (cause) {\n throw meshKernelFailure(\n 'shell',\n 'E_SHELL_FAILED',\n `shell(thickness=${thickness}) removing ${faceHandles.length} face(s)`,\n cause,\n )\n }\n return meshSolidProduct(entry, result)\n}\n\n/**\n * 抽壳:移除指定面并把余下面偏置成等厚薄壁。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name shell\n * @note 中立 op:L1 shell 两引擎同实现。`openFaces` 为空数组时生成全封闭薄壁。\n * 精度链按 role 线路选面(`cad.faceRef`);网格链按序号或几何选面——网格零件\n * 没有 role 层,序号是唯一无歧义的指认方式。\n * @returns Shape 抽壳后的薄壁体。\n * @param input - 目标几何。type:Shape required:true\n * @param params.openFaces - 要移除的面(FaceTopoRef[],cad.faceRef 产物;网格链可用 1 起序号)。type:FaceTopoRef[] required:true\n * @param params.thickness - 壁厚(mm,>0)。type:number required:true\n * @param params.tolerance - 容差(mm)。type:number required:false\n * @example\n * const sh = await cad.shell(part0, { openFaces: [cad.faceRef(part0, 1)], thickness: 2 })\n * const sm = await cad.shell(meshPart, { openFaces: [4], thickness: 2 })\n */\nexport const shell = defineOp({\n meshEngines: ['brepkit'],\n mesh(input: Shape, params: ShellParams) {\n return shellMeshSolid(input, params)\n },\n brep(input: Shape, params: ShellParams) {\n return shellBrep(input, params)\n },\n naming: { kind: 'kernel', newFaces: { via: 'byAdjacency' } } as Provenance,\n\n paramDims: {},\n})\n"]}
@@ -22,7 +22,7 @@ import { meshBackendOrThrow, meshFacePlane, meshFaceProduct, resolveMeshFaceHand
22
22
  */
23
23
  export function resolveFaceHandle(kernel, on, selector) {
24
24
  const solid = brepOf(on);
25
- if (!solid) {
25
+ if (solid === undefined) {
26
26
  throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'face', 'sketchOnFace: target `on` has no BREP solid');
27
27
  }
28
28
  if (typeof selector === 'number') {
@@ -1 +1 @@
1
- {"version":3,"file":"sketch-on-face.js","sourceRoot":"","sources":["../../src/api/sketch-on-face.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `cad.sketchOnFace`: place 2D contours onto a **planar** face of an existing\n * solid (E3 placement), engine-neutral.\n *\n * 2026-10-09 split: `sketchOnFace` now owns the *planar* case only and carries\n * **no `engines` declaration** — it is a common op, available on the OCCT BREP\n * engine, the brepkit BREP engine, and the brepkit mesh backend. The\n * curved / UV-conform capability (map into the face's `(u,v)` domain + snap to\n * the surface via `fixWireOnFace`, which needs OCCT) moved to\n * `cad.sketchOnSurface` (`engines: ['occt']`).\n *\n * For a planar face the contour is laid on the face's own plane frame via\n * `assembleWireOnFace` (identity `original` scale mode, or `bounds`/`native`\n * where the engine exposes a finite UV domain) and wrapped as a face with\n * `makeFace` — exact, no surface snapping needed. Non-planar faces are refused\n * up front with a pointer to `cad.sketchOnSurface`.\n *\n * **Two chains, two mechanisms** (plan 2026-10-01 §4 Phase 3). The BREP branch\n * above maps the contour onto the host face's plane. The **mesh branch**\n * (`buildSketchOnFaceMesh`, `meshEngines: ['brepkit']`) works on a mesh solid's\n * approximate topology and deliberately does NOT use a UV domain: a brepkit\n * plane's domain is ±1e6 (an infinite domain), so \"place the sketch in the face's\n * UV range\" is meaningless there. It places the contour in the face's own plane\n * frame instead (origin = face bbox centre, normal = `surfaceNormal`), which\n * matches `cad.sketchOnPlane`'s semantics. The result is a **mesh-chain face**\n * (`ShapeSlot.meshFace`), ready for the mesh branch of `cad.extrude`.\n *\n * For both chains a planar face is required; curved faces are rejected by the\n * BREP branch (→ `cad.sketchOnSurface`) and by the mesh branch (→\n * `E_MESH_SOLID_UNSUPPORTED`).\n * @module\n */\nimport type { Shape } from '../mesh/types'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { solidToShape } from '../brep/brep-ops'\nimport { fromBrep, fromBrepCurve, brepOf } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { TopoRefError, resolveTopoRef, type FaceTopoRef } from '../topology/naming'\nimport { buildEdgeResolutionContext } from './topo-resolve'\nimport { Blueprint } from '../geometry2d/blueprint'\nimport { CompoundBlueprint } from '../geometry2d/compound-blueprint'\nimport { organiseBlueprints } from '../geometry2d/organise'\nimport { assembleWireOnFace, type FaceScaleMode } from '../geometry2d/bridge/on-surface'\nimport { assembleWire, type Plane, type Vec3 } from '../geometry2d/bridge/lift-on-plane'\nimport { profileSegToCurve, type ProfileSegLike } from '../geometry2d/adapt'\nimport { assertProfileParams, toContourBlueprints, type ProfileLoop } from './profile'\nimport { MeshUnsupportedError, BrepUnsupportedError, getCurrentStmt, getBackends } from '../runtime-state'\nimport {\n meshBackendOrThrow,\n meshFacePlane,\n meshFaceProduct,\n resolveMeshFaceHandle,\n} from './internal/mesh-solid-op'\n\n/** A face selector on a target solid: a 1-based ordinal (FreeCAD `FaceN`) or a `cad.faceRef` result. */\nexport type FaceSelector = number | FaceTopoRef\n\n/** Contour loops + the target face for `cad.sketchOnFace`. */\nexport interface SketchOnFaceParams {\n /** Ordered 2D closed contours (same shape as `cad.profile` / `cad.sketchOnPlane`). */\n contours: ProfileLoop[]\n /** The target solid on which the host face lives. */\n on: Shape\n /** The host face: a 1-based face ordinal, or a `cad.faceRef(on, n)` result. */\n face: FaceSelector\n /** How 2D coords map onto the face UV space: `'original'` (identity) | `'bounds'`/`'native'` (affine-fit). */\n scaleMode?: FaceScaleMode\n /** Result shape: `'face'` (default) builds a face on the contour; `'wire'` returns the outer-loop wire only. */\n as?: 'face' | 'wire'\n}\n\n/**\n * Resolve a face on `on` to its live BREP handle (1-based ordinal or `cad.faceRef`).\n * @param kernel - the BREP engine surface.\n * @param on - the target solid shape.\n * @param selector - the face ordinal or `cad.faceRef` result.\n * @returns the live face handle.\n */\nexport function resolveFaceHandle(kernel: BrepEngineApi, on: Shape, selector: FaceSelector): BrepHandle {\n const solid = brepOf(on) as BrepHandle | undefined\n if (!solid) {\n throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'face', 'sketchOnFace: target `on` has no BREP solid')\n }\n if (typeof selector === 'number') {\n const faces = kernel.getSubShapes(solid, 'face')\n if (!Number.isInteger(selector) || selector < 1 || selector > faces.length) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `sketchOnFace: face ordinal ${selector} out of range [1, ${faces.length}]`,\n )\n }\n return faces[selector - 1] as BrepHandle\n }\n if (selector.kind !== 'face') {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: selector must be a face ordinal or a face ref')\n }\n const ctx = buildEdgeResolutionContext(kernel, on as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: input has no naming context')\n const resolved = resolveTopoRef(selector, ctx)\n if (resolved.handle === undefined) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: face ref resolved to no live handle')\n }\n return resolved.handle as BrepHandle\n}\n\n/**\n * Make the placed face inherit the host face's outward orientation: surface\n * normals must point the same way, otherwise the resulting face — and any prism\n * extruded from it — carries a reversed shell whose OCCT volume collapses to ~0.\n * @param kernel - the BREP engine surface.\n * @param host - the host face handle (its normal is the reference).\n * @param face - the face built on the host surface.\n * @returns a handle to the (possibly re-oriented) face.\n */\nexport function orientLikeHost(kernel: BrepEngineApi, host: BrepHandle, face: BrepHandle): BrepHandle {\n const a = kernel.surfaceNormal(host, 0, 0)\n const b = kernel.surfaceNormal(face, 0, 0)\n const dot = a.x * b.x + a.y * b.y + a.z * b.z\n return dot < 0 ? kernel.reverseShape(face) : face\n}\n\n/**\n * Build one planar face (outer + optional holes) on the host **planar** face for\n * an organised contour entry. No surface snapping (`fixWireOnFace`) is needed:\n * the wire from `assembleWireOnFace` already lies in the face's plane, so\n * `makeFace` is exact.\n * @param kernel - the BREP engine surface.\n * @param host - the resolved live host face handle.\n * @param mode - the face-scale mode (`original` everywhere; `bounds`/`native` only where the engine exposes a finite UV domain).\n * @param entry - the organised contour entry (outer + optional holes).\n * @returns the built face handle.\n */\nfunction buildPlanarFaceOnHost(\n kernel: BrepEngineApi,\n host: BrepHandle,\n mode: FaceScaleMode,\n entry: Blueprint | CompoundBlueprint,\n): BrepHandle {\n const outerBp = entry instanceof CompoundBlueprint ? entry.blueprints[0]! : entry\n const outerWire = assembleWireOnFace(kernel, host, mode, outerBp).wire\n let face = orientLikeHost(kernel, host, kernel.makeFace(outerWire))\n if (entry instanceof CompoundBlueprint) {\n const holes = entry.blueprints.slice(1).map((h) => assembleWireOnFace(kernel, host, mode, h).wire as never)\n if (holes.length > 0) face = kernel.addHolesInFace(face, holes)\n }\n return face\n}\n\n/**\n * Build a 3D Shape from closed contours placed on the host **planar** face.\n * @param kernel - the BREP engine surface.\n * @param params - contour loops + target solid + face + scale mode.\n * @returns Shape on the target face (face, or the outer wire when `as:'wire'`).\n * @throws {BrepUnsupportedError} when the host face is not planar (use `cad.sketchOnSurface`).\n */\nexport function buildSketchOnFaceWith(kernel: BrepEngineApi, params: SketchOnFaceParams): Shape {\n const host = resolveFaceHandle(kernel, params.on, params.face)\n const mode = params.scaleMode ?? 'original'\n\n // Planar-only gate: the curved / UV-conform capability moved to `cad.sketchOnSurface`.\n const surface = kernel.surfaceType(host)\n if (surface !== 'plane') {\n throw new BrepUnsupportedError(\n `E_BREP_UNSUPPORTED: cad.sketchOnFace only supports planar faces (got surfaceType '${surface}'). ` +\n 'Use cad.sketchOnSurface for curved/UV-conform placement.',\n getCurrentStmt(),\n )\n }\n // brepkit planar faces expose an infinite (±1e6) UV domain, so only the\n // identity 'original' scale mode is meaningful there (mirrors the mesh branch\n // restriction); OCCT planar faces keep full scaleMode support.\n const engineId = getBackends().config.brepEngineId\n if (engineId === 'brepkit' && mode !== 'original') {\n throw new BrepUnsupportedError(\n `E_BREP_UNSUPPORTED: cad.sketchOnFace on a brepkit planar face only supports scaleMode 'original' ` +\n \"(brepkit planar faces have no finite UV domain) — use the default 'original'.\",\n getCurrentStmt(),\n )\n }\n\n const blueprints = params.contours.map(\n (loop) => new Blueprint(loop.segments.map((s) => profileSegToCurve(s as ProfileSegLike))),\n )\n const organised = organiseBlueprints(blueprints)\n\n if (params.as === 'wire') {\n const outer =\n organised.blueprints[0] instanceof CompoundBlueprint\n ? organised.blueprints[0].blueprints[0]!\n : (organised.blueprints[0] as Blueprint)\n const { wire } = assembleWireOnFace(kernel, host, mode, outer)\n return fromBrepCurve(solidToShape(kernel, wire), { solid: wire })\n }\n\n const faces = organised.blueprints.map((entry) => buildPlanarFaceOnHost(kernel, host, mode, entry))\n if (faces.length === 0) throw new Error('sketchOnFace: no contours to place')\n if (faces.length === 1) {\n const f = faces[0]!\n return fromBrep(solidToShape(kernel, f), { solid: f })\n }\n const compound = kernel.makeCompound(faces)\n return fromBrep(solidToShape(kernel, compound), { solid: compound })\n}\n\n/**\n * 把一张平面的法向对齐到参考方向(外部朝向)。网格链上的复制版:BREP 侧的\n * `orientLikeHost` 存在的原因是\"面朝向反了 → 由它拉出的棱柱壳反向 → 体积归零\",\n * 这里同一个道理,只是参考法向来自**平面本身**(`meshFacePlane` 的法向就是从宿主面\n * 的 `surfaceNormal` 取的),不必再回读宿主句柄。\n *\n * @param kernel - the mesh backend's L1 kernel.\n * @param reference - the outward normal the placed face must agree with.\n * @param face - the face built on the plane.\n * @returns a handle to the (possibly re-oriented) face.\n */\nfunction orientFaceTo(kernel: BrepEngineApi, reference: Vec3, face: BrepHandle): BrepHandle {\n const uv = kernel.uvBounds(face)\n const n = kernel.surfaceNormal(face, (uv.uMin + uv.uMax) / 2, (uv.vMin + uv.vMax) / 2)\n const dot = reference.x * n.x + reference.y * n.y + reference.z * n.z\n return dot < 0 ? kernel.reverseShape(face) : face\n}\n\n/** 一个已归类的轮廓条目(外环 + 可选孔)在平面坐标系里造一张面。 */\nfunction buildFaceOnPlane(\n kernel: BrepEngineApi,\n plane: Plane,\n outward: Vec3,\n entry: Blueprint | CompoundBlueprint,\n): BrepHandle {\n const outerBp = entry instanceof CompoundBlueprint ? entry.blueprints[0]! : entry\n let face = orientFaceTo(kernel, outward, kernel.makeFace(assembleWire(kernel, plane, outerBp)))\n if (entry instanceof CompoundBlueprint) {\n const holes = entry.blueprints.slice(1).map((h) => assembleWire(kernel, plane, h) as never)\n if (holes.length > 0) face = kernel.addHolesInFace(face, holes)\n }\n return face\n}\n\n/**\n * 网格链上的 `cad.sketchOnFace`:在**识别出的平面网格面**上铺 2D 轮廓,产出网格链面\n * (方案 2026-10-01 §4 Phase 3-2),可交给 `cad.extrude` 的网格路径拉伸出新实体。\n *\n * 与 BREP 侧的三点差别,每一点都是\"近似拓扑没有 UV 语义\"的直接后果:\n *\n * 1. **不走 UV 域**。BREP 侧 `assembleWireOnFace` 把 2D 采样点映射进面的 `(u,v)` 域;\n * 而平面在 brepkit 上的域是 ±1e6 的**无限域**(方案 §4 Phase 3-1 实测),拿它当\n * 草图范围毫无意义。这里改用面自己的平面框(原点 = 面包围盒中心、法向 = 面法向),\n * 2D 坐标直接落在平面坐标系里——与 `cad.sketchOnPlane` 的语义一致。\n * 2. **`scaleMode` 只接受 `'original'`**。`'bounds'` / `'native'` 的定义是\"拟合到面的\n * UV 域\",而那个域在这里要么是无限域、要么不存在。静默按 `'original'` 处理会造成\n * \"同一次调用在两条链上得到不同尺寸\"的隐性分歧,所以如实拒绝。\n * 3. **`as:'wire'` 不支持**。网格链上的一根 wire 没有身份槽(面才有),而 sketch 的\n * 下游用途(拉伸)要的正是面。需要 wire 的场景请走 BREP 链。\n *\n * 另外只支持**平面面**:曲面面(STL 圆柱的侧面片)上铺草图需要曲面嵌入,属未开的能力。\n *\n * @param params - contour loops + target mesh solid + face selector + scale mode.\n * @returns the mesh-chain face Shape on the target face.\n * @throws {MeshUnsupportedError} when the target is not a mesh solid, the face is not planar,\n * the scale mode is not `'original'`, `as:'wire'` was requested, or the sketch is multi-island.\n */\nexport function buildSketchOnFaceMesh(params: SketchOnFaceParams): Shape {\n const opLabel = 'sketchOnFace'\n const backend = meshBackendOrThrow(opLabel)\n const kernel = backend.kernel\n\n const mode = params.scaleMode ?? 'original'\n if (mode !== 'original') {\n throw new MeshUnsupportedError(\n `E_MESH_SOLID_UNSUPPORTED: ${opLabel} scaleMode '${mode}' is defined against the face's UV domain, ` +\n 'which a mesh-chain face does not have — use the default \\'original\\' (2D coords live in the face plane)',\n getCurrentStmt(),\n )\n }\n if (params.as === 'wire') {\n throw new MeshUnsupportedError(\n `E_MESH_SOLID_UNSUPPORTED: ${opLabel} as:'wire' has no mesh-chain representation — ` +\n 'a mesh-chain wire has no identity slot; ask for the default as:\\'face\\' instead',\n getCurrentStmt(),\n )\n }\n\n const host = resolveMeshFaceHandle(kernel, params.on, params.face, opLabel)\n const surface = kernel.surfaceType(host)\n if (surface !== 'plane') {\n throw new MeshUnsupportedError(\n `E_MESH_SOLID_UNSUPPORTED: ${opLabel} needs a planar mesh face to sketch on — this face reports ` +\n `surfaceType '${surface}'. Sketches on curved approximate faces are not implemented ` +\n '(an STL cylinder is 32 planar strips, each of which IS usable)',\n getCurrentStmt(),\n )\n }\n\n const plane = meshFacePlane(kernel, host)\n const outward: Vec3 = { x: plane.zDir.x, y: plane.zDir.y, z: plane.zDir.z }\n\n const organised = organiseBlueprints(toContourBlueprints(params.contours))\n if (organised.blueprints.length === 0) throw new Error('sketchOnFace: no contours to place')\n if (organised.blueprints.length > 1) {\n throw new MeshUnsupportedError(\n `E_MESH_SOLID_UNSUPPORTED: ${opLabel} produced ${organised.blueprints.length} disjoint islands — ` +\n 'the mesh backend can only extrude a single face (brepkit extrude takes one face, not a compound). ' +\n 'Sketch and extrude each island separately, then unite them with a boolean',\n getCurrentStmt(),\n )\n }\n\n const face = buildFaceOnPlane(kernel, plane, outward, organised.blueprints[0]!)\n return meshFaceProduct(backend, face)\n}\n\n/**\n * `cad.sketchOnFace`: place 2D contours on a **planar** face of a solid and\n * construct a Shape. Engine-neutral (no `engines` declaration): works on the\n * OCCT BREP engine, the brepkit BREP engine, and the brepkit mesh backend.\n *\n * **网格链**(`meshEngines: ['brepkit']`,方案 2026-10-01 §4 Phase 3):`on` 是网格实体\n * 时,轮廓按面自己的**平面框**铺放(原点 = 面包围盒中心、法向 = 面法向),产物是一张\n * 网格链面,可直接交给 `cad.extrude` 拉伸。该分支只接受平面面、只接受默认\n * `scaleMode`(`'bounds'`/`'native'` 相对 UV 域定义,网格链上没有 UV 域)、只接受\n * `as:'face'`、且只接受单个轮廓岛——每一条越界都以 `E_MESH_SOLID_UNSUPPORTED` 说明原因。\n *\n * @group 创建\n * @inputs 0\n * @async false\n * @qual ok\n * @name sketchOnFace\n * @returns Shape on the target face (face, or a wire curve when `as:'wire'`).\n * @param params.contours - ordered 2D closed contours (same shape as `cad.profile`).type:any[] required:true\n * @param params.on - the target solid on which the face lives.type:Shape required:true\n * @param params.face - the host face: a 1-based ordinal or a `cad.faceRef(on, n)` result.type:any required:true\n * @param params.scaleMode - UV mapping: 'original' (identity) | 'bounds'/'native' (affine-fit; OCCT planar only).type:string required:false\n * @param params.as - `'face'` (default) or `'wire'` (outer loop only).type:string required:false\n */\nexport const sketchOnFace = defineOp({\n meshEngines: ['brepkit'],\n mesh(params: Record<string, unknown>) {\n assertProfileParams(params)\n return buildSketchOnFaceMesh(params as unknown as SketchOnFaceParams)\n },\n brep(params: Record<string, unknown>) {\n assertProfileParams(params)\n return buildSketchOnFaceWith(getBrepApi(), params as unknown as SketchOnFaceParams)\n },\n naming: { kind: 'construct', newFaces: { via: 'explicit', vocab: [] } } as Provenance,\n\n paramDims: {},\n})\n"]}
1
+ {"version":3,"file":"sketch-on-face.js","sourceRoot":"","sources":["../../src/api/sketch-on-face.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `cad.sketchOnFace`: place 2D contours onto a **planar** face of an existing\n * solid (E3 placement), engine-neutral.\n *\n * 2026-10-09 split: `sketchOnFace` now owns the *planar* case only and carries\n * **no `engines` declaration** — it is a common op, available on the OCCT BREP\n * engine, the brepkit BREP engine, and the brepkit mesh backend. The\n * curved / UV-conform capability (map into the face's `(u,v)` domain + snap to\n * the surface via `fixWireOnFace`, which needs OCCT) moved to\n * `cad.sketchOnSurface` (`engines: ['occt']`).\n *\n * For a planar face the contour is laid on the face's own plane frame via\n * `assembleWireOnFace` (identity `original` scale mode, or `bounds`/`native`\n * where the engine exposes a finite UV domain) and wrapped as a face with\n * `makeFace` — exact, no surface snapping needed. Non-planar faces are refused\n * up front with a pointer to `cad.sketchOnSurface`.\n *\n * **Two chains, two mechanisms** (plan 2026-10-01 §4 Phase 3). The BREP branch\n * above maps the contour onto the host face's plane. The **mesh branch**\n * (`buildSketchOnFaceMesh`, `meshEngines: ['brepkit']`) works on a mesh solid's\n * approximate topology and deliberately does NOT use a UV domain: a brepkit\n * plane's domain is ±1e6 (an infinite domain), so \"place the sketch in the face's\n * UV range\" is meaningless there. It places the contour in the face's own plane\n * frame instead (origin = face bbox centre, normal = `surfaceNormal`), which\n * matches `cad.sketchOnPlane`'s semantics. The result is a **mesh-chain face**\n * (`ShapeSlot.meshFace`), ready for the mesh branch of `cad.extrude`.\n *\n * For both chains a planar face is required; curved faces are rejected by the\n * BREP branch (→ `cad.sketchOnSurface`) and by the mesh branch (→\n * `E_MESH_SOLID_UNSUPPORTED`).\n * @module\n */\nimport type { Shape } from '../mesh/types'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { solidToShape } from '../brep/brep-ops'\nimport { fromBrep, fromBrepCurve, brepOf } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { TopoRefError, resolveTopoRef, type FaceTopoRef } from '../topology/naming'\nimport { buildEdgeResolutionContext } from './topo-resolve'\nimport { Blueprint } from '../geometry2d/blueprint'\nimport { CompoundBlueprint } from '../geometry2d/compound-blueprint'\nimport { organiseBlueprints } from '../geometry2d/organise'\nimport { assembleWireOnFace, type FaceScaleMode } from '../geometry2d/bridge/on-surface'\nimport { assembleWire, type Plane, type Vec3 } from '../geometry2d/bridge/lift-on-plane'\nimport { profileSegToCurve, type ProfileSegLike } from '../geometry2d/adapt'\nimport { assertProfileParams, toContourBlueprints, type ProfileLoop } from './profile'\nimport { MeshUnsupportedError, BrepUnsupportedError, getCurrentStmt, getBackends } from '../runtime-state'\nimport {\n meshBackendOrThrow,\n meshFacePlane,\n meshFaceProduct,\n resolveMeshFaceHandle,\n} from './internal/mesh-solid-op'\n\n/** A face selector on a target solid: a 1-based ordinal (FreeCAD `FaceN`) or a `cad.faceRef` result. */\nexport type FaceSelector = number | FaceTopoRef\n\n/** Contour loops + the target face for `cad.sketchOnFace`. */\nexport interface SketchOnFaceParams {\n /** Ordered 2D closed contours (same shape as `cad.profile` / `cad.sketchOnPlane`). */\n contours: ProfileLoop[]\n /** The target solid on which the host face lives. */\n on: Shape\n /** The host face: a 1-based face ordinal, or a `cad.faceRef(on, n)` result. */\n face: FaceSelector\n /** How 2D coords map onto the face UV space: `'original'` (identity) | `'bounds'`/`'native'` (affine-fit). */\n scaleMode?: FaceScaleMode\n /** Result shape: `'face'` (default) builds a face on the contour; `'wire'` returns the outer-loop wire only. */\n as?: 'face' | 'wire'\n}\n\n/**\n * Resolve a face on `on` to its live BREP handle (1-based ordinal or `cad.faceRef`).\n * @param kernel - the BREP engine surface.\n * @param on - the target solid shape.\n * @param selector - the face ordinal or `cad.faceRef` result.\n * @returns the live face handle.\n */\nexport function resolveFaceHandle(kernel: BrepEngineApi, on: Shape, selector: FaceSelector): BrepHandle {\n const solid = brepOf(on) as BrepHandle | undefined\n if (solid === undefined) {\n throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'face', 'sketchOnFace: target `on` has no BREP solid')\n }\n if (typeof selector === 'number') {\n const faces = kernel.getSubShapes(solid, 'face')\n if (!Number.isInteger(selector) || selector < 1 || selector > faces.length) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `sketchOnFace: face ordinal ${selector} out of range [1, ${faces.length}]`,\n )\n }\n return faces[selector - 1] as BrepHandle\n }\n if (selector.kind !== 'face') {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: selector must be a face ordinal or a face ref')\n }\n const ctx = buildEdgeResolutionContext(kernel, on as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: input has no naming context')\n const resolved = resolveTopoRef(selector, ctx)\n if (resolved.handle === undefined) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: face ref resolved to no live handle')\n }\n return resolved.handle as BrepHandle\n}\n\n/**\n * Make the placed face inherit the host face's outward orientation: surface\n * normals must point the same way, otherwise the resulting face — and any prism\n * extruded from it — carries a reversed shell whose OCCT volume collapses to ~0.\n * @param kernel - the BREP engine surface.\n * @param host - the host face handle (its normal is the reference).\n * @param face - the face built on the host surface.\n * @returns a handle to the (possibly re-oriented) face.\n */\nexport function orientLikeHost(kernel: BrepEngineApi, host: BrepHandle, face: BrepHandle): BrepHandle {\n const a = kernel.surfaceNormal(host, 0, 0)\n const b = kernel.surfaceNormal(face, 0, 0)\n const dot = a.x * b.x + a.y * b.y + a.z * b.z\n return dot < 0 ? kernel.reverseShape(face) : face\n}\n\n/**\n * Build one planar face (outer + optional holes) on the host **planar** face for\n * an organised contour entry. No surface snapping (`fixWireOnFace`) is needed:\n * the wire from `assembleWireOnFace` already lies in the face's plane, so\n * `makeFace` is exact.\n * @param kernel - the BREP engine surface.\n * @param host - the resolved live host face handle.\n * @param mode - the face-scale mode (`original` everywhere; `bounds`/`native` only where the engine exposes a finite UV domain).\n * @param entry - the organised contour entry (outer + optional holes).\n * @returns the built face handle.\n */\nfunction buildPlanarFaceOnHost(\n kernel: BrepEngineApi,\n host: BrepHandle,\n mode: FaceScaleMode,\n entry: Blueprint | CompoundBlueprint,\n): BrepHandle {\n const outerBp = entry instanceof CompoundBlueprint ? entry.blueprints[0]! : entry\n const outerWire = assembleWireOnFace(kernel, host, mode, outerBp).wire\n let face = orientLikeHost(kernel, host, kernel.makeFace(outerWire))\n if (entry instanceof CompoundBlueprint) {\n const holes = entry.blueprints.slice(1).map((h) => assembleWireOnFace(kernel, host, mode, h).wire as never)\n if (holes.length > 0) face = kernel.addHolesInFace(face, holes)\n }\n return face\n}\n\n/**\n * Build a 3D Shape from closed contours placed on the host **planar** face.\n * @param kernel - the BREP engine surface.\n * @param params - contour loops + target solid + face + scale mode.\n * @returns Shape on the target face (face, or the outer wire when `as:'wire'`).\n * @throws {BrepUnsupportedError} when the host face is not planar (use `cad.sketchOnSurface`).\n */\nexport function buildSketchOnFaceWith(kernel: BrepEngineApi, params: SketchOnFaceParams): Shape {\n const host = resolveFaceHandle(kernel, params.on, params.face)\n const mode = params.scaleMode ?? 'original'\n\n // Planar-only gate: the curved / UV-conform capability moved to `cad.sketchOnSurface`.\n const surface = kernel.surfaceType(host)\n if (surface !== 'plane') {\n throw new BrepUnsupportedError(\n `E_BREP_UNSUPPORTED: cad.sketchOnFace only supports planar faces (got surfaceType '${surface}'). ` +\n 'Use cad.sketchOnSurface for curved/UV-conform placement.',\n getCurrentStmt(),\n )\n }\n // brepkit planar faces expose an infinite (±1e6) UV domain, so only the\n // identity 'original' scale mode is meaningful there (mirrors the mesh branch\n // restriction); OCCT planar faces keep full scaleMode support.\n const engineId = getBackends().config.brepEngineId\n if (engineId === 'brepkit' && mode !== 'original') {\n throw new BrepUnsupportedError(\n `E_BREP_UNSUPPORTED: cad.sketchOnFace on a brepkit planar face only supports scaleMode 'original' ` +\n \"(brepkit planar faces have no finite UV domain) — use the default 'original'.\",\n getCurrentStmt(),\n )\n }\n\n const blueprints = params.contours.map(\n (loop) => new Blueprint(loop.segments.map((s) => profileSegToCurve(s as ProfileSegLike))),\n )\n const organised = organiseBlueprints(blueprints)\n\n if (params.as === 'wire') {\n const outer =\n organised.blueprints[0] instanceof CompoundBlueprint\n ? organised.blueprints[0].blueprints[0]!\n : (organised.blueprints[0] as Blueprint)\n const { wire } = assembleWireOnFace(kernel, host, mode, outer)\n return fromBrepCurve(solidToShape(kernel, wire), { solid: wire })\n }\n\n const faces = organised.blueprints.map((entry) => buildPlanarFaceOnHost(kernel, host, mode, entry))\n if (faces.length === 0) throw new Error('sketchOnFace: no contours to place')\n if (faces.length === 1) {\n const f = faces[0]!\n return fromBrep(solidToShape(kernel, f), { solid: f })\n }\n const compound = kernel.makeCompound(faces)\n return fromBrep(solidToShape(kernel, compound), { solid: compound })\n}\n\n/**\n * 把一张平面的法向对齐到参考方向(外部朝向)。网格链上的复制版:BREP 侧的\n * `orientLikeHost` 存在的原因是\"面朝向反了 → 由它拉出的棱柱壳反向 → 体积归零\",\n * 这里同一个道理,只是参考法向来自**平面本身**(`meshFacePlane` 的法向就是从宿主面\n * 的 `surfaceNormal` 取的),不必再回读宿主句柄。\n *\n * @param kernel - the mesh backend's L1 kernel.\n * @param reference - the outward normal the placed face must agree with.\n * @param face - the face built on the plane.\n * @returns a handle to the (possibly re-oriented) face.\n */\nfunction orientFaceTo(kernel: BrepEngineApi, reference: Vec3, face: BrepHandle): BrepHandle {\n const uv = kernel.uvBounds(face)\n const n = kernel.surfaceNormal(face, (uv.uMin + uv.uMax) / 2, (uv.vMin + uv.vMax) / 2)\n const dot = reference.x * n.x + reference.y * n.y + reference.z * n.z\n return dot < 0 ? kernel.reverseShape(face) : face\n}\n\n/** 一个已归类的轮廓条目(外环 + 可选孔)在平面坐标系里造一张面。 */\nfunction buildFaceOnPlane(\n kernel: BrepEngineApi,\n plane: Plane,\n outward: Vec3,\n entry: Blueprint | CompoundBlueprint,\n): BrepHandle {\n const outerBp = entry instanceof CompoundBlueprint ? entry.blueprints[0]! : entry\n let face = orientFaceTo(kernel, outward, kernel.makeFace(assembleWire(kernel, plane, outerBp)))\n if (entry instanceof CompoundBlueprint) {\n const holes = entry.blueprints.slice(1).map((h) => assembleWire(kernel, plane, h) as never)\n if (holes.length > 0) face = kernel.addHolesInFace(face, holes)\n }\n return face\n}\n\n/**\n * 网格链上的 `cad.sketchOnFace`:在**识别出的平面网格面**上铺 2D 轮廓,产出网格链面\n * (方案 2026-10-01 §4 Phase 3-2),可交给 `cad.extrude` 的网格路径拉伸出新实体。\n *\n * 与 BREP 侧的三点差别,每一点都是\"近似拓扑没有 UV 语义\"的直接后果:\n *\n * 1. **不走 UV 域**。BREP 侧 `assembleWireOnFace` 把 2D 采样点映射进面的 `(u,v)` 域;\n * 而平面在 brepkit 上的域是 ±1e6 的**无限域**(方案 §4 Phase 3-1 实测),拿它当\n * 草图范围毫无意义。这里改用面自己的平面框(原点 = 面包围盒中心、法向 = 面法向),\n * 2D 坐标直接落在平面坐标系里——与 `cad.sketchOnPlane` 的语义一致。\n * 2. **`scaleMode` 只接受 `'original'`**。`'bounds'` / `'native'` 的定义是\"拟合到面的\n * UV 域\",而那个域在这里要么是无限域、要么不存在。静默按 `'original'` 处理会造成\n * \"同一次调用在两条链上得到不同尺寸\"的隐性分歧,所以如实拒绝。\n * 3. **`as:'wire'` 不支持**。网格链上的一根 wire 没有身份槽(面才有),而 sketch 的\n * 下游用途(拉伸)要的正是面。需要 wire 的场景请走 BREP 链。\n *\n * 另外只支持**平面面**:曲面面(STL 圆柱的侧面片)上铺草图需要曲面嵌入,属未开的能力。\n *\n * @param params - contour loops + target mesh solid + face selector + scale mode.\n * @returns the mesh-chain face Shape on the target face.\n * @throws {MeshUnsupportedError} when the target is not a mesh solid, the face is not planar,\n * the scale mode is not `'original'`, `as:'wire'` was requested, or the sketch is multi-island.\n */\nexport function buildSketchOnFaceMesh(params: SketchOnFaceParams): Shape {\n const opLabel = 'sketchOnFace'\n const backend = meshBackendOrThrow(opLabel)\n const kernel = backend.kernel\n\n const mode = params.scaleMode ?? 'original'\n if (mode !== 'original') {\n throw new MeshUnsupportedError(\n `E_MESH_SOLID_UNSUPPORTED: ${opLabel} scaleMode '${mode}' is defined against the face's UV domain, ` +\n 'which a mesh-chain face does not have — use the default \\'original\\' (2D coords live in the face plane)',\n getCurrentStmt(),\n )\n }\n if (params.as === 'wire') {\n throw new MeshUnsupportedError(\n `E_MESH_SOLID_UNSUPPORTED: ${opLabel} as:'wire' has no mesh-chain representation — ` +\n 'a mesh-chain wire has no identity slot; ask for the default as:\\'face\\' instead',\n getCurrentStmt(),\n )\n }\n\n const host = resolveMeshFaceHandle(kernel, params.on, params.face, opLabel)\n const surface = kernel.surfaceType(host)\n if (surface !== 'plane') {\n throw new MeshUnsupportedError(\n `E_MESH_SOLID_UNSUPPORTED: ${opLabel} needs a planar mesh face to sketch on — this face reports ` +\n `surfaceType '${surface}'. Sketches on curved approximate faces are not implemented ` +\n '(an STL cylinder is 32 planar strips, each of which IS usable)',\n getCurrentStmt(),\n )\n }\n\n const plane = meshFacePlane(kernel, host)\n const outward: Vec3 = { x: plane.zDir.x, y: plane.zDir.y, z: plane.zDir.z }\n\n const organised = organiseBlueprints(toContourBlueprints(params.contours))\n if (organised.blueprints.length === 0) throw new Error('sketchOnFace: no contours to place')\n if (organised.blueprints.length > 1) {\n throw new MeshUnsupportedError(\n `E_MESH_SOLID_UNSUPPORTED: ${opLabel} produced ${organised.blueprints.length} disjoint islands — ` +\n 'the mesh backend can only extrude a single face (brepkit extrude takes one face, not a compound). ' +\n 'Sketch and extrude each island separately, then unite them with a boolean',\n getCurrentStmt(),\n )\n }\n\n const face = buildFaceOnPlane(kernel, plane, outward, organised.blueprints[0]!)\n return meshFaceProduct(backend, face)\n}\n\n/**\n * `cad.sketchOnFace`: place 2D contours on a **planar** face of a solid and\n * construct a Shape. Engine-neutral (no `engines` declaration): works on the\n * OCCT BREP engine, the brepkit BREP engine, and the brepkit mesh backend.\n *\n * **网格链**(`meshEngines: ['brepkit']`,方案 2026-10-01 §4 Phase 3):`on` 是网格实体\n * 时,轮廓按面自己的**平面框**铺放(原点 = 面包围盒中心、法向 = 面法向),产物是一张\n * 网格链面,可直接交给 `cad.extrude` 拉伸。该分支只接受平面面、只接受默认\n * `scaleMode`(`'bounds'`/`'native'` 相对 UV 域定义,网格链上没有 UV 域)、只接受\n * `as:'face'`、且只接受单个轮廓岛——每一条越界都以 `E_MESH_SOLID_UNSUPPORTED` 说明原因。\n *\n * @group 创建\n * @inputs 0\n * @async false\n * @qual ok\n * @name sketchOnFace\n * @returns Shape on the target face (face, or a wire curve when `as:'wire'`).\n * @param params.contours - ordered 2D closed contours (same shape as `cad.profile`).type:any[] required:true\n * @param params.on - the target solid on which the face lives.type:Shape required:true\n * @param params.face - the host face: a 1-based ordinal or a `cad.faceRef(on, n)` result.type:any required:true\n * @param params.scaleMode - UV mapping: 'original' (identity) | 'bounds'/'native' (affine-fit; OCCT planar only).type:string required:false\n * @param params.as - `'face'` (default) or `'wire'` (outer loop only).type:string required:false\n */\nexport const sketchOnFace = defineOp({\n meshEngines: ['brepkit'],\n mesh(params: Record<string, unknown>) {\n assertProfileParams(params)\n return buildSketchOnFaceMesh(params as unknown as SketchOnFaceParams)\n },\n brep(params: Record<string, unknown>) {\n assertProfileParams(params)\n return buildSketchOnFaceWith(getBrepApi(), params as unknown as SketchOnFaceParams)\n },\n naming: { kind: 'construct', newFaces: { via: 'explicit', vocab: [] } } as Provenance,\n\n paramDims: {},\n})\n"]}
@@ -25,7 +25,7 @@ function splitByPlaneBrep(input, params) {
25
25
  const { point, normal } = params ?? {};
26
26
  const kernel = getBrepApi();
27
27
  const solid = brepOf(input);
28
- if (!solid)
28
+ if (solid === undefined)
29
29
  throw new Error('E_SPLIT_BY_PLANE_NO_BREP: splitByPlane input is not BREP');
30
30
  const halves = kernel.splitByPlane(solid, toVec3(point), toVec3(normal));
31
31
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"split-by-plane.js","sourceRoot":"","sources":["../../src/api/split-by-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAajC,8DAA8D;AAC9D,SAAS,MAAM,CAAC,CAAO;IACrB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvG,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAA;IAC1F,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACtC,CAAC;AAED,wCAAwC;AACxC,SAAS,gBAAgB,CAAC,KAAY,EAAE,MAA0B;IAChE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAK,EAAyB,CAAA;IAC9D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;IACrD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IACvF,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IACxE,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrF,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;KACtF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;IACnC,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;IACjC,SAAS,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE;IAClE,IAAI,CAAC,KAAY,EAAE,MAA0B;QAC3C,OAAO,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IACxC,CAAC;IACD,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAgB;CAC5C,CAAC,CAAA","sourcesContent":["/**\n * api splitByPlane — 平面二分:把 solid 沿无限平面切成两半(手写中立 op,Phase 6.1)\n *\n * 中立 op:L1 `splitByPlane(shape, point, normal)` 在 occt 与 brepkit 两侧均有\n * 真实现(occt-kernel/occt-primitives.ts / brepkit-kernel/brepkitKernel.ts)⇒\n * 走 `getBrepApi()`(D12),不声明 engines(实现只经 L1 契约面)。\n *\n * 产物形态(设计原则 5):两半不是数组,是具名 `outputs: ['positive','negative']`\n * ——法向正侧 = positive(L1 契约 §3.7 口径,occt 适配器按质心投影分类)。\n * 先例:occt 适配器的 splitByPlane 直接返回 `{positive, negative}`。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { BrepHandle, BrepVec3 } from '../brep/engine/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { brepOf, fromBrep } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\n\ntype Vec3 = [number, number, number]\n\n/** `cad.splitByPlane` 参数。 */\nexport interface SplitByPlaneParams {\n /** 平面上一点(mm)。 */\n point: Vec3\n /** 平面法向(不必单位化;其正侧为 `positive`)。 */\n normal: Vec3\n}\n\n/** 元组 → BrepVec3 对象(GOTCHA:L1 向量形参是 {x,y,z} 对象,元组会被读成零向量)。 */\nfunction toVec3(v: Vec3): BrepVec3 {\n if (!Array.isArray(v) || v.length !== 3 || v.some((n) => typeof n !== 'number' || !Number.isFinite(n))) {\n throw new Error('E_SPLIT_BY_PLANE_BAD_VEC: point/normal must be [x,y,z] finite numbers')\n }\n return { x: v[0], y: v[1], z: v[2] }\n}\n\n/** BREP 路径:L1 splitByPlane → 两半各自收养。 */\nfunction splitByPlaneBrep(input: Shape, params: SplitByPlaneParams): Record<string, Shape> {\n const { point, normal } = params ?? ({} as SplitByPlaneParams)\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (!solid) throw new Error('E_SPLIT_BY_PLANE_NO_BREP: splitByPlane input is not BREP')\n const halves = kernel.splitByPlane(solid, toVec3(point), toVec3(normal))\n return {\n positive: fromBrep(solidToShape(kernel, halves.positive), { solid: halves.positive }),\n negative: fromBrep(solidToShape(kernel, halves.negative), { solid: halves.negative }),\n }\n}\n\n/**\n * 沿无限平面把实体切成两半,返回法向正/负两半(具名产物)。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name splitByPlane\n * @note 中立 op:L1 splitByPlane 两引擎同实现。产物是具名两半\n * `{ positive, negative }`(不是数组);法向正侧 = positive。\n * @returns 具名产物 positive/negative。\n * @param input - 目标实体。type:Shape required:true\n * @param params.point - 平面上一点 [x,y,z](mm)。type:Vec3 required:true\n * @param params.normal - 平面法向 [x,y,z]。type:Vec3 required:true\n * @example\n * const { positive, negative } = await cad.splitByPlane(part0, { point: [0,0,5], normal: [0,0,1] })\n */\nexport const splitByPlane = defineOp({\n outputs: ['positive', 'negative'],\n paramDims: { 'params.point': 'length', 'params.normal': 'length' },\n brep(input: Shape, params: SplitByPlaneParams) {\n return splitByPlaneBrep(input, params)\n },\n naming: { kind: 'subdivide' } as Provenance,\n})\n"]}
1
+ {"version":3,"file":"split-by-plane.js","sourceRoot":"","sources":["../../src/api/split-by-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAajC,8DAA8D;AAC9D,SAAS,MAAM,CAAC,CAAO;IACrB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvG,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAA;IAC1F,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACtC,CAAC;AAED,wCAAwC;AACxC,SAAS,gBAAgB,CAAC,KAAY,EAAE,MAA0B;IAChE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAK,EAAyB,CAAA;IAC9D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;IACrD,IAAI,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IACpG,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IACxE,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrF,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;KACtF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;IACnC,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;IACjC,SAAS,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE;IAClE,IAAI,CAAC,KAAY,EAAE,MAA0B;QAC3C,OAAO,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IACxC,CAAC;IACD,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAgB;CAC5C,CAAC,CAAA","sourcesContent":["/**\n * api splitByPlane — 平面二分:把 solid 沿无限平面切成两半(手写中立 op,Phase 6.1)\n *\n * 中立 op:L1 `splitByPlane(shape, point, normal)` 在 occt 与 brepkit 两侧均有\n * 真实现(occt-kernel/occt-primitives.ts / brepkit-kernel/brepkitKernel.ts)⇒\n * 走 `getBrepApi()`(D12),不声明 engines(实现只经 L1 契约面)。\n *\n * 产物形态(设计原则 5):两半不是数组,是具名 `outputs: ['positive','negative']`\n * ——法向正侧 = positive(L1 契约 §3.7 口径,occt 适配器按质心投影分类)。\n * 先例:occt 适配器的 splitByPlane 直接返回 `{positive, negative}`。\n */\n\nimport type { Shape } from '../mesh/types'\nimport type { BrepHandle, BrepVec3 } from '../brep/engine/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { brepOf, fromBrep } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\n\ntype Vec3 = [number, number, number]\n\n/** `cad.splitByPlane` 参数。 */\nexport interface SplitByPlaneParams {\n /** 平面上一点(mm)。 */\n point: Vec3\n /** 平面法向(不必单位化;其正侧为 `positive`)。 */\n normal: Vec3\n}\n\n/** 元组 → BrepVec3 对象(GOTCHA:L1 向量形参是 {x,y,z} 对象,元组会被读成零向量)。 */\nfunction toVec3(v: Vec3): BrepVec3 {\n if (!Array.isArray(v) || v.length !== 3 || v.some((n) => typeof n !== 'number' || !Number.isFinite(n))) {\n throw new Error('E_SPLIT_BY_PLANE_BAD_VEC: point/normal must be [x,y,z] finite numbers')\n }\n return { x: v[0], y: v[1], z: v[2] }\n}\n\n/** BREP 路径:L1 splitByPlane → 两半各自收养。 */\nfunction splitByPlaneBrep(input: Shape, params: SplitByPlaneParams): Record<string, Shape> {\n const { point, normal } = params ?? ({} as SplitByPlaneParams)\n const kernel = getBrepApi()\n const solid = brepOf(input) as BrepHandle | undefined\n if (solid === undefined) throw new Error('E_SPLIT_BY_PLANE_NO_BREP: splitByPlane input is not BREP')\n const halves = kernel.splitByPlane(solid, toVec3(point), toVec3(normal))\n return {\n positive: fromBrep(solidToShape(kernel, halves.positive), { solid: halves.positive }),\n negative: fromBrep(solidToShape(kernel, halves.negative), { solid: halves.negative }),\n }\n}\n\n/**\n * 沿无限平面把实体切成两半,返回法向正/负两半(具名产物)。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name splitByPlane\n * @note 中立 op:L1 splitByPlane 两引擎同实现。产物是具名两半\n * `{ positive, negative }`(不是数组);法向正侧 = positive。\n * @returns 具名产物 positive/negative。\n * @param input - 目标实体。type:Shape required:true\n * @param params.point - 平面上一点 [x,y,z](mm)。type:Vec3 required:true\n * @param params.normal - 平面法向 [x,y,z]。type:Vec3 required:true\n * @example\n * const { positive, negative } = await cad.splitByPlane(part0, { point: [0,0,5], normal: [0,0,1] })\n */\nexport const splitByPlane = defineOp({\n outputs: ['positive', 'negative'],\n paramDims: { 'params.point': 'length', 'params.normal': 'length' },\n brep(input: Shape, params: SplitByPlaneParams) {\n return splitByPlaneBrep(input, params)\n },\n naming: { kind: 'subdivide' } as Provenance,\n})\n"]}
package/dist/api/split.js CHANGED
@@ -28,7 +28,7 @@ function splitBrep(input, tools) {
28
28
  // 平台面:原生 split(occt-only,D3)。
29
29
  const occtKernel = getOcctKernel();
30
30
  const inputSolid = brepOf(input);
31
- if (!inputSolid)
31
+ if (inputSolid === undefined)
32
32
  throw new Error('[api/split] input is not BREP');
33
33
  const toolSolids = tools
34
34
  .map((t) => brepOf(t))
@@ -1 +1 @@
1
- {"version":3,"file":"split.js","sourceRoot":"","sources":["../../src/api/split.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api split — split(手写覆盖生成投影,Phase 3: L4 `splinter(#j)` 角色表)\n *\n * @platform occt — 平台 op:内核原生 `split`(BRepAlgoAPI_Splitter)是 occt-only\n * (engine-method-map `split` → occt-only;L1 只有 `splitByPlane`)。本文件\n * 只取 L1 面做枚举/命名(getBrepApi),切分本身走平台面 getOcctKernel()(D3),\n * defineOp 声明 `engines: ['occt']`(D11),不声明其它收窄。\n *\n * 生成投影是 brep-only compatOp,不带角色表;本文件手写 defineOp,BREP 路径\n * 用内核原生 split(BRepAlgoAPI_Splitter)切分,存活面(hash 逐字不变)回投原\n * role,新造面(截面 + 被切细的侧面片)记 `splinter(#j)`,片序按质心排序保证跨\n * 重放稳定。\n *\n * 机制(无 splitWithHistory,纯 hash 比对 + 几何排序):`subdivide` 类零声明,\n * 新造面不对应任何输入面的因果坐标,故挂消费它的那条语句、用稳定的片序 `#j` 区分。\n */\n\nimport type { Shape } from '../mesh/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getFaceHashes } from '../brep/face-evolution'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { getCurrentStmt } from '../runtime-state'\nimport { fromBrep, brepOf, inputRoleTable } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport type { RoleTable } from '../topology/naming/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { getOcctKernel, type ShapeHandle } from '../occt-kernel/occtKernel'\n\n/** BREP 路径:用 tools 切分 input,存活面回投、新造面记 `splinter(#j)`。 */\nfunction splitBrep(input: Shape, tools: Shape[]): Shape {\n // L1 面:枚举/命名(getBrepApi,D12)。\n const kernel = getBrepApi()\n // 平台面:原生 split(occt-only,D3)。\n const occtKernel = getOcctKernel()\n const inputSolid = brepOf(input) as BrepHandle | undefined\n if (!inputSolid) throw new Error('[api/split] input is not BREP')\n const toolSolids = tools\n .map((t) => brepOf(t) as BrepHandle | undefined)\n .filter((s): s is BrepHandle => !!s)\n if (toolSolids.length === 0) return input\n\n // Phase 1.6:origin = 本次语句 StmtId。\n const outStmt = String(getCurrentStmt()?.id ?? '')\n const inputTable = inputRoleTable(input) as RoleTable | undefined\n\n const inputHashes = getFaceHashes(kernel, inputSolid)\n const inputHashSet = new Set(inputHashes)\n const inputHashToRole = new Map<number, string>()\n if (inputTable) {\n for (const roles of inputTable.values()) {\n for (const [role, hashes] of roles) for (const h of hashes) inputHashToRole.set(h, role)\n }\n }\n\n // 内核原生 split(BRepAlgoAPI_Splitter)返回包含所有碎片的 compound。\n // BrepHandle(branded number)与 occt-wasm ShapeHandle(branded number)运行时同构,\n // 品牌转换只发生在平台边界(occt-only 原生面,非跨引擎断言)。\n const resultSolid = occtKernel.split(\n inputSolid as unknown as ShapeHandle,\n toolSolids as unknown as ShapeHandle[],\n )\n\n const resultHashes = getFaceHashes(kernel, resultSolid as unknown as BrepHandle)\n const resultFaces = kernel.getSubShapes(resultSolid as unknown as BrepHandle, 'face')\n const resultCentroids = resultFaces.map((f) => kernel.surfaceCenterOfMass(f))\n\n const roleTable = new Map<string, Map<string, number[]>>()\n const inner = new Map<string, number[]>()\n\n // 存活面(hash 逐字不变)→ 回投原 role;其余 = 新造面 → splinter(#j)\n const newFaces: { hash: number; c: { x: number; y: number; z: number } }[] = []\n for (let i = 0; i < resultHashes.length; i++) {\n const h = resultHashes[i]!\n if (inputHashSet.has(h)) {\n const role = inputHashToRole.get(h)\n if (role) {\n if (!inner.has(role)) inner.set(role, [])\n inner.get(role)!.push(h)\n }\n } else {\n newFaces.push({ hash: h, c: resultCentroids[i]! })\n }\n }\n // 按质心 (x,y,z) 排序保证片序跨重放稳定\n newFaces.sort((a, b) => a.c.x - b.c.x || a.c.y - b.c.y || a.c.z - b.c.z)\n for (let j = 0; j < newFaces.length; j++) {\n const role = `splinter(#${j})`\n inner.set(role, [newFaces[j].hash])\n }\n roleTable.set(outStmt, inner)\n\n return fromBrep(solidToShape(kernel, resultSolid as unknown as BrepHandle), {\n solid: resultSolid as unknown as BrepHandle,\n roleTable,\n })\n}\n\n/**\n * 用工具几何切分目标几何(BRepAlgoAPI_Splitter),返回所有碎片组成的几何。\n * @group 特征\n * @inputs 2\n * @async true\n * @qual ok\n * @name split\n * @note BREP-only:非 BREP 输入抛 E_MESH_UNSUPPORTED。切分产生的截面 / 被切细的侧面\n * 片记 `splinter(#j)`(Phase 3 L4 抗重放词汇)。平台 op:仅 occt 引擎(原生 split)。\n * @returns Shape 切分后的几何(compound of pieces)。\n * @param input - 目标几何。type:Shape required:true\n * @param tools - 切刀几何(数组)。type:Shape[] required:true\n * @example\n * const pieces = await cad.split(part0, [part1])\n */\nexport const split = defineOp({\n brep(input: Shape, tools: Shape[]) {\n return splitBrep(input, tools)\n },\n engines: ['occt'],\n naming: { kind: 'subdivide' } as Provenance,\n\n paramDims: {},\n})\n"]}
1
+ {"version":3,"file":"split.js","sourceRoot":"","sources":["../../src/api/split.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api split — split(手写覆盖生成投影,Phase 3: L4 `splinter(#j)` 角色表)\n *\n * @platform occt — 平台 op:内核原生 `split`(BRepAlgoAPI_Splitter)是 occt-only\n * (engine-method-map `split` → occt-only;L1 只有 `splitByPlane`)。本文件\n * 只取 L1 面做枚举/命名(getBrepApi),切分本身走平台面 getOcctKernel()(D3),\n * defineOp 声明 `engines: ['occt']`(D11),不声明其它收窄。\n *\n * 生成投影是 brep-only compatOp,不带角色表;本文件手写 defineOp,BREP 路径\n * 用内核原生 split(BRepAlgoAPI_Splitter)切分,存活面(hash 逐字不变)回投原\n * role,新造面(截面 + 被切细的侧面片)记 `splinter(#j)`,片序按质心排序保证跨\n * 重放稳定。\n *\n * 机制(无 splitWithHistory,纯 hash 比对 + 几何排序):`subdivide` 类零声明,\n * 新造面不对应任何输入面的因果坐标,故挂消费它的那条语句、用稳定的片序 `#j` 区分。\n */\n\nimport type { Shape } from '../mesh/types'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getFaceHashes } from '../brep/face-evolution'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { getCurrentStmt } from '../runtime-state'\nimport { fromBrep, brepOf, inputRoleTable } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport type { RoleTable } from '../topology/naming/types'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { getOcctKernel, type ShapeHandle } from '../occt-kernel/occtKernel'\n\n/** BREP 路径:用 tools 切分 input,存活面回投、新造面记 `splinter(#j)`。 */\nfunction splitBrep(input: Shape, tools: Shape[]): Shape {\n // L1 面:枚举/命名(getBrepApi,D12)。\n const kernel = getBrepApi()\n // 平台面:原生 split(occt-only,D3)。\n const occtKernel = getOcctKernel()\n const inputSolid = brepOf(input) as BrepHandle | undefined\n if (inputSolid === undefined) throw new Error('[api/split] input is not BREP')\n const toolSolids = tools\n .map((t) => brepOf(t) as BrepHandle | undefined)\n .filter((s): s is BrepHandle => !!s)\n if (toolSolids.length === 0) return input\n\n // Phase 1.6:origin = 本次语句 StmtId。\n const outStmt = String(getCurrentStmt()?.id ?? '')\n const inputTable = inputRoleTable(input) as RoleTable | undefined\n\n const inputHashes = getFaceHashes(kernel, inputSolid)\n const inputHashSet = new Set(inputHashes)\n const inputHashToRole = new Map<number, string>()\n if (inputTable) {\n for (const roles of inputTable.values()) {\n for (const [role, hashes] of roles) for (const h of hashes) inputHashToRole.set(h, role)\n }\n }\n\n // 内核原生 split(BRepAlgoAPI_Splitter)返回包含所有碎片的 compound。\n // BrepHandle(branded number)与 occt-wasm ShapeHandle(branded number)运行时同构,\n // 品牌转换只发生在平台边界(occt-only 原生面,非跨引擎断言)。\n const resultSolid = occtKernel.split(\n inputSolid as unknown as ShapeHandle,\n toolSolids as unknown as ShapeHandle[],\n )\n\n const resultHashes = getFaceHashes(kernel, resultSolid as unknown as BrepHandle)\n const resultFaces = kernel.getSubShapes(resultSolid as unknown as BrepHandle, 'face')\n const resultCentroids = resultFaces.map((f) => kernel.surfaceCenterOfMass(f))\n\n const roleTable = new Map<string, Map<string, number[]>>()\n const inner = new Map<string, number[]>()\n\n // 存活面(hash 逐字不变)→ 回投原 role;其余 = 新造面 → splinter(#j)\n const newFaces: { hash: number; c: { x: number; y: number; z: number } }[] = []\n for (let i = 0; i < resultHashes.length; i++) {\n const h = resultHashes[i]!\n if (inputHashSet.has(h)) {\n const role = inputHashToRole.get(h)\n if (role) {\n if (!inner.has(role)) inner.set(role, [])\n inner.get(role)!.push(h)\n }\n } else {\n newFaces.push({ hash: h, c: resultCentroids[i]! })\n }\n }\n // 按质心 (x,y,z) 排序保证片序跨重放稳定\n newFaces.sort((a, b) => a.c.x - b.c.x || a.c.y - b.c.y || a.c.z - b.c.z)\n for (let j = 0; j < newFaces.length; j++) {\n const role = `splinter(#${j})`\n inner.set(role, [newFaces[j].hash])\n }\n roleTable.set(outStmt, inner)\n\n return fromBrep(solidToShape(kernel, resultSolid as unknown as BrepHandle), {\n solid: resultSolid as unknown as BrepHandle,\n roleTable,\n })\n}\n\n/**\n * 用工具几何切分目标几何(BRepAlgoAPI_Splitter),返回所有碎片组成的几何。\n * @group 特征\n * @inputs 2\n * @async true\n * @qual ok\n * @name split\n * @note BREP-only:非 BREP 输入抛 E_MESH_UNSUPPORTED。切分产生的截面 / 被切细的侧面\n * 片记 `splinter(#j)`(Phase 3 L4 抗重放词汇)。平台 op:仅 occt 引擎(原生 split)。\n * @returns Shape 切分后的几何(compound of pieces)。\n * @param input - 目标几何。type:Shape required:true\n * @param tools - 切刀几何(数组)。type:Shape[] required:true\n * @example\n * const pieces = await cad.split(part0, [part1])\n */\nexport const split = defineOp({\n brep(input: Shape, tools: Shape[]) {\n return splitBrep(input, tools)\n },\n engines: ['occt'],\n naming: { kind: 'subdivide' } as Provenance,\n\n paramDims: {},\n})\n"]}
@@ -45,7 +45,7 @@ export const thicken = defineOp({
45
45
  }
46
46
  const kernel = getBrepApi();
47
47
  const handle = brepOf(input);
48
- if (!handle)
48
+ if (handle === undefined)
49
49
  throw new Error('[thicken] input is not BREP');
50
50
  // C5 实现面接入:occt 提供 thickenWithHistory ⇒ 用权威面演化,不用无历史 thicken。
51
51
  // thicken 是 occt-only 平台 op(engines:['occt']),brepkit 在执行前已被拒绝,
@@ -1 +1 @@
1
- {"version":3,"file":"thicken.js","sourceRoot":"","sources":["../../src/api/thicken.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAGzF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC;IAC9B,IAAI,EAAE,SAAS;IACf,IAAI,CAAC,KAAY,EAAE,SAAiB;QAClC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;QACtF,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;YACpF,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAA;QACzF,CAAC;QACD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;QACtD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;QAC3D,8DAA8D;QAC9D,gEAAgE;QAChE,mCAAmC;QACnC,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACjD,kEAAkE;QAClE,sCAAsC;QACtC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC,kBAAkB,CAC5C,MAAe,EACf,SAAS,EACT,IAAI,EACJ,WAAW,EACX,gBAAgB,CACe,CAAA;QACjC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;QACzB,MAAM,aAAa,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAClE,OAAO,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAU,CAAA;IAC1F,CAAC;IACD,OAAO,EAAE,CAAC,MAAM,CAAC;IACjB,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,4CAA4C,EAAgB;IAEjG,SAAS,EAAE,EAAE;CACd,CAAC,CAAA","sourcesContent":["/**\n * api thicken — 加厚:把面(或壳)沿法向偏置成等厚实体(手写平台 op,Phase 4 → G5 core 直连)\n *\n * @platform occt — 实现走 occt-wasm 原生 `thicken`(BRepOffsetAPI_MakeThickSolid;\n * engine-method-map 里 `thicken` 为 occt-only)⇒ 平台 op:defineOp 声明\n * `engines: ['occt']`(D11)。\n *\n * core-decouple G5:旧 `thicken`(brepjs modifierFns,thickenWithHistory)\n * 替换为 occt-wasm 原生直连,产物经 `fromBrep` 收养(替代 l3-bridge adoptEntity)。\n */\n\nimport type { Shape } from '../mesh/types'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { brepOf, fromBrep } from '../shape'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getOcctKernel } from '../occt-kernel/occtKernel'\nimport { HASH_UPPER_BOUND, decodeEvolution, getFaceHashes } from '../brep/face-evolution'\nimport type { BrepHandle, BrepEvolutionData } from '../brep/engine/types'\n\n/**\n * 加厚:把面(或壳)沿法向偏置成等厚实体。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name thicken\n * @note 平台 op:仅 occt 引擎(BRepOffset)。输入为面/壳 Shape(如 cad.profile 产物);\n * 正厚度沿法向、负厚度反向。非 occt 引擎执行前报错;brep_mock 不拦截。\n * @returns Shape 加厚后的实体。\n * @param input - 面/壳几何(cad.profile 产物等)。type:Shape required:true\n * @param thickness - 厚度(mm,≠0;正沿法向,负反向)。type:number required:true\n * @example\n * const face = cad.profile({ contours: [{ segments: [\n * { kind: 'line', x1: -10, y1: -10, x2: 10, y2: -10 },\n * { kind: 'line', x1: 10, y1: -10, x2: 10, y2: 10 },\n * { kind: 'line', x1: 10, y1: 10, x2: -10, y2: 10 },\n * { kind: 'line', x1: -10, y1: 10, x2: -10, y2: -10 },\n * ] }] })\n * const solid = await cad.thicken(face, 2)\n */\nexport const thicken = defineOp({\n name: 'thicken',\n brep(input: Shape, thickness: number) {\n if (!input) throw new Error('E_THICKEN_NO_SHAPE: thicken requires a face/shell shape')\n if (typeof thickness !== 'number' || !Number.isFinite(thickness) || thickness === 0) {\n throw new Error('E_THICKEN_BAD_THICKNESS: thicken.thickness must be a non-zero number')\n }\n const kernel = getBrepApi()\n const handle = brepOf(input) as BrepHandle | undefined\n if (!handle) throw new Error('[thicken] input is not BREP')\n // C5 实现面接入:occt 提供 thickenWithHistory ⇒ 用权威面演化,不用无历史 thicken。\n // thicken 是 occt-only 平台 op(engines:['occt']),brepkit 在执行前已被拒绝,\n // 因此不存在「有裸方法却因无 history 而报错」的回退分支。\n const inputHashes = getFaceHashes(kernel, handle)\n // wasm EvolutionData.result 是 ShapeHandle 品牌;与 topologyFns.ts 同款,\n // 在来源处一次性 cast 为引擎中立槽型(运行时同构,句柄不跨引擎)。\n const evo = getOcctKernel().thickenWithHistory(\n handle as never,\n thickness,\n 1e-6,\n inputHashes,\n HASH_UPPER_BOUND,\n ) as unknown as BrepEvolutionData\n const result = evo.result\n const faceEvolution = decodeEvolution(kernel, evo, handle, result)\n return fromBrep(solidToShape(kernel, result), { solid: result, faceEvolution }) as Shape\n },\n engines: ['occt'],\n naming: { kind: 'unmodeled', reason: 'thickened-body face vocabulary not defined' } as Provenance,\n\n paramDims: {},\n})\n"]}
1
+ {"version":3,"file":"thicken.js","sourceRoot":"","sources":["../../src/api/thicken.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAGzF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC;IAC9B,IAAI,EAAE,SAAS;IACf,IAAI,CAAC,KAAY,EAAE,SAAiB;QAClC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;QACtF,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;YACpF,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAA;QACzF,CAAC;QACD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAA;QACtD,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;QACxE,8DAA8D;QAC9D,gEAAgE;QAChE,mCAAmC;QACnC,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACjD,kEAAkE;QAClE,sCAAsC;QACtC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC,kBAAkB,CAC5C,MAAe,EACf,SAAS,EACT,IAAI,EACJ,WAAW,EACX,gBAAgB,CACe,CAAA;QACjC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;QACzB,MAAM,aAAa,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAClE,OAAO,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAU,CAAA;IAC1F,CAAC;IACD,OAAO,EAAE,CAAC,MAAM,CAAC;IACjB,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,4CAA4C,EAAgB;IAEjG,SAAS,EAAE,EAAE;CACd,CAAC,CAAA","sourcesContent":["/**\n * api thicken — 加厚:把面(或壳)沿法向偏置成等厚实体(手写平台 op,Phase 4 → G5 core 直连)\n *\n * @platform occt — 实现走 occt-wasm 原生 `thicken`(BRepOffsetAPI_MakeThickSolid;\n * engine-method-map 里 `thicken` 为 occt-only)⇒ 平台 op:defineOp 声明\n * `engines: ['occt']`(D11)。\n *\n * core-decouple G5:旧 `thicken`(brepjs modifierFns,thickenWithHistory)\n * 替换为 occt-wasm 原生直连,产物经 `fromBrep` 收养(替代 l3-bridge adoptEntity)。\n */\n\nimport type { Shape } from '../mesh/types'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { brepOf, fromBrep } from '../shape'\nimport { solidToShape } from '../brep/brep-ops'\nimport { getOcctKernel } from '../occt-kernel/occtKernel'\nimport { HASH_UPPER_BOUND, decodeEvolution, getFaceHashes } from '../brep/face-evolution'\nimport type { BrepHandle, BrepEvolutionData } from '../brep/engine/types'\n\n/**\n * 加厚:把面(或壳)沿法向偏置成等厚实体。\n * @group 特征\n * @inputs 1\n * @async true\n * @qual ok\n * @name thicken\n * @note 平台 op:仅 occt 引擎(BRepOffset)。输入为面/壳 Shape(如 cad.profile 产物);\n * 正厚度沿法向、负厚度反向。非 occt 引擎执行前报错;brep_mock 不拦截。\n * @returns Shape 加厚后的实体。\n * @param input - 面/壳几何(cad.profile 产物等)。type:Shape required:true\n * @param thickness - 厚度(mm,≠0;正沿法向,负反向)。type:number required:true\n * @example\n * const face = cad.profile({ contours: [{ segments: [\n * { kind: 'line', x1: -10, y1: -10, x2: 10, y2: -10 },\n * { kind: 'line', x1: 10, y1: -10, x2: 10, y2: 10 },\n * { kind: 'line', x1: 10, y1: 10, x2: -10, y2: 10 },\n * { kind: 'line', x1: -10, y1: 10, x2: -10, y2: -10 },\n * ] }] })\n * const solid = await cad.thicken(face, 2)\n */\nexport const thicken = defineOp({\n name: 'thicken',\n brep(input: Shape, thickness: number) {\n if (!input) throw new Error('E_THICKEN_NO_SHAPE: thicken requires a face/shell shape')\n if (typeof thickness !== 'number' || !Number.isFinite(thickness) || thickness === 0) {\n throw new Error('E_THICKEN_BAD_THICKNESS: thicken.thickness must be a non-zero number')\n }\n const kernel = getBrepApi()\n const handle = brepOf(input) as BrepHandle | undefined\n if (handle === undefined) throw new Error('[thicken] input is not BREP')\n // C5 实现面接入:occt 提供 thickenWithHistory ⇒ 用权威面演化,不用无历史 thicken。\n // thicken 是 occt-only 平台 op(engines:['occt']),brepkit 在执行前已被拒绝,\n // 因此不存在「有裸方法却因无 history 而报错」的回退分支。\n const inputHashes = getFaceHashes(kernel, handle)\n // wasm EvolutionData.result 是 ShapeHandle 品牌;与 topologyFns.ts 同款,\n // 在来源处一次性 cast 为引擎中立槽型(运行时同构,句柄不跨引擎)。\n const evo = getOcctKernel().thickenWithHistory(\n handle as never,\n thickness,\n 1e-6,\n inputHashes,\n HASH_UPPER_BOUND,\n ) as unknown as BrepEvolutionData\n const result = evo.result\n const faceEvolution = decodeEvolution(kernel, evo, handle, result)\n return fromBrep(solidToShape(kernel, result), { solid: result, faceEvolution }) as Shape\n },\n engines: ['occt'],\n naming: { kind: 'unmodeled', reason: 'thickened-body face vocabulary not defined' } as Provenance,\n\n paramDims: {},\n})\n"]}
@@ -196,7 +196,7 @@ export function buildShapeResolutionContext(kernel, shape) {
196
196
  const part = nameOf(shape);
197
197
  const roleTable = (part ? runtimeLineage.tableOfPart(part) : undefined);
198
198
  const solid = slot.solid;
199
- if (kernel && solid) {
199
+ if (kernel && solid !== undefined) {
200
200
  const handles = kernel.getSubShapes(solid, 'face');
201
201
  const hashes = Array.from(kernel.subShapeHashes(solid, 'face', HASH_UPPER_BOUND));
202
202
  const faces = handles.map((handle, i) => ({ ordinal: i + 1, hash: hashes[i] ?? 0, handle }));
@@ -235,7 +235,7 @@ export function buildEdgeResolutionContext(kernel, shape) {
235
235
  const edgePart = nameOf(shape);
236
236
  const roleTable = (edgePart ? runtimeLineage.tableOfPart(edgePart) : undefined);
237
237
  const solid = slot.solid;
238
- if (!solid)
238
+ if (solid === undefined)
239
239
  return undefined;
240
240
  const faceHandles = kernel.getSubShapes(solid, 'face');
241
241
  const faceHashes = Array.from(kernel.subShapeHashes(solid, 'face', HASH_UPPER_BOUND));
@@ -290,7 +290,7 @@ export function buildMeshEdgeResolutionContext(kernel, shape) {
290
290
  return undefined;
291
291
  const slot = getSlot(shape);
292
292
  const solid = slot?.meshSolid;
293
- if (!solid)
293
+ if (solid === undefined)
294
294
  return undefined;
295
295
  const faceHandles = kernel.getSubShapes(solid, 'face');
296
296
  const faces = faceHandles.map((handle, i) => ({ ordinal: i + 1, handle }));
@@ -323,7 +323,7 @@ export function buildMeshFaceResolutionContext(kernel, shape) {
323
323
  return undefined;
324
324
  const slot = getSlot(shape);
325
325
  const solid = slot?.meshSolid;
326
- if (!solid)
326
+ if (solid === undefined)
327
327
  return undefined;
328
328
  const faces = kernel.getSubShapes(solid, 'face').map((handle, i) => ({ ordinal: i + 1, handle }));
329
329
  return { kernel, faces };
@@ -1 +1 @@
1
- {"version":3,"file":"topo-resolve.js","sourceRoot":"","sources":["../../src/api/topo-resolve.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * topo-resolve — 运行期 TopoRef 解析(装配/钻孔等面引用 op 共享,§6.2)\n *\n * 与 core/topology/naming 的分工:\n * - naming(core)负责纯解析:给 ReactsContext + FaceTopoRef → ordinal/handle;\n * - 本模块(api 层)是「活 Shape → React块(ResolutionContext)」的引擎胶水:\n * 从 Shape 身份槽读出运行期数据(BREP 活句柄 + RoleTable,或 setTopology 注入的\n * 面 hint 快照),并把解析结果还原成求解需要的几何(center/normal/surfaceType)。\n *\n * 这样 drill(面 → 法向)与 compound/assembly(约束固定/移动面 → 几何)共用同一\n * 条「TopoRef 执行期派生」通道,且不动 core 的纯 naming 层职责。\n *\n * 任何一步定不了案 → 抛 TopoRefError(三态错误码),绝不静默拿序号硬取。\n */\n\nimport type { Shape } from '../mesh/types'\nimport { getSlot } from '../shape'\nimport { nameOf } from '../runtime-state'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { HASH_UPPER_BOUND } from '../brep/face-evolution'\nimport { runtimeLineage, isHashEvolution } from '../topology/naming/lineage'\nimport { resolveViaLineage } from '../topology/naming/lineage-resolve'\nimport { asStmtId, type PartName, type StmtId } from '../identity'\nimport {\n resolveTopoRef,\n captureFaceHint,\n captureEdgeHint,\n faceRowToHint,\n TopoRefError,\n type FaceTopoRef,\n type ResolutionContext,\n type FaceHint,\n type AxisHint,\n type EdgeCandidateEntry,\n type RoleTable,\n} from '../topology/naming'\n\n/** 求解面需要的当前几何(执行期派生):与旧契约 fixedFace/movingFace 的 surfaceType/center/normal 同形。 */\nexport interface ResolvedFaceGeometry {\n surfaceType?: string\n center: [number, number, number]\n normal: [number, number, number]\n /** 圆柱/回转面的轴(装配轴约束用;平面/球面缺省,mesh 行快照由宿主注入)。 */\n axis?: AxisHint\n}\n\n/**\n * roleTable miss 时沿血缘回走重算(1.10 前置③:缓存降级)。\n *\n * 「产物上的 roleTable 不再是身份的唯一来源」的落点:精确解析读不到\n * `roleTable[origin][role]` 时,按血缘 DAG 从 origin 语句回走推进到当前 part,\n * 产出该 role 的 hash 集合并**回填缓存**——miss 可恢复,不再是无名错误。\n * 回走失败(链断 / 无演化)返回 undefined,调用方落到几何兜底(既有三态语义不变)。\n *\n * @param ref - the face TopoRef being resolved.\n * @param shape - the live part shape being resolved against.\n * @param kernel - the OCCT kernel (hash 换算需要;null 时回走不可用).\n * @returns the recomputed hash list, or undefined when lineage re-walk cannot produce one.\n */\nfunction recomputeViaLineage(\n ref: FaceTopoRef,\n shape: object,\n kernel: BrepEngineApi | null,\n): readonly number[] | undefined {\n if (!kernel) return undefined\n const part = nameOf(shape)\n if (!part) return undefined\n const result = resolveViaLineage(runtimeLineage, asStmtId(ref.origin), ref.role, part, {\n // 回走链上的中间产物句柄:优先血缘图旁挂(本仓 op 执行时记录),\n // 兜底读 runtime 全局 nameOf 反查(slot.solid)。\n brepOf: (p) => {\n const stmt = runtimeLineage.stmtOf(p)\n const h = stmt ? runtimeLineage.outputHandleOf(stmt) : undefined\n if (h !== undefined) return h\n return undefined\n },\n faceHashes: (handle) =>\n Array.from(kernel.subShapeHashes(handle as BrepHandle, 'face', HASH_UPPER_BOUND)),\n roleTableOf: (p) => {\n const stmt = runtimeLineage.stmtOf(p)\n return stmt ? runtimeLineage.outputTableOf(stmt) : undefined\n },\n })\n if (!('hashes' in result)) return undefined\n return result.hashes\n}\n\n/**\n * 边路径「无血统邻面」恢复(1.10 前置③ 推广到边路径)。\n *\n * `edgeRef` 正查 `findOriginRole(table, ordinalToHash, fo)`(面 hash → `(origin, role)`)\n * 失败,通常因为**目标 part** 的 roleTable 缓存**残缺**(丢了该面的条目),而**根节点**\n * (名字的诞生处)的表是完整的:该面的 `(origin, role)` 真实存在,只是没被缓存到目标 part\n * 的表副本里(drift / 中间 op 原样带走旧 hash 表)。\n *\n * 与面路径 `recomputeViaLineage` 同构、方向相反——已知面 hash、不知道它属于哪个\n * `(origin, role)`。恢复策略:遍历血缘图所有候选 `(origin, role)`,沿 DAG 从根走到目标\n * part,把根序(该 role 的 hash 集)推进到目标序、换算成目标 hash 集,取**含该面当前 hash**\n * 者,即该面的真实身份。\n *\n * 推进规则(自带,不依赖 `resolveViaLineage` 的严格契约):\n * - 中间节点有 ordinal 演化(Map)→ 照用;\n * - 中间节点是 identity 类(copy/place/transform)但**未挂演化**(既有缺口,见\n * `place.ts` 经 compat 路径未 `attachEvolution`)→ 按 1:1 推进(语义正确:identity 不改\n * 面序);\n * - 其余无演化 → 放弃该候选(不乱猜)。\n *\n * 命中即回填 `{origin, role}` 让 `resolveEdgeTopo` 继续(exact 或几何兜底);未命中返回\n * undefined,调用方保持原 `no role lineage` 报错——零回归。仅跑在 `findOriginRole` 失败\n * 之后(错误路径),不进热路径。\n *\n * @param kernel - the OCCT kernel (hash 换算需要).\n * @param part - the live part whose adjacent face we are recovering.\n * @param faceOrdinal - the 1-based face ordinal whose role is missing.\n * @param ordinalToHash - the part's current face ordinal → hash array.\n * @returns the recovered `{origin, role}`, or undefined when no lineage walk matches.\n */\nexport function recoverEdgeFaceRole(\n kernel: BrepEngineApi,\n part: PartName,\n faceOrdinal: number,\n ordinalToHash: readonly number[],\n): { origin: StmtId; role: string } | undefined {\n const targetHash = ordinalToHash[faceOrdinal - 1]\n if (targetHash === undefined) return undefined\n\n // 候选 (origin, role):血缘图所有节点的输出表(根节点诞生名字处)\n const candidates: Array<{ origin: string; role: string }> = []\n for (const stmt of runtimeLineage.stmtIds()) {\n const tbl = runtimeLineage.outputTableOf(stmt)\n if (!tbl) continue\n for (const [origin, roles] of tbl) {\n for (const roleLine of roles.keys()) candidates.push({ origin, role: roleLine })\n }\n }\n\n for (const c of candidates) {\n const hashes = lineageHashesAt(kernel, c.origin, c.role, part)\n if (hashes && hashes.includes(targetHash)) return { origin: asStmtId(c.origin), role: c.role }\n }\n return undefined\n}\n\n/**\n * 沿血缘从 `(origin, role)` 的根走到 `targetPart`,把根序推进到目标序、换算成目标 hash 集。\n *\n * @returns the target part's face hashes for this role, or undefined when the\n * chain cannot be walked (unreachable / non-identity node without evolution).\n */\nfunction lineageHashesAt(\n kernel: BrepEngineApi,\n origin: string,\n roleLine: string,\n targetPart: PartName,\n): readonly number[] | undefined {\n const root = runtimeLineage.node(asStmtId(origin))\n if (!root) return undefined\n const rootPart = root.outputs[0]\n if (!rootPart) return undefined\n const rootHandle = runtimeLineage.outputHandleOf(asStmtId(origin))\n const rootTable = runtimeLineage.outputTableOf(asStmtId(origin))\n if (rootHandle === undefined || !rootTable) return undefined\n\n const rootHashes = rootTable.get(origin)?.get(roleLine)\n if (rootHashes === undefined || rootHashes.length === 0) return undefined\n const rootAll = Array.from(kernel.subShapeHashes(rootHandle as BrepHandle, 'face', HASH_UPPER_BOUND))\n let ordinals: number[] = []\n for (const h of rootHashes) {\n const idx = rootAll.indexOf(h)\n if (idx >= 0) ordinals.push(idx + 1) // ordinal 1 起\n }\n if (ordinals.length === 0) return undefined\n\n // 逐节点推进:root → targetPart(带访问守卫,防异常 DAG 环路挂死执行)\n const seen = new Set<PartName>()\n let currentPart: PartName = rootPart\n while (currentPart !== targetPart) {\n if (seen.has(currentPart)) return undefined\n seen.add(currentPart)\n const consumer = runtimeLineage.nodeConsuming(currentPart)\n if (!consumer) return undefined\n const evo = consumer.evolution\n if (evo && !isHashEvolution(evo)) {\n ordinals = ordinals.flatMap((o) => evo.get(o) ?? [o])\n } else if (consumer.provenance.kind === 'identity') {\n // identity 类未挂演化 → 1:1 推进(语义正确)\n } else {\n return undefined\n }\n const carry = consumer.outputs.includes(targetPart) ? targetPart : consumer.outputs[0]!\n currentPart = carry\n }\n\n const targetHandle = runtimeLineage.outputHandleOf(runtimeLineage.stmtOf(targetPart) ?? asStmtId(''))\n if (targetHandle === undefined) return undefined\n const targetAll = Array.from(kernel.subShapeHashes(targetHandle as BrepHandle, 'face', HASH_UPPER_BOUND))\n const hashes: number[] = []\n for (const o of ordinals) {\n const h = targetAll[o - 1]\n if (h !== undefined) hashes.push(h)\n }\n return hashes.length > 0 ? hashes : undefined\n}\n\n/**\n * 从活 Shape 的组装槽构建 ResolutionContext(BREAK 或 mesh/primitive 两条路)。\n *\n * - BREAK:`slot.solid` + `slot.roleTable` → 内核现场句柄 + hash(exact 路径);\n * - mesh/primitive:`slot.faceHints`(setTopology 注入时提炼写入)→ 行快照(geometric 路径)。\n *\n * 无命名槽 → undefined(调用方按 not-found 抛错)。\n *\n * @param kernel - the OCCT kernel (null on the mesh/primitive path).\n * @param shape - the live input shape whose naming slot is read.\n * @returns the resolution context (live BREP or hint rows), or undefined when the shape has no naming slot.\n */\nexport function buildShapeResolutionContext(\n kernel: BrepEngineApi | null,\n shape: object | undefined,\n): ResolutionContext | undefined {\n if (!shape) return undefined\n const slot = getSlot(shape)\n if (!slot) return undefined\n // 1.10 前置③:roleTable 权威落点在血缘图旁挂(part 键),slot 缓存字段已删。\n const part = nameOf(shape)\n const roleTable = (part ? runtimeLineage.tableOfPart(part) : undefined) as RoleTable | undefined\n\n const solid = slot.solid as BrepHandle | undefined\n if (kernel && solid) {\n const handles = kernel.getSubShapes(solid, 'face')\n const hashes = Array.from(kernel.subShapeHashes(solid, 'face', HASH_UPPER_BOUND))\n const faces = handles.map((handle, i) => ({ ordinal: i + 1, hash: hashes[i] ?? 0, handle }))\n return { kernel, faces, roleTable }\n }\n\n const hints = slot.faceHints as FaceHint[] | undefined\n if (Array.isArray(hints) && hints.length > 0) {\n const faces = hints.map((row, i) => ({ ordinal: i + 1, row }))\n return { kernel: null, faces, roleTable }\n }\n return undefined\n}\n\n/**\n * 为 BREP 现场构建带边候选与邻接表的 ResolutionContext(edge TopoRef 解析用)。\n *\n * 与 `buildBrepResolutionContext` 的面候选同源(同一枚举序),并补充:\n * - `edges`:边候选表(ordinal 1 起,含活句柄 + length/midpoint hint);\n * - `faceEdgeAdjacency` / `edgeFaceAdjacency`:邻接表(edge lineage 解析用,M3)。\n *\n * 只支持 BREP 现场(edge lineage 需要 face→edge 邻接);mesh/primitive 路径\n * 没有邻接能力(build-mesh-topology.ts:262,§5.3)→ 返回 undefined,调用方\n * 按 E_TOPO_NOT_FOUND 处理。\n *\n * @param kernel - the OCCT kernel.\n * @param shape - the live input shape whose naming slot is read.\n * @returns the edge-enabled resolution context, or undefined when the shape has\n * no BREP naming slot.\n */\nexport function buildEdgeResolutionContext(\n kernel: BrepEngineApi,\n shape: object | undefined,\n): ResolutionContext | undefined {\n if (!shape) return undefined\n const slot = getSlot(shape)\n if (!slot) return undefined\n // 1.10 前置③:roleTable 权威落点在血缘图旁挂(part 键),slot 缓存字段已删。\n const edgePart = nameOf(shape)\n const roleTable = (edgePart ? runtimeLineage.tableOfPart(edgePart) : undefined) as RoleTable | undefined\n const solid = slot.solid as BrepHandle | undefined\n if (!solid) return undefined\n\n const faceHandles = kernel.getSubShapes(solid, 'face')\n const faceHashes = Array.from(kernel.subShapeHashes(solid, 'face', HASH_UPPER_BOUND))\n const faces = faceHandles.map((handle, i) => ({ ordinal: i + 1, hash: faceHashes[i] ?? 0, handle }))\n\n const edgeHandles = kernel.getSubShapes(solid, 'edge')\n const edges: EdgeCandidateEntry[] = edgeHandles.map((handle, i) => ({\n ordinal: i + 1,\n handle,\n hint: captureEdgeHint(kernel, handle),\n }))\n const liveEdges = edges.filter((e): e is EdgeCandidateEntry & { handle: BrepHandle } => e.handle !== undefined)\n\n // face → 邻接 edges(ordinal 1 起)。mesh 无 face→edge 邻接,这里 BREP 现场直取。\n const faceHandlesArr = faceHandles.map((h, hi) => ({ handle: h, ordinal: hi + 1 }))\n\n const faceEdgeAdjacency = faceHandlesArr.map(({ handle }) => {\n const sub = kernel.getSubShapes(handle, 'edge')\n return sub\n .map((eh) => liveEdges.find((e) => kernel.isSame(eh, e.handle))?.ordinal)\n .filter((v): v is number => v !== undefined)\n })\n const edgeFaceAdjacency = liveEdges.map(({ handle }) => {\n const adj: number[] = []\n for (const f of faceHandlesArr) {\n if (kernel.getSubShapes(f.handle, 'edge').some((fe) => kernel.isSame(fe, handle))) {\n adj.push(f.ordinal)\n }\n }\n return adj\n })\n\n return { kernel, faces, edges, roleTable, faceEdgeAdjacency, edgeFaceAdjacency }\n}\n\n/**\n * 为**网格实体**构建带边候选的 ResolutionContext(近似拓扑的边解析入口)。\n *\n * 与 `buildEdgeResolutionContext` 的差别只有两点,两点都是\"网格零件没有 role 层\"\n * 的直接后果:\n * 1. 句柄来自 `slot.meshSolid`(网格实体句柄)而不是 `slot.solid`(BREP 句柄);\n * 2. **刻意不给 `faceEdgeAdjacency` / `edgeFaceAdjacency`**。\n *\n * 第 2 点是有意的,不是省事:邻接表存在的意义是\"两面之交 = 一条边\",\n * 而 `EdgeTopoRef.faces` 是两个 **RoleQualifier**——网格零件没有 role\n * (Phase 1.8:primitive/mesh 行 origin/role 显式为 null),限定符只是占位。\n * 带上邻接表反而会让 `resolveEdgeTopo` 走进\"按角色取公共边\"的分支并必然 not-found;\n * 不带它,解析就走「无 role 层 → 整个边表的纯几何匹配」(resolve-edge.ts 明写)。\n * 边身份只能几何,这是如实的能力边界。\n *\n * @param kernel - the mesh backend's L1 kernel (live edge handles come from it).\n * @param shape - the live mesh-solid shape whose naming slot is read.\n * @returns the edge-enabled resolution context, or undefined when the shape is not a mesh solid.\n */\nexport function buildMeshEdgeResolutionContext(\n kernel: BrepEngineApi,\n shape: object | undefined,\n): ResolutionContext | undefined {\n if (!shape) return undefined\n const slot = getSlot(shape)\n const solid = slot?.meshSolid as BrepHandle | undefined\n if (!solid) return undefined\n\n const faceHandles = kernel.getSubShapes(solid, 'face')\n const faces = faceHandles.map((handle, i) => ({ ordinal: i + 1, handle }))\n const edges: EdgeCandidateEntry[] = kernel.getSubShapes(solid, 'edge').map((handle, i) => ({\n ordinal: i + 1,\n handle,\n hint: captureEdgeHint(kernel, handle),\n }))\n return { kernel, faces, edges }\n}\n\n/**\n * 为**网格实体**构建面解析上下文(`sketchOnFace` 的网格链入口)。\n *\n * 面候选直接来自 `slot.meshSolid` 的 `getSubShapes(solid,'face')` 现场句柄——\n * 与 `buildMeshEdgeResolutionContext` 的面表同源(同一枚举序)。**刻意不给\n * `roleTable` / 邻接**:网格零件没有 role 层(Phase 1.8),带上 roleTable 只会让\n * `resolveFaceTopo` 走进 exact 分支并必然落空;不带它,解析就走「全形状按 hint\n * 几何打分」(resolve-face.ts 步骤 2),命中即 `geometric-fallback`。\n *\n * 也因此,网格链上的面只能用**几何**识别(法向 / 中心 / 面积)——这是如实的能力\n * 边界,不是省事:同尺寸的两个平行面(如立方体的顶面与底面)在几何上无法区分,\n * 调用方应改用序号。\n *\n * @param kernel - the mesh backend's L1 kernel (live face handles come from it).\n * @param shape - the live mesh-solid shape whose naming slot is read.\n * @returns the face resolution context, or undefined when the shape is not a mesh solid.\n */\nexport function buildMeshFaceResolutionContext(\n kernel: BrepEngineApi,\n shape: object | undefined,\n): ResolutionContext | undefined {\n if (!shape) return undefined\n const slot = getSlot(shape)\n const solid = slot?.meshSolid as BrepHandle | undefined\n if (!solid) return undefined\n\n const faces = kernel.getSubShapes(solid, 'face').map((handle, i) => ({ ordinal: i + 1, handle }))\n return { kernel, faces }\n}\n\n/**\n * 从 BREP 现场句柄建带边候选/邻接的 ResolutionContext(twoDistances 逐边重建用)。\n *\n * 与 `buildEdgeResolutionContext` 相同,但直接接收 live solid 句柄而非 Shape 槽——\n * 用于真机逐边构建后,输入已从上一次结果演化,Shape 槽里的 roleTable/hash 不再\n * 与当前句柄对应(§3.6 逐边构建,后续边要重新 resolve)。\n *\n * @param kernel - the OCCT kernel.\n * @param solid - the live BREP solid handle.\n * @param roleTable - optional role table to carry over (not used for strict matching).\n * @returns the edge-enabled resolution context (faces/edges/adjacency live from `solid`).\n */\nexport function buildEdgeContextFromSolid(\n kernel: BrepEngineApi,\n solid: BrepHandle,\n roleTable?: RoleTable,\n): ResolutionContext {\n const faceHandles = kernel.getSubShapes(solid, 'face')\n const faces = faceHandles.map((handle, i) => ({ ordinal: i + 1, hash: undefined, handle }))\n const edgeHandles = kernel.getSubShapes(solid, 'edge')\n const edges: EdgeCandidateEntry[] = edgeHandles.map((handle, i) => ({\n ordinal: i + 1,\n handle,\n hint: captureEdgeHint(kernel, handle),\n }))\n const liveEdges = edges.filter((e): e is EdgeCandidateEntry & { handle: BrepHandle } => e.handle !== undefined)\n const faceHandlesArr = faceHandles.map((h, hi) => ({ handle: h, ordinal: hi + 1 }))\n const faceEdgeAdjacency = faceHandlesArr.map(({ handle }) => {\n const sub = kernel.getSubShapes(handle, 'edge')\n return sub\n .map((eh) => liveEdges.find((e) => kernel.isSame(eh, e.handle))?.ordinal)\n .filter((v): v is number => v !== undefined)\n })\n const edgeFaceAdjacency = liveEdges.map(({ handle }) => {\n const adj: number[] = []\n for (const f of faceHandlesArr) {\n if (kernel.getSubShapes(f.handle, 'edge').some((fe) => kernel.isSame(fe, handle))) adj.push(f.ordinal)\n }\n return adj\n })\n return { kernel, faces, edges, roleTable, faceEdgeAdjacency, edgeFaceAdjacency }\n}\n\n/**\n * 解析一个 face TopoRef 到「当前几何」,并从解析出的候选面派生求解几何。\n *\n * 结果与命名行的 surfaceType/center/normal 同口径(BREAK 现场 captureFaceHint /\n * mesh 行快照),保证两侧派生值一致。\n *\n * @param kernel - the OCCT kernel (can be null on the mesh/primitive path).\n * @param shape - the live input shape whose naming slot is being resolved.\n * @param ref - the FaceTopoRef captured earlier.\n * @returns the current face geometry (center/normal/surfaceType).\n */\nexport function resolveFaceGeometry(\n kernel: BrepEngineApi | null,\n shape: Shape,\n ref: FaceTopoRef,\n): ResolvedFaceGeometry {\n // 1.10 前置③:roleTable 是解析缓存。origin/role 条目 miss 时先沿血缘回走\n // 重算并回填(miss 可恢复),再进解析——不再是\"缓存 miss = 无名\"。\n let ctx = buildShapeResolutionContext(kernel, shape)\n if (!ctx) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `no naming context on input shape (origin=${ref.origin}, role=${ref.role})`,\n )\n }\n // 1.10 前置③:roleTable 是解析缓存。origin/role 条目 miss 时先沿血缘回走\n // 重算并回填(miss 可恢复),再进解析——不再是\"缓存 miss = 无名\"。\n if (ctx.roleTable && kernel) {\n const entry0 = ctx.roleTable.get(ref.origin)?.get(ref.role)\n if (entry0 === undefined) {\n const recomputed = recomputeViaLineage(ref, shape, kernel)\n if (recomputed) {\n const merged = new Map(ctx.roleTable as ReadonlyMap<string, ReadonlyMap<string, readonly number[]>>)\n merged.set(ref.origin, new Map([...(ctx.roleTable.get(ref.origin) ?? new Map()), [ref.role, recomputed]]))\n ctx = { ...ctx, roleTable: merged as unknown as typeof ctx.roleTable }\n }\n }\n }\n const entity = resolveTopoRef(ref, ctx)\n const entry = ctx.faces[entity.ordinal - 1]\n if (!entry) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `resolved ordinal ${entity.ordinal} has no candidate in context (origin=${ref.origin}, role=${ref.role})`,\n )\n }\n // BREAK 现场:capture 出当前法向/中心(圆柱面含轴)\n if (entry.handle && ctx.kernel) {\n const hint = captureFaceHint(ctx.kernel, entry.handle)\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 // mesh/行快照:直接用行几何(axis 由宿主行注入,缺省 undefined)\n const ext = entry.row\n const hint = ext ? faceRowToHint(ext) : undefined\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\n/**\n * FaceMateConstraint 面的等价重载:face 参数对象(`{topoRef}` 或旧快照)的守卫。\n *\n * @param v - the value to test.\n * @returns true when the value is a FaceTopoRef (kind === 'face').\n */\nexport function isFaceTopoRef(v: unknown): v is FaceTopoRef {\n return !!v && typeof v === 'object' && (v as { kind?: unknown }).kind === 'face'\n}"]}
1
+ {"version":3,"file":"topo-resolve.js","sourceRoot":"","sources":["../../src/api/topo-resolve.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * topo-resolve — 运行期 TopoRef 解析(装配/钻孔等面引用 op 共享,§6.2)\n *\n * 与 core/topology/naming 的分工:\n * - naming(core)负责纯解析:给 ReactsContext + FaceTopoRef → ordinal/handle;\n * - 本模块(api 层)是「活 Shape → React块(ResolutionContext)」的引擎胶水:\n * 从 Shape 身份槽读出运行期数据(BREP 活句柄 + RoleTable,或 setTopology 注入的\n * 面 hint 快照),并把解析结果还原成求解需要的几何(center/normal/surfaceType)。\n *\n * 这样 drill(面 → 法向)与 compound/assembly(约束固定/移动面 → 几何)共用同一\n * 条「TopoRef 执行期派生」通道,且不动 core 的纯 naming 层职责。\n *\n * 任何一步定不了案 → 抛 TopoRefError(三态错误码),绝不静默拿序号硬取。\n */\n\nimport type { Shape } from '../mesh/types'\nimport { getSlot } from '../shape'\nimport { nameOf } from '../runtime-state'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { HASH_UPPER_BOUND } from '../brep/face-evolution'\nimport { runtimeLineage, isHashEvolution } from '../topology/naming/lineage'\nimport { resolveViaLineage } from '../topology/naming/lineage-resolve'\nimport { asStmtId, type PartName, type StmtId } from '../identity'\nimport {\n resolveTopoRef,\n captureFaceHint,\n captureEdgeHint,\n faceRowToHint,\n TopoRefError,\n type FaceTopoRef,\n type ResolutionContext,\n type FaceHint,\n type AxisHint,\n type EdgeCandidateEntry,\n type RoleTable,\n} from '../topology/naming'\n\n/** 求解面需要的当前几何(执行期派生):与旧契约 fixedFace/movingFace 的 surfaceType/center/normal 同形。 */\nexport interface ResolvedFaceGeometry {\n surfaceType?: string\n center: [number, number, number]\n normal: [number, number, number]\n /** 圆柱/回转面的轴(装配轴约束用;平面/球面缺省,mesh 行快照由宿主注入)。 */\n axis?: AxisHint\n}\n\n/**\n * roleTable miss 时沿血缘回走重算(1.10 前置③:缓存降级)。\n *\n * 「产物上的 roleTable 不再是身份的唯一来源」的落点:精确解析读不到\n * `roleTable[origin][role]` 时,按血缘 DAG 从 origin 语句回走推进到当前 part,\n * 产出该 role 的 hash 集合并**回填缓存**——miss 可恢复,不再是无名错误。\n * 回走失败(链断 / 无演化)返回 undefined,调用方落到几何兜底(既有三态语义不变)。\n *\n * @param ref - the face TopoRef being resolved.\n * @param shape - the live part shape being resolved against.\n * @param kernel - the OCCT kernel (hash 换算需要;null 时回走不可用).\n * @returns the recomputed hash list, or undefined when lineage re-walk cannot produce one.\n */\nfunction recomputeViaLineage(\n ref: FaceTopoRef,\n shape: object,\n kernel: BrepEngineApi | null,\n): readonly number[] | undefined {\n if (!kernel) return undefined\n const part = nameOf(shape)\n if (!part) return undefined\n const result = resolveViaLineage(runtimeLineage, asStmtId(ref.origin), ref.role, part, {\n // 回走链上的中间产物句柄:优先血缘图旁挂(本仓 op 执行时记录),\n // 兜底读 runtime 全局 nameOf 反查(slot.solid)。\n brepOf: (p) => {\n const stmt = runtimeLineage.stmtOf(p)\n const h = stmt ? runtimeLineage.outputHandleOf(stmt) : undefined\n if (h !== undefined) return h\n return undefined\n },\n faceHashes: (handle) =>\n Array.from(kernel.subShapeHashes(handle as BrepHandle, 'face', HASH_UPPER_BOUND)),\n roleTableOf: (p) => {\n const stmt = runtimeLineage.stmtOf(p)\n return stmt ? runtimeLineage.outputTableOf(stmt) : undefined\n },\n })\n if (!('hashes' in result)) return undefined\n return result.hashes\n}\n\n/**\n * 边路径「无血统邻面」恢复(1.10 前置③ 推广到边路径)。\n *\n * `edgeRef` 正查 `findOriginRole(table, ordinalToHash, fo)`(面 hash → `(origin, role)`)\n * 失败,通常因为**目标 part** 的 roleTable 缓存**残缺**(丢了该面的条目),而**根节点**\n * (名字的诞生处)的表是完整的:该面的 `(origin, role)` 真实存在,只是没被缓存到目标 part\n * 的表副本里(drift / 中间 op 原样带走旧 hash 表)。\n *\n * 与面路径 `recomputeViaLineage` 同构、方向相反——已知面 hash、不知道它属于哪个\n * `(origin, role)`。恢复策略:遍历血缘图所有候选 `(origin, role)`,沿 DAG 从根走到目标\n * part,把根序(该 role 的 hash 集)推进到目标序、换算成目标 hash 集,取**含该面当前 hash**\n * 者,即该面的真实身份。\n *\n * 推进规则(自带,不依赖 `resolveViaLineage` 的严格契约):\n * - 中间节点有 ordinal 演化(Map)→ 照用;\n * - 中间节点是 identity 类(copy/place/transform)但**未挂演化**(既有缺口,见\n * `place.ts` 经 compat 路径未 `attachEvolution`)→ 按 1:1 推进(语义正确:identity 不改\n * 面序);\n * - 其余无演化 → 放弃该候选(不乱猜)。\n *\n * 命中即回填 `{origin, role}` 让 `resolveEdgeTopo` 继续(exact 或几何兜底);未命中返回\n * undefined,调用方保持原 `no role lineage` 报错——零回归。仅跑在 `findOriginRole` 失败\n * 之后(错误路径),不进热路径。\n *\n * @param kernel - the OCCT kernel (hash 换算需要).\n * @param part - the live part whose adjacent face we are recovering.\n * @param faceOrdinal - the 1-based face ordinal whose role is missing.\n * @param ordinalToHash - the part's current face ordinal → hash array.\n * @returns the recovered `{origin, role}`, or undefined when no lineage walk matches.\n */\nexport function recoverEdgeFaceRole(\n kernel: BrepEngineApi,\n part: PartName,\n faceOrdinal: number,\n ordinalToHash: readonly number[],\n): { origin: StmtId; role: string } | undefined {\n const targetHash = ordinalToHash[faceOrdinal - 1]\n if (targetHash === undefined) return undefined\n\n // 候选 (origin, role):血缘图所有节点的输出表(根节点诞生名字处)\n const candidates: Array<{ origin: string; role: string }> = []\n for (const stmt of runtimeLineage.stmtIds()) {\n const tbl = runtimeLineage.outputTableOf(stmt)\n if (!tbl) continue\n for (const [origin, roles] of tbl) {\n for (const roleLine of roles.keys()) candidates.push({ origin, role: roleLine })\n }\n }\n\n for (const c of candidates) {\n const hashes = lineageHashesAt(kernel, c.origin, c.role, part)\n if (hashes && hashes.includes(targetHash)) return { origin: asStmtId(c.origin), role: c.role }\n }\n return undefined\n}\n\n/**\n * 沿血缘从 `(origin, role)` 的根走到 `targetPart`,把根序推进到目标序、换算成目标 hash 集。\n *\n * @returns the target part's face hashes for this role, or undefined when the\n * chain cannot be walked (unreachable / non-identity node without evolution).\n */\nfunction lineageHashesAt(\n kernel: BrepEngineApi,\n origin: string,\n roleLine: string,\n targetPart: PartName,\n): readonly number[] | undefined {\n const root = runtimeLineage.node(asStmtId(origin))\n if (!root) return undefined\n const rootPart = root.outputs[0]\n if (!rootPart) return undefined\n const rootHandle = runtimeLineage.outputHandleOf(asStmtId(origin))\n const rootTable = runtimeLineage.outputTableOf(asStmtId(origin))\n if (rootHandle === undefined || !rootTable) return undefined\n\n const rootHashes = rootTable.get(origin)?.get(roleLine)\n if (rootHashes === undefined || rootHashes.length === 0) return undefined\n const rootAll = Array.from(kernel.subShapeHashes(rootHandle as BrepHandle, 'face', HASH_UPPER_BOUND))\n let ordinals: number[] = []\n for (const h of rootHashes) {\n const idx = rootAll.indexOf(h)\n if (idx >= 0) ordinals.push(idx + 1) // ordinal 1 起\n }\n if (ordinals.length === 0) return undefined\n\n // 逐节点推进:root → targetPart(带访问守卫,防异常 DAG 环路挂死执行)\n const seen = new Set<PartName>()\n let currentPart: PartName = rootPart\n while (currentPart !== targetPart) {\n if (seen.has(currentPart)) return undefined\n seen.add(currentPart)\n const consumer = runtimeLineage.nodeConsuming(currentPart)\n if (!consumer) return undefined\n const evo = consumer.evolution\n if (evo && !isHashEvolution(evo)) {\n ordinals = ordinals.flatMap((o) => evo.get(o) ?? [o])\n } else if (consumer.provenance.kind === 'identity') {\n // identity 类未挂演化 → 1:1 推进(语义正确)\n } else {\n return undefined\n }\n const carry = consumer.outputs.includes(targetPart) ? targetPart : consumer.outputs[0]!\n currentPart = carry\n }\n\n const targetHandle = runtimeLineage.outputHandleOf(runtimeLineage.stmtOf(targetPart) ?? asStmtId(''))\n if (targetHandle === undefined) return undefined\n const targetAll = Array.from(kernel.subShapeHashes(targetHandle as BrepHandle, 'face', HASH_UPPER_BOUND))\n const hashes: number[] = []\n for (const o of ordinals) {\n const h = targetAll[o - 1]\n if (h !== undefined) hashes.push(h)\n }\n return hashes.length > 0 ? hashes : undefined\n}\n\n/**\n * 从活 Shape 的组装槽构建 ResolutionContext(BREAK 或 mesh/primitive 两条路)。\n *\n * - BREAK:`slot.solid` + `slot.roleTable` → 内核现场句柄 + hash(exact 路径);\n * - mesh/primitive:`slot.faceHints`(setTopology 注入时提炼写入)→ 行快照(geometric 路径)。\n *\n * 无命名槽 → undefined(调用方按 not-found 抛错)。\n *\n * @param kernel - the OCCT kernel (null on the mesh/primitive path).\n * @param shape - the live input shape whose naming slot is read.\n * @returns the resolution context (live BREP or hint rows), or undefined when the shape has no naming slot.\n */\nexport function buildShapeResolutionContext(\n kernel: BrepEngineApi | null,\n shape: object | undefined,\n): ResolutionContext | undefined {\n if (!shape) return undefined\n const slot = getSlot(shape)\n if (!slot) return undefined\n // 1.10 前置③:roleTable 权威落点在血缘图旁挂(part 键),slot 缓存字段已删。\n const part = nameOf(shape)\n const roleTable = (part ? runtimeLineage.tableOfPart(part) : undefined) as RoleTable | undefined\n\n const solid = slot.solid as BrepHandle | undefined\n if (kernel && solid !== undefined) {\n const handles = kernel.getSubShapes(solid, 'face')\n const hashes = Array.from(kernel.subShapeHashes(solid, 'face', HASH_UPPER_BOUND))\n const faces = handles.map((handle, i) => ({ ordinal: i + 1, hash: hashes[i] ?? 0, handle }))\n return { kernel, faces, roleTable }\n }\n\n const hints = slot.faceHints as FaceHint[] | undefined\n if (Array.isArray(hints) && hints.length > 0) {\n const faces = hints.map((row, i) => ({ ordinal: i + 1, row }))\n return { kernel: null, faces, roleTable }\n }\n return undefined\n}\n\n/**\n * 为 BREP 现场构建带边候选与邻接表的 ResolutionContext(edge TopoRef 解析用)。\n *\n * 与 `buildBrepResolutionContext` 的面候选同源(同一枚举序),并补充:\n * - `edges`:边候选表(ordinal 1 起,含活句柄 + length/midpoint hint);\n * - `faceEdgeAdjacency` / `edgeFaceAdjacency`:邻接表(edge lineage 解析用,M3)。\n *\n * 只支持 BREP 现场(edge lineage 需要 face→edge 邻接);mesh/primitive 路径\n * 没有邻接能力(build-mesh-topology.ts:262,§5.3)→ 返回 undefined,调用方\n * 按 E_TOPO_NOT_FOUND 处理。\n *\n * @param kernel - the OCCT kernel.\n * @param shape - the live input shape whose naming slot is read.\n * @returns the edge-enabled resolution context, or undefined when the shape has\n * no BREP naming slot.\n */\nexport function buildEdgeResolutionContext(\n kernel: BrepEngineApi,\n shape: object | undefined,\n): ResolutionContext | undefined {\n if (!shape) return undefined\n const slot = getSlot(shape)\n if (!slot) return undefined\n // 1.10 前置③:roleTable 权威落点在血缘图旁挂(part 键),slot 缓存字段已删。\n const edgePart = nameOf(shape)\n const roleTable = (edgePart ? runtimeLineage.tableOfPart(edgePart) : undefined) as RoleTable | undefined\n const solid = slot.solid as BrepHandle | undefined\n if (solid === undefined) return undefined\n\n const faceHandles = kernel.getSubShapes(solid, 'face')\n const faceHashes = Array.from(kernel.subShapeHashes(solid, 'face', HASH_UPPER_BOUND))\n const faces = faceHandles.map((handle, i) => ({ ordinal: i + 1, hash: faceHashes[i] ?? 0, handle }))\n\n const edgeHandles = kernel.getSubShapes(solid, 'edge')\n const edges: EdgeCandidateEntry[] = edgeHandles.map((handle, i) => ({\n ordinal: i + 1,\n handle,\n hint: captureEdgeHint(kernel, handle),\n }))\n const liveEdges = edges.filter((e): e is EdgeCandidateEntry & { handle: BrepHandle } => e.handle !== undefined)\n\n // face → 邻接 edges(ordinal 1 起)。mesh 无 face→edge 邻接,这里 BREP 现场直取。\n const faceHandlesArr = faceHandles.map((h, hi) => ({ handle: h, ordinal: hi + 1 }))\n\n const faceEdgeAdjacency = faceHandlesArr.map(({ handle }) => {\n const sub = kernel.getSubShapes(handle, 'edge')\n return sub\n .map((eh) => liveEdges.find((e) => kernel.isSame(eh, e.handle))?.ordinal)\n .filter((v): v is number => v !== undefined)\n })\n const edgeFaceAdjacency = liveEdges.map(({ handle }) => {\n const adj: number[] = []\n for (const f of faceHandlesArr) {\n if (kernel.getSubShapes(f.handle, 'edge').some((fe) => kernel.isSame(fe, handle))) {\n adj.push(f.ordinal)\n }\n }\n return adj\n })\n\n return { kernel, faces, edges, roleTable, faceEdgeAdjacency, edgeFaceAdjacency }\n}\n\n/**\n * 为**网格实体**构建带边候选的 ResolutionContext(近似拓扑的边解析入口)。\n *\n * 与 `buildEdgeResolutionContext` 的差别只有两点,两点都是\"网格零件没有 role 层\"\n * 的直接后果:\n * 1. 句柄来自 `slot.meshSolid`(网格实体句柄)而不是 `slot.solid`(BREP 句柄);\n * 2. **刻意不给 `faceEdgeAdjacency` / `edgeFaceAdjacency`**。\n *\n * 第 2 点是有意的,不是省事:邻接表存在的意义是\"两面之交 = 一条边\",\n * 而 `EdgeTopoRef.faces` 是两个 **RoleQualifier**——网格零件没有 role\n * (Phase 1.8:primitive/mesh 行 origin/role 显式为 null),限定符只是占位。\n * 带上邻接表反而会让 `resolveEdgeTopo` 走进\"按角色取公共边\"的分支并必然 not-found;\n * 不带它,解析就走「无 role 层 → 整个边表的纯几何匹配」(resolve-edge.ts 明写)。\n * 边身份只能几何,这是如实的能力边界。\n *\n * @param kernel - the mesh backend's L1 kernel (live edge handles come from it).\n * @param shape - the live mesh-solid shape whose naming slot is read.\n * @returns the edge-enabled resolution context, or undefined when the shape is not a mesh solid.\n */\nexport function buildMeshEdgeResolutionContext(\n kernel: BrepEngineApi,\n shape: object | undefined,\n): ResolutionContext | undefined {\n if (!shape) return undefined\n const slot = getSlot(shape)\n const solid = slot?.meshSolid as BrepHandle | undefined\n if (solid === undefined) return undefined\n\n const faceHandles = kernel.getSubShapes(solid, 'face')\n const faces = faceHandles.map((handle, i) => ({ ordinal: i + 1, handle }))\n const edges: EdgeCandidateEntry[] = kernel.getSubShapes(solid, 'edge').map((handle, i) => ({\n ordinal: i + 1,\n handle,\n hint: captureEdgeHint(kernel, handle),\n }))\n return { kernel, faces, edges }\n}\n\n/**\n * 为**网格实体**构建面解析上下文(`sketchOnFace` 的网格链入口)。\n *\n * 面候选直接来自 `slot.meshSolid` 的 `getSubShapes(solid,'face')` 现场句柄——\n * 与 `buildMeshEdgeResolutionContext` 的面表同源(同一枚举序)。**刻意不给\n * `roleTable` / 邻接**:网格零件没有 role 层(Phase 1.8),带上 roleTable 只会让\n * `resolveFaceTopo` 走进 exact 分支并必然落空;不带它,解析就走「全形状按 hint\n * 几何打分」(resolve-face.ts 步骤 2),命中即 `geometric-fallback`。\n *\n * 也因此,网格链上的面只能用**几何**识别(法向 / 中心 / 面积)——这是如实的能力\n * 边界,不是省事:同尺寸的两个平行面(如立方体的顶面与底面)在几何上无法区分,\n * 调用方应改用序号。\n *\n * @param kernel - the mesh backend's L1 kernel (live face handles come from it).\n * @param shape - the live mesh-solid shape whose naming slot is read.\n * @returns the face resolution context, or undefined when the shape is not a mesh solid.\n */\nexport function buildMeshFaceResolutionContext(\n kernel: BrepEngineApi,\n shape: object | undefined,\n): ResolutionContext | undefined {\n if (!shape) return undefined\n const slot = getSlot(shape)\n const solid = slot?.meshSolid as BrepHandle | undefined\n if (solid === undefined) return undefined\n\n const faces = kernel.getSubShapes(solid, 'face').map((handle, i) => ({ ordinal: i + 1, handle }))\n return { kernel, faces }\n}\n\n/**\n * 从 BREP 现场句柄建带边候选/邻接的 ResolutionContext(twoDistances 逐边重建用)。\n *\n * 与 `buildEdgeResolutionContext` 相同,但直接接收 live solid 句柄而非 Shape 槽——\n * 用于真机逐边构建后,输入已从上一次结果演化,Shape 槽里的 roleTable/hash 不再\n * 与当前句柄对应(§3.6 逐边构建,后续边要重新 resolve)。\n *\n * @param kernel - the OCCT kernel.\n * @param solid - the live BREP solid handle.\n * @param roleTable - optional role table to carry over (not used for strict matching).\n * @returns the edge-enabled resolution context (faces/edges/adjacency live from `solid`).\n */\nexport function buildEdgeContextFromSolid(\n kernel: BrepEngineApi,\n solid: BrepHandle,\n roleTable?: RoleTable,\n): ResolutionContext {\n const faceHandles = kernel.getSubShapes(solid, 'face')\n const faces = faceHandles.map((handle, i) => ({ ordinal: i + 1, hash: undefined, handle }))\n const edgeHandles = kernel.getSubShapes(solid, 'edge')\n const edges: EdgeCandidateEntry[] = edgeHandles.map((handle, i) => ({\n ordinal: i + 1,\n handle,\n hint: captureEdgeHint(kernel, handle),\n }))\n const liveEdges = edges.filter((e): e is EdgeCandidateEntry & { handle: BrepHandle } => e.handle !== undefined)\n const faceHandlesArr = faceHandles.map((h, hi) => ({ handle: h, ordinal: hi + 1 }))\n const faceEdgeAdjacency = faceHandlesArr.map(({ handle }) => {\n const sub = kernel.getSubShapes(handle, 'edge')\n return sub\n .map((eh) => liveEdges.find((e) => kernel.isSame(eh, e.handle))?.ordinal)\n .filter((v): v is number => v !== undefined)\n })\n const edgeFaceAdjacency = liveEdges.map(({ handle }) => {\n const adj: number[] = []\n for (const f of faceHandlesArr) {\n if (kernel.getSubShapes(f.handle, 'edge').some((fe) => kernel.isSame(fe, handle))) adj.push(f.ordinal)\n }\n return adj\n })\n return { kernel, faces, edges, roleTable, faceEdgeAdjacency, edgeFaceAdjacency }\n}\n\n/**\n * 解析一个 face TopoRef 到「当前几何」,并从解析出的候选面派生求解几何。\n *\n * 结果与命名行的 surfaceType/center/normal 同口径(BREAK 现场 captureFaceHint /\n * mesh 行快照),保证两侧派生值一致。\n *\n * @param kernel - the OCCT kernel (can be null on the mesh/primitive path).\n * @param shape - the live input shape whose naming slot is being resolved.\n * @param ref - the FaceTopoRef captured earlier.\n * @returns the current face geometry (center/normal/surfaceType).\n */\nexport function resolveFaceGeometry(\n kernel: BrepEngineApi | null,\n shape: Shape,\n ref: FaceTopoRef,\n): ResolvedFaceGeometry {\n // 1.10 前置③:roleTable 是解析缓存。origin/role 条目 miss 时先沿血缘回走\n // 重算并回填(miss 可恢复),再进解析——不再是\"缓存 miss = 无名\"。\n let ctx = buildShapeResolutionContext(kernel, shape)\n if (!ctx) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `no naming context on input shape (origin=${ref.origin}, role=${ref.role})`,\n )\n }\n // 1.10 前置③:roleTable 是解析缓存。origin/role 条目 miss 时先沿血缘回走\n // 重算并回填(miss 可恢复),再进解析——不再是\"缓存 miss = 无名\"。\n if (ctx.roleTable && kernel) {\n const entry0 = ctx.roleTable.get(ref.origin)?.get(ref.role)\n if (entry0 === undefined) {\n const recomputed = recomputeViaLineage(ref, shape, kernel)\n if (recomputed) {\n const merged = new Map(ctx.roleTable as ReadonlyMap<string, ReadonlyMap<string, readonly number[]>>)\n merged.set(ref.origin, new Map([...(ctx.roleTable.get(ref.origin) ?? new Map()), [ref.role, recomputed]]))\n ctx = { ...ctx, roleTable: merged as unknown as typeof ctx.roleTable }\n }\n }\n }\n const entity = resolveTopoRef(ref, ctx)\n const entry = ctx.faces[entity.ordinal - 1]\n if (!entry) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `resolved ordinal ${entity.ordinal} has no candidate in context (origin=${ref.origin}, role=${ref.role})`,\n )\n }\n // BREAK 现场:capture 出当前法向/中心(圆柱面含轴)\n if (entry.handle && ctx.kernel) {\n const hint = captureFaceHint(ctx.kernel, entry.handle)\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 // mesh/行快照:直接用行几何(axis 由宿主行注入,缺省 undefined)\n const ext = entry.row\n const hint = ext ? faceRowToHint(ext) : undefined\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\n/**\n * FaceMateConstraint 面的等价重载:face 参数对象(`{topoRef}` 或旧快照)的守卫。\n *\n * @param v - the value to test.\n * @returns true when the value is a FaceTopoRef (kind === 'face').\n */\nexport function isFaceTopoRef(v: unknown): v is FaceTopoRef {\n return !!v && typeof v === 'object' && (v as { kind?: unknown }).kind === 'face'\n}"]}