@faicad/faijs 0.33.1 → 0.35.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/dist/api/api-namespace.d.ts +14 -14
  2. package/dist/api/api-namespace.d.ts.map +1 -1
  3. package/dist/api/api-namespace.js +40 -14
  4. package/dist/api/api-namespace.js.map +1 -1
  5. package/dist/api/appearance.d.ts +47 -6
  6. package/dist/api/appearance.d.ts.map +1 -1
  7. package/dist/api/appearance.js.map +1 -1
  8. package/dist/api/assembly/solve-tree.d.ts +1 -0
  9. package/dist/api/assembly/solve-tree.d.ts.map +1 -1
  10. package/dist/api/assembly/solve-tree.js +42 -17
  11. package/dist/api/assembly/solve-tree.js.map +1 -1
  12. package/dist/api/assembly/solve.d.ts +1 -0
  13. package/dist/api/assembly/solve.d.ts.map +1 -1
  14. package/dist/api/assembly/solve.js.map +1 -1
  15. package/dist/api/assembly/solvers/global-solver.d.ts.map +1 -1
  16. package/dist/api/assembly/solvers/global-solver.js +130 -23
  17. package/dist/api/assembly/solvers/global-solver.js.map +1 -1
  18. package/dist/api/assembly/solvers/pose-from-delta.d.ts +9 -0
  19. package/dist/api/assembly/solvers/pose-from-delta.d.ts.map +1 -1
  20. package/dist/api/assembly/solvers/pose-from-delta.js +23 -0
  21. package/dist/api/assembly/solvers/pose-from-delta.js.map +1 -1
  22. package/dist/api/assembly/solvers/types.d.ts +20 -2
  23. package/dist/api/assembly/solvers/types.d.ts.map +1 -1
  24. package/dist/api/assembly/solvers/types.js.map +1 -1
  25. package/dist/api/chamfer.d.ts +7 -4
  26. package/dist/api/chamfer.d.ts.map +1 -1
  27. package/dist/api/chamfer.js +57 -13
  28. package/dist/api/chamfer.js.map +1 -1
  29. package/dist/api/draft.d.ts +7 -4
  30. package/dist/api/draft.d.ts.map +1 -1
  31. package/dist/api/draft.js +11 -6
  32. package/dist/api/draft.js.map +1 -1
  33. package/dist/api/edge-ref.d.ts +4 -4
  34. package/dist/api/edge-ref.js +4 -4
  35. package/dist/api/edge-ref.js.map +1 -1
  36. package/dist/api/extrude.d.ts +10 -6
  37. package/dist/api/extrude.d.ts.map +1 -1
  38. package/dist/api/extrude.js +50 -25
  39. package/dist/api/extrude.js.map +1 -1
  40. package/dist/api/face-ref.d.ts +4 -4
  41. package/dist/api/face-ref.js +4 -4
  42. package/dist/api/face-ref.js.map +1 -1
  43. package/dist/api/feature-repair.d.ts +6 -3
  44. package/dist/api/feature-repair.d.ts.map +1 -1
  45. package/dist/api/feature-repair.js +12 -7
  46. package/dist/api/feature-repair.js.map +1 -1
  47. package/dist/api/fillet-variable.d.ts +6 -2
  48. package/dist/api/fillet-variable.d.ts.map +1 -1
  49. package/dist/api/fillet-variable.js +24 -6
  50. package/dist/api/fillet-variable.js.map +1 -1
  51. package/dist/api/fillet.d.ts +7 -4
  52. package/dist/api/fillet.d.ts.map +1 -1
  53. package/dist/api/fillet.js +45 -12
  54. package/dist/api/fillet.js.map +1 -1
  55. package/dist/api/helix.d.ts +13 -13
  56. package/dist/api/helix.d.ts.map +1 -1
  57. package/dist/api/helix.js +19 -24
  58. package/dist/api/helix.js.map +1 -1
  59. package/dist/api/import-brep.d.ts +10 -18
  60. package/dist/api/import-brep.d.ts.map +1 -1
  61. package/dist/api/import-brep.js +17 -25
  62. package/dist/api/import-brep.js.map +1 -1
  63. package/dist/api/import-step.d.ts +10 -15
  64. package/dist/api/import-step.d.ts.map +1 -1
  65. package/dist/api/import-step.js +24 -29
  66. package/dist/api/import-step.js.map +1 -1
  67. package/dist/api/index.js +2 -2
  68. package/dist/api/index.js.map +1 -1
  69. package/dist/api/internal/export-members.js.map +1 -1
  70. package/dist/api/loft-advanced.d.ts +4 -0
  71. package/dist/api/loft-advanced.d.ts.map +1 -1
  72. package/dist/api/loft-advanced.js +4 -0
  73. package/dist/api/loft-advanced.js.map +1 -1
  74. package/dist/api/loft.d.ts +5 -3
  75. package/dist/api/loft.d.ts.map +1 -1
  76. package/dist/api/loft.js +5 -3
  77. package/dist/api/loft.js.map +1 -1
  78. package/dist/api/meta.d.ts +42 -6
  79. package/dist/api/meta.d.ts.map +1 -1
  80. package/dist/api/meta.js.map +1 -1
  81. package/dist/api/punch-hole.d.ts +5 -2
  82. package/dist/api/punch-hole.d.ts.map +1 -1
  83. package/dist/api/punch-hole.js +4 -1
  84. package/dist/api/punch-hole.js.map +1 -1
  85. package/dist/api/replicate.d.ts +4 -5
  86. package/dist/api/replicate.d.ts.map +1 -1
  87. package/dist/api/replicate.js +4 -5
  88. package/dist/api/replicate.js.map +1 -1
  89. package/dist/api/selectors-to-refs.d.ts +83 -0
  90. package/dist/api/selectors-to-refs.d.ts.map +1 -0
  91. package/dist/api/selectors-to-refs.js +211 -0
  92. package/dist/api/selectors-to-refs.js.map +1 -0
  93. package/dist/api/shell.d.ts +17 -8
  94. package/dist/api/shell.d.ts.map +1 -1
  95. package/dist/api/shell.js +45 -15
  96. package/dist/api/shell.js.map +1 -1
  97. package/dist/api/sketch-on-face.d.ts +12 -6
  98. package/dist/api/sketch-on-face.d.ts.map +1 -1
  99. package/dist/api/sketch-on-face.js +31 -4
  100. package/dist/api/sketch-on-face.js.map +1 -1
  101. package/dist/api/sketch-on-plane.d.ts +3 -0
  102. package/dist/api/sketch-on-plane.d.ts.map +1 -1
  103. package/dist/api/sketch-on-plane.js +3 -0
  104. package/dist/api/sketch-on-plane.js.map +1 -1
  105. package/dist/api/sketch-on-surface.d.ts +4 -1
  106. package/dist/api/sketch-on-surface.d.ts.map +1 -1
  107. package/dist/api/sketch-on-surface.js +4 -1
  108. package/dist/api/sketch-on-surface.js.map +1 -1
  109. package/dist/api/view/view-projection.d.ts.map +1 -1
  110. package/dist/api/view/view-projection.js +9 -2
  111. package/dist/api/view/view-projection.js.map +1 -1
  112. package/dist/brep/brep-chain.js +2 -2
  113. package/dist/brep/brep-chain.js.map +1 -1
  114. package/dist/brep/brep-ops.d.ts +1 -1
  115. package/dist/brep/brep-ops.js +1 -1
  116. package/dist/brep/brep-ops.js.map +1 -1
  117. package/dist/brep/engine/adapters/brep-mock.d.ts.map +1 -1
  118. package/dist/brep/engine/adapters/brep-mock.js +45 -0
  119. package/dist/brep/engine/adapters/brep-mock.js.map +1 -1
  120. package/dist/brep/engine/primitives.d.ts +87 -3
  121. package/dist/brep/engine/primitives.d.ts.map +1 -1
  122. package/dist/brep/engine/primitives.js +1 -1
  123. package/dist/brep/engine/primitives.js.map +1 -1
  124. package/dist/brep/engine/registry.d.ts +33 -7
  125. package/dist/brep/engine/registry.d.ts.map +1 -1
  126. package/dist/brep/engine/registry.js +47 -16
  127. package/dist/brep/engine/registry.js.map +1 -1
  128. package/dist/brep/handle-bridge.d.ts +14 -15
  129. package/dist/brep/handle-bridge.d.ts.map +1 -1
  130. package/dist/brep/handle-bridge.js +35 -15
  131. package/dist/brep/handle-bridge.js.map +1 -1
  132. package/dist/brep/primitives-brep.js +2 -2
  133. package/dist/brep/primitives-brep.js.map +1 -1
  134. package/dist/brep/text/text-to-solid.d.ts +40 -3
  135. package/dist/brep/text/text-to-solid.d.ts.map +1 -1
  136. package/dist/brep/text/text-to-solid.js +104 -24
  137. package/dist/brep/text/text-to-solid.js.map +1 -1
  138. package/dist/brepkit-kernel/brepkitKernel.d.ts +2 -2
  139. package/dist/brepkit-kernel/brepkitKernel.d.ts.map +1 -1
  140. package/dist/brepkit-kernel/brepkitKernel.js +315 -36
  141. package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
  142. package/dist/brepkit-kernel/brepkitWasm.js +7 -7
  143. package/dist/brepkit-kernel/brepkitWasm.js.map +1 -1
  144. package/dist/brepkit-kernel/multi-version-test.d.ts +2 -2
  145. package/dist/brepkit-kernel/multi-version-test.d.ts.map +1 -1
  146. package/dist/brepkit-kernel/multi-version-test.js +6 -6
  147. package/dist/brepkit-kernel/multi-version-test.js.map +1 -1
  148. package/dist/browser.d.ts +2 -3
  149. package/dist/browser.d.ts.map +1 -1
  150. package/dist/browser.js +1 -2
  151. package/dist/browser.js.map +1 -1
  152. package/dist/cad-runtime/admit-compat-lib.d.ts +9 -1
  153. package/dist/cad-runtime/admit-compat-lib.d.ts.map +1 -1
  154. package/dist/cad-runtime/admit-compat-lib.js +11 -1
  155. package/dist/cad-runtime/admit-compat-lib.js.map +1 -1
  156. package/dist/cad-runtime/browser-lib-loader.d.ts +16 -11
  157. package/dist/cad-runtime/browser-lib-loader.d.ts.map +1 -1
  158. package/dist/cad-runtime/browser-lib-loader.js +16 -8
  159. package/dist/cad-runtime/browser-lib-loader.js.map +1 -1
  160. package/dist/cad-runtime/direct-executor.d.ts +62 -5
  161. package/dist/cad-runtime/direct-executor.d.ts.map +1 -1
  162. package/dist/cad-runtime/direct-executor.js +617 -42
  163. package/dist/cad-runtime/direct-executor.js.map +1 -1
  164. package/dist/cad-runtime/interp/closure.d.ts +3 -2
  165. package/dist/cad-runtime/interp/closure.d.ts.map +1 -1
  166. package/dist/cad-runtime/interp/closure.js.map +1 -1
  167. package/dist/cad-runtime/interp/env.d.ts +9 -6
  168. package/dist/cad-runtime/interp/env.d.ts.map +1 -1
  169. package/dist/cad-runtime/interp/env.js +4 -2
  170. package/dist/cad-runtime/interp/env.js.map +1 -1
  171. package/dist/cad-runtime/lib-decl.d.ts +121 -0
  172. package/dist/cad-runtime/lib-decl.d.ts.map +1 -0
  173. package/dist/cad-runtime/lib-decl.js +100 -0
  174. package/dist/cad-runtime/lib-decl.js.map +1 -0
  175. package/dist/cad-runtime/ports.d.ts +12 -0
  176. package/dist/cad-runtime/ports.d.ts.map +1 -1
  177. package/dist/cad-runtime/ports.js.map +1 -1
  178. package/dist/cad-runtime/runtime.d.ts +59 -5
  179. package/dist/cad-runtime/runtime.d.ts.map +1 -1
  180. package/dist/cad-runtime/runtime.js +237 -18
  181. package/dist/cad-runtime/runtime.js.map +1 -1
  182. package/dist/extra/browser.d.ts +9 -0
  183. package/dist/extra/browser.d.ts.map +1 -0
  184. package/dist/extra/browser.js +9 -0
  185. package/dist/extra/browser.js.map +1 -0
  186. package/dist/extra/editor-ops.d.ts +22 -0
  187. package/dist/extra/editor-ops.d.ts.map +1 -0
  188. package/dist/extra/editor-ops.js +21 -0
  189. package/dist/extra/editor-ops.js.map +1 -0
  190. package/dist/extra/extras/svg-extrude.d.ts +45 -0
  191. package/dist/extra/extras/svg-extrude.d.ts.map +1 -0
  192. package/dist/extra/extras/svg-extrude.js +110 -0
  193. package/dist/extra/extras/svg-extrude.js.map +1 -0
  194. package/dist/extra/extras/text-cjk.d.ts +36 -0
  195. package/dist/extra/extras/text-cjk.d.ts.map +1 -0
  196. package/dist/extra/extras/text-cjk.js +86 -0
  197. package/dist/extra/extras/text-cjk.js.map +1 -0
  198. package/dist/extra/extras/text-geometry.d.ts +69 -0
  199. package/dist/extra/extras/text-geometry.d.ts.map +1 -0
  200. package/dist/extra/extras/text-geometry.js +188 -0
  201. package/dist/extra/extras/text-geometry.js.map +1 -0
  202. package/dist/extra/index.d.ts +40 -0
  203. package/dist/extra/index.d.ts.map +1 -0
  204. package/dist/extra/index.js +40 -0
  205. package/dist/extra/index.js.map +1 -0
  206. package/dist/extra/mesh/drill-hole/DrillHoleCore.d.ts +94 -0
  207. package/dist/extra/mesh/drill-hole/DrillHoleCore.d.ts.map +1 -0
  208. package/dist/extra/mesh/drill-hole/DrillHoleCore.js +282 -0
  209. package/dist/extra/mesh/drill-hole/DrillHoleCore.js.map +1 -0
  210. package/dist/extra/mesh/drill-hole/drill-types.d.ts +34 -0
  211. package/dist/extra/mesh/drill-hole/drill-types.d.ts.map +1 -0
  212. package/dist/extra/mesh/drill-hole/drill-types.js +7 -0
  213. package/dist/extra/mesh/drill-hole/drill-types.js.map +1 -0
  214. package/dist/extra/mesh/engrave-decoration.d.ts +42 -0
  215. package/dist/extra/mesh/engrave-decoration.d.ts.map +1 -0
  216. package/dist/extra/mesh/engrave-decoration.js +39 -0
  217. package/dist/extra/mesh/engrave-decoration.js.map +1 -0
  218. package/dist/extra/mesh/fai_drill.d.ts +21 -0
  219. package/dist/extra/mesh/fai_drill.d.ts.map +1 -0
  220. package/dist/extra/mesh/fai_drill.js +89 -0
  221. package/dist/extra/mesh/fai_drill.js.map +1 -0
  222. package/dist/extra/mesh/fai_extrude.d.ts +24 -0
  223. package/dist/extra/mesh/fai_extrude.d.ts.map +1 -0
  224. package/dist/extra/mesh/fai_extrude.js +42 -0
  225. package/dist/extra/mesh/fai_extrude.js.map +1 -0
  226. package/dist/extra/mesh/fai_split.d.ts +164 -0
  227. package/dist/extra/mesh/fai_split.d.ts.map +1 -0
  228. package/dist/extra/mesh/fai_split.js +269 -0
  229. package/dist/extra/mesh/fai_split.js.map +1 -0
  230. package/dist/extra/mesh/primitives.d.ts +29 -0
  231. package/dist/extra/mesh/primitives.d.ts.map +1 -0
  232. package/dist/extra/mesh/primitives.js +41 -0
  233. package/dist/extra/mesh/primitives.js.map +1 -0
  234. package/dist/extra/op-names.d.ts +37 -0
  235. package/dist/extra/op-names.d.ts.map +1 -0
  236. package/dist/extra/op-names.js +46 -0
  237. package/dist/extra/op-names.js.map +1 -0
  238. package/dist/extra/ops/compound.d.ts +140 -0
  239. package/dist/extra/ops/compound.d.ts.map +1 -0
  240. package/dist/extra/ops/compound.js +167 -0
  241. package/dist/extra/ops/compound.js.map +1 -0
  242. package/dist/extra/ops/copy.d.ts +40 -0
  243. package/dist/extra/ops/copy.d.ts.map +1 -0
  244. package/dist/extra/ops/copy.js +83 -0
  245. package/dist/extra/ops/copy.js.map +1 -0
  246. package/dist/extra/ops/fai_drill.d.ts +51 -0
  247. package/dist/extra/ops/fai_drill.d.ts.map +1 -0
  248. package/dist/extra/ops/fai_drill.js +233 -0
  249. package/dist/extra/ops/fai_drill.js.map +1 -0
  250. package/dist/extra/ops/fai_extrude.d.ts +36 -0
  251. package/dist/extra/ops/fai_extrude.d.ts.map +1 -0
  252. package/dist/extra/ops/fai_extrude.js +84 -0
  253. package/dist/extra/ops/fai_extrude.js.map +1 -0
  254. package/dist/extra/ops/fai_split.d.ts +46 -0
  255. package/dist/extra/ops/fai_split.d.ts.map +1 -0
  256. package/dist/extra/ops/fai_split.js +235 -0
  257. package/dist/extra/ops/fai_split.js.map +1 -0
  258. package/dist/extra/ops/load.d.ts +49 -0
  259. package/dist/extra/ops/load.d.ts.map +1 -0
  260. package/dist/extra/ops/load.js +385 -0
  261. package/dist/extra/ops/load.js.map +1 -0
  262. package/dist/extra/ops/svg-extrude.d.ts +32 -0
  263. package/dist/extra/ops/svg-extrude.d.ts.map +1 -0
  264. package/dist/extra/ops/svg-extrude.js +74 -0
  265. package/dist/extra/ops/svg-extrude.js.map +1 -0
  266. package/dist/extra/ops/text.d.ts +34 -0
  267. package/dist/extra/ops/text.d.ts.map +1 -0
  268. package/dist/extra/ops/text.js +100 -0
  269. package/dist/extra/ops/text.js.map +1 -0
  270. package/dist/geometry2d/bridge/lift-on-plane.d.ts +8 -5
  271. package/dist/geometry2d/bridge/lift-on-plane.d.ts.map +1 -1
  272. package/dist/geometry2d/bridge/lift-on-plane.js +8 -5
  273. package/dist/geometry2d/bridge/lift-on-plane.js.map +1 -1
  274. package/dist/index.d.ts +6 -2
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +8 -2
  277. package/dist/index.js.map +1 -1
  278. package/dist/lang/symbol-table.d.ts +3 -3
  279. package/dist/lang/symbol-table.generated.d.ts +10 -0
  280. package/dist/lang/symbol-table.generated.d.ts.map +1 -1
  281. package/dist/lang/symbol-table.generated.js +10 -0
  282. package/dist/lang/symbol-table.generated.js.map +1 -1
  283. package/dist/lang/symbol-table.js +3 -3
  284. package/dist/lang/symbol-table.js.map +1 -1
  285. package/dist/math3d/index.d.ts +1 -1
  286. package/dist/math3d/index.js +1 -1
  287. package/dist/math3d/index.js.map +1 -1
  288. package/dist/mesh/engrave.d.ts +2 -3
  289. package/dist/mesh/engrave.d.ts.map +1 -1
  290. package/dist/mesh/engrave.js +4 -11
  291. package/dist/mesh/engrave.js.map +1 -1
  292. package/dist/mesh/index.d.ts +0 -2
  293. package/dist/mesh/index.d.ts.map +1 -1
  294. package/dist/mesh/index.js +0 -2
  295. package/dist/mesh/index.js.map +1 -1
  296. package/dist/mesh/primitives.d.ts +1 -1
  297. package/dist/mesh/primitives.js +1 -1
  298. package/dist/mesh/primitives.js.map +1 -1
  299. package/dist/node-host/cli.d.ts +20 -4
  300. package/dist/node-host/cli.d.ts.map +1 -1
  301. package/dist/node-host/cli.js +84 -35
  302. package/dist/node-host/cli.js.map +1 -1
  303. package/dist/node-host/execute-script.d.ts.map +1 -1
  304. package/dist/node-host/execute-script.js +3 -4
  305. package/dist/node-host/execute-script.js.map +1 -1
  306. package/dist/node-host/lib-package-json.d.ts +36 -0
  307. package/dist/node-host/lib-package-json.d.ts.map +1 -0
  308. package/dist/node-host/lib-package-json.js +73 -0
  309. package/dist/node-host/lib-package-json.js.map +1 -0
  310. package/dist/occt-kernel/occt-primitives.d.ts.map +1 -1
  311. package/dist/occt-kernel/occt-primitives.js +41 -0
  312. package/dist/occt-kernel/occt-primitives.js.map +1 -1
  313. package/dist/occt-kernel/occtKernel.d.ts +1 -1
  314. package/dist/occt-kernel/occtKernel.js +1 -1
  315. package/dist/occt-kernel/occtKernel.js.map +1 -1
  316. package/dist/runtime-state.d.ts +28 -1
  317. package/dist/runtime-state.d.ts.map +1 -1
  318. package/dist/runtime-state.js +14 -1
  319. package/dist/runtime-state.js.map +1 -1
  320. package/dist/sdk.d.ts +2 -2
  321. package/dist/sdk.d.ts.map +1 -1
  322. package/dist/sdk.js +11 -2
  323. package/dist/sdk.js.map +1 -1
  324. package/dist/sketch/bspline.d.ts +58 -0
  325. package/dist/sketch/bspline.d.ts.map +1 -0
  326. package/dist/sketch/bspline.js +104 -0
  327. package/dist/sketch/bspline.js.map +1 -0
  328. package/dist/sketch/canonical.d.ts +239 -0
  329. package/dist/sketch/canonical.d.ts.map +1 -0
  330. package/dist/sketch/canonical.js +17 -0
  331. package/dist/sketch/canonical.js.map +1 -0
  332. package/dist/sketch/contour.d.ts +43 -0
  333. package/dist/sketch/contour.d.ts.map +1 -0
  334. package/dist/sketch/contour.js +202 -0
  335. package/dist/sketch/contour.js.map +1 -0
  336. package/dist/sketch/dim-spec.d.ts +32 -0
  337. package/dist/sketch/dim-spec.d.ts.map +1 -0
  338. package/dist/sketch/dim-spec.js +112 -0
  339. package/dist/sketch/dim-spec.js.map +1 -0
  340. package/dist/sketch/faces.d.ts +45 -0
  341. package/dist/sketch/faces.d.ts.map +1 -0
  342. package/dist/sketch/faces.js +108 -0
  343. package/dist/sketch/faces.js.map +1 -0
  344. package/dist/sketch/fcstd-types.d.ts +194 -0
  345. package/dist/sketch/fcstd-types.d.ts.map +1 -0
  346. package/dist/sketch/fcstd-types.js +67 -0
  347. package/dist/sketch/fcstd-types.js.map +1 -0
  348. package/dist/sketch/index.d.ts +46 -0
  349. package/dist/sketch/index.d.ts.map +1 -0
  350. package/dist/sketch/index.js +39 -0
  351. package/dist/sketch/index.js.map +1 -0
  352. package/dist/sketch/node.d.ts +14 -0
  353. package/dist/sketch/node.d.ts.map +1 -0
  354. package/dist/sketch/node.js +43 -0
  355. package/dist/sketch/node.js.map +1 -0
  356. package/dist/sketch/op.d.ts +111 -0
  357. package/dist/sketch/op.d.ts.map +1 -0
  358. package/dist/sketch/op.js +185 -0
  359. package/dist/sketch/op.js.map +1 -0
  360. package/dist/sketch/planegcs-backend.d.ts +69 -0
  361. package/dist/sketch/planegcs-backend.d.ts.map +1 -0
  362. package/dist/sketch/planegcs-backend.js +812 -0
  363. package/dist/sketch/planegcs-backend.js.map +1 -0
  364. package/dist/sketch/project.d.ts +176 -0
  365. package/dist/sketch/project.d.ts.map +1 -0
  366. package/dist/sketch/project.js +656 -0
  367. package/dist/sketch/project.js.map +1 -0
  368. package/dist/sketch/shapes.d.ts +370 -0
  369. package/dist/sketch/shapes.d.ts.map +1 -0
  370. package/dist/sketch/shapes.js +959 -0
  371. package/dist/sketch/shapes.js.map +1 -0
  372. package/dist/sketch/solve.d.ts +21 -0
  373. package/dist/sketch/solve.d.ts.map +1 -0
  374. package/dist/sketch/solve.js +144 -0
  375. package/dist/sketch/solve.js.map +1 -0
  376. package/dist/sketch/solver-loader.d.ts +47 -0
  377. package/dist/sketch/solver-loader.d.ts.map +1 -0
  378. package/dist/sketch/solver-loader.js +77 -0
  379. package/dist/sketch/solver-loader.js.map +1 -0
  380. package/dist/sketch/solver.d.ts +63 -0
  381. package/dist/sketch/solver.d.ts.map +1 -0
  382. package/dist/sketch/solver.js +28 -0
  383. package/dist/sketch/solver.js.map +1 -0
  384. package/dist/sketch/verify.d.ts +53 -0
  385. package/dist/sketch/verify.d.ts.map +1 -0
  386. package/dist/sketch/verify.js +71 -0
  387. package/dist/sketch/verify.js.map +1 -0
  388. package/dist/weapp.d.ts +1 -1
  389. package/dist/weapp.d.ts.map +1 -1
  390. package/dist/weapp.js +1 -1
  391. package/dist/weapp.js.map +1 -1
  392. package/package.json +43 -11
@@ -0,0 +1,959 @@
1
+ /** 形状展开/写回失败。`code` 前缀在 message 里,宿主按既有习惯用正则匹配。 */
2
+ export class SketchShapeError extends Error {
3
+ code;
4
+ constructor(
5
+ /** `E_SKETCHC_*` 错误码。 */
6
+ code, detail) {
7
+ super(`${code}: ${detail}`);
8
+ this.code = code;
9
+ this.name = 'SketchShapeError';
10
+ }
11
+ }
12
+ /** 画笔图元的 kind(可串成链、可被 `close` 闭合)。 */
13
+ const PEN_KINDS = ['line', 'arc', 'bspline'];
14
+ // ────────────────────────────────────────────────────────────────────────────
15
+ // tag 分配
16
+ // ────────────────────────────────────────────────────────────────────────────
17
+ const TAG_RE = /^g(\d+)(?:\.\d+)?$/;
18
+ /** 从一组 tag 里取出 `g<N>` 的最大编号(不认识的名字返回 0)。 */
19
+ function maxTagNumber(tags) {
20
+ let max = 0;
21
+ for (const tag of tags) {
22
+ const m = TAG_RE.exec(tag ?? '');
23
+ if (m)
24
+ max = Math.max(max, Number(m[1]));
25
+ }
26
+ return max;
27
+ }
28
+ /**
29
+ * 给一个新形状分配名字:`g<N>`,N = 既有最大编号 + 1(不复用已删除的编号)。
30
+ *
31
+ * @param shapes - 当前草稿里的全部形状(含 auto 分配过的名字)。
32
+ * @returns 未被占用的 `g<N>`。
33
+ */
34
+ export function nextShapeTag(shapes) {
35
+ const used = new Set();
36
+ for (const s of shapes) {
37
+ if ('tag' in s && s.tag !== undefined)
38
+ used.add(s.tag);
39
+ }
40
+ let next = maxTagNumber(used) + 1;
41
+ while (used.has(`g${next}`))
42
+ next++;
43
+ return `g${next}`;
44
+ }
45
+ /** 罐头形状的派生 geom tag:`<形状名>.<下标>`。 */
46
+ function derivedTag(shapeTag, index) {
47
+ return `${shapeTag}.${index}`;
48
+ }
49
+ // ────────────────────────────────────────────────────────────────────────────
50
+ // 形状轮廓数学(罐头形状的唯一实现;faijs-cadquery 也调这里,不许再抄一份)
51
+ // ────────────────────────────────────────────────────────────────────────────
52
+ /** 把局部坐标点平移到形状中心。 */
53
+ function place(p, cx, cy) {
54
+ return [p[0] + cx, p[1] + cy];
55
+ }
56
+ /**
57
+ * 矩形的四个角点(局部 + 中心平移后),顺序 `bottom-left → bottom-right →
58
+ * top-right → top-left`(与 3d_editor 既有矩形命令的边顺序一致)。
59
+ *
60
+ * @param w - 宽(沿 +X)。
61
+ * @param d - 高(沿 +Y)。
62
+ * @param cx - 中心 x(缺省 0)。
63
+ * @param cy - 中心 y(缺省 0)。
64
+ * @returns 四个角点。
65
+ */
66
+ export function rectCorners(w, d, cx = 0, cy = 0) {
67
+ return [
68
+ place([-w / 2, -d / 2], cx, cy),
69
+ place([w / 2, -d / 2], cx, cy),
70
+ place([w / 2, d / 2], cx, cy),
71
+ place([-w / 2, d / 2], cx, cy),
72
+ ];
73
+ }
74
+ /**
75
+ * 正多边形顶点。**第一个顶点在 `angle` 方向**,随后逆时针均匀分布。
76
+ *
77
+ * 宿主约定由调用方给:`expandShapes` 用 (angle, ccw=true);CadQuery 的
78
+ * `regularPolygon` 是"首顶点在 +Y、顺时针",它自己传 `(π/2, false)`——
79
+ * 公式只在这里写一次,约定由调用方显式声明。
80
+ *
81
+ * @param r - 外接圆半径。
82
+ * @param n - 边数(≥3)。
83
+ * @param angle - 首顶点极角(弧度)。
84
+ * @param ccw - 是否逆时针排列(缺省 true)。
85
+ * @returns n 个顶点(不重复首点)。
86
+ */
87
+ export function regularPolygonVertices(r, n, angle = 0, ccw = true) {
88
+ const out = [];
89
+ const dir = ccw ? 1 : -1;
90
+ for (let i = 0; i < n; i++) {
91
+ const a = angle + (dir * i * 2 * Math.PI) / n;
92
+ out.push([r * Math.cos(a), r * Math.sin(a)]);
93
+ }
94
+ return out;
95
+ }
96
+ /**
97
+ * 长圆槽(stadium):两条直边在 `y = ±d/2` 上、沿 X 跨 `w`,两端各一个半径
98
+ * `d/2` 的半圆,圆心在 `(±w/2, 0)`。**总长 = w + d,总高 = d**(CadQuery
99
+ * `Sketch.slot(w, h)` 的语义,`d` 即它的 `h`)。
100
+ *
101
+ * @param w - 直边长度(两圆心距)。
102
+ * @param d - 槽宽(= 端部半圆直径)。
103
+ * @param cx - 中心 x。
104
+ * @param cy - 中心 y。
105
+ * @returns 两条直边(左→右 的底边、右→左 的顶边)与两个半圆(圆心、半径、起止角)。
106
+ */
107
+ export function slotOutline(w, d, cx = 0, cy = 0) {
108
+ const r = d / 2;
109
+ return {
110
+ bottom: [place([-w / 2, -r], cx, cy), place([w / 2, -r], cx, cy)],
111
+ top: [place([w / 2, r], cx, cy), place([-w / 2, r], cx, cy)],
112
+ // 右端:-90° → 90°(逆时针,经过 +X 侧的最远点 (w/2 + r, 0))
113
+ rightArc: { c: place([w / 2, 0], cx, cy), r, a0: -Math.PI / 2, a1: Math.PI / 2 },
114
+ // 左端:90° → 270°(逆时针,经过 -X 侧)
115
+ leftArc: { c: place([-w / 2, 0], cx, cy), r, a0: Math.PI / 2, a1: (3 * Math.PI) / 2 },
116
+ };
117
+ }
118
+ /**
119
+ * 梯形四角(底边在下、居中):底宽 `w`、高 `h`,左右底角 `a1`/`a2`(**弧度**)。
120
+ *
121
+ * 与 CadQuery `Sketch.trapezoid(w, h, a1, a2)` 同几何(它在边界把度换成弧度):
122
+ * 顶点按 `bottom-left → bottom-right → top-right → top-left` 排列(逆时针)。
123
+ *
124
+ * @param w - 底宽。
125
+ * @param h - 高。
126
+ * @param a1 - 左下底角(弧度)。
127
+ * @param a2 - 右下底角(弧度)。
128
+ * @param cx - 中心 x。
129
+ * @param cy - 中心 y。
130
+ * @returns 四个角点。
131
+ */
132
+ export function trapezoidCorners(w, h, a1, a2, cx = 0, cy = 0) {
133
+ const t1 = h / Math.tan(a1);
134
+ const t2 = h / Math.tan(a2);
135
+ return [
136
+ place([-w / 2, -h / 2], cx, cy),
137
+ place([w / 2, -h / 2], cx, cy),
138
+ place([w / 2 - t2, h / 2], cx, cy),
139
+ place([-w / 2 + t1, h / 2], cx, cy),
140
+ ];
141
+ }
142
+ // ────────────────────────────────────────────────────────────────────────────
143
+ // 参数校验
144
+ // ────────────────────────────────────────────────────────────────────────────
145
+ function badParam(detail) {
146
+ throw new SketchShapeError('E_SKETCHC_SHAPE_BAD_PARAM', detail);
147
+ }
148
+ function positive(value, what) {
149
+ if (!Number.isFinite(value) || value <= 0)
150
+ badParam(`${what} must be a positive number, got ${value}`);
151
+ return value;
152
+ }
153
+ /**
154
+ * 形状是否声明了"区域语义"(能当孔)。单条 line/arc/bspline/point 不构成区域:
155
+ * 它们的孔身份(如果有)由闭合链的轮廓包含关系决定,不由形状本身声明。
156
+ */
157
+ function regionSemantics(shape) {
158
+ return shape.kind !== 'line' && shape.kind !== 'arc' && shape.kind !== 'bspline'
159
+ && shape.kind !== 'point' && shape.kind !== 'close';
160
+ }
161
+ /** 校验 mode/construction 组合,返回该形状是否为构造几何。 */
162
+ function resolveFlags(shape) {
163
+ const mode = ('mode' in shape ? shape.mode : undefined) ?? 'a';
164
+ if (mode === 'i' || mode === 'r') {
165
+ throw new SketchShapeError('E_SKETCHC_UNSUPPORTED_MODE', `mode "${mode}" on ${shape.kind} is not supported in this version (add/subtract/construction only)`);
166
+ }
167
+ if (mode === 's' && !regionSemantics(shape)) {
168
+ throw new SketchShapeError('E_SKETCHC_SHAPE_MODE_NOT_APPLICABLE', `${shape.kind} is a single edge, not a region — "subtract" cannot apply ` +
169
+ '(a closed pen chain becomes a hole by contour containment instead)');
170
+ }
171
+ const construction = ('construction' in shape && shape.construction === true) || mode === 'c';
172
+ return { construction, mode };
173
+ }
174
+ function line(a, b, tag, construction) {
175
+ return {
176
+ tag, kind: 'line', x1: a[0], y1: a[1], x2: b[0], y2: b[1],
177
+ ...(construction ? { construction: true } : {}),
178
+ };
179
+ }
180
+ /** 闭合多边形(若干 line 首尾相接)→ n 条线 + n 个角重合 + (n-1) 条等长。 */
181
+ function closedPolylineGeoms(corners, tags, construction) {
182
+ const geoms = corners.map((p, i) => line(p, corners[(i + 1) % corners.length], tags[i], construction));
183
+ const constraints = corners.map((_, i) => ({
184
+ kind: 'coincident',
185
+ a: { tag: tags[i], at: 'end' },
186
+ b: { tag: tags[(i + 1) % corners.length], at: 'start' },
187
+ }));
188
+ for (let i = 1; i < corners.length; i++) {
189
+ constraints.push({ kind: 'equal', a: { tag: tags[0] }, b: { tag: tags[i] } });
190
+ }
191
+ return { geoms, constraints };
192
+ }
193
+ /**
194
+ * 展开一个形状。
195
+ *
196
+ * @param shape - 语义图元。
197
+ * @param tags - 该形状可用的 tag(罐头形状多个、画笔图元一个)。
198
+ * @param construction - 是否打构造标记(由 `resolveFlags` 统一裁定)。
199
+ * @returns 几何与形状自带约束。
200
+ */
201
+ function expandOne(shape, tags, construction) {
202
+ const cxOf = (s) => s.cx ?? 0;
203
+ const cyOf = (s) => s.cy ?? 0;
204
+ switch (shape.kind) {
205
+ case 'rect': {
206
+ positive(shape.w, 'rect.w');
207
+ positive(shape.d, 'rect.d');
208
+ const c = rectCorners(shape.w, shape.d, cxOf(shape), cyOf(shape));
209
+ const { geoms, constraints } = closedPolylineGeoms(c, tags, construction);
210
+ // 形状约束按 3d_editor 既有矩形命令的**同一顺序**:底/顶水平、右/左垂直。
211
+ constraints.splice(4, 4, { kind: 'horizontal', of: { tag: tags[0] } }, // 底
212
+ { kind: 'horizontal', of: { tag: tags[2] } }, // 顶
213
+ { kind: 'vertical', of: { tag: tags[1] } }, // 右
214
+ { kind: 'vertical', of: { tag: tags[3] } });
215
+ return { geoms, constraints };
216
+ }
217
+ case 'roundedRect': {
218
+ positive(shape.w, 'roundedRect.w');
219
+ positive(shape.d, 'roundedRect.d');
220
+ positive(shape.r, 'roundedRect.r');
221
+ if (shape.r * 2 > Math.min(shape.w, shape.d)) {
222
+ badParam(`roundedRect.r=${shape.r} does not fit in ${shape.w}x${shape.d}`);
223
+ }
224
+ const cx = cxOf(shape);
225
+ const cy = cyOf(shape);
226
+ const { w, d, r } = shape;
227
+ const hw = w / 2;
228
+ const hd = d / 2;
229
+ // 顺序:底边、右下弧、右边、右上弧、顶边、左上弧、左边、左下弧。
230
+ const geoms = [
231
+ line(place([-hw + r, -hd], cx, cy), place([hw - r, -hd], cx, cy), tags[0], construction),
232
+ {
233
+ tag: tags[1], kind: 'arc', cx: cx + hw - r, cy: cy - hd + r, r,
234
+ a0: -Math.PI / 2, a1: 0, ccw: true, ...(construction ? { construction: true } : {}),
235
+ },
236
+ line(place([hw, -hd + r], cx, cy), place([hw, hd - r], cx, cy), tags[2], construction),
237
+ {
238
+ tag: tags[3], kind: 'arc', cx: cx + hw - r, cy: cy + hd - r, r,
239
+ a0: 0, a1: Math.PI / 2, ccw: true, ...(construction ? { construction: true } : {}),
240
+ },
241
+ line(place([hw - r, hd], cx, cy), place([-hw + r, hd], cx, cy), tags[4], construction),
242
+ {
243
+ tag: tags[5], kind: 'arc', cx: cx - hw + r, cy: cy + hd - r, r,
244
+ a0: Math.PI / 2, a1: Math.PI, ccw: true, ...(construction ? { construction: true } : {}),
245
+ },
246
+ line(place([-hw, hd - r], cx, cy), place([-hw, -hd + r], cx, cy), tags[6], construction),
247
+ {
248
+ tag: tags[7], kind: 'arc', cx: cx - hw + r, cy: cy - hd + r, r,
249
+ a0: Math.PI, a1: (3 * Math.PI) / 2, ccw: true, ...(construction ? { construction: true } : {}),
250
+ },
251
+ ];
252
+ const constraints = [];
253
+ for (let i = 0; i < geoms.length; i++) {
254
+ constraints.push({
255
+ kind: 'coincident',
256
+ a: { tag: tags[i], at: 'end' },
257
+ b: { tag: tags[(i + 1) % geoms.length], at: 'start' },
258
+ });
259
+ }
260
+ constraints.push({ kind: 'horizontal', of: { tag: tags[0] } });
261
+ constraints.push({ kind: 'horizontal', of: { tag: tags[4] } });
262
+ constraints.push({ kind: 'vertical', of: { tag: tags[2] } });
263
+ constraints.push({ kind: 'vertical', of: { tag: tags[6] } });
264
+ // 圆角半径:同 slot 的缺口说明——弧上的 radius 约束当前不生效(后端无 arc_rules),
265
+ // 圆角半径靠形状参数(初值)+ UI 写回把守。
266
+ for (const t of [tags[1], tags[3], tags[5], tags[7]]) {
267
+ constraints.push({ kind: 'radius', of: { tag: t }, value: r });
268
+ }
269
+ return { geoms, constraints };
270
+ }
271
+ case 'circle': {
272
+ positive(shape.r, 'circle.r');
273
+ return {
274
+ geoms: [{
275
+ tag: tags[0], kind: 'circle', cx: cxOf(shape), cy: cyOf(shape), r: shape.r,
276
+ ...(construction ? { construction: true } : {}),
277
+ }],
278
+ constraints: [],
279
+ };
280
+ }
281
+ case 'ellipse': {
282
+ positive(shape.rx, 'ellipse.rx');
283
+ positive(shape.ry, 'ellipse.ry');
284
+ const angle = shape.angle ?? 0;
285
+ return {
286
+ geoms: [{
287
+ tag: tags[0], kind: 'ellipse', cx: cxOf(shape), cy: cyOf(shape),
288
+ rx: shape.rx, ry: shape.ry, ...(angle !== 0 ? { angle } : {}),
289
+ ...(construction ? { construction: true } : {}),
290
+ }],
291
+ constraints: [],
292
+ };
293
+ }
294
+ case 'polygon': {
295
+ if (!Number.isInteger(shape.n) || shape.n < 3)
296
+ badParam(`polygon.n must be an integer ≥ 3, got ${shape.n}`);
297
+ positive(shape.r, 'polygon.r');
298
+ const cx = cxOf(shape);
299
+ const cy = cyOf(shape);
300
+ const verts = regularPolygonVertices(shape.r, shape.n, shape.angle ?? 0)
301
+ .map((p) => place(p, cx, cy));
302
+ return closedPolylineGeoms(verts, tags, construction);
303
+ }
304
+ case 'slot': {
305
+ positive(shape.w, 'slot.w');
306
+ positive(shape.d, 'slot.d');
307
+ const o = slotOutline(shape.w, shape.d, cxOf(shape), cyOf(shape));
308
+ const geoms = [
309
+ line(o.bottom[0], o.bottom[1], tags[0], construction),
310
+ {
311
+ tag: tags[1], kind: 'arc', cx: o.rightArc.c[0], cy: o.rightArc.c[1], r: o.rightArc.r,
312
+ a0: o.rightArc.a0, a1: o.rightArc.a1, ccw: true, ...(construction ? { construction: true } : {}),
313
+ },
314
+ line(o.top[0], o.top[1], tags[2], construction),
315
+ {
316
+ tag: tags[3], kind: 'arc', cx: o.leftArc.c[0], cy: o.leftArc.c[1], r: o.leftArc.r,
317
+ a0: o.leftArc.a0, a1: o.leftArc.a1, ccw: true, ...(construction ? { construction: true } : {}),
318
+ },
319
+ ];
320
+ const constraints = [];
321
+ for (let i = 0; i < 4; i++) {
322
+ constraints.push({
323
+ kind: 'coincident',
324
+ a: { tag: tags[i], at: 'end' },
325
+ b: { tag: tags[(i + 1) % 4], at: 'start' },
326
+ });
327
+ }
328
+ constraints.push({ kind: 'horizontal', of: { tag: tags[0] } });
329
+ constraints.push({ kind: 'horizontal', of: { tag: tags[2] } });
330
+ // ⚠️ 缺口(2026-10-03 实测):**圆弧上的 `radius` 约束在 faijs 的 planegcs 后端
331
+ // 里当前无效**——后端没 push `arc_rules`,弧的 radius/角度是只写参数,而回读
332
+ // 半径是按 |start − centre| 算的(见 planegcs-backend.ts 的 GOTCHA、
333
+ // shapes-solve.test.ts 的"缺口登记")。这两条照样发出:后端一补 arc_rules 就
334
+ // 立刻生效;在此之前 slot 的端半径由**形状参数(初值)+ UI 写回**把守——求解
335
+ // 不会主动破坏它,但别的约束把它推歪时也拉不回来(唯一实测过的修法会让
336
+ // slot + 调用方约束被 GCS 误判成 conflicting,所以先不修)。
337
+ constraints.push({ kind: 'radius', of: { tag: tags[1] }, value: shape.d / 2 });
338
+ constraints.push({ kind: 'radius', of: { tag: tags[3] }, value: shape.d / 2 });
339
+ return { geoms, constraints };
340
+ }
341
+ case 'trapezoid': {
342
+ positive(shape.w, 'trapezoid.w');
343
+ positive(shape.h, 'trapezoid.h');
344
+ const a2 = shape.a2 ?? shape.a1;
345
+ for (const [name, a] of [['a1', shape.a1], ['a2', a2]]) {
346
+ if (!Number.isFinite(a) || a <= 0 || a >= Math.PI) {
347
+ badParam(`trapezoid.${name} must be in (0, π) radians, got ${a}`);
348
+ }
349
+ }
350
+ const c = trapezoidCorners(shape.w, shape.h, shape.a1, a2, cxOf(shape), cyOf(shape));
351
+ const geoms = c.map((p, i) => line(p, c[(i + 1) % 4], tags[i], construction));
352
+ const constraints = [];
353
+ for (let i = 0; i < 4; i++) {
354
+ constraints.push({
355
+ kind: 'coincident',
356
+ a: { tag: tags[i], at: 'end' },
357
+ b: { tag: tags[(i + 1) % 4], at: 'start' },
358
+ });
359
+ }
360
+ constraints.push({ kind: 'horizontal', of: { tag: tags[0] } });
361
+ // 两个底角:planegcs 的 l2l_angle_pppp 值是 **dir(b) − dir(a) 的有向差**
362
+ // (实测,见 shapes-solve.test.ts 的探针;不是"两线夹角取锐角"),所以每个角
363
+ // 都要用 `at` 把两条线的方向挑出来,值正好等于内角:
364
+ // 左下角 a1:底边正向(+X) → 左腰向上(几何按 tl→bl 存,取 at:'end' 反向)
365
+ // 右下角 a2:右腰向上(几何按 br→tr 存,正向即是向上)→ 底边反向(−X)
366
+ constraints.push({ kind: 'angle', a: { tag: tags[0], at: 'start' }, b: { tag: tags[3], at: 'end' }, value: shape.a1 });
367
+ constraints.push({ kind: 'angle', a: { tag: tags[1], at: 'start' }, b: { tag: tags[0], at: 'end' }, value: a2 });
368
+ return { geoms, constraints };
369
+ }
370
+ case 'line':
371
+ return {
372
+ geoms: [line(shape.from, shape.to, tags[0], construction)],
373
+ constraints: [],
374
+ };
375
+ case 'arc': {
376
+ positive(shape.r, 'arc.r');
377
+ return {
378
+ geoms: [{
379
+ tag: tags[0], kind: 'arc', cx: shape.cx, cy: shape.cy, r: shape.r,
380
+ a0: shape.a0, a1: shape.a1, ...(shape.ccw !== undefined ? { ccw: shape.ccw } : {}),
381
+ ...(construction ? { construction: true } : {}),
382
+ }],
383
+ constraints: [],
384
+ };
385
+ }
386
+ case 'bspline': {
387
+ if (shape.pts.length < 2)
388
+ badParam(`bspline needs at least 2 poles, got ${shape.pts.length}`);
389
+ const degree = shape.degree ?? Math.min(3, shape.pts.length - 1);
390
+ if (!Number.isInteger(degree) || degree < 1 || degree >= shape.pts.length) {
391
+ badParam(`bspline.degree must be an integer in 1..${shape.pts.length - 1}, got ${shape.degree}`);
392
+ }
393
+ return {
394
+ geoms: [{
395
+ tag: tags[0], kind: 'bspline',
396
+ poles: shape.pts.map(([x, y]) => ({ x, y })),
397
+ knots: clampedUniformKnots(shape.pts.length, degree),
398
+ degree,
399
+ ...(construction ? { construction: true } : {}),
400
+ }],
401
+ constraints: [],
402
+ };
403
+ }
404
+ case 'point':
405
+ return {
406
+ geoms: [{
407
+ tag: tags[0], kind: 'point', x: shape.x, y: shape.y,
408
+ ...(construction ? { construction: true } : {}),
409
+ }],
410
+ constraints: [],
411
+ };
412
+ case 'close':
413
+ // close 由 expandShapes 单独处理(它需要链上其它形状的几何)。
414
+ throw new SketchShapeError('E_SKETCHC_CLOSE_NO_CHAIN', 'close is handled by expandShapes, not expandOne');
415
+ }
416
+ }
417
+ /**
418
+ * 钳位均匀节点向量:两端各重复 `degree+1` 次(clamped),内部节点均匀。
419
+ *
420
+ * `pts` 是**控制点**(不是插值点)——canonical 的 bspline 存的就是 poles/knots/degree,
421
+ * 本层负责把一个"可手写的点列"变成合法的样条载荷。
422
+ *
423
+ * @param poleCount - 控制点个数。
424
+ * @param degree - 次数。
425
+ * @returns 长度 `poleCount + degree + 1` 的节点向量。
426
+ */
427
+ export function clampedUniformKnots(poleCount, degree) {
428
+ const inner = poleCount - degree - 1;
429
+ const knots = [];
430
+ for (let i = 0; i <= degree; i++)
431
+ knots.push(0);
432
+ for (let i = 1; i <= inner; i++)
433
+ knots.push(i / (inner + 1));
434
+ for (let i = 0; i <= degree; i++)
435
+ knots.push(1);
436
+ return knots;
437
+ }
438
+ /** 某形状会产出多少条 geom(罐头形状固定、画笔图元 1)。 */
439
+ function geomCountOfShape(shape) {
440
+ switch (shape.kind) {
441
+ case 'rect': return 4;
442
+ case 'roundedRect': return 8;
443
+ case 'polygon': return shape.n;
444
+ case 'slot': return 4;
445
+ case 'trapezoid': return 4;
446
+ case 'close': return 0;
447
+ default: return 1;
448
+ }
449
+ }
450
+ /** 链上的起点(画笔形状的“入口”点)。 */
451
+ function penStart(geom) {
452
+ switch (geom.kind) {
453
+ case 'line': return [geom.x1, geom.y1];
454
+ case 'arc': return [geom.cx + geom.r * Math.cos(geom.a0), geom.cy + geom.r * Math.sin(geom.a0)];
455
+ case 'bspline': {
456
+ const p = geom.poles[0];
457
+ return p ? [p.x, p.y] : undefined;
458
+ }
459
+ default: return undefined;
460
+ }
461
+ }
462
+ /** 链上的终点(画笔形状的“出口”点)。 */
463
+ function penEnd(geom) {
464
+ switch (geom.kind) {
465
+ case 'line': return [geom.x2, geom.y2];
466
+ case 'arc': return [geom.cx + geom.r * Math.cos(geom.a1), geom.cy + geom.r * Math.sin(geom.a1)];
467
+ case 'bspline': {
468
+ const p = geom.poles[geom.poles.length - 1];
469
+ return p ? [p.x, p.y] : undefined;
470
+ }
471
+ default: return undefined;
472
+ }
473
+ }
474
+ /** `close` 判定"已经闭合"的容差(mm,与 contour.ts 的 JOIN_TOL 同量级)。 */
475
+ const CLOSE_TOL = 1e-7;
476
+ /**
477
+ * 展开语义图元清单。
478
+ *
479
+ * 正向规则见文件头。`close` 会把它前面那一段**连续的画笔形状**(line/arc/bspline)
480
+ * 收口:链尾到链首补一条直线;链首尾本来就重合(≤1e-7)时不补。链不存在、
481
+ * 或 `ref` 指向的不是链上的画笔形状,都显式报错。
482
+ *
483
+ * @param shapes - 语义图元清单(顺序即链序)。
484
+ * @returns 展开结果(几何、形状自带约束、tag 映射、内环区间、下一个可用编号)。
485
+ * @throws SketchShapeError 参数非法 / 模式不支持 / close 无链。
486
+ */
487
+ export function expandShapes(shapes) {
488
+ // Pass A:把显式命名的 tag 全部登记,并算出自动编号的起点(与形状顺序无关,
489
+ // 否则「后面的 g100」会让前面的自动编号撞车)。
490
+ const usedTags = new Set();
491
+ for (const s of shapes) {
492
+ const tag = 'tag' in s ? s.tag : undefined;
493
+ if (tag === undefined)
494
+ continue;
495
+ usedTags.add(tag);
496
+ if (s.kind === 'close')
497
+ continue;
498
+ for (let i = 0; i < geomCountOfShape(s); i++)
499
+ usedTags.add(derivedTag(tag, i));
500
+ }
501
+ let nextNumber = maxTagNumber(usedTags) + 1;
502
+ const allocNumber = () => {
503
+ while (usedTags.has(`g${nextNumber}`))
504
+ nextNumber++;
505
+ const tag = `g${nextNumber}`;
506
+ usedTags.add(tag);
507
+ return tag;
508
+ };
509
+ const geoms = [];
510
+ const constraints = [];
511
+ const spans = [];
512
+ const owners = [];
513
+ const subtractRanges = [];
514
+ /** 画笔形状在 `spans` 里的下标(链式 close 用)。 */
515
+ const penSpans = [];
516
+ /** 每个 span 产出的几何(close 复核链首尾用)。 */
517
+ const spanGeoms = [];
518
+ for (let index = 0; index < shapes.length; index++) {
519
+ const shape = shapes[index];
520
+ const start = geoms.length;
521
+ if (shape.kind !== 'close') {
522
+ const { mode, construction } = resolveFlags(shape);
523
+ const explicitTag = 'tag' in shape ? shape.tag : undefined;
524
+ const shapeTag = explicitTag ?? allocNumber();
525
+ const count = geomCountOfShape(shape);
526
+ const tags = shape.kind === 'line' || shape.kind === 'arc' || shape.kind === 'bspline' || shape.kind === 'point'
527
+ ? [shapeTag]
528
+ : Array.from({ length: count }, (_, i) => derivedTag(shapeTag, i));
529
+ for (const t of tags)
530
+ usedTags.add(t);
531
+ const expanded = expandOne(shape, tags, construction ? true : undefined);
532
+ for (const g of expanded.geoms) {
533
+ geoms.push(g);
534
+ owners.push(index);
535
+ }
536
+ constraints.push(...expanded.constraints);
537
+ spans.push({ index, kind: shape.kind, tag: shapeTag, start, count: expanded.geoms.length });
538
+ spanGeoms.push(expanded.geoms);
539
+ if (PEN_KINDS.includes(shape.kind))
540
+ penSpans.push(index);
541
+ if (mode === 's') {
542
+ subtractRanges.push({ index, start, count: expanded.geoms.length });
543
+ }
544
+ continue;
545
+ }
546
+ // ── close:把链收口 ──
547
+ // 链 = **紧邻 close 之前**的一段连续画笔形状(line/arc/bspline);`ref` 只决定
548
+ // **收口目标点**(缺省 = 链首起点),不改变链本身——与"闭合到该 tag 起点"一致。
549
+ // 链与 close 之间隔着非画笔形状(罐头形状/点)即视为无链:让"哪条链被闭合"
550
+ // 由语句顺序唯一决定,而不是靠猜。
551
+ let targetSpan = -1;
552
+ if (shape.ref !== undefined) {
553
+ const at = spans.findIndex((s) => s.tag === shape.ref);
554
+ const refKind = at >= 0 ? spans[at].kind : undefined;
555
+ if (refKind === undefined || !PEN_KINDS.includes(refKind)) {
556
+ throw new SketchShapeError('E_SKETCHC_CLOSE_BAD_REF', `close.ref "${shape.ref}" does not name a pen shape (line/arc/bspline) in this sketch`);
557
+ }
558
+ targetSpan = at;
559
+ }
560
+ const lastPenSpan = penSpans[penSpans.length - 1];
561
+ if (lastPenSpan === undefined || lastPenSpan !== index - 1) {
562
+ throw new SketchShapeError('E_SKETCHC_CLOSE_NO_CHAIN', 'close must directly follow the pen chain it closes (line/arc/bspline),' +
563
+ '或先用 ref 指明要闭合到哪个形状');
564
+ }
565
+ const chainMemberSpans = [];
566
+ for (let i = penSpans.length - 1; i >= 0; i--) {
567
+ const spanIndex = penSpans[i];
568
+ const prev = chainMemberSpans[0];
569
+ if (prev === undefined || spanIndex === prev - 1)
570
+ chainMemberSpans.unshift(spanIndex);
571
+ else
572
+ break;
573
+ }
574
+ if (chainMemberSpans.length === 0) {
575
+ throw new SketchShapeError('E_SKETCHC_CLOSE_NO_CHAIN', 'close found no pen chain to close (画一条线/弧之后再闭合,或去掉这条 close)');
576
+ }
577
+ if (targetSpan < 0) {
578
+ targetSpan = chainMemberSpans[0];
579
+ }
580
+ else if (!chainMemberSpans.includes(targetSpan)) {
581
+ throw new SketchShapeError('E_SKETCHC_CLOSE_BAD_REF', `close.ref "${shape.ref}" is not part of the pen chain immediately before this close`);
582
+ }
583
+ const lastGeooms = spanGeoms[chainMemberSpans[chainMemberSpans.length - 1]];
584
+ const lastGeom = lastGeooms[lastGeooms.length - 1];
585
+ const targetGeoms = spanGeoms[targetSpan];
586
+ const from = penEnd(lastGeom);
587
+ const to = penStart(targetGeoms[0]);
588
+ if (from === undefined || to === undefined) {
589
+ throw new SketchShapeError('E_SKETCHC_CLOSE_NO_CHAIN', `close cannot read the chain ends (${lastGeom.kind} → ${targetGeoms[0].kind})`);
590
+ }
591
+ let emitted = 0;
592
+ let emittedTag = '';
593
+ if (Math.hypot(from[0] - to[0], from[1] - to[1]) > CLOSE_TOL) {
594
+ emittedTag = allocNumber();
595
+ geoms.push(line(from, to, emittedTag, undefined));
596
+ owners.push(index);
597
+ emitted = 1;
598
+ }
599
+ spans.push({ index, kind: 'close', tag: emittedTag, start, count: emitted });
600
+ spanGeoms.push([]);
601
+ }
602
+ // nextTag = 第一个仍然空闲的编号(自动分配可能已经吃掉若干编号)。
603
+ let freeNumber = nextNumber;
604
+ while (usedTags.has(`g${freeNumber}`))
605
+ freeNumber++;
606
+ return { geoms, constraints, spans, owners, subtractRanges, nextTag: freeNumber };
607
+ }
608
+ /** 这个形状/手柄在本版本拖不动 → 显式报错(宁可拖不动,也不静默变形)。 */
609
+ function editUnsupported(shape, what) {
610
+ throw new SketchShapeError('E_SKETCHC_SHAPE_EDIT_UNSUPPORTED', `${shape.kind}: ${what} is not editable in this version`);
611
+ }
612
+ /** 形状中心(无中心的形状取原点)。 */
613
+ function shapeCenter(shape) {
614
+ const cx = 'cx' in shape ? (shape.cx ?? 0) : 0;
615
+ const cy = 'cy' in shape ? (shape.cy ?? 0) : 0;
616
+ return [cx, cy];
617
+ }
618
+ /** 两点中点。 */
619
+ function midpoint(a, b) {
620
+ return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
621
+ }
622
+ /** 把点按中心平移。 */
623
+ function translatePoint(p, from, to) {
624
+ return [p[0] + (to[0] - from[0]), p[1] + (to[1] - from[1])];
625
+ }
626
+ /**
627
+ * 圆弧的有符号扫掠跨度(弧度)。**这条规则的全仓库唯一实现。**
628
+ *
629
+ * 规则**必须与 core 的 `geometry2d/adapt.ts#profileSegToCurve` 完全一致**,否则
630
+ * 编辑期预览画的弧与执行后内核建的弧不是同一条——那正是仓库里踩过的
631
+ * `sketch-arc-ccw-gotcha`(CW 短弧被当成 CCW 补角长弧,revolve 退化、BRepMesh 挂死):
632
+ *
633
+ * - **方向由 `ccw` 决定,不由 `a1 - a0` 的符号决定**;`ccw` 缺省时按 `a1 > a0` 推断
634
+ * (canonical 的既有约定:`cq-compat` 产 `ccw: da > 0`)。
635
+ * - 扫角用 `while` 平移到 (0, 2π],**不能用 `%`**——整圆恰为 2π 时 `x % x === 0`
636
+ * 会把它消成退化线段。
637
+ * - `a0 === a1` 视为整圆(扫角 2π)。
638
+ *
639
+ * 消费方:本模块的 `arcMidAngle` / `arcPoints`;宿主(3d_editor)的弧采样与吸附
640
+ * (原先自己抄了一份 `arcSpan`,2026-10-03 收归到这里);faijs-cadquery 的三点弧。
641
+ *
642
+ * @param a0 - 起始角(弧度)。
643
+ * @param a1 - 终止角(弧度)。
644
+ * @param ccw - 逆时针为正;缺省按 `a1 > a0` 推断。
645
+ * @returns 有符号扫角:`ccw` 为正、顺为负,绝对值落在 (0, 2π]。
646
+ */
647
+ export function arcSweep(a0, a1, ccw) {
648
+ const dir = ccw ?? a1 - a0 > 0;
649
+ let sweep = dir ? a1 - a0 : a0 - a1;
650
+ while (sweep <= 0)
651
+ sweep += Math.PI * 2;
652
+ while (sweep > Math.PI * 2)
653
+ sweep -= Math.PI * 2;
654
+ return dir ? sweep : -sweep;
655
+ }
656
+ /** 圆弧中点手柄的角度。 */
657
+ function arcMidAngle(a0, a1, ccw) {
658
+ return a0 + arcSweep(a0, a1, ccw) / 2;
659
+ }
660
+ /**
661
+ * 圆弧的三个关键点(三点弧构造的唯一实现:`start → mid → end`,`mid` 一定在弧上)。
662
+ *
663
+ * 消费方:`shapeHandles` 的弧手柄;faijs-cadquery 的 `slot()` 与 `arc(c,r,a,da)`
664
+ * 重载(OCCT `makeArcEdge` / CadQuery 都是三点弧签名)。反向遍历同一条弧只需交换
665
+ * `start` / `end`(`mid` 不变——同一段弧反向走,半程点是同一个点)。
666
+ *
667
+ * @param c - 圆心。
668
+ * @param r - 半径。
669
+ * @param a0 - 起始角(弧度)。
670
+ * @param a1 - 终止角(弧度)。
671
+ * @param ccw - 方向(缺省按 `a1 > a0` 推)。
672
+ * @returns 三点(草图局部坐标)。
673
+ */
674
+ export function arcPoints(c, r, a0, a1, ccw) {
675
+ const at = (a) => [c[0] + r * Math.cos(a), c[1] + r * Math.sin(a)];
676
+ return { start: at(a0), mid: at(arcMidAngle(a0, a1, ccw)), end: at(a1) };
677
+ }
678
+ /**
679
+ * 形状的可拖拽手柄清单(下标即 `dragShapeHandle` / `handleForGeomControl` 的 handle 参数)。
680
+ *
681
+ * 布局(顺序固定,测试与宿主都依赖):
682
+ * - `rect`:`[角0, 角1, 角2, 角3, 边0中, 边1中, 边2中, 边3中, 中心]`(角顺序同展开:左下起逆时针)
683
+ * - `circle`:`[中心, 半径]`;`ellipse`:`[中心, rx, ry]`;`polygon`:`[中心, 顶点0..n-1]`
684
+ * - `line`:`[起点, 终点, 中点]`;`arc`:`[圆心, 起点, 终点, 中点]`;`point`:`[点]`
685
+ * - `bspline`:`[控制点0..n-1]`
686
+ * - `slot` / `roundedRect` / `trapezoid`:**只有中心**(v1 支持整体平移)
687
+ *
688
+ * @param shape - 语义图元。
689
+ * @returns 手柄清单(可能为空,如 `close`)。
690
+ */
691
+ export function shapeHandles(shape) {
692
+ const [c, y] = shapeCenter(shape);
693
+ switch (shape.kind) {
694
+ case 'rect': {
695
+ const corners = rectCorners(shape.w, shape.d, c, y);
696
+ return [
697
+ ...corners.map((point) => ({ point, kind: 'corner' })),
698
+ ...corners.map((p, i) => ({ point: midpoint(p, corners[(i + 1) % 4]), kind: 'edge' })),
699
+ { point: [c, y], kind: 'center' },
700
+ ];
701
+ }
702
+ case 'circle':
703
+ return [
704
+ { point: [c, y], kind: 'center' },
705
+ { point: [c + shape.r, y], kind: 'radius' },
706
+ ];
707
+ case 'ellipse':
708
+ return [
709
+ { point: [c, y], kind: 'center' },
710
+ { point: [c + shape.rx, y], kind: 'radius' },
711
+ { point: [c, y + shape.ry], kind: 'radius' },
712
+ ];
713
+ case 'polygon': {
714
+ const verts = regularPolygonVertices(shape.r, shape.n, shape.angle ?? 0).map((p) => place(p, c, y));
715
+ return [
716
+ { point: [c, y], kind: 'center' },
717
+ ...verts.map((point) => ({ point, kind: 'vertex' })),
718
+ ];
719
+ }
720
+ case 'slot':
721
+ case 'roundedRect':
722
+ case 'trapezoid':
723
+ return [{ point: [c, y], kind: 'center' }];
724
+ case 'line':
725
+ return [
726
+ { point: shape.from, kind: 'start' },
727
+ { point: shape.to, kind: 'end' },
728
+ { point: midpoint(shape.from, shape.to), kind: 'mid' },
729
+ ];
730
+ case 'arc': {
731
+ const { mid, start, end } = arcPoints([shape.cx, shape.cy], shape.r, shape.a0, shape.a1, shape.ccw);
732
+ return [
733
+ { point: [shape.cx, shape.cy], kind: 'center' },
734
+ { point: start, kind: 'start' },
735
+ { point: end, kind: 'end' },
736
+ { point: mid, kind: 'mid' },
737
+ ];
738
+ }
739
+ case 'bspline':
740
+ return shape.pts.map((pt) => ({ point: [pt[0], pt[1]], kind: 'vertex' }));
741
+ case 'point':
742
+ return [{ point: [shape.x, shape.y], kind: 'start' }];
743
+ case 'close':
744
+ return [];
745
+ }
746
+ }
747
+ /** 拖动矩形的角:对角不动、这一角跟手(轴对齐,靠 H/V 形状约束维持)。 */
748
+ function dragRectCorner(shape, handle, to) {
749
+ const [c, y] = shapeCenter(shape);
750
+ const opposite = rectCorners(shape.w, shape.d, c, y)[(handle + 2) % 4];
751
+ const w = Math.abs(to[0] - opposite[0]);
752
+ const d = Math.abs(to[1] - opposite[1]);
753
+ if (w <= 0 || d <= 0)
754
+ badParam(`rect drag collapsed the rectangle (w=${w}, d=${d})`);
755
+ return { ...shape, w, d, cx: (to[0] + opposite[0]) / 2, cy: (to[1] + opposite[1]) / 2 };
756
+ }
757
+ /** 拖动矩形的边:那条边跟手、对边不动。 */
758
+ function dragRectEdge(shape, edge, to) {
759
+ const [c, y] = shapeCenter(shape);
760
+ switch (edge) {
761
+ case 0: { // 底边(y = cy - d/2)
762
+ const top = y + shape.d / 2;
763
+ const d = top - to[1];
764
+ if (d <= 0)
765
+ badParam(`rect bottom edge drag collapsed the height (d=${d})`);
766
+ return { ...shape, d, cx: c, cy: (top + to[1]) / 2 };
767
+ }
768
+ case 2: { // 顶边
769
+ const bottom = y - shape.d / 2;
770
+ const d = to[1] - bottom;
771
+ if (d <= 0)
772
+ badParam(`rect top edge drag collapsed the height (d=${d})`);
773
+ return { ...shape, d, cx: c, cy: (bottom + to[1]) / 2 };
774
+ }
775
+ case 1: { // 右边(x = cx + w/2)
776
+ const left = c - shape.w / 2;
777
+ const w = to[0] - left;
778
+ if (w <= 0)
779
+ badParam(`rect right edge drag collapsed the width (w=${w})`);
780
+ return { ...shape, w, cx: (left + to[0]) / 2, cy: y };
781
+ }
782
+ default: { // 左边
783
+ const right = c + shape.w / 2;
784
+ const w = right - to[0];
785
+ if (w <= 0)
786
+ badParam(`rect left edge drag collapsed the width (w=${w})`);
787
+ return { ...shape, w, cx: (right + to[0]) / 2, cy: y };
788
+ }
789
+ }
790
+ }
791
+ /**
792
+ * 拖动形状的某个手柄,返回**新的形状参数**(拖拽写回的唯一入口)。
793
+ *
794
+ * 语义:形状保持自己的形状语义——拖矩形的角,得到"对角不动、这一角跟手"的矩形;
795
+ * 拖矩形某条边,得到"那条边移动"的矩形;拖中心,整体平移。宿主不需要任何形状数学。
796
+ *
797
+ * @param shape - 原形状。
798
+ * @param handle - `shapeHandles(shape)` 的下标。
799
+ * @param to - 目标点(草图局部坐标,已含吸附结果)。
800
+ * @returns 写回后的形状。
801
+ * @throws SketchShapeError 手柄越界 / 该形状不支持这种拖拽 / 拖成退化几何。
802
+ */
803
+ export function dragShapeHandle(shape, handle, to) {
804
+ const handles = shapeHandles(shape);
805
+ const h = handles[handle];
806
+ if (!h) {
807
+ throw new SketchShapeError('E_SKETCHC_SHAPE_EDIT_MISMATCH', `handle ${handle} out of range for ${shape.kind} (${handles.length} handles)`);
808
+ }
809
+ const center = shapeCenter(shape);
810
+ switch (shape.kind) {
811
+ case 'rect': {
812
+ if (h.kind === 'center') {
813
+ return { ...shape, cx: to[0], cy: to[1] };
814
+ }
815
+ if (h.kind === 'corner')
816
+ return dragRectCorner(shape, handle, to);
817
+ return dragRectEdge(shape, handle - 4, to);
818
+ }
819
+ case 'circle': {
820
+ if (h.kind === 'center')
821
+ return { ...shape, cx: to[0], cy: to[1] };
822
+ const r = Math.hypot(to[0] - center[0], to[1] - center[1]);
823
+ if (!(r > 0))
824
+ badParam('circle drag collapsed the radius to 0');
825
+ return { ...shape, r };
826
+ }
827
+ case 'ellipse': {
828
+ if (h.kind === 'center')
829
+ return { ...shape, cx: to[0], cy: to[1] };
830
+ if (handle === 1) {
831
+ const rx = Math.abs(to[0] - center[0]);
832
+ if (!(rx > 0))
833
+ badParam('ellipse drag collapsed rx to 0');
834
+ return { ...shape, rx };
835
+ }
836
+ const ry = Math.abs(to[1] - center[1]);
837
+ if (!(ry > 0))
838
+ badParam('ellipse drag collapsed ry to 0');
839
+ return { ...shape, ry };
840
+ }
841
+ case 'polygon': {
842
+ if (h.kind === 'center')
843
+ return { ...shape, cx: to[0], cy: to[1] };
844
+ const r = Math.hypot(to[0] - center[0], to[1] - center[1]);
845
+ if (!(r > 0))
846
+ badParam('polygon drag collapsed the circumradius to 0');
847
+ const k = handle - 1;
848
+ let angle = Math.atan2(to[1] - center[1], to[0] - center[0]) - (k * 2 * Math.PI) / shape.n;
849
+ while (angle <= -Math.PI)
850
+ angle += Math.PI * 2;
851
+ while (angle > Math.PI)
852
+ angle -= Math.PI * 2;
853
+ return { ...shape, r, angle };
854
+ }
855
+ case 'slot':
856
+ case 'roundedRect':
857
+ case 'trapezoid':
858
+ if (h.kind === 'center')
859
+ return { ...shape, cx: to[0], cy: to[1] };
860
+ return editUnsupported(shape, `${h.kind} handle`);
861
+ case 'line': {
862
+ if (h.kind === 'start')
863
+ return { ...shape, from: [to[0], to[1]] };
864
+ if (h.kind === 'end')
865
+ return { ...shape, to: [to[0], to[1]] };
866
+ // 中点 = 整体平移(保持长度与方向)。
867
+ return { ...shape, from: translatePoint(shape.from, h.point, to), to: translatePoint(shape.to, h.point, to) };
868
+ }
869
+ case 'arc': {
870
+ if (h.kind === 'center')
871
+ return { ...shape, cx: to[0], cy: to[1] };
872
+ const r = Math.hypot(to[0] - shape.cx, to[1] - shape.cy);
873
+ if (!(r > 0))
874
+ badParam('arc drag collapsed the radius to 0');
875
+ if (h.kind === 'mid')
876
+ return { ...shape, r };
877
+ const angle = Math.atan2(to[1] - shape.cy, to[0] - shape.cx);
878
+ return h.kind === 'start' ? { ...shape, r, a0: angle } : { ...shape, r, a1: angle };
879
+ }
880
+ case 'bspline': {
881
+ const pts = shape.pts.map((p, i) => (i === handle ? [to[0], to[1]] : p));
882
+ return { ...shape, pts };
883
+ }
884
+ case 'point':
885
+ return { ...shape, x: to[0], y: to[1] };
886
+ case 'close':
887
+ return editUnsupported(shape, 'handle');
888
+ }
889
+ }
890
+ /**
891
+ * 把「派生几何上的某个控制点」翻译成形状手柄下标——宿主不知道任何形状数学,
892
+ * 只做 `tag → 形状 + 形状内几何下标` 的查表。
893
+ *
894
+ * 罐头形状的派生几何顺序与展开一致(见 `expandShapes` 注释):
895
+ * - `rect` / `roundedRect` 的线:第 k 条线 = 角 k → 角 k+1(`start`/`end`/`mid` 一一对应);
896
+ * - 其余罐头形状的切线点/圆心等控制点与形状定义参数不是一一对应(拖它意味着改哪个
897
+ * 参数并不唯一),本版本显式报不支持——宁可拖不动,也不静默变形。
898
+ *
899
+ * @param shape - 该几何所属的形状。
900
+ * @param geomIndexInShape - 该几何在形状展开结果里的下标。
901
+ * @param control - 控制点语义。
902
+ * @returns `shapeHandles(shape)` 的下标。
903
+ * @throws SketchShapeError 该组合不支持拖拽。
904
+ */
905
+ export function handleForGeomControl(shape, geomIndexInShape, control) {
906
+ const corners = () => {
907
+ if (control === 'mid')
908
+ return 4 + geomIndexInShape;
909
+ return control === 'start' ? geomIndexInShape : (geomIndexInShape + 1) % 4;
910
+ };
911
+ switch (shape.kind) {
912
+ case 'rect':
913
+ return corners();
914
+ case 'roundedRect':
915
+ // 弧的切点/圆心与形状定义参数不是一一对应(拖它意味着改 w/d/r 并不唯一);
916
+ // 直边虽然有序,但拖动会牵动圆角——v1 只给中心手柄(`shapeHandles`)。
917
+ return editUnsupported(shape, `${control} handle`);
918
+ case 'polygon':
919
+ if (control === 'mid')
920
+ return editUnsupported(shape, 'mid on an edge');
921
+ return control === 'start' ? 1 + geomIndexInShape : 1 + ((geomIndexInShape + 1) % shape.n);
922
+ case 'line':
923
+ return control === 'start' ? 0 : control === 'end' ? 1 : 2;
924
+ case 'arc':
925
+ switch (control) {
926
+ case 'center': return 0;
927
+ case 'start': return 1;
928
+ case 'end': return 2;
929
+ case 'mid': return 3;
930
+ default: return editUnsupported(shape, `${control} on an arc`);
931
+ }
932
+ case 'circle':
933
+ return control === 'center' ? 0 : 1;
934
+ case 'point':
935
+ return 0;
936
+ case 'bspline':
937
+ return editUnsupported(shape, `${control} (bspline 用 shapeHandles 直接取控制点)`);
938
+ case 'ellipse':
939
+ case 'slot':
940
+ case 'trapezoid':
941
+ case 'close':
942
+ return editUnsupported(shape, `${control} handle`);
943
+ }
944
+ }
945
+ /**
946
+ * 形状的自带约束里引用了哪些 tag(宿主删形状时用来级联清理自己的约束)。
947
+ * @param constraint - 待提取引用的约束。type:SketchConstraint required:true
948
+ * @returns string[] 约束值里出现的全部 tag(可能为空数组)。
949
+ */
950
+ export function refsOfConstraint(constraint) {
951
+ const out = [];
952
+ for (const value of Object.values(constraint)) {
953
+ const ref = value;
954
+ if (ref && typeof ref === 'object' && 'tag' in ref && typeof ref.tag === 'string')
955
+ out.push(ref.tag);
956
+ }
957
+ return out;
958
+ }
959
+ //# sourceMappingURL=shapes.js.map