@faicad/faijs 0.19.0 → 0.29.3

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 (510) hide show
  1. package/LICENSE +202 -28
  2. package/dist/api/api-namespace.d.ts +5 -5
  3. package/dist/api/api-namespace.d.ts.map +1 -1
  4. package/dist/api/api-namespace.js +4 -4
  5. package/dist/api/api-namespace.js.map +1 -1
  6. package/dist/api/assert.d.ts +2 -2
  7. package/dist/api/assert.js +8 -8
  8. package/dist/api/assert.js.map +1 -1
  9. package/dist/api/asset.d.ts +1 -1
  10. package/dist/api/asset.js +2 -2
  11. package/dist/api/asset.js.map +1 -1
  12. package/dist/api/boolean.d.ts +1 -1
  13. package/dist/api/boolean.d.ts.map +1 -1
  14. package/dist/api/boolean.js +128 -16
  15. package/dist/api/boolean.js.map +1 -1
  16. package/dist/api/brep-mirror/joinery-brep.d.ts.map +1 -1
  17. package/dist/api/brep-mirror/joinery-brep.js +3 -1
  18. package/dist/api/brep-mirror/joinery-brep.js.map +1 -1
  19. package/dist/api/cadquery-selectors/borrow-bridge.d.ts +14 -0
  20. package/dist/api/cadquery-selectors/borrow-bridge.d.ts.map +1 -0
  21. package/dist/api/cadquery-selectors/borrow-bridge.js +52 -0
  22. package/dist/api/cadquery-selectors/borrow-bridge.js.map +1 -0
  23. package/dist/api/cadquery-selectors/edge.d.ts +40 -0
  24. package/dist/api/cadquery-selectors/edge.d.ts.map +1 -0
  25. package/dist/api/cadquery-selectors/edge.js +70 -0
  26. package/dist/api/cadquery-selectors/edge.js.map +1 -0
  27. package/dist/api/cadquery-selectors/entity.d.ts +121 -0
  28. package/dist/api/cadquery-selectors/entity.d.ts.map +1 -0
  29. package/dist/api/cadquery-selectors/entity.js +167 -0
  30. package/dist/api/cadquery-selectors/entity.js.map +1 -0
  31. package/dist/api/cadquery-selectors/face.d.ts +33 -0
  32. package/dist/api/cadquery-selectors/face.d.ts.map +1 -0
  33. package/dist/api/cadquery-selectors/face.js +353 -0
  34. package/dist/api/cadquery-selectors/face.js.map +1 -0
  35. package/dist/api/cadquery-selectors/grammar.d.ts +50 -0
  36. package/dist/api/cadquery-selectors/grammar.d.ts.map +1 -0
  37. package/dist/api/cadquery-selectors/grammar.js +345 -0
  38. package/dist/api/cadquery-selectors/grammar.js.map +1 -0
  39. package/dist/api/cadquery-selectors/index.d.ts +18 -0
  40. package/dist/api/cadquery-selectors/index.d.ts.map +1 -0
  41. package/dist/api/cadquery-selectors/index.js +17 -0
  42. package/dist/api/cadquery-selectors/index.js.map +1 -0
  43. package/dist/api/cadquery-selectors/predicates.d.ts +76 -0
  44. package/dist/api/cadquery-selectors/predicates.d.ts.map +1 -0
  45. package/dist/api/cadquery-selectors/predicates.js +85 -0
  46. package/dist/api/cadquery-selectors/predicates.js.map +1 -0
  47. package/dist/api/cadquery-selectors/resolve.d.ts +41 -0
  48. package/dist/api/cadquery-selectors/resolve.d.ts.map +1 -0
  49. package/dist/api/cadquery-selectors/resolve.js +243 -0
  50. package/dist/api/cadquery-selectors/resolve.js.map +1 -0
  51. package/dist/api/cadquery-selectors/types.d.ts +96 -0
  52. package/dist/api/cadquery-selectors/types.d.ts.map +1 -0
  53. package/dist/api/cadquery-selectors/types.js +11 -0
  54. package/dist/api/cadquery-selectors/types.js.map +1 -0
  55. package/dist/api/chamfer.d.ts +13 -5
  56. package/dist/api/chamfer.d.ts.map +1 -1
  57. package/dist/api/chamfer.js +63 -7
  58. package/dist/api/chamfer.js.map +1 -1
  59. package/dist/api/compound-geom.d.ts +1 -1
  60. package/dist/api/compound-geom.js +1 -1
  61. package/dist/api/compound-geom.js.map +1 -1
  62. package/dist/api/draft.d.ts +1 -1
  63. package/dist/api/draft.js +1 -1
  64. package/dist/api/draft.js.map +1 -1
  65. package/dist/api/edge-ref.d.ts +1 -1
  66. package/dist/api/edge-ref.d.ts.map +1 -1
  67. package/dist/api/edge-ref.js +11 -3
  68. package/dist/api/edge-ref.js.map +1 -1
  69. package/dist/api/engrave.d.ts +1 -1
  70. package/dist/api/engrave.js +7 -7
  71. package/dist/api/engrave.js.map +1 -1
  72. package/dist/api/extrude.d.ts +6 -1
  73. package/dist/api/extrude.d.ts.map +1 -1
  74. package/dist/api/extrude.js +59 -5
  75. package/dist/api/extrude.js.map +1 -1
  76. package/dist/api/face-ref.d.ts +1 -1
  77. package/dist/api/face-ref.js +1 -1
  78. package/dist/api/face-ref.js.map +1 -1
  79. package/dist/api/feature-repair.d.ts +1 -1
  80. package/dist/api/feature-repair.js +1 -1
  81. package/dist/api/feature-repair.js.map +1 -1
  82. package/dist/api/fillet-variable.d.ts +1 -1
  83. package/dist/api/fillet-variable.js +1 -1
  84. package/dist/api/fillet-variable.js.map +1 -1
  85. package/dist/api/fillet.d.ts +28 -8
  86. package/dist/api/fillet.d.ts.map +1 -1
  87. package/dist/api/fillet.js +64 -9
  88. package/dist/api/fillet.js.map +1 -1
  89. package/dist/api/generated/script-face-manifest.d.ts +4 -0
  90. package/dist/api/generated/script-face-manifest.d.ts.map +1 -1
  91. package/dist/api/generated/script-face-manifest.js.map +1 -1
  92. package/dist/api/geom.d.ts +1 -1
  93. package/dist/api/geom.js +1 -1
  94. package/dist/api/geom.js.map +1 -1
  95. package/dist/api/helix.d.ts +1 -1
  96. package/dist/api/helix.js +1 -1
  97. package/dist/api/helix.js.map +1 -1
  98. package/dist/api/import-brep.d.ts +20 -2
  99. package/dist/api/import-brep.d.ts.map +1 -1
  100. package/dist/api/import-brep.js +28 -3
  101. package/dist/api/import-brep.js.map +1 -1
  102. package/dist/api/import-step.d.ts +17 -2
  103. package/dist/api/import-step.d.ts.map +1 -1
  104. package/dist/api/import-step.js +23 -3
  105. package/dist/api/import-step.js.map +1 -1
  106. package/dist/api/index.d.ts +2 -1
  107. package/dist/api/index.d.ts.map +1 -1
  108. package/dist/api/index.js +5 -2
  109. package/dist/api/index.js.map +1 -1
  110. package/dist/api/internal/compat-op.js +1 -1
  111. package/dist/api/internal/compat-op.js.map +1 -1
  112. package/dist/api/internal/mesh-solid-op.d.ts +236 -0
  113. package/dist/api/internal/mesh-solid-op.d.ts.map +1 -0
  114. package/dist/api/internal/mesh-solid-op.js +352 -0
  115. package/dist/api/internal/mesh-solid-op.js.map +1 -0
  116. package/dist/api/internal/svg-asset-resolver.d.ts +1 -1
  117. package/dist/api/internal/svg-asset-resolver.js +1 -1
  118. package/dist/api/internal/svg-asset-resolver.js.map +1 -1
  119. package/dist/api/knurl.d.ts +1 -1
  120. package/dist/api/knurl.d.ts.map +1 -1
  121. package/dist/api/knurl.js +3 -2
  122. package/dist/api/knurl.js.map +1 -1
  123. package/dist/api/loft.d.ts +1 -1
  124. package/dist/api/loft.js +1 -1
  125. package/dist/api/loft.js.map +1 -1
  126. package/dist/api/measurement/index.d.ts +9 -5
  127. package/dist/api/measurement/index.d.ts.map +1 -1
  128. package/dist/api/measurement/index.js +47 -20
  129. package/dist/api/measurement/index.js.map +1 -1
  130. package/dist/api/pattern.d.ts +5 -3
  131. package/dist/api/pattern.d.ts.map +1 -1
  132. package/dist/api/pattern.js +32 -4
  133. package/dist/api/pattern.js.map +1 -1
  134. package/dist/api/place.d.ts +1 -1
  135. package/dist/api/place.js +1 -1
  136. package/dist/api/place.js.map +1 -1
  137. package/dist/api/primitives.d.ts +5 -4
  138. package/dist/api/primitives.d.ts.map +1 -1
  139. package/dist/api/primitives.js +11 -5
  140. package/dist/api/primitives.js.map +1 -1
  141. package/dist/api/profile.d.ts +64 -8
  142. package/dist/api/profile.d.ts.map +1 -1
  143. package/dist/api/profile.js +61 -10
  144. package/dist/api/profile.js.map +1 -1
  145. package/dist/api/punch-hole.d.ts +3 -0
  146. package/dist/api/punch-hole.d.ts.map +1 -1
  147. package/dist/api/punch-hole.js.map +1 -1
  148. package/dist/api/reconcile.d.ts +1 -1
  149. package/dist/api/reconcile.js +1 -1
  150. package/dist/api/reconcile.js.map +1 -1
  151. package/dist/api/replicate.d.ts.map +1 -1
  152. package/dist/api/replicate.js +122 -1
  153. package/dist/api/replicate.js.map +1 -1
  154. package/dist/api/screw.d.ts +1 -1
  155. package/dist/api/screw.js +3 -3
  156. package/dist/api/screw.js.map +1 -1
  157. package/dist/api/sdf.d.ts +1 -1
  158. package/dist/api/sdf.js +2 -2
  159. package/dist/api/sdf.js.map +1 -1
  160. package/dist/api/section-by-plane.d.ts +1 -1
  161. package/dist/api/section-by-plane.d.ts.map +1 -1
  162. package/dist/api/section-by-plane.js +2 -1
  163. package/dist/api/section-by-plane.js.map +1 -1
  164. package/dist/api/shell.d.ts +11 -4
  165. package/dist/api/shell.d.ts.map +1 -1
  166. package/dist/api/shell.js +53 -7
  167. package/dist/api/shell.js.map +1 -1
  168. package/dist/api/sketch-on-face.d.ts +41 -0
  169. package/dist/api/sketch-on-face.d.ts.map +1 -1
  170. package/dist/api/sketch-on-face.js +100 -1
  171. package/dist/api/sketch-on-face.js.map +1 -1
  172. package/dist/api/sketch-on-plane.d.ts +17 -9
  173. package/dist/api/sketch-on-plane.d.ts.map +1 -1
  174. package/dist/api/sketch-on-plane.js +12 -10
  175. package/dist/api/sketch-on-plane.js.map +1 -1
  176. package/dist/api/split-by-plane.d.ts +1 -1
  177. package/dist/api/split-by-plane.d.ts.map +1 -1
  178. package/dist/api/split-by-plane.js +2 -1
  179. package/dist/api/split-by-plane.js.map +1 -1
  180. package/dist/api/split.d.ts +1 -1
  181. package/dist/api/split.js +2 -2
  182. package/dist/api/split.js.map +1 -1
  183. package/dist/api/surface/arg-spec.d.ts +8 -0
  184. package/dist/api/surface/arg-spec.d.ts.map +1 -1
  185. package/dist/api/surface/arg-spec.js.map +1 -1
  186. package/dist/api/sweep.d.ts +1 -1
  187. package/dist/api/sweep.js +1 -1
  188. package/dist/api/sweep.js.map +1 -1
  189. package/dist/api/thicken.d.ts +1 -1
  190. package/dist/api/thicken.js +1 -1
  191. package/dist/api/thicken.js.map +1 -1
  192. package/dist/api/topo-resolve.d.ts +74 -1
  193. package/dist/api/topo-resolve.d.ts.map +1 -1
  194. package/dist/api/topo-resolve.js +180 -2
  195. package/dist/api/topo-resolve.js.map +1 -1
  196. package/dist/api/transform.d.ts +10 -6
  197. package/dist/api/transform.d.ts.map +1 -1
  198. package/dist/api/transform.js +74 -21
  199. package/dist/api/transform.js.map +1 -1
  200. package/dist/api/wire.d.ts +1 -1
  201. package/dist/api/wire.js +1 -1
  202. package/dist/api/wire.js.map +1 -1
  203. package/dist/api/xml-dom.d.ts +27 -0
  204. package/dist/api/xml-dom.d.ts.map +1 -0
  205. package/dist/api/xml-dom.js +45 -0
  206. package/dist/api/xml-dom.js.map +1 -0
  207. package/dist/brep/brep-ops.d.ts +13 -11
  208. package/dist/brep/brep-ops.d.ts.map +1 -1
  209. package/dist/brep/brep-ops.js +31 -30
  210. package/dist/brep/brep-ops.js.map +1 -1
  211. package/dist/brep/brep-topology.d.ts +5 -4
  212. package/dist/brep/brep-topology.d.ts.map +1 -1
  213. package/dist/brep/brep-topology.js +9 -6
  214. package/dist/brep/brep-topology.js.map +1 -1
  215. package/dist/brep/effective-deflection.d.ts +35 -0
  216. package/dist/brep/effective-deflection.d.ts.map +1 -0
  217. package/dist/brep/effective-deflection.js +49 -0
  218. package/dist/brep/effective-deflection.js.map +1 -0
  219. package/dist/brep/engine/adapters/brep-mock.d.ts.map +1 -1
  220. package/dist/brep/engine/adapters/brep-mock.js +27 -0
  221. package/dist/brep/engine/adapters/brep-mock.js.map +1 -1
  222. package/dist/brep/engine/adapters/brepkit.d.ts +18 -0
  223. package/dist/brep/engine/adapters/brepkit.d.ts.map +1 -1
  224. package/dist/brep/engine/adapters/brepkit.js +46 -1
  225. package/dist/brep/engine/adapters/brepkit.js.map +1 -1
  226. package/dist/brep/engine/adapters/occt.d.ts +1 -1
  227. package/dist/brep/engine/adapters/occt.d.ts.map +1 -1
  228. package/dist/brep/engine/adapters/occt.js +3 -1
  229. package/dist/brep/engine/adapters/occt.js.map +1 -1
  230. package/dist/brep/engine/primitives.d.ts +29 -7
  231. package/dist/brep/engine/primitives.d.ts.map +1 -1
  232. package/dist/brep/engine/primitives.js.map +1 -1
  233. package/dist/brep/engine/registry.d.ts +24 -1
  234. package/dist/brep/engine/registry.d.ts.map +1 -1
  235. package/dist/brep/engine/registry.js +24 -0
  236. package/dist/brep/engine/registry.js.map +1 -1
  237. package/dist/brep/engine/types.d.ts +54 -3
  238. package/dist/brep/engine/types.d.ts.map +1 -1
  239. package/dist/brep/engine/types.js +10 -0
  240. package/dist/brep/engine/types.js.map +1 -1
  241. package/dist/brep/export/export-model.d.ts +79 -0
  242. package/dist/brep/export/export-model.d.ts.map +1 -0
  243. package/dist/brep/export/export-model.js +336 -0
  244. package/dist/brep/export/export-model.js.map +1 -0
  245. package/dist/brep/face-evolution.d.ts +1 -1
  246. package/dist/brep/face-evolution.d.ts.map +1 -1
  247. package/dist/brep/face-evolution.js +20 -1
  248. package/dist/brep/face-evolution.js.map +1 -1
  249. package/dist/brep/handle-bridge.d.ts +1 -1
  250. package/dist/brep/handle-bridge.js +1 -1
  251. package/dist/brep/handle-bridge.js.map +1 -1
  252. package/dist/brep/mesh-solid.d.ts +218 -0
  253. package/dist/brep/mesh-solid.d.ts.map +1 -0
  254. package/dist/brep/mesh-solid.js +229 -0
  255. package/dist/brep/mesh-solid.js.map +1 -0
  256. package/dist/brep/primitives-brep.d.ts.map +1 -1
  257. package/dist/brep/primitives-brep.js +3 -1
  258. package/dist/brep/primitives-brep.js.map +1 -1
  259. package/dist/brep/text/fontRegistry.d.ts +32 -1
  260. package/dist/brep/text/fontRegistry.d.ts.map +1 -1
  261. package/dist/brep/text/fontRegistry.js +51 -1
  262. package/dist/brep/text/fontRegistry.js.map +1 -1
  263. package/dist/brep/text/text-to-solid.d.ts.map +1 -1
  264. package/dist/brep/text/text-to-solid.js +8 -3
  265. package/dist/brep/text/text-to-solid.js.map +1 -1
  266. package/dist/brepkit-kernel/brepkitKernel.d.ts +18 -2
  267. package/dist/brepkit-kernel/brepkitKernel.d.ts.map +1 -1
  268. package/dist/brepkit-kernel/brepkitKernel.js +453 -125
  269. package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
  270. package/dist/brepkit-kernel/brepkitWasm.d.ts +8 -0
  271. package/dist/brepkit-kernel/brepkitWasm.d.ts.map +1 -1
  272. package/dist/brepkit-kernel/brepkitWasm.js +10 -0
  273. package/dist/brepkit-kernel/brepkitWasm.js.map +1 -1
  274. package/dist/browser-host/browser-font-provider.d.ts +12 -0
  275. package/dist/browser-host/browser-font-provider.d.ts.map +1 -1
  276. package/dist/browser-host/browser-font-provider.js +25 -0
  277. package/dist/browser-host/browser-font-provider.js.map +1 -1
  278. package/dist/browser.d.ts +6 -3
  279. package/dist/browser.d.ts.map +1 -1
  280. package/dist/browser.js +10 -3
  281. package/dist/browser.js.map +1 -1
  282. package/dist/cad-runtime/backend-dispatch.d.ts +28 -3
  283. package/dist/cad-runtime/backend-dispatch.d.ts.map +1 -1
  284. package/dist/cad-runtime/backend-dispatch.js +119 -4
  285. package/dist/cad-runtime/backend-dispatch.js.map +1 -1
  286. package/dist/cad-runtime/browser-lib-loader.js.map +1 -1
  287. package/dist/cad-runtime/direct-executor.d.ts +19 -3
  288. package/dist/cad-runtime/direct-executor.d.ts.map +1 -1
  289. package/dist/cad-runtime/direct-executor.js +49 -1
  290. package/dist/cad-runtime/direct-executor.js.map +1 -1
  291. package/dist/cad-runtime/ports.d.ts +3 -3
  292. package/dist/cad-runtime/ports.d.ts.map +1 -1
  293. package/dist/cad-runtime/ports.js.map +1 -1
  294. package/dist/cad-runtime/preview-exec.d.ts +4 -4
  295. package/dist/cad-runtime/preview-exec.js +2 -2
  296. package/dist/cad-runtime/preview-exec.js.map +1 -1
  297. package/dist/cad-runtime/runtime.d.ts +66 -8
  298. package/dist/cad-runtime/runtime.d.ts.map +1 -1
  299. package/dist/cad-runtime/runtime.js +121 -8
  300. package/dist/cad-runtime/runtime.js.map +1 -1
  301. package/dist/define-op.d.ts +37 -1
  302. package/dist/define-op.d.ts.map +1 -1
  303. package/dist/define-op.js +25 -1
  304. package/dist/define-op.js.map +1 -1
  305. package/dist/env-agnostic.d.ts +7 -7
  306. package/dist/env-agnostic.d.ts.map +1 -1
  307. package/dist/env-agnostic.js +3 -3
  308. package/dist/env-agnostic.js.map +1 -1
  309. package/dist/geometry2d/adapt.d.ts +39 -4
  310. package/dist/geometry2d/adapt.d.ts.map +1 -1
  311. package/dist/geometry2d/adapt.js +14 -3
  312. package/dist/geometry2d/adapt.js.map +1 -1
  313. package/dist/geometry2d/blueprint.d.ts +2 -2
  314. package/dist/geometry2d/blueprint.js +2 -2
  315. package/dist/geometry2d/blueprint.js.map +1 -1
  316. package/dist/geometry2d/bridge/lift-on-plane.d.ts +18 -8
  317. package/dist/geometry2d/bridge/lift-on-plane.d.ts.map +1 -1
  318. package/dist/geometry2d/bridge/lift-on-plane.js +71 -12
  319. package/dist/geometry2d/bridge/lift-on-plane.js.map +1 -1
  320. package/dist/geometry2d/bridge/plane.d.ts +17 -0
  321. package/dist/geometry2d/bridge/plane.d.ts.map +1 -1
  322. package/dist/geometry2d/bridge/plane.js +22 -0
  323. package/dist/geometry2d/bridge/plane.js.map +1 -1
  324. package/dist/geometry2d/curve2d.d.ts +25 -0
  325. package/dist/geometry2d/curve2d.d.ts.map +1 -1
  326. package/dist/geometry2d/curve2d.js +41 -5
  327. package/dist/geometry2d/curve2d.js.map +1 -1
  328. package/dist/geometry2d/pen-sketcher.d.ts +25 -0
  329. package/dist/geometry2d/pen-sketcher.d.ts.map +1 -1
  330. package/dist/geometry2d/pen-sketcher.js +43 -0
  331. package/dist/geometry2d/pen-sketcher.js.map +1 -1
  332. package/dist/index.d.ts +6 -2
  333. package/dist/index.d.ts.map +1 -1
  334. package/dist/index.js +10 -3
  335. package/dist/index.js.map +1 -1
  336. package/dist/io/bytes-node.d.ts +11 -0
  337. package/dist/io/bytes-node.d.ts.map +1 -0
  338. package/dist/io/bytes-node.js +28 -0
  339. package/dist/io/bytes-node.js.map +1 -0
  340. package/dist/io/bytes.d.ts +33 -0
  341. package/dist/io/bytes.d.ts.map +1 -0
  342. package/dist/io/bytes.js +41 -0
  343. package/dist/io/bytes.js.map +1 -0
  344. package/dist/io/fai-zip/container-read.d.ts +102 -0
  345. package/dist/io/fai-zip/container-read.d.ts.map +1 -0
  346. package/dist/io/fai-zip/container-read.js +305 -0
  347. package/dist/io/fai-zip/container-read.js.map +1 -0
  348. package/dist/io/fai-zip/container-write.d.ts +70 -0
  349. package/dist/io/fai-zip/container-write.d.ts.map +1 -0
  350. package/dist/io/fai-zip/container-write.js +186 -0
  351. package/dist/io/fai-zip/container-write.js.map +1 -0
  352. package/dist/io/fai-zip/container.d.ts +107 -0
  353. package/dist/io/fai-zip/container.d.ts.map +1 -0
  354. package/dist/io/fai-zip/container.js +89 -0
  355. package/dist/io/fai-zip/container.js.map +1 -0
  356. package/dist/io/fai-zip/index.d.ts +18 -0
  357. package/dist/io/fai-zip/index.d.ts.map +1 -0
  358. package/dist/io/fai-zip/index.js +4 -0
  359. package/dist/io/fai-zip/index.js.map +1 -0
  360. package/dist/io/index.d.ts +10 -0
  361. package/dist/io/index.d.ts.map +1 -0
  362. package/dist/io/index.js +13 -0
  363. package/dist/io/index.js.map +1 -0
  364. package/dist/io/zip.d.ts +68 -0
  365. package/dist/io/zip.d.ts.map +1 -0
  366. package/dist/io/zip.js +147 -0
  367. package/dist/io/zip.js.map +1 -0
  368. package/dist/lang/code-to-args.d.ts +30 -4
  369. package/dist/lang/code-to-args.d.ts.map +1 -1
  370. package/dist/lang/code-to-args.js +42 -3
  371. package/dist/lang/code-to-args.js.map +1 -1
  372. package/dist/lang/codegen.d.ts +45 -1
  373. package/dist/lang/codegen.d.ts.map +1 -1
  374. package/dist/lang/codegen.js +135 -6
  375. package/dist/lang/codegen.js.map +1 -1
  376. package/dist/lang/host-arg.d.ts +6 -0
  377. package/dist/lang/host-arg.d.ts.map +1 -1
  378. package/dist/lang/host-arg.js.map +1 -1
  379. package/dist/lang/metadata-extractor.d.ts +25 -0
  380. package/dist/lang/metadata-extractor.d.ts.map +1 -1
  381. package/dist/lang/metadata-extractor.js +289 -25
  382. package/dist/lang/metadata-extractor.js.map +1 -1
  383. package/dist/lang/parse-error.d.ts +7 -1
  384. package/dist/lang/parse-error.d.ts.map +1 -1
  385. package/dist/lang/parse-error.js.map +1 -1
  386. package/dist/lang/security-scanner.d.ts +9 -1
  387. package/dist/lang/security-scanner.d.ts.map +1 -1
  388. package/dist/lang/security-scanner.js +38 -1
  389. package/dist/lang/security-scanner.js.map +1 -1
  390. package/dist/lang/statement-summary.d.ts +1 -1
  391. package/dist/lang/statement-summary.js +1 -1
  392. package/dist/lang/statement-summary.js.map +1 -1
  393. package/dist/lang/types.d.ts +1 -1
  394. package/dist/lang/types.js +1 -1
  395. package/dist/lang/types.js.map +1 -1
  396. package/dist/mesh/index.d.ts +2 -1
  397. package/dist/mesh/index.d.ts.map +1 -1
  398. package/dist/mesh/index.js +2 -1
  399. package/dist/mesh/index.js.map +1 -1
  400. package/dist/mesh/io.d.ts +69 -9
  401. package/dist/mesh/io.d.ts.map +1 -1
  402. package/dist/mesh/io.js +121 -17
  403. package/dist/mesh/io.js.map +1 -1
  404. package/dist/mesh/reconcile.d.ts +1 -1
  405. package/dist/mesh/reconcile.js +1 -1
  406. package/dist/mesh/reconcile.js.map +1 -1
  407. package/dist/mesh/rigid-transform.d.ts +3 -3
  408. package/dist/mesh/rigid-transform.js +3 -3
  409. package/dist/mesh/rigid-transform.js.map +1 -1
  410. package/dist/mesh/stl-unit.d.ts +24 -0
  411. package/dist/mesh/stl-unit.d.ts.map +1 -0
  412. package/dist/mesh/stl-unit.js +17 -0
  413. package/dist/mesh/stl-unit.js.map +1 -0
  414. package/dist/mesh/threemf-bambu.d.ts +142 -0
  415. package/dist/mesh/threemf-bambu.d.ts.map +1 -0
  416. package/dist/mesh/threemf-bambu.js +334 -0
  417. package/dist/mesh/threemf-bambu.js.map +1 -0
  418. package/dist/mesh/threemf-loader.d.ts +50 -0
  419. package/dist/mesh/threemf-loader.d.ts.map +1 -0
  420. package/dist/mesh/threemf-loader.js +628 -0
  421. package/dist/mesh/threemf-loader.js.map +1 -0
  422. package/dist/mesh/threemf-view.d.ts +71 -0
  423. package/dist/mesh/threemf-view.d.ts.map +1 -0
  424. package/dist/mesh/threemf-view.js +100 -0
  425. package/dist/mesh/threemf-view.js.map +1 -0
  426. package/dist/mesh/transform.d.ts +2 -2
  427. package/dist/mesh/transform.d.ts.map +1 -1
  428. package/dist/mesh/transform.js +3 -3
  429. package/dist/mesh/transform.js.map +1 -1
  430. package/dist/mesh/types.d.ts +1 -1
  431. package/dist/mesh/types.d.ts.map +1 -1
  432. package/dist/mesh/types.js.map +1 -1
  433. package/dist/node-host/cli.d.ts +44 -6
  434. package/dist/node-host/cli.d.ts.map +1 -1
  435. package/dist/node-host/cli.js +127 -50
  436. package/dist/node-host/cli.js.map +1 -1
  437. package/dist/node-host/font-family-index.d.ts +93 -0
  438. package/dist/node-host/font-family-index.d.ts.map +1 -0
  439. package/dist/node-host/font-family-index.js +273 -0
  440. package/dist/node-host/font-family-index.js.map +1 -0
  441. package/dist/node-host/fs-asset-resolver.d.ts +8 -3
  442. package/dist/node-host/fs-asset-resolver.d.ts.map +1 -1
  443. package/dist/node-host/fs-asset-resolver.js +13 -5
  444. package/dist/node-host/fs-asset-resolver.js.map +1 -1
  445. package/dist/node-host/index.d.ts +8 -0
  446. package/dist/node-host/index.d.ts.map +1 -1
  447. package/dist/node-host/index.js +1 -0
  448. package/dist/node-host/index.js.map +1 -1
  449. package/dist/node-host/node-font-provider.d.ts +28 -0
  450. package/dist/node-host/node-font-provider.d.ts.map +1 -1
  451. package/dist/node-host/node-font-provider.js +49 -0
  452. package/dist/node-host/node-font-provider.js.map +1 -1
  453. package/dist/node.d.ts +1 -0
  454. package/dist/node.d.ts.map +1 -1
  455. package/dist/node.js +1 -0
  456. package/dist/node.js.map +1 -1
  457. package/dist/occt-kernel/occt-primitives.d.ts.map +1 -1
  458. package/dist/occt-kernel/occt-primitives.js +35 -2
  459. package/dist/occt-kernel/occt-primitives.js.map +1 -1
  460. package/dist/occt-kernel/occtKernel.d.ts +18 -13
  461. package/dist/occt-kernel/occtKernel.d.ts.map +1 -1
  462. package/dist/occt-kernel/occtKernel.js +20 -31
  463. package/dist/occt-kernel/occtKernel.js.map +1 -1
  464. package/dist/occt-kernel/stepColorParser.d.ts +18 -0
  465. package/dist/occt-kernel/stepColorParser.d.ts.map +1 -1
  466. package/dist/occt-kernel/stepColorParser.js +38 -0
  467. package/dist/occt-kernel/stepColorParser.js.map +1 -1
  468. package/dist/occt-kernel/topologyExt.d.ts +1 -1
  469. package/dist/occt-kernel/topologyExt.d.ts.map +1 -1
  470. package/dist/occt-kernel/topologyExt.js +8 -6
  471. package/dist/occt-kernel/topologyExt.js.map +1 -1
  472. package/dist/primitives/text/cjk-font.d.ts +2 -15
  473. package/dist/primitives/text/cjk-font.d.ts.map +1 -1
  474. package/dist/primitives/text/cjk-font.js +13 -1
  475. package/dist/primitives/text/cjk-font.js.map +1 -1
  476. package/dist/runtime-state.d.ts +103 -9
  477. package/dist/runtime-state.d.ts.map +1 -1
  478. package/dist/runtime-state.js +95 -2
  479. package/dist/runtime-state.js.map +1 -1
  480. package/dist/sdk.d.ts +4 -4
  481. package/dist/sdk.d.ts.map +1 -1
  482. package/dist/sdk.js +6 -6
  483. package/dist/sdk.js.map +1 -1
  484. package/dist/shape.d.ts +97 -2
  485. package/dist/shape.d.ts.map +1 -1
  486. package/dist/shape.js +126 -1
  487. package/dist/shape.js.map +1 -1
  488. package/dist/tolerance.d.ts +38 -0
  489. package/dist/tolerance.d.ts.map +1 -0
  490. package/dist/tolerance.js +40 -0
  491. package/dist/tolerance.js.map +1 -0
  492. package/dist/topology/naming/lineage.d.ts +6 -0
  493. package/dist/topology/naming/lineage.d.ts.map +1 -1
  494. package/dist/topology/naming/lineage.js +8 -0
  495. package/dist/topology/naming/lineage.js.map +1 -1
  496. package/dist/topology/naming/resolve-edge.d.ts +20 -1
  497. package/dist/topology/naming/resolve-edge.d.ts.map +1 -1
  498. package/dist/topology/naming/resolve-edge.js +54 -20
  499. package/dist/topology/naming/resolve-edge.js.map +1 -1
  500. package/dist/topology/naming/score.js +3 -3
  501. package/dist/topology/naming/score.js.map +1 -1
  502. package/dist/units.d.ts +248 -0
  503. package/dist/units.d.ts.map +1 -0
  504. package/dist/units.js +397 -0
  505. package/dist/units.js.map +1 -0
  506. package/dist/weapp.d.ts +6 -2
  507. package/dist/weapp.d.ts.map +1 -1
  508. package/dist/weapp.js +7 -1
  509. package/dist/weapp.js.map +1 -1
  510. package/package.json +28 -3
@@ -18,10 +18,55 @@
18
18
  * - 链纪律:kernel.meshFallbackCount 计数差 > 0 → getMeshFallbackCount 暴露给宿主,
19
19
  * 由宿主标记 BREP 链中断(不在此层静默处理)。
20
20
  */
21
- import { initBrepkitWasm } from './brepkitWasm.js';
22
- // ── 句柄桥接:brepkit number 句柄 ↔ BrepHandle(零运行时成本,与 occt 适配器同构) ──
21
+ import { initBrepkitWasm, resetBrepkitWasm } from './brepkitWasm.js';
22
+ import { DEFAULT_LINEAR_DEFLECTION } from '../tolerance.js';
23
+ import { mm } from '../units.js';
24
+ import { BREP_HASH_BOUND } from '../brep/engine/types.js';
25
+ // ── 句柄桥接:brepkit number 句柄 ↔ BrepHandle ──
26
+ //
27
+ // GOTCHA(2026-10-01 实测,本文件最容易踩的坑):brepkit 句柄是**按类型分命名空间的裸 u32**
28
+ // ——同一个 box 上 solid=0、face=0..5、edge=0..11 同时存在,且**内核没有任何可靠的类型判别式**:
29
+ // getEdgeCurveType(0) / edgeLength(0) → 命中 edge 0
30
+ // getFaceEdges(0) / getFaceNormal(0) → 命中 face 0
31
+ // getSolidFaces(0) → 命中 solid 0
32
+ // 三者对同一个数字 0 各自返回「自己命名空间里 0 号」的数据,谁都不抛错(`getSolidFaces(1)` 才抛)。
33
+ // 因此「拿句柄去问内核它是什么类型」在 brepkit 上**不可能实现**。
34
+ //
35
+ // 但 L1 契约里 `hashCode(handle)` 只收到一个数字,必须知道类型才能选面/边指纹函数。
36
+ // 解法:适配器在**出口**把类型编进句柄高位。类型随句柄流动,`asNum()` 剥掉标签后仍是内核
37
+ // 裸句柄号——内部集合(knownFaces/knownEdges/…)、hashRegistry、全部 `kernel.*` 调用语义不变。
38
+ // 高 3 位(bit 26..28)放类型;虚拟 compound 句柄从 bit 29(0x20000000)起分配,互不干扰。
39
+ // 注意 `asNum` 用**算术减法**而非 `& ~KIND_MASK`:后者把 ≥2^31 的虚拟句柄按 int32 截断成负数。
40
+ const KIND_SHIFT = 26;
41
+ const KIND_MASK = 7 << KIND_SHIFT;
42
+ const KIND_SOLID = 0 << KIND_SHIFT; // 也是 compound / 未登记句柄的默认类
43
+ const KIND_FACE = 1 << KIND_SHIFT;
44
+ const KIND_EDGE = 2 << KIND_SHIFT;
45
+ const KIND_WIRE = 3 << KIND_SHIFT;
46
+ const KIND_VERTEX = 4 << KIND_SHIFT;
47
+ /** 句柄类型标签(0 = SOLID/COMPOUND 默认类)。 */
48
+ const kindOf = (h) => h & KIND_MASK;
49
+ /** 剥掉类型标签,得到 brepkit 内核裸句柄号——所有 `kernel.*` 调用必须用这个。 */
50
+ const asNum = (h) => {
51
+ const n = h;
52
+ return n - (n & KIND_MASK);
53
+ };
54
+ /** 实体/compound 句柄(默认类,保持历史行为:无标签 ⇒ 内核按 solid 路径分发)。 */
23
55
  const asHandle = (n) => n;
24
- const asNum = (h) => h;
56
+ const asFace = (n) => (n | KIND_FACE);
57
+ const asEdge = (n) => (n | KIND_EDGE);
58
+ const asWire = (n) => (n | KIND_WIRE);
59
+ const asVertex = (n) => (n | KIND_VERTEX);
60
+ /** 按已有类型标签给裸句柄重新打标签(copy/transform 族保持入参类型)。 */
61
+ const withKind = (kind, n) => {
62
+ switch (kind) {
63
+ case KIND_FACE: return asFace(n);
64
+ case KIND_EDGE: return asEdge(n);
65
+ case KIND_WIRE: return asWire(n);
66
+ case KIND_VERTEX: return asVertex(n);
67
+ default: return asHandle(n);
68
+ }
69
+ };
25
70
  const arr = (x) => Array.from(x);
26
71
  /** 适配器内部的显式失败(替代旧 unsupported 桩:能力缺失一律在静态判定拦截,不在实现体里伪装)。 */
27
72
  function fail(detail) {
@@ -77,6 +122,26 @@ function toNumArray(v) {
77
122
  }
78
123
  return [];
79
124
  }
125
+ /**
126
+ * brepkit 无任何 hash API(228 个方法里没有 hashCode/subShapeHashes)——faijs 侧的
127
+ * 面/边 hash 全部由适配器**合成指纹**,因此「合成函数」与「消费函数的取模上界」
128
+ * 必须同源:
129
+ * - 消费端 `topologyExt.buildSelectorManifestCore` 用
130
+ * `kernel.hashCode(subShape, BREP_HASH_BOUND)` 回查 faceGroups/edgeGroups 里的 hash;
131
+ * - 生产端(本文件的 solidMesh / faceMesh / compoundMesh / wireframe)必须用**同一个**
132
+ * `BREP_HASH_BOUND` 生成,否则查表恒 miss。
133
+ *
134
+ * GOTCHA(2026-10-01 实测修复):本文件此前生产端用 1e9、消费端用 2147483647,
135
+ * 导致 brepkit 引擎下面行的 triStart/triCount 恒为 0(面积/法向随之丢失)。
136
+ */
137
+ function fnv1a(basis, upperBound) {
138
+ let h = 0x811c9dc5;
139
+ for (let i = 0; i < basis.length; i++) {
140
+ h ^= basis.charCodeAt(i);
141
+ h = Math.imul(h, 0x01000193) >>> 0;
142
+ }
143
+ return upperBound > 0 ? h % upperBound : h;
144
+ }
80
145
  /** 面稳定指纹:解析曲面参数 + 面积 → FNV-1a → 取模上界(跨操作可追溯,与句柄无关)。 */
81
146
  function faceFingerprint(kernel, face, upperBound) {
82
147
  let params = '';
@@ -89,13 +154,45 @@ function faceFingerprint(kernel, face, upperBound) {
89
154
  area = Number(kernel.faceArea?.(face, 0.1) ?? 0);
90
155
  }
91
156
  catch { /* 查询失败用 0 */ }
92
- const basis = `${params}|${area.toFixed(6)}`;
93
- let h = 0x811c9dc5;
94
- for (let i = 0; i < basis.length; i++) {
95
- h ^= basis.charCodeAt(i);
96
- h = Math.imul(h, 0x01000193) >>> 0;
157
+ return fnv1a(`${params}|${area.toFixed(6)}`, upperBound);
158
+ }
159
+ /**
160
+ * 边稳定指纹:曲线类型 + 参数区间 + 两端/中点采样 + 长度 → FNV-1a。
161
+ *
162
+ * 为什么不能用 `faceFingerprint`:它对边恒得同一个值(`getAnalyticSurfaceParams`
163
+ * 与 `faceArea` 对 edge 句柄都抛错)——旧实现在 box 上 12 条边 hash 全同,
164
+ * 使 `topologyExt` 的 edgeGroupByHash 只留最后一条折线(实测 2026-10-01)。
165
+ *
166
+ * 为什么不能只用端点:整圆(圆柱端盖)首末点重合,z 不同的两个圆会撞 —
167
+ * 故补 UV 中点采样。`getBoundingBox(edge)` 在 brepkit 上抛错,不可用。
168
+ */
169
+ function edgeFingerprint(kernel, edge, upperBound) {
170
+ let curveType = '';
171
+ try {
172
+ curveType = String(kernel.getEdgeCurveType(edge) ?? '');
97
173
  }
98
- return upperBound > 0 ? h % upperBound : h;
174
+ catch { /* 非边 */ }
175
+ let range = '';
176
+ let mid = '';
177
+ try {
178
+ // 方言:`getEdgeCurveParameters` 返回 **Float64Array**(如 [0, 10]),不是 JSON 字符串
179
+ // (见本文件头的返回值归一化说明)。曾误用 JSON.parse → 恒定抛错 → range/mid 恒空,
180
+ // 于是所有同类型同长度的边(box 的 12 条棱)挤成同一个指纹(2026-10-01 实测)。
181
+ const p = toNumArray(kernel.getEdgeCurveParameters(edge));
182
+ const first = p[0] ?? 0;
183
+ const last = p[1] ?? 0;
184
+ range = `${first.toFixed(9)},${last.toFixed(9)}`;
185
+ const midT = (first + last) / 2;
186
+ const sample = (t) => toNumArray(kernel.evaluateEdgeCurve(edge, t)).map((x) => x.toFixed(6)).join(',');
187
+ mid = `${sample(first)}|${sample(midT)}|${sample(last)}`;
188
+ }
189
+ catch { /* 采样失败 → 退化为类型 + 空几何 */ }
190
+ let length = 0;
191
+ try {
192
+ length = Number(kernel.edgeLength(edge) ?? 0);
193
+ }
194
+ catch { /* 长度不可用用 0 */ }
195
+ return fnv1a(`${curveType}|${range}|${mid}|${length.toFixed(6)}`, upperBound);
99
196
  }
100
197
  /** 面演化数据(BrepEvolutionData):brepkit evolution JSON → hash 编码三元组。 */
101
198
  function mapEvolution(kernel, raw, resultHandle, inputFaceHashes, hashUpperBound) {
@@ -146,7 +243,18 @@ function mapEvolution(kernel, raw, resultHandle, inputFaceHashes, hashUpperBound
146
243
  }
147
244
  /** per-kernel 的 hash↔handle 登记表(fuseWithHistory 溯源对齐用)。 */
148
245
  const hashRegistry = new WeakMap();
149
- /** 三点外接圆(makeArcEdge 方言消化:occt 吃 3 点,brepkit 吃圆心+轴)。 */
246
+ /**
247
+ * 三点外接圆(makeArcEdge 方言消化:occt 吃 3 点,brepkit 吃圆心+轴)。
248
+ *
249
+ * 圆心偏移取标准式 `d = ( |ab|²·(ac×n) + |ac|²·(n×ab) ) / (2·|n|²)`,`n = ab×ac`。
250
+ * GOTCHA(弧缺陷,2026-09-30 实证):旧式 `( |ac|²·(ab×n) + |ab|²·(n×ac) ) / (2·|n|²)`
251
+ * 把两项的系数对调,等于整体取反——圆心落到弦的另一侧(实测 quarter arc 圆心 (20,0,0)
252
+ * 而非 (0,0,0),三点不等距),brepkit 据此生成 333° 的补弧(弧长 58.195 而非 πr/2)。
253
+ * 判据是等距不变量 `|center−a| = |center−b| = |center−c|`。
254
+ *
255
+ * axis = n/|n| 即三点逆时针扫掠方向的右手法向;brepkit `makeCircleArc3d` 语义为
256
+ * "沿法向看从 start 逆时针到 end",故该轴向同时正确表达 ccw 与 cw 弧。
257
+ */
150
258
  function circumcircle(a, b, c) {
151
259
  const abx = b.x - a.x, aby = b.y - a.y, abz = b.z - a.z;
152
260
  const acx = c.x - a.x, acy = c.y - a.y, acz = c.z - a.z;
@@ -158,16 +266,16 @@ function circumcircle(a, b, c) {
158
266
  fail('makeArcEdge: 3 points are collinear (no circumscribed circle)');
159
267
  const ab2 = abx * abx + aby * aby + abz * abz;
160
268
  const ac2 = acx * acx + acy * acy + acz * acz;
161
- // center = a + ( ac2·(ab×n) + ab2·(n×ac) ) / (2·n²)
162
- const abxnx = aby * nz - abz * ny, abxny = abz * nx - abx * nz, abxnz = abx * ny - aby * nx;
163
- const nxacx = ny * acz - nz * acy, nxacy = nz * acx - nx * acz, nxacz = nx * acy - ny * acx;
269
+ // center = a + ( ab2·(ac×n) + ac2·(n×ab) ) / (2·n²)
270
+ const acxnx = acy * nz - acz * ny, acxny = acz * nx - acx * nz, acxnz = acx * ny - acy * nx;
271
+ const nxabx = ny * abz - nz * aby, nxaby = nz * abx - nx * abz, nxabz = nx * aby - ny * abx;
164
272
  const denom = 2 * n2;
165
273
  const len = Math.sqrt(n2);
166
274
  return {
167
275
  center: {
168
- x: a.x + (ac2 * abxnx + ab2 * nxacx) / denom,
169
- y: a.y + (ac2 * abxny + ab2 * nxacy) / denom,
170
- z: a.z + (ac2 * abxnz + ab2 * nxacz) / denom,
276
+ x: a.x + (ab2 * acxnx + ac2 * nxabx) / denom,
277
+ y: a.y + (ab2 * acxny + ac2 * nxaby) / denom,
278
+ z: a.z + (ab2 * acxnz + ac2 * nxabz) / denom,
171
279
  },
172
280
  axis: { x: nx / len, y: ny / len, z: nz / len },
173
281
  };
@@ -190,6 +298,13 @@ function flattenPoints(points) {
190
298
  */
191
299
  export async function createBrepkitPrimitives() {
192
300
  const kernel = await initBrepkitWasm();
301
+ // GOTCHA(2026-10-01):本函数此前每次调用都新建一套闭包(knownFaces/knownSolids/
302
+ // derivedFaces…)——即**新建一套句柄桥接层**。句柄号虽然对同一个 wasm 内核全局有效,
303
+ // 但桥接层侧的类型登记表不同:A 层枚举出的面句柄交给 B 层做 getSubShapes,会被当
304
+ // 实体走 tessellateSolidGrouped。BREP 引擎装配点与网格后端装配点都调本函数,必须
305
+ // 拿到**同一个**对象。故按内核实例 memo 化(disposeBrepkit 会清掉)。
306
+ if (memoizedPrimitives && memoizedKernel === kernel)
307
+ return memoizedPrimitives;
193
308
  liveKernel = kernel;
194
309
  let lastFallbackCount = 0;
195
310
  // 已知面句柄集合:getSubShapes(shape,'face') / makeRectangle / makeFace / buildTriFace
@@ -223,14 +338,45 @@ export async function createBrepkitPrimitives() {
223
338
  // (实测 box makeBox 返回 solid 2,撞 profile 早先的 face 2 → cloneShape 走 copyFace 崩溃)。
224
339
  // 建 solid 时必须从 knownFaces/knownWires/knownEdges/derivedFaces 抹掉该编号,让默认 solid 路径生效。
225
340
  // derivedFaces 也必须清:getSubShapes(solid,'edge') 会查 derivedFaces.has(s) 误走 getFaceEdges。
341
+ // GOTCHA(2026-10-01 实测):`derivedFaces` 是**全局**面句柄号集合,枚举 solid A 的面会把
342
+ // 面号 0..N 写进去;此后对**裸句柄号恰好相同的另一个 solid**做 getSubShapes(...,'edge')
343
+ // 时,`derivedFaces.has(s)` 为真 → isFaceLike 误判 → 走 getFaceEdges 只拿到一张面的 4 条边
344
+ // (实测 box:12 条边变 4 条)。故必须显式登记 solid 句柄:实体永远不是 face-like。
345
+ const knownSolids = new Set();
226
346
  const markSolid = (h) => {
227
347
  knownFaces.delete(h);
228
348
  knownWires.delete(h);
229
349
  knownEdges.delete(h);
230
350
  knownCompounds.delete(h);
231
351
  derivedFaces.delete(h);
352
+ knownSolids.add(h);
232
353
  return h;
233
354
  };
355
+ /** 实体数组批量登记(阵列族返回多个实体)。 */
356
+ const markSolids = (list) => list.map(markSolid);
357
+ /**
358
+ * 句柄类型解析:**标签优先**,无标签时退回已知集合(历史行为)。
359
+ *
360
+ * 标签是权威来源——集合的 key 是**裸句柄号**,face 3 与 edge 3 会共存于不同集合,
361
+ * 靠集合无法区分。故有标签(非 0)时直接用标签,集合里的同名条目一律忽略;
362
+ * 只有无标签句柄(实体/compound/构造 API 外传入)才走集合分发,行为与加标签前一致。
363
+ */
364
+ const resolveKind = (shape, s) => {
365
+ const tagged = kindOf(shape);
366
+ if (tagged !== KIND_SOLID)
367
+ return tagged;
368
+ // 已登记的实体优先:knownFaces/knownEdges 的 key 是裸句柄号,历史登记(如
369
+ // getSubShapes(face,'edge') 写 knownEdges)与后来新建的 solid 号同号时会把实体判成边。
370
+ if (knownSolids.has(s))
371
+ return KIND_SOLID;
372
+ if (knownFaces.has(s))
373
+ return KIND_FACE;
374
+ if (knownWires.has(s))
375
+ return KIND_WIRE;
376
+ if (knownEdges.has(s))
377
+ return KIND_EDGE;
378
+ return KIND_SOLID;
379
+ };
234
380
  const readFallback = () => {
235
381
  try {
236
382
  return typeof kernel.meshFallbackCount === 'function' ? Number(kernel.meshFallbackCount()) : 0;
@@ -262,7 +408,7 @@ export async function createBrepkitPrimitives() {
262
408
  indices: new Uint32Array(indices),
263
409
  vertexCount: positions.length / 3,
264
410
  triangleCount: triCount,
265
- faceGroups: new Int32Array([0, triCount, faceFingerprint(kernel, f, 1e9)]),
411
+ faceGroups: new Int32Array([0, indices.length, faceFingerprint(kernel, f, BREP_HASH_BOUND)]),
266
412
  faceCount: 1,
267
413
  };
268
414
  };
@@ -302,21 +448,24 @@ export async function createBrepkitPrimitives() {
302
448
  const solidMesh = (solid, deflection, angular) => {
303
449
  const raw = kernel.tessellateSolidGrouped(solid, deflection, angular);
304
450
  const m = JSON.parse(raw);
305
- // faceOffsets 形如 [start0, start1, ..., totalEnd]:以「索引数组单位」计的每面偏移(2026-09-19 实测),
306
- // 除以 3 换算为 faijs 契约的三角形单位 [triStart, triCount, faceHash]
451
+ // faceOffsets 形如 [start0, start1, ..., totalEnd]:以「索引数组单位」计的每面偏移
452
+ // (2026-09-19 实测)。**L1 契约的 faceGroups 就是索引单位**(与 OCCT 一致,
453
+ // 见 brep/engine/types.BrepMeshResult)——此处原样透传,不得再除以 3。
454
+ // GOTCHA(2026-10-01):旧实现除以 3 换成三角形单位,与 topologyExt 的 `/3`
455
+ // 叠加 → 面行区间缩小 3 倍。
307
456
  const fo = m.faceOffsets;
308
457
  const faceCount = Math.max(0, fo.length - 1);
309
458
  const faceHashes = [];
310
459
  try {
311
460
  const faces = arr(kernel.getSolidFaces(solid));
312
461
  for (let i = 0; i < faceCount && i < faces.length; i++)
313
- faceHashes.push(faceFingerprint(kernel, faces[i], 1e9));
462
+ faceHashes.push(faceFingerprint(kernel, faces[i], BREP_HASH_BOUND));
314
463
  }
315
464
  catch { /* 非实体:faceHash 置 0 */ }
316
465
  const faceGroups = new Int32Array(faceCount * 3);
317
466
  for (let i = 0; i < faceCount; i++) {
318
- faceGroups[i * 3] = fo[i] / 3;
319
- faceGroups[i * 3 + 1] = (fo[i + 1] - fo[i]) / 3;
467
+ faceGroups[i * 3] = fo[i];
468
+ faceGroups[i * 3 + 1] = fo[i + 1] - fo[i];
320
469
  faceGroups[i * 3 + 2] = faceHashes[i] ?? 0;
321
470
  }
322
471
  return {
@@ -335,7 +484,7 @@ export async function createBrepkitPrimitives() {
335
484
  const normals = [];
336
485
  const indices = [];
337
486
  const faceGroups = [];
338
- let triOffset = 0;
487
+ let indexOffset = 0;
339
488
  for (const c of children) {
340
489
  let sub;
341
490
  if (knownFaces.has(c)) {
@@ -360,10 +509,10 @@ export async function createBrepkitPrimitives() {
360
509
  indices.push(sub.indices[i] + vOffset);
361
510
  if (sub.faceGroups) {
362
511
  for (let i = 0; i < sub.faceGroups.length; i += 3) {
363
- faceGroups.push((sub.faceGroups[i] ?? 0) + triOffset, sub.faceGroups[i + 1] ?? 0, sub.faceGroups[i + 2] ?? 0);
512
+ faceGroups.push((sub.faceGroups[i] ?? 0) + indexOffset, sub.faceGroups[i + 1] ?? 0, sub.faceGroups[i + 2] ?? 0);
364
513
  }
365
514
  }
366
- triOffset += sub.triangleCount;
515
+ indexOffset += sub.indices.length;
367
516
  }
368
517
  return {
369
518
  positions: new Float32Array(positions),
@@ -380,13 +529,15 @@ export async function createBrepkitPrimitives() {
380
529
  // 传错即崩溃;transformSolid/transformFace/transformWire 均为「原地修改」void。
381
530
  // place/translate 等会作用在 profile 产出的 face 上(sew 用例 place 两张 face),
382
531
  // 故必须按 knownFaces/knownWires 分发,不能一律 copySolid。
383
- const cloneShape = (shape) => {
384
- if (knownFaces.has(shape)) {
532
+ // kind 为调用方句柄的类型标签(0 = solid):句柄标签是**权威判定**,knownFaces/knownWires
533
+ // 只作无标签时的兜底(枚举出的面句柄只进 derivedFaces,不在 knownFaces 里)。
534
+ const cloneShape = (shape, kind = KIND_SOLID) => {
535
+ if (kind === KIND_FACE || knownFaces.has(shape)) {
385
536
  const c = kernel.copyFace(shape);
386
537
  knownFaces.add(c);
387
538
  return c;
388
539
  }
389
- if (knownWires.has(shape)) {
540
+ if (kind === KIND_WIRE || knownWires.has(shape)) {
390
541
  const c = kernel.copyWire(shape);
391
542
  knownWires.add(c);
392
543
  return c;
@@ -394,12 +545,12 @@ export async function createBrepkitPrimitives() {
394
545
  return markSolid(kernel.copySolid(shape));
395
546
  };
396
547
  /** 深拷贝 + 原地仿射变换(matrix 已经 toKernelMatrix 归一为 16 元素 4×4)。 */
397
- const cloneAndTransform = (shape, matrix16) => {
398
- const c = cloneShape(shape);
548
+ const cloneAndTransform = (shape, matrix16, kind = KIND_SOLID) => {
549
+ const c = cloneShape(shape, kind);
399
550
  const m = Float64Array.from(matrix16);
400
- if (knownFaces.has(c))
551
+ if (kind === KIND_FACE || knownFaces.has(c))
401
552
  kernel.transformFace(c, m);
402
- else if (knownWires.has(c))
553
+ else if (kind === KIND_WIRE || knownWires.has(c))
403
554
  kernel.transformWire(c, m);
404
555
  else
405
556
  kernel.transformSolid(c, m);
@@ -433,11 +584,11 @@ export async function createBrepkitPrimitives() {
433
584
  makeRectangle(width, height) {
434
585
  const f = kernel.makeRectangle(width, height);
435
586
  knownFaces.add(f);
436
- return asHandle(f);
587
+ return asFace(f);
437
588
  },
438
589
  makeEllipsoid(rx, ry, rz) { return asHandle(markSolid(kernel.makeEllipsoid(rx, ry, rz))); },
439
590
  makeTorus(majorRadius, minorRadius) { return asHandle(markSolid(kernel.makeTorus(majorRadius, minorRadius, 64))); },
440
- makeVertex(x, y, z) { return asHandle(kernel.makeVertex(x, y, z)); },
591
+ makeVertex(x, y, z) { return asVertex(kernel.makeVertex(x, y, z)); },
441
592
  // ── 造型运算 ──
442
593
  extrude(shape, dx, dy, dz) {
443
594
  // 方言归一(§3.6-1):L1 口径是挤出向量;brepkit 原生吃 (face, dir, distance)。
@@ -459,7 +610,7 @@ export async function createBrepkitPrimitives() {
459
610
  return asHandle(markSolid(kernel.revolve(s, center.x, center.y, center.z, direction.x, direction.y, direction.z, angleDeg)));
460
611
  },
461
612
  sew(shapesList, tolerance) {
462
- return asHandle(kernel.sewFaces(Uint32Array.from(shapesList.map(asNum)), tolerance ?? 1e-6));
613
+ return asHandle(markSolid(kernel.sewFaces(Uint32Array.from(shapesList.map(asNum)), tolerance ?? 1e-6)));
463
614
  },
464
615
  sewAndSolidify(faces, _tolerance) {
465
616
  // brepkit 的 makeSolid 即「缝合并固化成实体」(sewFaces + 建 solid)。
@@ -467,10 +618,10 @@ export async function createBrepkitPrimitives() {
467
618
  },
468
619
  shell(solid, facesToRemove, thickness, _tolerance) {
469
620
  // brepkit 参数序 (solid, thickness, open_faces);tolerance 内核固定。
470
- return asHandle(kernel.shell(asNum(solid), thickness, Uint32Array.from(facesToRemove.map(asNum))));
621
+ return asHandle(markSolid(kernel.shell(asNum(solid), thickness, Uint32Array.from(facesToRemove.map(asNum)))));
471
622
  },
472
623
  hullFromPoints(points, _tolerance) {
473
- return asHandle(kernel.convexHull(flattenPoints(points)));
624
+ return asHandle(markSolid(kernel.convexHull(flattenPoints(points))));
474
625
  },
475
626
  // ── 布尔与分割 ──
476
627
  fuse(a, b) { return asHandle(markSolid(trackFallback(kernel.fuse(asNum(a), asNum(b))))); },
@@ -479,7 +630,7 @@ export async function createBrepkitPrimitives() {
479
630
  // brepkit 无 `common` 原生名;交集语义由 `intersect` 承担(method-map: dialect)。
480
631
  return asHandle(markSolid(trackFallback(kernel.intersect(asNum(a), asNum(b)))));
481
632
  },
482
- intersect(a, b) { return asHandle(trackFallback(kernel.intersect(asNum(a), asNum(b)))); },
633
+ intersect(a, b) { return asHandle(markSolid(trackFallback(kernel.intersect(asNum(a), asNum(b))))); },
483
634
  fuseAll(shapesList) {
484
635
  return asHandle(markSolid(trackFallback(kernel.fuseAll(Int32Array.from(shapesList.map(asNum))))));
485
636
  },
@@ -513,24 +664,24 @@ export async function createBrepkitPrimitives() {
513
664
  },
514
665
  // ── 倒角与圆角(Q7:只对齐等距 + 距角两种粒度)──
515
666
  chamfer(solid, edges, distance) {
516
- return asHandle(trackFallback(kernel.chamfer(asNum(solid), Uint32Array.from(edges.map(asNum)), distance)));
667
+ return asHandle(markSolid(trackFallback(kernel.chamfer(asNum(solid), Uint32Array.from(edges.map(asNum)), distance))));
517
668
  },
518
669
  chamferDistAngle(solid, edges, distance, angleDeg) {
519
670
  // 方言映射:L1 chamferDistAngle ↔ brepkit chamferDistanceAngle。
520
671
  // ⚠️ 单位方言:L1 口径是**度**(`AddDA(distance, angleDeg, E, F)`),brepkit 内核吃**弧度**
521
672
  // (2026-09-25 实证:传 45 抛 "angle must be less than π/2";传 π/4 正常倒角)。
522
673
  const angleRad = (angleDeg * Math.PI) / 180;
523
- return asHandle(trackFallback(kernel.chamferDistanceAngle(asNum(solid), Uint32Array.from(edges.map(asNum)), distance, angleRad)));
674
+ return asHandle(markSolid(trackFallback(kernel.chamferDistanceAngle(asNum(solid), Uint32Array.from(edges.map(asNum)), distance, angleRad))));
524
675
  },
525
676
  fillet(solid, edges, radius) {
526
- return asHandle(trackFallback(kernel.fillet(asNum(solid), Uint32Array.from(edges.map(asNum)), radius)));
677
+ return asHandle(markSolid(trackFallback(kernel.fillet(asNum(solid), Uint32Array.from(edges.map(asNum)), radius))));
527
678
  },
528
679
  filletVariable(solid, edge, startRadius, endRadius) {
529
680
  // 方言映射:L1 filletVariable(solid, edge, startRadius, endRadius)(单边变半径)
530
681
  // ↔ brepkit filletVariable(solid, json) 吃**序列** `[{edge, radius1, radius2}, ...]`。
531
682
  // ⚠️ 直接传 4 个位置参数会触发 wasm memory out of bounds(2026-09-25 实证)。
532
683
  const spec = JSON.stringify([{ edge: asNum(edge), radius1: startRadius, radius2: endRadius }]);
533
- return asHandle(trackFallback(kernel.filletVariable(asNum(solid), spec)));
684
+ return asHandle(markSolid(trackFallback(kernel.filletVariable(asNum(solid), spec))));
534
685
  },
535
686
  filletWithHistory(solid, edges, radius, inputFaceHashes, hashUpperBound) {
536
687
  const raw = kernel.filletWithEvolution(asNum(solid), Int32Array.from(edges.map(asNum)), radius);
@@ -543,61 +694,62 @@ export async function createBrepkitPrimitives() {
543
694
  // brepkit transformSolid 吃 4×4 行主序 16 元素。所有 transformSolid 调用一律经
544
695
  // toKernelMatrix 归一(下方唯一转换点),禁止直出 16 元素旁路。
545
696
  translate(shape, dx, dy, dz) {
546
- return asHandle(cloneAndTransform(asNum(shape), toKernelMatrix(translationMatrix(dx, dy, dz))));
697
+ return withKind(kindOf(shape), cloneAndTransform(asNum(shape), toKernelMatrix(translationMatrix(dx, dy, dz)), kindOf(shape)));
547
698
  },
548
699
  scale(shape, center, factor) {
549
- return asHandle(cloneAndTransform(asNum(shape), toKernelMatrix(scaleMatrix(center, factor))));
700
+ return withKind(kindOf(shape), cloneAndTransform(asNum(shape), toKernelMatrix(scaleMatrix(center, factor)), kindOf(shape)));
550
701
  },
551
702
  transform(shape, matrix) {
552
- return asHandle(cloneAndTransform(asNum(shape), toKernelMatrix(matrix)));
703
+ return withKind(kindOf(shape), cloneAndTransform(asNum(shape), toKernelMatrix(matrix), kindOf(shape)));
553
704
  },
554
705
  located(shape, matrix) {
555
- return asHandle(cloneAndTransform(asNum(shape), toKernelMatrix(matrix)));
706
+ return withKind(kindOf(shape), cloneAndTransform(asNum(shape), toKernelMatrix(matrix), kindOf(shape)));
556
707
  },
557
708
  locate(shape, matrix) {
558
- return asHandle(cloneAndTransform(asNum(shape), toKernelMatrix(matrix)));
709
+ return withKind(kindOf(shape), cloneAndTransform(asNum(shape), toKernelMatrix(matrix), kindOf(shape)));
559
710
  },
560
711
  generalTransform(shape, matrix) {
561
- return asHandle(cloneAndTransform(asNum(shape), toKernelMatrix(matrix)));
712
+ return withKind(kindOf(shape), cloneAndTransform(asNum(shape), toKernelMatrix(matrix), kindOf(shape)));
562
713
  },
563
- copy(shape) { return asHandle(cloneShape(asNum(shape))); },
564
- copyShape(shape) { return asHandle(cloneShape(asNum(shape))); },
714
+ copy(shape) { return withKind(kindOf(shape), cloneShape(asNum(shape), kindOf(shape))); },
715
+ copyShape(shape) { return withKind(kindOf(shape), cloneShape(asNum(shape), kindOf(shape))); },
565
716
  composeTransform(m1, m2) {
566
717
  // 方言:L1 是 3×4 行主序 12 元素;brepkit composeTransforms 吃/回 4×4 16 元素。
567
718
  const r = kernel.composeTransforms(Float64Array.from(toKernelMatrix(m1)), Float64Array.from(toKernelMatrix(m2)));
568
719
  return Array.from(r).slice(0, 12);
569
720
  },
570
721
  mirror(shape, point, normal) {
571
- return asHandle(kernel.mirror(asNum(shape), point.x, point.y, point.z, normal.x, normal.y, normal.z));
722
+ return withKind(kindOf(shape), kernel.mirror(asNum(shape), point.x, point.y, point.z, normal.x, normal.y, normal.z));
572
723
  },
573
724
  // ── 阵列 ──
574
725
  // brepkit wasm 的 pattern 内核函数都返回 compound(含全部副本)→ 经 getCompoundSolids 拆成数组。
575
726
  linearPattern(shape, direction, spacing, count) {
576
727
  const compound = kernel.linearPattern(asNum(shape), direction.x, direction.y, direction.z, spacing, count);
577
- return arr(kernel.getCompoundSolids(compound)).map(asHandle);
728
+ // 阵列内核返回 compound(原始句柄)→ 拆成实体数组并逐个登记(见 markSolid GOTCHA)。
729
+ return markSolids(arr(kernel.getCompoundSolids(compound))).map(asHandle);
578
730
  },
579
731
  circularPattern(shape, _center, axis, _angleStep, count) {
580
732
  // ⚠️ brepkit wasm circularPattern(solid, ax, ay, az, count) 无 center/angle 参数:
581
733
  // 固定整圆均分(每份 360°/count)。fullAngle=360 时与 vendored/occt 语义一致;
582
734
  // 其它角度跨度无法表达(语义限制记录于此,parity 测试用整圆)。
583
735
  const compound = kernel.circularPattern(asNum(shape), axis.x, axis.y, axis.z, count);
584
- return arr(kernel.getCompoundSolids(compound)).map(asHandle);
736
+ return markSolids(arr(kernel.getCompoundSolids(compound))).map(asHandle);
585
737
  },
586
738
  gridPattern(shape, directionX, directionY, spacingX, spacingY, countX, countY) {
587
- return asHandle(kernel.gridPattern(asNum(shape), directionX.x, directionX.y, directionX.z, directionY.x, directionY.y, directionY.z, spacingX, spacingY, countX, countY));
739
+ return asHandle(markSolid(kernel.gridPattern(asNum(shape), directionX.x, directionX.y, directionX.z, directionY.x, directionY.y, directionY.z, spacingX, spacingY, countX, countY)));
588
740
  },
589
741
  // ── 曲线构造 ──
590
742
  makeLineEdge(start, end) {
591
743
  const e = kernel.makeLineEdge(start.x, start.y, start.z, end.x, end.y, end.z);
592
744
  knownEdges.add(e);
593
- return asHandle(e);
745
+ return asEdge(e);
594
746
  },
595
747
  makeArcEdge(start, mid, end) {
596
748
  // 方言消化:occt 吃 3 点;brepkit makeCircleArc3d 吃 (start,end,center,axis)。
597
749
  const { center, axis } = circumcircle(start, mid, end);
598
750
  const e = kernel.makeCircleArc3d(start.x, start.y, start.z, end.x, end.y, end.z, center.x, center.y, center.z, axis.x, axis.y, axis.z);
599
751
  knownEdges.add(e);
600
- return asHandle(e);
752
+ return asEdge(e);
601
753
  },
602
754
  makeBezierEdge(controlPoints) {
603
755
  // 方言消化:L1 吃控制点(Bezier);brepkit 原生 makeNurbsEdge 吃
@@ -615,34 +767,59 @@ export async function createBrepkitPrimitives() {
615
767
  const last = controlPoints[n - 1];
616
768
  const e = kernel.makeNurbsEdge(first.x, first.y, first.z, last.x, last.y, last.z, degree, knots, flattenPoints(controlPoints), weights);
617
769
  knownEdges.add(e);
618
- return asHandle(e);
770
+ return asEdge(e);
771
+ },
772
+ makeBSplineEdge(poles, weights, knots, multiplicities, degree, periodic = false) {
773
+ // 方言消化:L1 吃 occt 形态 (poles, weights, knots, multiplicities, degree),
774
+ // brepkit makeNurbsEdge 吃 (start,end,degree, **展开后** knots, control_points, weights)。
775
+ // 两侧 knots 的表示不同:occt 给「去重值 + 多重度」两个平行数组,brepkit 要
776
+ // 逐项重复后的整条节点向量(与 makeBezierEdge 里手工铺 [0]×n+[1]×n 同一口径)。
777
+ const controlPoints = [];
778
+ for (let i = 0; i + 2 < poles.length; i += 3) {
779
+ controlPoints.push({ x: poles[i], y: poles[i + 1], z: poles[i + 2] });
780
+ }
781
+ const n = controlPoints.length;
782
+ if (n < 2)
783
+ fail('makeBSplineEdge: need at least 2 poles');
784
+ const expanded = [];
785
+ for (let i = 0; i < knots.length; i++) {
786
+ const rep = multiplicities[i] ?? 1;
787
+ for (let j = 0; j < rep; j++)
788
+ expanded.push(knots[i]);
789
+ }
790
+ const first = controlPoints[0];
791
+ const last = controlPoints[n - 1];
792
+ const e = kernel.makeNurbsEdge(first.x, first.y, first.z, last.x, last.y, last.z, degree, Float64Array.from(expanded), flattenPoints(controlPoints), weights.length === n ? Float64Array.from(weights) : new Float64Array(n).fill(1));
793
+ void periodic;
794
+ knownEdges.add(e);
795
+ return asEdge(e);
619
796
  },
620
797
  makeCircleEdge(center, normal, radius) {
621
798
  const e = kernel.makeCircleEdge(center.x, center.y, center.z, normal.x, normal.y, normal.z, radius);
622
799
  knownEdges.add(e);
623
- return asHandle(e);
800
+ return asEdge(e);
624
801
  },
625
802
  // ── 拓扑构造 ──
626
803
  makeWire(edges) {
627
804
  const w = kernel.makeWire(Int32Array.from(edges.map(asNum)), false);
628
805
  knownWires.add(w);
629
- return asHandle(w);
806
+ return asWire(w);
630
807
  },
631
808
  makeFace(wire) {
632
809
  const f = kernel.makeFaceFromWire(asNum(wire));
633
810
  knownFaces.add(f);
634
- return asHandle(f);
811
+ return asFace(f);
635
812
  },
636
813
  makeCompound(shapesList) {
637
814
  const kids = arr(shapesList.map(asNum));
638
815
  // GOTCHA(2026-09-26):brepkit makeCompound 只接受 **solid** 句柄——把 face/wire
639
816
  // (sectionByPlane 截面面组)塞进去即抛 "invalid solid handle"。非实体子句柄时
640
- // 不调内核,改用高段虚拟句柄(0x80000000+),仅靠 knownCompounds 表分发
817
+ // 不调内核,改用高段虚拟句柄(0x20000000+,避开类型标签 bit26..28),仅靠 knownCompounds 表分发
641
818
  // meshShape/wireframe/getSubShapes/getBoundingBox,不触达内核。
642
819
  const hasNonSolid = kids.some((k) => knownFaces.has(k) || knownWires.has(k) || knownEdges.has(k) || knownCompounds.has(k));
643
820
  if (hasNonSolid) {
644
821
  virtualCompoundCounter++;
645
- const v = 0x80000000 + virtualCompoundCounter;
822
+ const v = 0x20000000 + virtualCompoundCounter;
646
823
  knownCompounds.set(v, kids);
647
824
  return asHandle(v);
648
825
  }
@@ -655,25 +832,26 @@ export async function createBrepkitPrimitives() {
655
832
  // 否则 profile(op) 带孔面送 meshShape 会落到 solid 路径崩溃。
656
833
  const f = kernel.addHolesToFace(asNum(face), Int32Array.from(holeWires.map(asNum)));
657
834
  knownFaces.add(f);
658
- return asHandle(f);
835
+ return asFace(f);
659
836
  },
660
837
  buildTriFace(a, b, c) {
661
838
  const f = kernel.makePolygon(flattenPoints([a, b, c]));
662
839
  knownFaces.add(f);
663
- return asHandle(f);
840
+ return asFace(f);
664
841
  },
665
842
  // ── 三角化(BREP→mesh 唯一出口) ──
666
843
  meshShape(shape, options) {
667
- const deflection = options?.linearDeflection ?? 0.1;
844
+ const deflection = options?.linearDeflection ?? DEFAULT_LINEAR_DEFLECTION.as(mm);
668
845
  const angular = options?.angularDeflection ?? 0.5;
669
846
  const s = asNum(shape);
670
- // 非实体句柄按已知集合静态分发(见文件头 GOTCHA:brepkit 无 shape-type 分类 API)。
847
+ // 非实体句柄按类型静态分发(标签优先,见文件头 GOTCHA:brepkit 无 shape-type 分类 API)。
671
848
  // 顺序:face → wire → edge → compound → solid。实体创建不入任何集合,天然落 solid 路径。
672
- if (knownFaces.has(s))
849
+ const kind = resolveKind(shape, s);
850
+ if (kind === KIND_FACE)
673
851
  return faceMesh(s, deflection, angular);
674
- if (knownWires.has(s))
852
+ if (kind === KIND_WIRE)
675
853
  return wireMesh(s, deflection);
676
- if (knownEdges.has(s))
854
+ if (kind === KIND_EDGE)
677
855
  return edgesToLineMesh([s], deflection);
678
856
  const compoundChildren = knownCompounds.get(s);
679
857
  if (compoundChildren)
@@ -682,17 +860,18 @@ export async function createBrepkitPrimitives() {
682
860
  return solidMesh(s, deflection, angular);
683
861
  },
684
862
  wireframe(shape, deflection) {
685
- const d = deflection ?? 0.1;
863
+ const d = deflection ?? DEFAULT_LINEAR_DEFLECTION.as(mm);
686
864
  const s = asNum(shape);
687
865
  // GOTCHA:meshEdgesAll 是 solid-only API——buildTopologyFromMesh 会对 face/wire/edge
688
866
  // 产物调 wireframe(...),传入非实体句柄即抛 "invalid solid handle"。按已知集合分发:
689
867
  // face -> getFaceEdges;wire -> getWireEdges;edge -> [self];否则 solid 走 meshEdgesAll。
690
868
  let edgeHandles = null;
691
- if (knownFaces.has(s))
869
+ const kind = resolveKind(shape, s);
870
+ if (kind === KIND_FACE)
692
871
  edgeHandles = arr(kernel.getFaceEdges(s));
693
- else if (knownWires.has(s))
872
+ else if (kind === KIND_WIRE)
694
873
  edgeHandles = arr(kernel.getWireEdges(s));
695
- else if (knownEdges.has(s))
874
+ else if (kind === KIND_EDGE)
696
875
  edgeHandles = [s];
697
876
  else if (knownCompounds.has(s)) {
698
877
  // compound(sectionByPlane 面组):逐子面收集边。
@@ -709,20 +888,22 @@ export async function createBrepkitPrimitives() {
709
888
  if (edgeHandles) {
710
889
  const nPts = Math.max(12, Math.round(4 / Math.max(d, 0.01)));
711
890
  const positions = [];
891
+ // offsets 为**浮点单位**(L1 契约:edgeGroups = [pointStart_float, pointCount_float, hash])
712
892
  const offsets = [0];
713
893
  for (const e of edgeHandles) {
714
894
  const pts = arr(kernel.tessellateEdge(e, nPts));
715
895
  for (const p of pts)
716
896
  positions.push(p);
717
- offsets.push(positions.length / 3);
897
+ offsets.push(positions.length);
718
898
  }
719
899
  const edgeGroups = new Int32Array(edgeHandles.length * 3);
720
900
  for (let i = 0; i < edgeHandles.length; i++) {
721
901
  edgeGroups[i * 3] = offsets[i] ?? 0;
722
- edgeGroups[i * 3 + 1] = (offsets[i + 1] ?? positions.length / 3) - (offsets[i] ?? 0);
723
- edgeGroups[i * 3 + 2] = i;
902
+ edgeGroups[i * 3 + 1] = (offsets[i + 1] ?? positions.length) - (offsets[i] ?? 0);
903
+ // hash 必须与 hashCode(edge, BREP_HASH_BOUND) 同源,否则 topologyExt 查不到折线。
904
+ edgeGroups[i * 3 + 2] = edgeFingerprint(kernel, edgeHandles[i], BREP_HASH_BOUND);
724
905
  }
725
- return { points: new Float32Array(positions), edgeGroups, pointCount: positions.length / 3, edgeCount: edgeHandles.length };
906
+ return { points: new Float32Array(positions), edgeGroups, pointCount: positions.length, edgeCount: edgeHandles.length };
726
907
  }
727
908
  // solid(或未登记句柄):走原 meshEdgesAll 路径。
728
909
  // JsEdgeLines:edgeCount/offsets/positions 均为属性(offsets/positions 为 TypedArray,2026-09-19 实测)
@@ -730,13 +911,25 @@ export async function createBrepkitPrimitives() {
730
911
  const edgeCount = Number(me.edgeCount);
731
912
  const offsets = arr(me.offsets);
732
913
  const positions = arr(me.positions);
914
+ // hash:meshEdgesAll 与 getSolidEdges 同序(同一次遍历),据此回填真实边指纹;
915
+ // 取不到(数量不符)时退化为序号——此时 topologyExt 查不到折线,曲面边走
916
+ // 曲线采样兜底(功能可用,只是丢掉内核线框数据)。
917
+ let solidEdges = null;
918
+ try {
919
+ const es = arr(kernel.getSolidEdges(s));
920
+ if (es.length === edgeCount)
921
+ solidEdges = es;
922
+ }
923
+ catch { /* 非实体 */ }
733
924
  const edgeGroups = new Int32Array(edgeCount * 3);
734
925
  for (let i = 0; i < edgeCount; i++) {
735
926
  const start = offsets[i] ?? 0;
736
- const count = (offsets[i + 1] ?? positions.length / 3) - start;
927
+ const count = (offsets[i + 1] ?? positions.length) - start;
737
928
  edgeGroups[i * 3] = start;
738
929
  edgeGroups[i * 3 + 1] = count;
739
- edgeGroups[i * 3 + 2] = i; // 线框 hash 用序号(wireframe 不参与跨操作溯源)
930
+ edgeGroups[i * 3 + 2] = solidEdges
931
+ ? edgeFingerprint(kernel, solidEdges[i], BREP_HASH_BOUND)
932
+ : i;
740
933
  }
741
934
  return { points: new Float32Array(positions), edgeGroups, pointCount: positions.length, edgeCount };
742
935
  },
@@ -749,49 +942,104 @@ export async function createBrepkitPrimitives() {
749
942
  // edge/vertex 查询时按句柄是否在 knownFaces 里分发到 face 级或 solid 级 API。
750
943
  getSubShapes(shape, type) {
751
944
  const s = asNum(shape);
945
+ // 出口一律按 **type 参数**打类型标签(`type` 就是调用方声明的子形类型,是权威来源;
946
+ // 不用内核反查——brepkit 无法反查类型,见文件头句柄桥接 GOTCHA)。
947
+ const tag = (list) => {
948
+ switch (type) {
949
+ case 'face': return list.map(asFace);
950
+ case 'edge': return list.map(asEdge);
951
+ case 'vertex': return list.map(asVertex);
952
+ case 'wire': return list.map(asWire);
953
+ default: return list.map(asHandle);
954
+ }
955
+ };
956
+ // ── A. 有类型标签:按标签分发,**不登记 knownEdges** ──
957
+ // GOTCHA(2026-10-01 实测回归):历史分支里 `es.forEach(e => knownEdges.add(e))` 会把
958
+ // 面/线的边号写进 knownEdges,而 knownEdges 的 key 是**裸句柄号**、会与后来新建的
959
+ // solid 号相撞 → resolveKind 把实体判成边 → getBoundingBox 返回边的退化 bbox
960
+ // (实测 8 个 op 的 AABB 全错)。标签句柄自带类型,登记已无必要,故这里一律不登记。
961
+ const tagged = kindOf(shape);
962
+ if (tagged === KIND_FACE) {
963
+ if (type === 'face')
964
+ return [asFace(s)];
965
+ if (type === 'edge')
966
+ return tag(arr(kernel.getFaceEdges(s)));
967
+ if (type === 'vertex')
968
+ return tag(arr(kernel.getFaceVertices(s)));
969
+ return [];
970
+ }
971
+ if (tagged === KIND_WIRE) {
972
+ if (type === 'edge')
973
+ return tag(arr(kernel.getWireEdges(s)));
974
+ return [];
975
+ }
976
+ if (tagged === KIND_EDGE) {
977
+ // input is itself an edge (wire.ts smooth path: interpolatePoints returns edge)
978
+ if (type === 'edge')
979
+ return [asEdge(s)];
980
+ return [];
981
+ }
982
+ // ── B. 无标签:逐字沿用历史集合分发(含 knownEdges 登记),行为与加标签前一致 ──
752
983
  // GOTCHA(2026-09-26 非实体句柄修复):buildTopologyFromMesh 会对**面产物**
753
984
  // 调 getSubShapes(face,'face')。brepkit 的 getSolidFaces/getShellFaces 是 solid-only,
754
985
  // 对面句柄一个抛 "invalid solid handle"、catch 里再抛 "invalid shell handle"——
755
- // 两跳都不被捕获即崩溃。brepkit 无 shape-type 分类 API,必须靠已知集合先分发:
756
- // 输入本身就是 knownFaces 里的一张面时,'face' 返回 [s],edge/vertex 走 face 级 API。
986
+ // 两跳都不被捕获即崩溃。无标签时靠已知集合先分发。
757
987
  if (knownFaces.has(s)) {
758
988
  if (type === 'face')
759
- return [asHandle(s)];
989
+ return [asFace(s)];
760
990
  if (type === 'edge') {
761
991
  const es = arr(kernel.getFaceEdges(s));
762
992
  es.forEach((e) => knownEdges.add(e));
763
- return es.map(asHandle);
993
+ return tag(es);
764
994
  }
765
995
  if (type === 'vertex')
766
- return arr(kernel.getFaceVertices(s)).map(asHandle);
996
+ return tag(arr(kernel.getFaceVertices(s)));
767
997
  return [];
768
998
  }
769
999
  if (knownWires.has(s)) {
770
1000
  if (type === 'edge') {
771
1001
  const es = arr(kernel.getWireEdges(s));
772
1002
  es.forEach((e) => knownEdges.add(e));
773
- return es.map(asHandle);
1003
+ return tag(es);
774
1004
  }
775
1005
  return [];
776
1006
  }
777
1007
  if (knownEdges.has(s)) {
778
- // input is itself an edge (wire.ts smooth path: interpolatePoints returns edge)
779
1008
  if (type === 'edge')
780
- return [asHandle(s)];
1009
+ return [asEdge(s)];
781
1010
  return [];
782
1011
  }
783
1012
  const compKids = knownCompounds.get(s);
784
1013
  if (compKids) {
785
1014
  // compound(sectionByPlane 面组 / makeCompound 多实体 / importStep 多实体):
786
- // 'face' 返回子面,'solid' 返回子实体,其余空。
1015
+ // 'face' 返回子面,'solid' 返回子实体,'edge' 返回各子句柄的边,其余空。
787
1016
  if (type === 'face')
788
- return compKids.filter((k) => knownFaces.has(k)).map(asHandle);
1017
+ return tag(compKids.filter((k) => knownFaces.has(k)));
789
1018
  if (type === 'solid')
790
- return compKids.filter((k) => !knownFaces.has(k) && !knownWires.has(k) && !knownEdges.has(k)).map(asHandle);
1019
+ return tag(compKids.filter((k) => !knownFaces.has(k) && !knownWires.has(k) && !knownEdges.has(k)));
1020
+ if (type === 'edge') {
1021
+ // 2026-10-02 补:此前 compound 的边查询落到 `return []`,会让归一化后的
1022
+ // getLength(compound) 静默变 0。按子句柄的已知类型分发,与 occt 的
1023
+ // getSubShapes(compound,'edge')(返回全部子边)对齐。
1024
+ const out = [];
1025
+ for (const kid of compKids) {
1026
+ if (knownFaces.has(kid))
1027
+ out.push(...arr(kernel.getFaceEdges(kid)));
1028
+ else if (knownWires.has(kid))
1029
+ out.push(...arr(kernel.getWireEdges(kid)));
1030
+ else if (knownEdges.has(kid))
1031
+ out.push(kid);
1032
+ else
1033
+ out.push(...arr(kernel.getSolidEdges(kid)));
1034
+ }
1035
+ return tag(out);
1036
+ }
791
1037
  return [];
792
1038
  }
793
1039
  let list;
794
- const isFaceLike = knownFaces.has(s) || derivedFaces.has(s);
1040
+ // 实体句柄(knownSolids)永远不是 face-like——`derivedFaces` 只装「枚举出来的面号」,
1041
+ // 与 solid 号共用一个裸数字空间,不排除会造成误判(见 knownSolids 注释)。
1042
+ const isFaceLike = knownFaces.has(s) || (derivedFaces.has(s) && !knownSolids.has(s));
795
1043
  if (type === 'face') {
796
1044
  // 输入可能是 solid 或 shell。枚举到的子面登记到 derivedFaces(不进 knownFaces,
797
1045
  // 避免与 solid 句柄撞号——见 derivedFaces 注释)。
@@ -829,18 +1077,19 @@ export async function createBrepkitPrimitives() {
829
1077
  else {
830
1078
  fail(`getSubShapes(${type}): unsupported sub-shape type`);
831
1079
  }
832
- return list.map(asHandle);
1080
+ return tag(list);
833
1081
  },
834
1082
  subShapeHashes(shape, type, hashUpperBound) {
835
1083
  if (type !== 'face')
836
1084
  fail(`subShapeHashes(${type}): only 'face' is supported by brepkit`);
837
1085
  const s = asNum(shape);
838
1086
  // GOTCHA:buildTopologyFromMesh 会对面产物调 subShapeHashes(face,'face');
839
- // getSolidFaces 是 solid-only,对面句柄崩溃。输入本身是已知面时,哈希即其自身指纹。
840
- if (knownFaces.has(s))
1087
+ // getSolidFaces 是 solid-only,对面句柄崩溃。输入本身是面时,哈希即其自身指纹。
1088
+ const kind = resolveKind(shape, s);
1089
+ if (kind === KIND_FACE)
841
1090
  return [faceFingerprint(kernel, s, hashUpperBound)];
842
1091
  // wire/edge 无面;compound(sectionByPlane 面组)的子句柄即面。
843
- if (knownWires.has(s) || knownEdges.has(s))
1092
+ if (kind === KIND_WIRE || kind === KIND_EDGE)
844
1093
  return [];
845
1094
  const compKids = knownCompounds.get(s);
846
1095
  if (compKids) {
@@ -856,9 +1105,25 @@ export async function createBrepkitPrimitives() {
856
1105
  return hashes;
857
1106
  },
858
1107
  hashCode(shape, upperBound) {
859
- return faceFingerprint(kernel, asNum(shape), upperBound);
1108
+ // 类型由句柄标签决定(见文件头句柄桥接 GOTCHA):brepkit 无法反查句柄类型,
1109
+ // 而面指纹与边指纹必须落在**互不相交**的取值域,否则 edgeGroups 的 hash 会
1110
+ // 命中面的指纹(实测:box 的 edge 0..5 句柄号与 face 0..5 相同)。
1111
+ // 生产端(faceMesh/solidMesh/compoundMesh → faceFingerprint;wireframe →
1112
+ // edgeFingerprint)与消费端(本函数)用同一组函数、同一个上界,查表才命中。
1113
+ const raw = asNum(shape);
1114
+ return kindOf(shape) === KIND_EDGE
1115
+ ? edgeFingerprint(kernel, raw, upperBound)
1116
+ : faceFingerprint(kernel, raw, upperBound);
1117
+ },
1118
+ isSame(a, b) {
1119
+ // 裸句柄号相同还不够:brepkit 的 face 3 与 edge 3 是**两个不同的形**。
1120
+ // 两侧都带标签时必须同类型;任一侧无标签(历史调用方)时退回纯数字比较,
1121
+ // 保证加标签不改变既有行为。
1122
+ if (asNum(a) !== asNum(b))
1123
+ return false;
1124
+ const ka = kindOf(a), kb = kindOf(b);
1125
+ return ka === kb || ka === KIND_SOLID || kb === KIND_SOLID;
860
1126
  },
861
- isSame(a, b) { return asNum(a) === asNum(b); },
862
1127
  isSolid(shape) {
863
1128
  try {
864
1129
  return arr(kernel.getSolidFaces(asNum(shape))).length > 0;
@@ -890,10 +1155,10 @@ export async function createBrepkitPrimitives() {
890
1155
  return kernel.edgeToFaceMap(asNum(shape));
891
1156
  },
892
1157
  adjacentFaces(shape, face) {
893
- return arr(kernel.adjacentFaces(asNum(shape), asNum(face))).map(asHandle);
1158
+ return arr(kernel.adjacentFaces(asNum(shape), asNum(face))).map(asFace);
894
1159
  },
895
1160
  sharedEdges(a, b) {
896
- return arr(kernel.sharedEdges(asNum(a), asNum(b))).map(asHandle);
1161
+ return arr(kernel.sharedEdges(asNum(a), asNum(b))).map(asEdge);
897
1162
  },
898
1163
  // ── 几何求值 ──
899
1164
  curveType(edge) { return String(kernel.getEdgeCurveType(asNum(edge))); },
@@ -966,21 +1231,42 @@ export async function createBrepkitPrimitives() {
966
1231
  getNurbsCurveData(edge) {
967
1232
  try {
968
1233
  const p = JSON.parse(kernel.getNurbsCurveData(asNum(edge)));
969
- if (p && typeof p.degree === 'number')
970
- return { degree: p.degree, periodic: !!p.periodic, rational: !!p.rational };
1234
+ if (p && typeof p.degree === 'number') {
1235
+ return {
1236
+ degree: p.degree,
1237
+ periodic: !!p.periodic,
1238
+ rational: !!p.rational,
1239
+ knots: (p.knots ?? []).map(Number),
1240
+ multiplicities: (p.multiplicities ?? []).map(Number),
1241
+ poles: (p.poles ?? []).map(Number),
1242
+ weights: (p.weights ?? []).map(Number),
1243
+ };
1244
+ }
971
1245
  }
972
1246
  catch { /* 非 Nurbs 边 */ }
973
1247
  return null;
974
1248
  },
1249
+ curveSplit(edge, param) {
1250
+ // brepkit 原生返回 Uint32Array 两元素(occt 返回 [ShapeHandle, ShapeHandle])——
1251
+ // 适配器统一为二元组。两个碎片都必须登记进 knownEdges,否则下游 meshShape 分发不到。
1252
+ const parts = kernel.curveSplit(asNum(edge), param);
1253
+ if (parts.length !== 2)
1254
+ fail(`curveSplit: expected 2 edges, got ${parts.length}`);
1255
+ const a = parts[0];
1256
+ const b = parts[1];
1257
+ knownEdges.add(a);
1258
+ knownEdges.add(b);
1259
+ return [asEdge(a), asEdge(b)];
1260
+ },
975
1261
  interpolatePoints(points, degree) {
976
1262
  // GOTCHA:interpolatePoints 返回 **edge** 句柄(不是 wire)——wire.ts smooth 路径把它
977
1263
  // 直接当 wire 送 solidToShape。登记进 knownEdges,meshShape 才能走 tessellateEdge 路径。
978
1264
  const e = kernel.interpolatePoints(flattenPoints(points), degree);
979
1265
  knownEdges.add(e);
980
- return asHandle(e);
1266
+ return asEdge(e);
981
1267
  },
982
1268
  defeature(shape, faces) {
983
- return asHandle(kernel.defeature(asNum(shape), Uint32Array.from(faces.map(asNum))));
1269
+ return asHandle(markSolid(kernel.defeature(asNum(shape), Uint32Array.from(faces.map(asNum)))));
984
1270
  },
985
1271
  draft(shape, faces, pull, neutral, angleDeg) {
986
1272
  return asHandle(kernel.draft(asNum(shape), Uint32Array.from(faces.map(asNum)), pull.x, pull.y, pull.z, neutral.x, neutral.y, neutral.z, angleDeg));
@@ -989,10 +1275,10 @@ export async function createBrepkitPrimitives() {
989
1275
  // removeHolesFromFace 返回**新 face 句柄**(去掉内孔的同曲面 face),必须登记进 knownFaces。
990
1276
  const f = kernel.removeHolesFromFace(asNum(face));
991
1277
  knownFaces.add(f);
992
- return asHandle(f);
1278
+ return asFace(f);
993
1279
  },
994
1280
  reverseShape(shape) {
995
- return asHandle(kernel.reverseShape(asNum(shape)));
1281
+ return withKind(kindOf(shape), kernel.reverseShape(asNum(shape)));
996
1282
  },
997
1283
  projectEdges(shape, origin, direction, xAxis, hiddenLines, deflection) {
998
1284
  // brepkit 返回 JSON 字符串 {"visible":[[x,y,…]],"hidden":[[…]]};L1 契约是 unknown。
@@ -1052,11 +1338,12 @@ export async function createBrepkitPrimitives() {
1052
1338
  }
1053
1339
  // GOTCHA:kernel.boundingBox 是 solid-only。face/wire/edge 句柄必须走 tessellation 采样,
1054
1340
  // 否则 measure() 对 wire/profile 调 getBoundingBox 即崩 "invalid solid handle"。
1055
- if (knownFaces.has(s))
1341
+ const kind = resolveKind(shape, s);
1342
+ if (kind === KIND_FACE)
1056
1343
  return bboxFromPoints(arr(kernel.tessellateFace(s, 0.1, 1).positions));
1057
- if (knownWires.has(s))
1344
+ if (kind === KIND_WIRE)
1058
1345
  return bboxFromPoints(arr(kernel.getWireEdges(s)).flatMap((e) => arr(kernel.tessellateEdge(e, 16))));
1059
- if (knownEdges.has(s))
1346
+ if (kind === KIND_EDGE)
1060
1347
  return bboxFromPoints(arr(kernel.tessellateEdge(s, 16)));
1061
1348
  return solidBbox(s);
1062
1349
  },
@@ -1070,17 +1357,15 @@ export async function createBrepkitPrimitives() {
1070
1357
  return Number(kernel.surfaceArea(asNum(shape), 0.05));
1071
1358
  },
1072
1359
  getLength(shape) {
1073
- // 方言映射:L1 getLength ↔ brepkit edgeLength(edge) / wireLength(wire)。
1074
- // wire 与 edge 的入参差异在适配器内判别(D6):edgeLength 只吃 edge 句柄,
1075
- // 对 wire 会抛错;据此分发,两条路径都失败时如实抛出(不静默返回 0)。
1076
- const s = asNum(shape);
1077
- try {
1078
- kernel.getEdgeCurveType(s);
1079
- return Number(kernel.edgeLength(s));
1080
- }
1081
- catch {
1082
- return Number(kernel.wireLength(s));
1083
- }
1360
+ // 唯一 edge 弧长之和(与 occt 适配器同口径,D5)。
1361
+ // 旧实现用 `try { getEdgeCurveType } catch { wireLength }` 判别入参差异,但
1362
+ // getEdgeCurveType 对 solid / face / wire **也不抛错**,于是统一走 edgeLength,
1363
+ // 返回「首条边」长度——值随构建历史漂移(盒 20 vs 10;两边的 wire 真长 15 却给 20),
1364
+ // 且与 occt 的 Σ 面周长(280)不同口径。改为按 topo 去重枚举边后求和。
1365
+ let total = 0;
1366
+ for (const e of api.getSubShapes(shape, 'edge'))
1367
+ total += api.curveLength(e);
1368
+ return total;
1084
1369
  },
1085
1370
  // ── 校验与修复 ──
1086
1371
  // ⚠️ brepkit 的修复类内核函数全部是「**原地修改入参实体 + 返回修复计数**」,不是返回新句柄:
@@ -1111,12 +1396,12 @@ export async function createBrepkitPrimitives() {
1111
1396
  return asHandle(c);
1112
1397
  },
1113
1398
  fixFaceOrientations(shape) {
1114
- const c = kernel.copySolid(asNum(shape));
1399
+ const c = markSolid(kernel.copySolid(asNum(shape)));
1115
1400
  kernel.fixFaceOrientations(c);
1116
1401
  return asHandle(c);
1117
1402
  },
1118
1403
  removeDegenerateEdges(shape, tolerance) {
1119
- const c = kernel.copySolid(asNum(shape));
1404
+ const c = markSolid(kernel.copySolid(asNum(shape)));
1120
1405
  kernel.removeDegenerateEdges(c, tolerance ?? 1e-6);
1121
1406
  return asHandle(c);
1122
1407
  },
@@ -1128,7 +1413,10 @@ export async function createBrepkitPrimitives() {
1128
1413
  const solids = arr(kernel.importStep(bytes));
1129
1414
  if (solids.length === 0)
1130
1415
  fail('importStep: kernel returned no solid');
1131
- return asHandle(solids.length === 1 ? solids[0] : kernel.makeCompound(Uint32Array.from(solids)));
1416
+ const c = solids.length === 1 ? solids[0] : kernel.makeCompound(Uint32Array.from(solids));
1417
+ if (solids.length > 1)
1418
+ knownCompounds.set(c, solids);
1419
+ return asHandle(markSolid(c));
1132
1420
  },
1133
1421
  exportStep(shape) {
1134
1422
  // brepkit exportStep 返回 UTF-8 字节(Uint8Array),需解码为 STEP 文本(2026-09-19 实测)
@@ -1137,14 +1425,39 @@ export async function createBrepkitPrimitives() {
1137
1425
  },
1138
1426
  importStl(data) {
1139
1427
  const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : new Uint8Array(data);
1140
- return asHandle(kernel.importStl(bytes));
1428
+ return asHandle(markSolid(kernel.importStl(bytes)));
1141
1429
  },
1142
1430
  fromBREP(data) {
1143
1431
  // ⚠️ brepkit fromBREP 吃字符串(STEP 文本或 toBREP/toBrepJson 输出,内核自动判别),
1144
1432
  // 与 serializeSolid/deserializeSolid 的**二进制 arena** 是两套机制。
1145
1433
  // 旧实现误用 `deserializeSolid(atob(data))`:base64 解码破坏了 STEP 文本 → 往返必失败
1146
1434
  // (2026-09-25 实证:fromBREP(STEP)、fromBREP(toBREP)、fromBREP(toBrepJson) 均成功)。
1147
- return asHandle(kernel.fromBREP(data));
1435
+ return asHandle(markSolid(kernel.fromBREP(data)));
1436
+ },
1437
+ // ── 网格实体原语(方案 2026-10-01 §3.3;内核私有,不在 L1 契约面) ──
1438
+ // 规范构造顺序**不可交换**:importMesh → weld → unify。
1439
+ // 实测(brepkit-kernel/mesh-solid-topology.test.ts):先 unify 后 weld 会把实体做坏
1440
+ // (体积归零、validate 非 0)。本对象只暴露原语,顺序由 `normalizeMeshSolid` 驱动。
1441
+ meshSolid: {
1442
+ importMesh(positions, indices) {
1443
+ // 方言:importIndexedMesh 吃 **Float64Array**(坐标)+ **Uint32Array**(索引),
1444
+ // 返回裸 solid 号。逐三角形建面、顶点按坐标焊接、边不共享(不是真流形)。
1445
+ const coords = new Float64Array(positions.length);
1446
+ for (let i = 0; i < positions.length; i++)
1447
+ coords[i] = positions[i];
1448
+ return asHandle(markSolid(kernel.importIndexedMesh(coords, indices)));
1449
+ },
1450
+ weld(faces, tolerance) {
1451
+ // weldShellsAndFaces 吃**裸面号数组**(Uint32Array),返回新 solid 号;
1452
+ // 把重复边缝成共享边 → 真流形(实测 10mm 立方体 36 边 → 18 边、validate 0)。
1453
+ return asHandle(markSolid(kernel.weldShellsAndFaces(Uint32Array.from(faces.map((f) => asNum(f))), tolerance)));
1454
+ },
1455
+ unify(solid) {
1456
+ // GOTCHA:unifyFaces 是**原地修改**入参实体,返回值是**被合并掉的面数**
1457
+ // (不是新句柄)。写成 `const s = unify(...)` 会拿到一个落在合法句柄区间的
1458
+ // 数字,后续全部查询打在一个无关实体上且不报错。
1459
+ return Number(kernel.unifyFaces(asNum(solid)));
1460
+ },
1148
1461
  },
1149
1462
  // ── 面演化(L1 三员 + filletWithHistory,双方都有对齐实现) ──
1150
1463
  cutWithHistory(a, b, inputFaceHashes, hashUpperBound) {
@@ -1160,6 +1473,8 @@ export async function createBrepkitPrimitives() {
1160
1473
  return mapEvolution(kernel, raw, JSON.parse(raw).solid, inputFaceHashes, hashUpperBound);
1161
1474
  },
1162
1475
  };
1476
+ memoizedKernel = kernel;
1477
+ memoizedPrimitives = api;
1163
1478
  return api;
1164
1479
  }
1165
1480
  // ── 工具 ──
@@ -1204,6 +1519,9 @@ function vec3Of(p) {
1204
1519
  }
1205
1520
  /** 已初始化的 brepkit 内核单例(createBrepkitPrimitives 时赋值)。 */
1206
1521
  let liveKernel = null;
1522
+ /** 已构建的 L1 契约面对象(按内核实例 memo;见 createBrepkitPrimitives 的 GOTCHA)。 */
1523
+ let memoizedPrimitives = null;
1524
+ let memoizedKernel = null;
1207
1525
  /**
1208
1526
  * 原生 brepkit 内核面(D3):平台特定代码访问 brepkit 独有能力的唯一入口
1209
1527
  * (chamfer2d/chamferV2/filletV2/sketch* 族、serializeSolid、meshBoolean 等)。
@@ -1219,7 +1537,14 @@ export function getBrepkitKernel() {
1219
1537
  }
1220
1538
  return liveKernel;
1221
1539
  }
1222
- /** 释放内核单例(测试收尾用;宿主常驻不需调用)。 */
1540
+ /**
1541
+ * 释放内核单例(测试收尾用;宿主常驻不需调用)。
1542
+ *
1543
+ * GOTCHA(2026-10-01 修复):旧实现只 `free()` 内核、**不重置** brepkitWasm.ts 里
1544
+ * 缓存的 `initPromise`,也不丢 memo 化的原语对象 → 第二次 `createBrepkitPrimitives()`
1545
+ * 拿到**已释放**的内核,随后一切内核调用报 `null pointer passed to rust`
1546
+ * (实测:`BrepKernel.makeBox`)。现在三样一起清:内核、initPromise、memo 原语。
1547
+ */
1223
1548
  export function disposeBrepkit() {
1224
1549
  try {
1225
1550
  if (liveKernel?.free)
@@ -1227,5 +1552,8 @@ export function disposeBrepkit() {
1227
1552
  }
1228
1553
  catch { /* 已释放 */ }
1229
1554
  liveKernel = null;
1555
+ memoizedPrimitives = null;
1556
+ memoizedKernel = null;
1557
+ resetBrepkitWasm();
1230
1558
  }
1231
1559
  //# sourceMappingURL=brepkitKernel.js.map