fluidcad 0.0.33 → 0.0.35

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 (363) hide show
  1. package/README.md +72 -2
  2. package/bin/commands/init.js +55 -0
  3. package/bin/commands/login.js +120 -0
  4. package/bin/commands/mcp.js +33 -0
  5. package/bin/commands/pack.js +49 -0
  6. package/bin/commands/publish.js +136 -0
  7. package/bin/commands/serve.js +77 -0
  8. package/bin/fluidcad.js +21 -107
  9. package/bin/lib/api-client.js +40 -0
  10. package/bin/lib/browser.js +16 -0
  11. package/bin/lib/config.js +39 -0
  12. package/bin/lib/model-config.js +38 -0
  13. package/bin/lib/workspace.js +57 -0
  14. package/lib/dist/common/scene-object.d.ts +2 -1
  15. package/lib/dist/common/scene-object.js +3 -2
  16. package/lib/dist/common/shape-factory.d.ts +2 -1
  17. package/lib/dist/common/shape-factory.js +4 -0
  18. package/lib/dist/common/transformable-primitive.d.ts +6 -5
  19. package/lib/dist/common/transformable-primitive.js +8 -7
  20. package/lib/dist/common/vertex.js +0 -1
  21. package/lib/dist/core/2d/aline.d.ts +4 -3
  22. package/lib/dist/core/2d/aline.js +3 -2
  23. package/lib/dist/core/2d/arc.d.ts +3 -2
  24. package/lib/dist/core/2d/arc.js +4 -3
  25. package/lib/dist/core/2d/bezier.d.ts +8 -6
  26. package/lib/dist/core/2d/circle.d.ts +4 -3
  27. package/lib/dist/core/2d/circle.js +3 -2
  28. package/lib/dist/core/2d/ellipse.d.ts +5 -4
  29. package/lib/dist/core/2d/ellipse.js +5 -4
  30. package/lib/dist/core/2d/hline.d.ts +4 -3
  31. package/lib/dist/core/2d/hline.js +5 -3
  32. package/lib/dist/core/2d/line.js +1 -0
  33. package/lib/dist/core/2d/offset.d.ts +3 -2
  34. package/lib/dist/core/2d/offset.js +6 -5
  35. package/lib/dist/core/2d/polygon.d.ts +5 -4
  36. package/lib/dist/core/2d/polygon.js +10 -9
  37. package/lib/dist/core/2d/rect.d.ts +4 -3
  38. package/lib/dist/core/2d/rect.js +10 -9
  39. package/lib/dist/core/2d/slot.d.ts +14 -6
  40. package/lib/dist/core/2d/slot.js +19 -8
  41. package/lib/dist/core/2d/tarc.d.ts +20 -2
  42. package/lib/dist/core/2d/tarc.js +24 -0
  43. package/lib/dist/core/2d/vline.d.ts +4 -3
  44. package/lib/dist/core/2d/vline.js +5 -3
  45. package/lib/dist/core/chamfer.d.ts +5 -4
  46. package/lib/dist/core/chamfer.js +7 -6
  47. package/lib/dist/core/color.d.ts +3 -2
  48. package/lib/dist/core/color.js +2 -1
  49. package/lib/dist/core/cut.d.ts +4 -3
  50. package/lib/dist/core/cut.js +5 -4
  51. package/lib/dist/core/cylinder.d.ts +2 -1
  52. package/lib/dist/core/cylinder.js +2 -1
  53. package/lib/dist/core/draft.d.ts +3 -2
  54. package/lib/dist/core/draft.js +3 -2
  55. package/lib/dist/core/extrude.d.ts +4 -3
  56. package/lib/dist/core/extrude.js +5 -4
  57. package/lib/dist/core/fillet.d.ts +5 -4
  58. package/lib/dist/core/fillet.js +6 -5
  59. package/lib/dist/core/index.d.ts +1 -0
  60. package/lib/dist/core/index.js +1 -0
  61. package/lib/dist/core/interfaces.d.ts +55 -25
  62. package/lib/dist/core/param.d.ts +74 -0
  63. package/lib/dist/core/param.js +147 -0
  64. package/lib/dist/core/repeat.d.ts +2 -1
  65. package/lib/dist/core/repeat.js +72 -54
  66. package/lib/dist/core/revolve.d.ts +2 -1
  67. package/lib/dist/core/revolve.js +3 -2
  68. package/lib/dist/core/rib.d.ts +3 -2
  69. package/lib/dist/core/rib.js +6 -2
  70. package/lib/dist/core/rotate.d.ts +5 -4
  71. package/lib/dist/core/rotate.js +4 -3
  72. package/lib/dist/core/shell.d.ts +3 -2
  73. package/lib/dist/core/shell.js +3 -2
  74. package/lib/dist/core/sphere.d.ts +3 -2
  75. package/lib/dist/core/sphere.js +2 -1
  76. package/lib/dist/core/translate.d.ts +7 -6
  77. package/lib/dist/core/translate.js +6 -5
  78. package/lib/dist/features/2d/arc.d.ts +8 -2
  79. package/lib/dist/features/2d/arc.js +94 -17
  80. package/lib/dist/features/2d/back.js +3 -2
  81. package/lib/dist/features/2d/bezier.js +16 -16
  82. package/lib/dist/features/2d/circle.js +4 -0
  83. package/lib/dist/features/2d/ellipse.js +4 -0
  84. package/lib/dist/features/2d/hline.d.ts +3 -0
  85. package/lib/dist/features/2d/hline.js +9 -2
  86. package/lib/dist/features/2d/line.d.ts +3 -0
  87. package/lib/dist/features/2d/line.js +11 -3
  88. package/lib/dist/features/2d/sketch.d.ts +4 -0
  89. package/lib/dist/features/2d/sketch.js +25 -0
  90. package/lib/dist/features/2d/slot.d.ts +5 -0
  91. package/lib/dist/features/2d/slot.js +52 -7
  92. package/lib/dist/features/2d/tarc-constrained.d.ts +2 -0
  93. package/lib/dist/features/2d/tarc-constrained.js +8 -0
  94. package/lib/dist/features/2d/tarc-radius-to-object.d.ts +16 -0
  95. package/lib/dist/features/2d/tarc-radius-to-object.js +58 -0
  96. package/lib/dist/features/2d/tarc-to-object.d.ts +18 -0
  97. package/lib/dist/features/2d/tarc-to-object.js +66 -0
  98. package/lib/dist/features/2d/tarc-to-point-tangent.d.ts +2 -0
  99. package/lib/dist/features/2d/tarc-to-point-tangent.js +6 -0
  100. package/lib/dist/features/2d/tarc-to-point.d.ts +2 -0
  101. package/lib/dist/features/2d/tarc-to-point.js +6 -0
  102. package/lib/dist/features/2d/tarc-with-tangent.d.ts +2 -0
  103. package/lib/dist/features/2d/tarc-with-tangent.js +6 -0
  104. package/lib/dist/features/2d/tarc.d.ts +2 -0
  105. package/lib/dist/features/2d/tarc.js +6 -0
  106. package/lib/dist/features/2d/vline.d.ts +3 -0
  107. package/lib/dist/features/2d/vline.js +9 -2
  108. package/lib/dist/features/copy-circular.d.ts +4 -3
  109. package/lib/dist/features/copy-circular.js +16 -9
  110. package/lib/dist/features/copy-circular2d.js +16 -9
  111. package/lib/dist/features/copy-linear.d.ts +4 -3
  112. package/lib/dist/features/copy-linear.js +18 -12
  113. package/lib/dist/features/copy-linear2d.js +18 -12
  114. package/lib/dist/features/extrude-base.d.ts +13 -3
  115. package/lib/dist/features/extrude-base.js +32 -3
  116. package/lib/dist/features/extrude-to-face.js +1 -5
  117. package/lib/dist/features/extrude-two-distances.js +1 -2
  118. package/lib/dist/features/extrude.js +1 -2
  119. package/lib/dist/features/mirror-feature.d.ts +3 -2
  120. package/lib/dist/features/mirror-feature.js +1 -1
  121. package/lib/dist/features/mirror-shape2d.js +2 -2
  122. package/lib/dist/features/repeat-base.d.ts +13 -0
  123. package/lib/dist/features/repeat-base.js +21 -0
  124. package/lib/dist/features/repeat-circular.d.ts +8 -7
  125. package/lib/dist/features/repeat-circular.js +7 -3
  126. package/lib/dist/features/repeat-linear.d.ts +9 -7
  127. package/lib/dist/features/repeat-linear.js +9 -3
  128. package/lib/dist/features/repeat-matrix.d.ts +3 -1
  129. package/lib/dist/features/repeat-matrix.js +7 -2
  130. package/lib/dist/features/shell.d.ts +4 -1
  131. package/lib/dist/features/shell.js +14 -3
  132. package/lib/dist/helpers/clone-transform.d.ts +2 -1
  133. package/lib/dist/index.d.ts +12 -1
  134. package/lib/dist/index.js +11 -4
  135. package/lib/dist/io/file-import.d.ts +7 -0
  136. package/lib/dist/io/file-import.js +30 -10
  137. package/lib/dist/math/lazy-matrix.d.ts +36 -0
  138. package/lib/dist/math/lazy-matrix.js +134 -0
  139. package/lib/dist/oc/boolean-ops.d.ts +2 -2
  140. package/lib/dist/oc/constraints/constraint-solver-adaptor.d.ts +5 -0
  141. package/lib/dist/oc/constraints/constraint-solver-adaptor.js +16 -0
  142. package/lib/dist/oc/constraints/constraint-solver.d.ts +4 -0
  143. package/lib/dist/oc/constraints/curve/curve-constraint-solver.d.ts +4 -0
  144. package/lib/dist/oc/constraints/curve/curve-constraint-solver.js +3 -0
  145. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.d.ts +6 -1
  146. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.js +4 -0
  147. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.d.ts +8 -0
  148. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.js +111 -0
  149. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.d.ts +8 -0
  150. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.js +161 -0
  151. package/lib/dist/oc/mesh.d.ts +9 -4
  152. package/lib/dist/oc/mesh.js +14 -13
  153. package/lib/dist/oc/shell-ops.d.ts +2 -1
  154. package/lib/dist/oc/shell-ops.js +5 -2
  155. package/lib/dist/param-registry.d.ts +34 -0
  156. package/lib/dist/param-registry.js +60 -0
  157. package/lib/dist/rendering/mesh-builder.d.ts +3 -0
  158. package/lib/dist/rendering/mesh-builder.js +10 -5
  159. package/lib/dist/rendering/render-edge.d.ts +2 -1
  160. package/lib/dist/rendering/render-edge.js +2 -2
  161. package/lib/dist/rendering/render-face.d.ts +2 -1
  162. package/lib/dist/rendering/render-face.js +2 -2
  163. package/lib/dist/rendering/render-solid.d.ts +2 -1
  164. package/lib/dist/rendering/render-solid.js +2 -2
  165. package/lib/dist/rendering/render-wire.d.ts +2 -1
  166. package/lib/dist/rendering/render-wire.js +2 -2
  167. package/lib/dist/rendering/render.d.ts +3 -0
  168. package/lib/dist/rendering/render.js +7 -2
  169. package/lib/dist/scene-manager.d.ts +4 -2
  170. package/lib/dist/scene-manager.js +12 -4
  171. package/lib/dist/tests/features/2d/arc.test.js +64 -0
  172. package/lib/dist/tests/features/2d/back.test.js +17 -1
  173. package/lib/dist/tests/features/2d/tarc.test.js +157 -0
  174. package/lib/dist/tests/features/copy-circular.test.js +1 -1
  175. package/lib/dist/tests/features/copy-linear.test.js +10 -10
  176. package/lib/dist/tests/features/repeat-user-repro-cache.test.d.ts +1 -0
  177. package/lib/dist/tests/features/repeat-user-repro-cache.test.js +97 -0
  178. package/lib/dist/tests/features/repeat-user-repro.test.d.ts +1 -0
  179. package/lib/dist/tests/features/repeat-user-repro.test.js +60 -0
  180. package/lib/dist/tests/features/shell.test.js +36 -0
  181. package/lib/dist/tests/global-setup.js +2 -1
  182. package/lib/dist/tests/helpers/extract-blocks.d.ts +9 -0
  183. package/lib/dist/tests/helpers/extract-blocks.js +56 -0
  184. package/lib/dist/tests/llm-docs-examples.test.d.ts +1 -0
  185. package/lib/dist/tests/llm-docs-examples.test.js +62 -0
  186. package/lib/dist/tests/setup.js +2 -1
  187. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  188. package/llm-docs/.coverage-allowlist.txt +9 -0
  189. package/llm-docs/api/arc.md +48 -0
  190. package/llm-docs/api/axis.md +42 -0
  191. package/llm-docs/api/bezier.md +41 -0
  192. package/llm-docs/api/booleans.md +44 -0
  193. package/llm-docs/api/chamfer.md +40 -0
  194. package/llm-docs/api/circle.md +36 -0
  195. package/llm-docs/api/color.md +34 -0
  196. package/llm-docs/api/connect.md +41 -0
  197. package/llm-docs/api/constraint-qualifiers.md +48 -0
  198. package/llm-docs/api/copy.md +63 -0
  199. package/llm-docs/api/cursor-lines.md +50 -0
  200. package/llm-docs/api/cursor-move.md +61 -0
  201. package/llm-docs/api/cut.md +55 -0
  202. package/llm-docs/api/draft.md +36 -0
  203. package/llm-docs/api/edge-filter.md +57 -0
  204. package/llm-docs/api/ellipse.md +34 -0
  205. package/llm-docs/api/extrude.md +74 -0
  206. package/llm-docs/api/face-filter.md +61 -0
  207. package/llm-docs/api/fillet.md +51 -0
  208. package/llm-docs/api/index.json +139 -0
  209. package/llm-docs/api/line.md +42 -0
  210. package/llm-docs/api/load.md +37 -0
  211. package/llm-docs/api/local.md +38 -0
  212. package/llm-docs/api/loft.md +37 -0
  213. package/llm-docs/api/mirror.md +44 -0
  214. package/llm-docs/api/offset.md +36 -0
  215. package/llm-docs/api/part.md +40 -0
  216. package/llm-docs/api/plane.md +44 -0
  217. package/llm-docs/api/polygon.md +37 -0
  218. package/llm-docs/api/primitive-solids.md +39 -0
  219. package/llm-docs/api/project-intersect.md +48 -0
  220. package/llm-docs/api/rect.md +48 -0
  221. package/llm-docs/api/remove.md +32 -0
  222. package/llm-docs/api/repeat.md +79 -0
  223. package/llm-docs/api/revolve.md +38 -0
  224. package/llm-docs/api/rib.md +40 -0
  225. package/llm-docs/api/rotate.md +37 -0
  226. package/llm-docs/api/select.md +41 -0
  227. package/llm-docs/api/shell.md +41 -0
  228. package/llm-docs/api/sketch.md +76 -0
  229. package/llm-docs/api/slot.md +36 -0
  230. package/llm-docs/api/split-trim.md +42 -0
  231. package/llm-docs/api/sweep.md +43 -0
  232. package/llm-docs/api/tarc.md +45 -0
  233. package/llm-docs/api/tcircle.md +38 -0
  234. package/llm-docs/api/tline.md +42 -0
  235. package/llm-docs/api/translate.md +40 -0
  236. package/llm-docs/api/types/aline.md +35 -0
  237. package/llm-docs/api/types/arc-angles.md +29 -0
  238. package/llm-docs/api/types/arc-points.md +48 -0
  239. package/llm-docs/api/types/axis-like.md +38 -0
  240. package/llm-docs/api/types/axis.md +21 -0
  241. package/llm-docs/api/types/boolean-operation.md +50 -0
  242. package/llm-docs/api/types/circular-repeat-options.md +31 -0
  243. package/llm-docs/api/types/common.md +32 -0
  244. package/llm-docs/api/types/cut.md +125 -0
  245. package/llm-docs/api/types/draft.md +21 -0
  246. package/llm-docs/api/types/extrudable-geometry.md +23 -0
  247. package/llm-docs/api/types/extrude.md +194 -0
  248. package/llm-docs/api/types/geometry.md +51 -0
  249. package/llm-docs/api/types/hline.md +35 -0
  250. package/llm-docs/api/types/linear-repeat-options.md +31 -0
  251. package/llm-docs/api/types/loft.md +154 -0
  252. package/llm-docs/api/types/mirror.md +35 -0
  253. package/llm-docs/api/types/offset.md +31 -0
  254. package/llm-docs/api/types/plane-like.md +35 -0
  255. package/llm-docs/api/types/plane-transform-options.md +29 -0
  256. package/llm-docs/api/types/plane.md +21 -0
  257. package/llm-docs/api/types/point-like.md +22 -0
  258. package/llm-docs/api/types/point2dlike.md +26 -0
  259. package/llm-docs/api/types/polygon.md +46 -0
  260. package/llm-docs/api/types/rect.md +128 -0
  261. package/llm-docs/api/types/revolve.md +102 -0
  262. package/llm-docs/api/types/rib.md +133 -0
  263. package/llm-docs/api/types/scene-object.md +33 -0
  264. package/llm-docs/api/types/select.md +21 -0
  265. package/llm-docs/api/types/shell.md +54 -0
  266. package/llm-docs/api/types/slot.md +43 -0
  267. package/llm-docs/api/types/sweep.md +189 -0
  268. package/llm-docs/api/types/tangent-arc-two-objects.md +46 -0
  269. package/llm-docs/api/types/transformable.md +93 -0
  270. package/llm-docs/api/types/trim.md +27 -0
  271. package/llm-docs/api/types/two-objects-tangent-line.md +46 -0
  272. package/llm-docs/api/types/vertex.md +17 -0
  273. package/llm-docs/api/types/vline.md +35 -0
  274. package/llm-docs/concepts/coordinate-system.md +45 -0
  275. package/llm-docs/concepts/history-and-rollback.md +40 -0
  276. package/llm-docs/concepts/last-selection.md +49 -0
  277. package/llm-docs/concepts/scene-graph.md +37 -0
  278. package/llm-docs/index.json +1750 -0
  279. package/mcp/dist/client.d.ts +65 -0
  280. package/mcp/dist/client.js +255 -0
  281. package/mcp/dist/discovery.d.ts +11 -0
  282. package/mcp/dist/discovery.js +78 -0
  283. package/mcp/dist/docs-index.d.ts +81 -0
  284. package/mcp/dist/docs-index.js +261 -0
  285. package/mcp/dist/resources.d.ts +4 -0
  286. package/mcp/dist/resources.js +115 -0
  287. package/mcp/dist/server.d.ts +12 -0
  288. package/mcp/dist/server.js +502 -0
  289. package/mcp/dist/tools/coordination.d.ts +9 -0
  290. package/mcp/dist/tools/coordination.js +46 -0
  291. package/mcp/dist/tools/docs.d.ts +66 -0
  292. package/mcp/dist/tools/docs.js +122 -0
  293. package/mcp/dist/tools/engine.d.ts +72 -0
  294. package/mcp/dist/tools/engine.js +190 -0
  295. package/mcp/dist/tools/inspection.d.ts +75 -0
  296. package/mcp/dist/tools/inspection.js +121 -0
  297. package/mcp/dist/tools/screenshot.d.ts +63 -0
  298. package/mcp/dist/tools/screenshot.js +263 -0
  299. package/mcp/dist/tools/source.d.ts +84 -0
  300. package/mcp/dist/tools/source.js +434 -0
  301. package/mcp/dist/tools/workspaces.d.ts +13 -0
  302. package/mcp/dist/tools/workspaces.js +33 -0
  303. package/mcp/dist/types.d.ts +18 -0
  304. package/mcp/dist/types.js +11 -0
  305. package/package.json +27 -7
  306. package/server/dist/api.d.ts +37 -0
  307. package/server/dist/api.js +44 -0
  308. package/server/dist/code-editor.d.ts +100 -0
  309. package/server/dist/code-editor.js +528 -2
  310. package/server/dist/fluidcad-server.d.ts +118 -1
  311. package/server/dist/fluidcad-server.js +350 -62
  312. package/server/dist/global-registry.d.ts +30 -0
  313. package/server/dist/global-registry.js +126 -0
  314. package/server/dist/host/blocked-imports.d.ts +8 -0
  315. package/server/dist/host/blocked-imports.js +30 -0
  316. package/server/dist/{vite-manager.d.ts → host/local-scene-host.d.ts} +3 -1
  317. package/server/dist/{vite-manager.js → host/local-scene-host.js} +6 -26
  318. package/server/dist/host/scene-host.d.ts +19 -0
  319. package/server/dist/host/scene-host.js +1 -0
  320. package/server/dist/index.js +175 -123
  321. package/server/dist/instance-file.d.ts +31 -0
  322. package/server/dist/instance-file.js +73 -0
  323. package/server/dist/lint-fluid-js.d.ts +15 -0
  324. package/server/dist/lint-fluid-js.js +271 -0
  325. package/server/dist/model-package/capture-params.d.ts +19 -0
  326. package/server/dist/model-package/capture-params.js +42 -0
  327. package/server/dist/model-package/pack.d.ts +23 -0
  328. package/server/dist/model-package/pack.js +229 -0
  329. package/server/dist/model-package/types.d.ts +78 -0
  330. package/server/dist/model-package/types.js +17 -0
  331. package/server/dist/routes/editor.d.ts +24 -0
  332. package/server/dist/routes/editor.js +44 -0
  333. package/server/dist/routes/export.d.ts +1 -1
  334. package/server/dist/routes/export.js +45 -8
  335. package/server/dist/routes/health.d.ts +7 -0
  336. package/server/dist/routes/health.js +14 -0
  337. package/server/dist/routes/hit-test.d.ts +3 -0
  338. package/server/dist/routes/hit-test.js +17 -0
  339. package/server/dist/routes/lint.d.ts +10 -0
  340. package/server/dist/routes/lint.js +28 -0
  341. package/server/dist/routes/pack.d.ts +10 -0
  342. package/server/dist/routes/pack.js +47 -0
  343. package/server/dist/routes/params.d.ts +3 -0
  344. package/server/dist/routes/params.js +75 -0
  345. package/server/dist/routes/render.d.ts +33 -0
  346. package/server/dist/routes/render.js +34 -0
  347. package/server/dist/routes/scene.d.ts +5 -0
  348. package/server/dist/routes/scene.js +48 -0
  349. package/server/dist/routes/screenshot.js +68 -1
  350. package/server/dist/routes/sketch-edits.d.ts +3 -0
  351. package/server/dist/routes/sketch-edits.js +542 -0
  352. package/server/dist/routes/timeline.d.ts +3 -0
  353. package/server/dist/routes/timeline.js +49 -0
  354. package/server/dist/server-core.d.ts +53 -0
  355. package/server/dist/server-core.js +147 -0
  356. package/server/dist/ws-protocol.d.ts +156 -3
  357. package/ui/dist/assets/index-CDJmUpFI.css +2 -0
  358. package/ui/dist/assets/index-MRqwG9Vh.js +5417 -0
  359. package/ui/dist/index.html +2 -2
  360. package/server/dist/routes/actions.d.ts +0 -3
  361. package/server/dist/routes/actions.js +0 -309
  362. package/ui/dist/assets/index-CFi9p7wR.js +0 -4946
  363. package/ui/dist/assets/index-DR7c2Qk9.css +0 -2
@@ -16,6 +16,8 @@ export class Arc extends GeometrySceneObject {
16
16
  _bulgeRadius = 0;
17
17
  _centerPoint = null;
18
18
  _centered = false;
19
+ _clockwise = false;
20
+ _major = false;
19
21
  _targetPlane;
20
22
  constructor(targetPlane = null) {
21
23
  super();
@@ -39,6 +41,13 @@ export class Arc extends GeometrySceneObject {
39
41
  arc._endAngle = endAngle;
40
42
  return arc;
41
43
  }
44
+ static circumcenter(a, b, c) {
45
+ const D = 2 * (a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y));
46
+ const aa = a.x * a.x + a.y * a.y;
47
+ const bb = b.x * b.x + b.y * b.y;
48
+ const cc = c.x * c.x + c.y * c.y;
49
+ return new Point2D((aa * (b.y - c.y) + bb * (c.y - a.y) + cc * (a.y - b.y)) / D, (aa * (c.x - b.x) + bb * (a.x - c.x) + cc * (b.x - a.x)) / D);
50
+ }
42
51
  // Chainable methods (IArc)
43
52
  radius(value) {
44
53
  this._bulgeRadius = value;
@@ -52,6 +61,14 @@ export class Arc extends GeometrySceneObject {
52
61
  this._centered = true;
53
62
  return this;
54
63
  }
64
+ cw() {
65
+ this._clockwise = true;
66
+ return this;
67
+ }
68
+ major() {
69
+ this._major = true;
70
+ return this;
71
+ }
55
72
  build() {
56
73
  if (this._startPoint && this._endPoint) {
57
74
  // Two explicit points: default center = current position
@@ -81,21 +98,26 @@ export class Arc extends GeometrySceneObject {
81
98
  const endPt = this._endPoint.asPoint2D();
82
99
  const centerPt = this._centerPoint
83
100
  ? this._centerPoint.asPoint2D()
84
- : this.getCurrentPosition();
101
+ : new Point2D((startPt.x + endPt.x) / 2, (startPt.y + endPt.y) / 2);
85
102
  const dx = startPt.x - centerPt.x;
86
103
  const dy = startPt.y - centerPt.y;
87
104
  const radius = Math.sqrt(dx * dx + dy * dy);
88
105
  const endAngle = Math.atan2(endPt.y - centerPt.y, endPt.x - centerPt.x);
106
+ const normal = this._clockwise ? plane.normal.negate() : plane.normal;
89
107
  const center = plane.localToWorld(centerPt);
90
108
  const start = plane.localToWorld(startPt);
91
109
  const end = plane.localToWorld(endPt);
92
- const arc = Geometry.makeArc(center, radius, plane.normal, start, end);
110
+ const arc = Geometry.makeArc(center, radius, normal, start, end);
93
111
  const edge = Geometry.makeEdgeFromCurve(arc);
94
- const tx = -Math.sin(endAngle);
95
- const ty = Math.cos(endAngle);
112
+ const sign = this._clockwise ? -1 : 1;
113
+ const tx = sign * (-Math.sin(endAngle));
114
+ const ty = sign * Math.cos(endAngle);
96
115
  this.setTangent(new Point2D(tx, ty));
97
116
  this.setState('start', Vertex.fromPoint2D(startPt));
98
117
  this.setState('end', Vertex.fromPoint2D(endPt));
118
+ const centerVertex = Vertex.fromPoint(center);
119
+ centerVertex.markAsMetaShape();
120
+ this.addShape(centerVertex);
99
121
  this.addShape(edge);
100
122
  if (this.sketch) {
101
123
  this.setCurrentPosition(endPt);
@@ -129,14 +151,19 @@ export class Arc extends GeometrySceneObject {
129
151
  const center = plane.localToWorld(centerPoint);
130
152
  const start = plane.localToWorld(startPoint);
131
153
  const end = plane.localToWorld(targetPoint);
132
- const arc = Geometry.makeArc(center, r, normal, start, end);
154
+ const arc = this._major
155
+ ? this.makeMajorArc(startPoint, targetPoint, centerPoint, cw, plane)
156
+ : Geometry.makeArc(center, r, normal, start, end);
133
157
  const edge = Geometry.makeEdgeFromCurve(arc);
134
- const signT = cw ? -1 : 1;
158
+ const signT = (cw ? -1 : 1) * (this._major ? -1 : 1);
135
159
  const endTx = signT * (-Math.sin(endAngle));
136
160
  const endTy = signT * Math.cos(endAngle);
137
161
  this.setTangent(new Point2D(endTx, endTy));
138
162
  this.setState('start', Vertex.fromPoint2D(startPoint));
139
163
  this.setState('end', Vertex.fromPoint2D(targetPoint));
164
+ const centerVertex = Vertex.fromPoint(center);
165
+ centerVertex.markAsMetaShape();
166
+ this.addShape(centerVertex);
140
167
  this.addShape(edge);
141
168
  if (this.sketch) {
142
169
  this.setCurrentPosition(targetPoint);
@@ -172,14 +199,19 @@ export class Arc extends GeometrySceneObject {
172
199
  const center = plane.localToWorld(centerPoint);
173
200
  const start = plane.localToWorld(startPoint);
174
201
  const end = plane.localToWorld(targetPoint);
175
- const arc = Geometry.makeArc(center, r, normal, start, end);
202
+ const arc = this._major
203
+ ? this.makeMajorArc(startPoint, targetPoint, centerPoint, cw, plane)
204
+ : Geometry.makeArc(center, r, normal, start, end);
176
205
  const edge = Geometry.makeEdgeFromCurve(arc);
177
- const signT = cw ? -1 : 1;
206
+ const signT = (cw ? -1 : 1) * (this._major ? -1 : 1);
178
207
  const endTx = signT * (-Math.sin(endAngle));
179
208
  const endTy = signT * Math.cos(endAngle);
180
209
  this.setTangent(new Point2D(endTx, endTy));
181
210
  this.setState('start', Vertex.fromPoint2D(startPoint));
182
211
  this.setState('end', Vertex.fromPoint2D(targetPoint));
212
+ const centerVertex = Vertex.fromPoint(center);
213
+ centerVertex.markAsMetaShape();
214
+ this.addShape(centerVertex);
183
215
  this.addShape(edge);
184
216
  if (this.sketch) {
185
217
  this.setCurrentPosition(targetPoint);
@@ -188,6 +220,24 @@ export class Arc extends GeometrySceneObject {
188
220
  this._targetPlane.removeShapes(this);
189
221
  }
190
222
  }
223
+ makeMajorArc(startPoint2D, endPoint2D, centerPoint2D, cw, plane) {
224
+ const startAngleRad = Math.atan2(startPoint2D.y - centerPoint2D.y, startPoint2D.x - centerPoint2D.x);
225
+ const endAngleRad = Math.atan2(endPoint2D.y - centerPoint2D.y, endPoint2D.x - centerPoint2D.x);
226
+ let minorSweep = cw ? startAngleRad - endAngleRad : endAngleRad - startAngleRad;
227
+ if (minorSweep <= 0) {
228
+ minorSweep += 2 * Math.PI;
229
+ }
230
+ const midAngle = cw
231
+ ? startAngleRad + (2 * Math.PI - minorSweep) / 2
232
+ : startAngleRad - (2 * Math.PI - minorSweep) / 2;
233
+ const r = Math.sqrt((startPoint2D.x - centerPoint2D.x) ** 2 +
234
+ (startPoint2D.y - centerPoint2D.y) ** 2);
235
+ const midPoint2D = new Point2D(centerPoint2D.x + r * Math.cos(midAngle), centerPoint2D.y + r * Math.sin(midAngle));
236
+ const start = plane.localToWorld(startPoint2D);
237
+ const mid = plane.localToWorld(midPoint2D);
238
+ const end = plane.localToWorld(endPoint2D);
239
+ return Geometry.makeArcThreePoints(start, mid, end);
240
+ }
191
241
  buildWithCenter() {
192
242
  const plane = this._targetPlane?.getPlane() || this.sketch.getPlane();
193
243
  const startPt = this._targetPlane
@@ -195,20 +245,33 @@ export class Arc extends GeometrySceneObject {
195
245
  : this.getCurrentPosition();
196
246
  const endPt = this._endPoint.asPoint2D();
197
247
  const centerPt = this._centerPoint.asPoint2D();
198
- const dx = startPt.x - centerPt.x;
199
- const dy = startPt.y - centerPt.y;
200
- const radius = Math.sqrt(dx * dx + dy * dy);
201
- const endAngle = Math.atan2(endPt.y - centerPt.y, endPt.x - centerPt.x);
202
- const center = plane.localToWorld(centerPt);
248
+ const aStart = Math.atan2(startPt.y - centerPt.y, startPt.x - centerPt.x);
249
+ const aEnd = Math.atan2(endPt.y - centerPt.y, endPt.x - centerPt.x);
250
+ let sweep = this._clockwise ? aStart - aEnd : aEnd - aStart;
251
+ if (sweep <= 0) {
252
+ sweep += 2 * Math.PI;
253
+ }
254
+ const midAngle = this._clockwise ? aStart - sweep / 2 : aStart + sweep / 2;
255
+ const rStart = Math.sqrt((startPt.x - centerPt.x) ** 2 + (startPt.y - centerPt.y) ** 2);
256
+ const rEnd = Math.sqrt((endPt.x - centerPt.x) ** 2 + (endPt.y - centerPt.y) ** 2);
257
+ const rMid = (rStart + rEnd) / 2;
258
+ const midPt = new Point2D(centerPt.x + rMid * Math.cos(midAngle), centerPt.y + rMid * Math.sin(midAngle));
259
+ const actualCenter = Arc.circumcenter(startPt, midPt, endPt);
260
+ const endAngle = Math.atan2(endPt.y - actualCenter.y, endPt.x - actualCenter.x);
203
261
  const start = plane.localToWorld(startPt);
204
262
  const end = plane.localToWorld(endPt);
205
- const arc = Geometry.makeArc(center, radius, plane.normal, start, end);
263
+ const mid = plane.localToWorld(midPt);
264
+ const arc = Geometry.makeArcThreePoints(start, mid, end);
206
265
  const edge = Geometry.makeEdgeFromCurve(arc);
207
- const tx = -Math.sin(endAngle);
208
- const ty = Math.cos(endAngle);
266
+ const sign = this._clockwise ? -1 : 1;
267
+ const tx = sign * (-Math.sin(endAngle));
268
+ const ty = sign * Math.cos(endAngle);
209
269
  this.setTangent(new Point2D(tx, ty));
210
270
  this.setState('start', Vertex.fromPoint2D(startPt));
211
271
  this.setState('end', Vertex.fromPoint2D(endPt));
272
+ const centerVertex = Vertex.fromPoint(plane.localToWorld(actualCenter));
273
+ centerVertex.markAsMetaShape();
274
+ this.addShape(centerVertex);
212
275
  this.addShape(edge);
213
276
  if (this.sketch) {
214
277
  this.setCurrentPosition(endPt);
@@ -254,6 +317,9 @@ export class Arc extends GeometrySceneObject {
254
317
  const edge = Geometry.makeEdgeFromCurve(arc);
255
318
  this.setState('start', Vertex.fromPoint2D(startPoint));
256
319
  this.setState('end', Vertex.fromPoint2D(endPoint));
320
+ const centerVertex = Vertex.fromPoint(center);
321
+ centerVertex.markAsMetaShape();
322
+ this.addShape(centerVertex);
257
323
  const sign = cw ? -1 : 1;
258
324
  const tx = sign * (-Math.sin(endAngleRad));
259
325
  const ty = sign * Math.cos(endAngleRad);
@@ -286,6 +352,8 @@ export class Arc extends GeometrySceneObject {
286
352
  copy._bulgeRadius = this._bulgeRadius;
287
353
  copy._centerPoint = this._centerPoint;
288
354
  copy._centered = this._centered;
355
+ copy._clockwise = this._clockwise;
356
+ copy._major = this._major;
289
357
  return copy;
290
358
  }
291
359
  compareTo(other) {
@@ -301,6 +369,9 @@ export class Arc extends GeometrySceneObject {
301
369
  if (this._targetPlane && other._targetPlane && !this._targetPlane.compareTo(other._targetPlane)) {
302
370
  return false;
303
371
  }
372
+ if (this._clockwise !== other._clockwise) {
373
+ return false;
374
+ }
304
375
  if (this._endPoint && other._endPoint) {
305
376
  if (!this._endPoint.compareTo(other._endPoint)) {
306
377
  return false;
@@ -316,7 +387,7 @@ export class Arc extends GeometrySceneObject {
316
387
  if (this._centerPoint && other._centerPoint) {
317
388
  return this._centerPoint.compareTo(other._centerPoint);
318
389
  }
319
- return this._bulgeRadius === other._bulgeRadius;
390
+ return this._bulgeRadius === other._bulgeRadius && this._major === other._major;
320
391
  }
321
392
  if (!this._endPoint && !other._endPoint) {
322
393
  return this._arcRadius === other._arcRadius &&
@@ -340,6 +411,12 @@ export class Arc extends GeometrySceneObject {
340
411
  if (this._bulgeRadius !== 0) {
341
412
  base.radius = this._bulgeRadius;
342
413
  }
414
+ if (this._clockwise) {
415
+ base.clockwise = true;
416
+ }
417
+ if (this._major) {
418
+ base.major = true;
419
+ }
343
420
  return base;
344
421
  }
345
422
  return {
@@ -9,8 +9,9 @@ export class Back extends GeometrySceneObject {
9
9
  return 'back';
10
10
  }
11
11
  build() {
12
- const target = this.sketch.getPreviousPosition(this, this.count);
13
- this.setCurrentPosition(target);
12
+ const { position, tangent } = this.sketch.getPreviousState(this, this.count);
13
+ this.setCurrentPosition(position);
14
+ this.setTangent(tangent);
14
15
  }
15
16
  getDependencies() {
16
17
  return [];
@@ -11,16 +11,20 @@ export class BezierCurve extends GeometrySceneObject {
11
11
  this.controlPoints = controlPoints;
12
12
  }
13
13
  build() {
14
- if (this.controlPoints.length === 0) {
14
+ const points = this.controlPoints.map(cp => cp.asPoint2D());
15
+ if (points.length < 2) {
16
+ // 0 args: interactive placeholder. 1 arg: start placed, no curve yet.
17
+ if (points.length === 1) {
18
+ this.setState('start', Vertex.fromPoint2D(points[0]));
19
+ this.setCurrentPosition(points[0]);
20
+ }
15
21
  return;
16
22
  }
17
23
  const plane = this.sketch.getPlane();
18
- const currentPos = this.getCurrentPosition();
19
- const points = this.controlPoints.map(cp => cp.asPoint2D());
20
- // Poles: [currentPos (start), ...controlPoints, endPoint]
21
- // All args are in order: control points then endpoint (last arg)
22
- const poles2D = [currentPos, ...points];
23
- const polesWorld = poles2D.map(p => plane.localToWorld(p));
24
+ const startPoint = points[0];
25
+ const endPoint = points[points.length - 1];
26
+ // Poles: all args in order — first is start, last is endpoint, middle are controls.
27
+ const polesWorld = points.map(p => plane.localToWorld(p));
24
28
  const bezierCurve = Geometry.makeBezierCurve(polesWorld);
25
29
  // Compute tangent at endpoint before creating the edge
26
30
  const oc = getOC();
@@ -29,11 +33,9 @@ export class BezierCurve extends GeometrySceneObject {
29
33
  bezierCurve.D1(bezierCurve.LastParameter(), gpP, gpV);
30
34
  const tangentWorld = Convert.toVector3d(gpV, true);
31
35
  gpP.delete();
32
- // Project tangent to 2D sketch coordinates
33
36
  const tangent2D = new Point2D(tangentWorld.dot(plane.xDirection), tangentWorld.dot(plane.yDirection)).normalize();
34
37
  const edge = Geometry.makeEdgeFromBezier(bezierCurve);
35
- const endPoint = points[points.length - 1];
36
- this.setState('start', Vertex.fromPoint2D(currentPos));
38
+ this.setState('start', Vertex.fromPoint2D(startPoint));
37
39
  this.setState('end', Vertex.fromPoint2D(endPoint));
38
40
  this.addShape(edge);
39
41
  this.setTangent(tangent2D);
@@ -63,14 +65,12 @@ export class BezierCurve extends GeometrySceneObject {
63
65
  return `bezier-${this.controlPoints.length}`;
64
66
  }
65
67
  serialize() {
66
- const startPos = this.getCurrentPosition();
67
- const resolved = this.controlPoints.map(cp => {
68
- const p = cp.asPoint2D();
69
- return [p.x, p.y];
70
- });
68
+ const points = this.controlPoints.map(cp => cp.asPoint2D());
69
+ const start = points[0];
70
+ const resolved = points.slice(1).map(p => [p.x, p.y]);
71
71
  return {
72
72
  controlPoints: this.controlPoints,
73
- startPoint: [startPos.x, startPos.y],
73
+ startPoint: start ? [start.x, start.y] : null,
74
74
  resolvedPoints: resolved,
75
75
  };
76
76
  }
@@ -1,4 +1,5 @@
1
1
  import { Geometry } from "../../oc/geometry.js";
2
+ import { Vertex } from "../../common/vertex.js";
2
3
  import { ExtrudableGeometryBase } from "./extrudable-base.js";
3
4
  export class Circle extends ExtrudableGeometryBase {
4
5
  diameter;
@@ -21,6 +22,9 @@ export class Circle extends ExtrudableGeometryBase {
21
22
  const circle = Geometry.makeCircle(plane.localToWorld(center), radius, plane.normal);
22
23
  let edge = Geometry.makeEdgeFromCircle(circle);
23
24
  this.addShape(edge);
25
+ const centerVertex = Vertex.fromPoint2D(center);
26
+ centerVertex.markAsMetaShape();
27
+ this.addShape(centerVertex);
24
28
  if (this.sketch) {
25
29
  this.setCurrentPosition(center);
26
30
  }
@@ -1,4 +1,5 @@
1
1
  import { Geometry } from "../../oc/geometry.js";
2
+ import { Vertex } from "../../common/vertex.js";
2
3
  import { ExtrudableGeometryBase } from "./extrudable-base.js";
3
4
  export class Ellipse extends ExtrudableGeometryBase {
4
5
  rx;
@@ -29,6 +30,9 @@ export class Ellipse extends ExtrudableGeometryBase {
29
30
  const majorAxisDir = rxIsMajor ? plane.xDirection : plane.yDirection;
30
31
  const edge = Geometry.makeEllipseEdge(plane.localToWorld(center), major, minor, plane.normal, majorAxisDir);
31
32
  this.addShape(edge);
33
+ const centerVertex = Vertex.fromPoint2D(center);
34
+ centerVertex.markAsMetaShape();
35
+ this.addShape(centerVertex);
32
36
  if (this.sketch) {
33
37
  this.setCurrentPosition(center);
34
38
  }
@@ -6,8 +6,10 @@ export declare class HorizontalLine extends GeometrySceneObject implements IHLin
6
6
  distanceOrTarget: number | SceneObject;
7
7
  private targetPlane;
8
8
  private _centered;
9
+ private _hasExplicitStart;
9
10
  constructor(distanceOrTarget: number | SceneObject, targetPlane?: PlaneObjectBase);
10
11
  centered(value?: boolean): this;
12
+ setHasExplicitStart(value?: boolean): this;
11
13
  build(): void;
12
14
  getDependencies(): SceneObject[];
13
15
  createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
@@ -17,5 +19,6 @@ export declare class HorizontalLine extends GeometrySceneObject implements IHLin
17
19
  serialize(): {
18
20
  distance: number;
19
21
  centered: boolean;
22
+ hasExplicitStart: boolean;
20
23
  };
21
24
  }
@@ -8,6 +8,7 @@ export class HorizontalLine extends GeometrySceneObject {
8
8
  distanceOrTarget;
9
9
  targetPlane;
10
10
  _centered = false;
11
+ _hasExplicitStart = false;
11
12
  constructor(distanceOrTarget, targetPlane = null) {
12
13
  super();
13
14
  this.distanceOrTarget = distanceOrTarget;
@@ -17,6 +18,10 @@ export class HorizontalLine extends GeometrySceneObject {
17
18
  this._centered = value;
18
19
  return this;
19
20
  }
21
+ setHasExplicitStart(value = true) {
22
+ this._hasExplicitStart = value;
23
+ return this;
24
+ }
20
25
  build() {
21
26
  const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
22
27
  const currentPos = this.targetPlane
@@ -78,6 +83,7 @@ export class HorizontalLine extends GeometrySceneObject {
78
83
  : this.distanceOrTarget;
79
84
  const copy = new HorizontalLine(distanceOrTarget, targetPlane);
80
85
  copy.centered(this._centered);
86
+ copy._hasExplicitStart = this._hasExplicitStart;
81
87
  return copy;
82
88
  }
83
89
  compareTo(other) {
@@ -104,7 +110,7 @@ export class HorizontalLine extends GeometrySceneObject {
104
110
  else if (this.distanceOrTarget !== other.distanceOrTarget) {
105
111
  return false;
106
112
  }
107
- return this._centered === other._centered;
113
+ return this._centered === other._centered && this._hasExplicitStart === other._hasExplicitStart;
108
114
  }
109
115
  getType() {
110
116
  return 'line';
@@ -115,7 +121,8 @@ export class HorizontalLine extends GeometrySceneObject {
115
121
  serialize() {
116
122
  return {
117
123
  distance: typeof this.distanceOrTarget === 'number' ? this.distanceOrTarget : null,
118
- centered: this._centered
124
+ centered: this._centered,
125
+ hasExplicitStart: this._hasExplicitStart,
119
126
  };
120
127
  }
121
128
  }
@@ -5,7 +5,9 @@ import { GeometrySceneObject } from "./geometry.js";
5
5
  export declare class LineTo extends GeometrySceneObject {
6
6
  endPoint: LazyVertex;
7
7
  private targetPlane;
8
+ private _hasExplicitStart;
8
9
  constructor(endPoint: LazyVertex, targetPlane?: PlaneObjectBase);
10
+ setHasExplicitStart(value?: boolean): this;
9
11
  build(): void;
10
12
  createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
11
13
  compareTo(other: LineTo): boolean;
@@ -13,5 +15,6 @@ export declare class LineTo extends GeometrySceneObject {
13
15
  getUniqueType(): string;
14
16
  serialize(): {
15
17
  end: LazyVertex;
18
+ hasExplicitStart: boolean;
16
19
  };
17
20
  }
@@ -4,11 +4,16 @@ import { GeometrySceneObject } from "./geometry.js";
4
4
  export class LineTo extends GeometrySceneObject {
5
5
  endPoint;
6
6
  targetPlane;
7
+ _hasExplicitStart = false;
7
8
  constructor(endPoint, targetPlane = null) {
8
9
  super();
9
10
  this.endPoint = endPoint;
10
11
  this.targetPlane = targetPlane;
11
12
  }
13
+ setHasExplicitStart(value = true) {
14
+ this._hasExplicitStart = value;
15
+ return this;
16
+ }
12
17
  build() {
13
18
  const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
14
19
  const targetPoint = this.endPoint.asPoint2D();
@@ -32,7 +37,9 @@ export class LineTo extends GeometrySceneObject {
32
37
  }
33
38
  createCopy(remap) {
34
39
  const targetPlane = this.targetPlane ? (remap.get(this.targetPlane) || this.targetPlane) : null;
35
- return new LineTo(this.endPoint, targetPlane);
40
+ const copy = new LineTo(this.endPoint, targetPlane);
41
+ copy._hasExplicitStart = this._hasExplicitStart;
42
+ return copy;
36
43
  }
37
44
  compareTo(other) {
38
45
  if (!(other instanceof LineTo)) {
@@ -47,7 +54,7 @@ export class LineTo extends GeometrySceneObject {
47
54
  if (this.targetPlane && other.targetPlane && !this.targetPlane.compareTo(other.targetPlane)) {
48
55
  return false;
49
56
  }
50
- return this.endPoint.compareTo(other.endPoint);
57
+ return this.endPoint.compareTo(other.endPoint) && this._hasExplicitStart === other._hasExplicitStart;
51
58
  }
52
59
  getType() {
53
60
  return 'line';
@@ -57,7 +64,8 @@ export class LineTo extends GeometrySceneObject {
57
64
  }
58
65
  serialize() {
59
66
  return {
60
- end: this.endPoint
67
+ end: this.endPoint,
68
+ hasExplicitStart: this._hasExplicitStart,
61
69
  };
62
70
  }
63
71
  }
@@ -15,6 +15,10 @@ export declare class Sketch extends SceneObject implements Extrudable {
15
15
  getTangentAt(currentObj: GeometrySceneObject): Point2D;
16
16
  getPositionAt(currentObj: GeometrySceneObject): Point2D;
17
17
  getPreviousPosition(currentObj: GeometrySceneObject, count?: number): Point2D;
18
+ getPreviousState(currentObj: GeometrySceneObject, count?: number): {
19
+ position: Point2D;
20
+ tangent: Point2D;
21
+ };
18
22
  getLastPosition(scope?: Set<SceneObject>): Point2D;
19
23
  build(context?: BuildSceneObjectContext): void;
20
24
  getEdges(): Edge[];
@@ -78,6 +78,31 @@ export class Sketch extends SceneObject {
78
78
  }
79
79
  return this.getStartPoint();
80
80
  }
81
+ getPreviousState(currentObj, count = 1) {
82
+ const children = this.getChildren();
83
+ const previous = children.slice(0, children.indexOf(currentObj));
84
+ let remaining = count;
85
+ for (let i = previous.length - 1; i >= 0; i--) {
86
+ const pos = previous[i].getState('current-position');
87
+ if (pos) {
88
+ if (remaining === 0) {
89
+ for (let j = i; j >= 0; j--) {
90
+ const prev = previous[j];
91
+ if (!(prev instanceof GeometrySceneObject)) {
92
+ continue;
93
+ }
94
+ const t = prev.getTangent();
95
+ if (t) {
96
+ return { position: pos, tangent: t };
97
+ }
98
+ }
99
+ return { position: pos, tangent: new Point2D(1, 0) };
100
+ }
101
+ remaining--;
102
+ }
103
+ }
104
+ return { position: this.getStartPoint(), tangent: new Point2D(1, 0) };
105
+ }
81
106
  getLastPosition(scope) {
82
107
  let children = this.getChildren().slice();
83
108
  if (scope) {
@@ -1,13 +1,17 @@
1
1
  import { SceneObject } from "../../common/scene-object.js";
2
2
  import { PlaneObjectBase } from "../plane-renderable-base.js";
3
3
  import { ExtrudableGeometryBase } from "./extrudable-base.js";
4
+ import { LazyVertex } from "../lazy-vertex.js";
4
5
  import { ISlot } from "../../core/interfaces.js";
5
6
  export declare class Slot extends ExtrudableGeometryBase implements ISlot {
6
7
  distance: number;
7
8
  radius: number;
8
9
  private _center;
9
10
  private _angle;
11
+ private _startPoint;
12
+ private _endPoint;
10
13
  constructor(distance: number, radius: number, targetPlane?: PlaneObjectBase);
14
+ static fromTwoPoints(start: LazyVertex, end: LazyVertex, radius: number, targetPlane?: PlaneObjectBase | null): Slot;
11
15
  centered(value?: boolean): this;
12
16
  rotate(angle: number): this;
13
17
  build(): void;
@@ -20,5 +24,6 @@ export declare class Slot extends ExtrudableGeometryBase implements ISlot {
20
24
  radius: number;
21
25
  centered: boolean;
22
26
  angle: number;
27
+ hasTwoPoints: boolean;
23
28
  };
24
29
  }
@@ -1,5 +1,6 @@
1
1
  import { Point2D } from "../../math/point.js";
2
2
  import { Geometry } from "../../oc/geometry.js";
3
+ import { Vertex } from "../../common/vertex.js";
3
4
  import { rad } from "../../helpers/math-helpers.js";
4
5
  import { ExtrudableGeometryBase } from "./extrudable-base.js";
5
6
  export class Slot extends ExtrudableGeometryBase {
@@ -7,11 +8,19 @@ export class Slot extends ExtrudableGeometryBase {
7
8
  radius;
8
9
  _center = false;
9
10
  _angle = 0;
11
+ _startPoint = null;
12
+ _endPoint = null;
10
13
  constructor(distance, radius, targetPlane = null) {
11
14
  super(targetPlane);
12
15
  this.distance = distance;
13
16
  this.radius = radius;
14
17
  }
18
+ static fromTwoPoints(start, end, radius, targetPlane = null) {
19
+ const slot = new Slot(0, radius, targetPlane);
20
+ slot._startPoint = start;
21
+ slot._endPoint = end;
22
+ return slot;
23
+ }
15
24
  centered(value = true) {
16
25
  this._center = value;
17
26
  return this;
@@ -21,21 +30,28 @@ export class Slot extends ExtrudableGeometryBase {
21
30
  return this;
22
31
  }
23
32
  build() {
24
- if (this.distance < 0) {
25
- throw new Error("Slot distance must be positive");
33
+ if (this._startPoint && this._endPoint) {
34
+ const s = this._startPoint.asPoint2D();
35
+ const e = this._endPoint.asPoint2D();
36
+ const dx = e.x - s.x;
37
+ const dy = e.y - s.y;
38
+ this.distance = Math.sqrt(dx * dx + dy * dy);
39
+ this._angle = Math.atan2(dy, dx) * 180 / Math.PI;
26
40
  }
41
+ const absDistance = Math.abs(this.distance);
42
+ const flipAngle = this.distance < 0 ? 180 : 0;
27
43
  const plane = this.targetPlane?.getPlane() || this.getParent().getPlane();
28
44
  const localToWorld = plane.localToWorld.bind(plane);
29
45
  let leftCenter = this.targetPlane
30
46
  ? plane.worldToLocal(this.targetPlane.getPlaneCenter())
31
47
  : this.getCurrentPosition();
32
48
  if (this._center) {
33
- const angleRad = rad(this._angle);
49
+ const angleRad = rad(this._angle + flipAngle);
34
50
  const cos = Math.cos(angleRad);
35
51
  const sin = Math.sin(angleRad);
36
- leftCenter = leftCenter.translate(-this.distance / 2 * cos, -this.distance / 2 * sin);
52
+ leftCenter = leftCenter.translate(-absDistance / 2 * cos, -absDistance / 2 * sin);
37
53
  }
38
- const angleRad = rad(this._angle);
54
+ const angleRad = rad(this._angle + flipAngle);
39
55
  const cos = Math.cos(angleRad);
40
56
  const sin = Math.sin(angleRad);
41
57
  // Direction along the slot axis
@@ -44,7 +60,7 @@ export class Slot extends ExtrudableGeometryBase {
44
60
  // Perpendicular direction (90 degrees CCW)
45
61
  const perpX = -sin;
46
62
  const perpY = cos;
47
- const rightCenter = new Point2D(leftCenter.x + this.distance * dirX, leftCenter.y + this.distance * dirY);
63
+ const rightCenter = new Point2D(leftCenter.x + absDistance * dirX, leftCenter.y + absDistance * dirY);
48
64
  // Four key points where lines meet arcs
49
65
  const topLeft = new Point2D(leftCenter.x + this.radius * perpX, leftCenter.y + this.radius * perpY);
50
66
  const topRight = new Point2D(rightCenter.x + this.radius * perpX, rightCenter.y + this.radius * perpY);
@@ -65,6 +81,12 @@ export class Slot extends ExtrudableGeometryBase {
65
81
  Geometry.makeEdgeFromCurve(leftArc),
66
82
  ];
67
83
  this.addShapes(edges);
84
+ const leftCenterVertex = Vertex.fromPoint2D(leftCenter);
85
+ leftCenterVertex.markAsMetaShape();
86
+ this.addShape(leftCenterVertex);
87
+ const rightCenterVertex = Vertex.fromPoint2D(rightCenter);
88
+ rightCenterVertex.markAsMetaShape();
89
+ this.addShape(rightCenterVertex);
68
90
  if (this.sketch) {
69
91
  if (this._center) {
70
92
  this.setCurrentPosition(this.getCurrentPosition());
@@ -85,7 +107,13 @@ export class Slot extends ExtrudableGeometryBase {
85
107
  }
86
108
  createCopy(remap) {
87
109
  const targetPlane = this.targetPlane ? (remap.get(this.targetPlane) || this.targetPlane) : null;
88
- const s = new Slot(this.distance, this.radius, targetPlane);
110
+ let s;
111
+ if (this._startPoint && this._endPoint) {
112
+ s = Slot.fromTwoPoints(this._startPoint, this._endPoint, this.radius, targetPlane);
113
+ }
114
+ else {
115
+ s = new Slot(this.distance, this.radius, targetPlane);
116
+ }
89
117
  s.centered(this._center);
90
118
  s.rotate(this._angle);
91
119
  return s;
@@ -103,6 +131,22 @@ export class Slot extends ExtrudableGeometryBase {
103
131
  if (this.targetPlane && other.targetPlane && !this.targetPlane.compareTo(other.targetPlane)) {
104
132
  return false;
105
133
  }
134
+ if ((this._startPoint === null) !== (other._startPoint === null)) {
135
+ return false;
136
+ }
137
+ if ((this._endPoint === null) !== (other._endPoint === null)) {
138
+ return false;
139
+ }
140
+ if (this._startPoint && other._startPoint && !this._startPoint.compareTo(other._startPoint)) {
141
+ return false;
142
+ }
143
+ if (this._endPoint && other._endPoint && !this._endPoint.compareTo(other._endPoint)) {
144
+ return false;
145
+ }
146
+ if (this._startPoint && this._endPoint) {
147
+ return this.radius === other.radius &&
148
+ this._center === other._center;
149
+ }
106
150
  return this.distance === other.distance &&
107
151
  this.radius === other.radius &&
108
152
  this._center === other._center &&
@@ -114,6 +158,7 @@ export class Slot extends ExtrudableGeometryBase {
114
158
  radius: this.radius,
115
159
  centered: this._center,
116
160
  angle: this._angle,
161
+ hasTwoPoints: this._startPoint !== null && this._endPoint !== null,
117
162
  };
118
163
  }
119
164
  }