@reekon-tools/boldr-cad 0.1.0 → 1.0.0

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 (547) hide show
  1. package/README.md +81 -9
  2. package/dist/app/edit.d.ts +12 -1
  3. package/dist/app/edit.js +96 -11
  4. package/dist/app/edit.js.map +1 -1
  5. package/dist/app/state.d.ts +31 -4
  6. package/dist/app/state.js +31 -2
  7. package/dist/app/state.js.map +1 -1
  8. package/dist/data/memory.d.ts +5 -0
  9. package/dist/data/memory.js +2 -2
  10. package/dist/data/memory.js.map +1 -1
  11. package/dist/data/provider.d.ts +19 -7
  12. package/dist/data/sync.d.ts +7 -2
  13. package/dist/data/sync.js +115 -64
  14. package/dist/data/sync.js.map +1 -1
  15. package/dist/editor/actions.d.ts +1 -2
  16. package/dist/editor/actions.js +391 -113
  17. package/dist/editor/actions.js.map +1 -1
  18. package/dist/editor/confirm.d.ts +23 -0
  19. package/dist/editor/confirm.js +35 -0
  20. package/dist/editor/confirm.js.map +1 -0
  21. package/dist/editor/elevation.d.ts +191 -90
  22. package/dist/editor/elevation.js +812 -616
  23. package/dist/editor/elevation.js.map +1 -1
  24. package/dist/editor/face.d.ts +73 -0
  25. package/dist/editor/face.js +269 -0
  26. package/dist/editor/face.js.map +1 -0
  27. package/dist/editor/files.d.ts +6 -0
  28. package/dist/editor/files.js +50 -4
  29. package/dist/editor/files.js.map +1 -1
  30. package/dist/editor/gestures.d.ts +18 -3
  31. package/dist/editor/gestures.js +145 -30
  32. package/dist/editor/gestures.js.map +1 -1
  33. package/dist/editor/keypad.d.ts +19 -4
  34. package/dist/editor/keypad.js +39 -9
  35. package/dist/editor/keypad.js.map +1 -1
  36. package/dist/editor/keys.js +36 -4
  37. package/dist/editor/keys.js.map +1 -1
  38. package/dist/editor/laser.d.ts +20 -4
  39. package/dist/editor/laser.js +104 -21
  40. package/dist/editor/laser.js.map +1 -1
  41. package/dist/editor/linkActions.d.ts +49 -0
  42. package/dist/editor/linkActions.js +210 -0
  43. package/dist/editor/linkActions.js.map +1 -0
  44. package/dist/editor/linkFollow.d.ts +2 -0
  45. package/dist/editor/linkFollow.js +53 -0
  46. package/dist/editor/linkFollow.js.map +1 -0
  47. package/dist/editor/linkWrite.d.ts +13 -0
  48. package/dist/editor/linkWrite.js +84 -0
  49. package/dist/editor/linkWrite.js.map +1 -0
  50. package/dist/editor/links.d.ts +84 -0
  51. package/dist/editor/links.js +130 -0
  52. package/dist/editor/links.js.map +1 -0
  53. package/dist/editor/markup.d.ts +29 -3
  54. package/dist/editor/markup.js +95 -10
  55. package/dist/editor/markup.js.map +1 -1
  56. package/dist/editor/mode.d.ts +40 -2
  57. package/dist/editor/mode.js +71 -4
  58. package/dist/editor/mode.js.map +1 -1
  59. package/dist/editor/ports.d.ts +36 -6
  60. package/dist/editor/ports.js +10 -1
  61. package/dist/editor/ports.js.map +1 -1
  62. package/dist/editor/project.d.ts +38 -6
  63. package/dist/editor/project.js +167 -27
  64. package/dist/editor/project.js.map +1 -1
  65. package/dist/editor/selection.d.ts +10 -0
  66. package/dist/editor/selection.js +62 -8
  67. package/dist/editor/selection.js.map +1 -1
  68. package/dist/editor/status.js +3 -0
  69. package/dist/editor/status.js.map +1 -1
  70. package/dist/editor/tooltips.d.ts +5 -0
  71. package/dist/editor/tooltips.js +48 -0
  72. package/dist/editor/tooltips.js.map +1 -0
  73. package/dist/editor/view3d.d.ts +17 -0
  74. package/dist/editor/view3d.js +135 -0
  75. package/dist/editor/view3d.js.map +1 -0
  76. package/dist/editor/view3dState.d.ts +39 -0
  77. package/dist/editor/view3dState.js +44 -0
  78. package/dist/editor/view3dState.js.map +1 -0
  79. package/dist/export/dxf.js +1 -1
  80. package/dist/export/dxf.js.map +1 -1
  81. package/dist/export/pdf.js +17 -12
  82. package/dist/export/pdf.js.map +1 -1
  83. package/dist/export/plan.d.ts +23 -6
  84. package/dist/export/plan.js +127 -44
  85. package/dist/export/plan.js.map +1 -1
  86. package/dist/index.d.ts +5 -0
  87. package/dist/index.js +2 -0
  88. package/dist/index.js.map +1 -1
  89. package/dist/model/constraints.js +15 -4
  90. package/dist/model/constraints.js.map +1 -1
  91. package/dist/model/demo.js +3 -3
  92. package/dist/model/demo.js.map +1 -1
  93. package/dist/model/elevation.d.ts +6 -9
  94. package/dist/model/elevation.js +5 -13
  95. package/dist/model/elevation.js.map +1 -1
  96. package/dist/model/faces.d.ts +58 -0
  97. package/dist/model/faces.js +197 -0
  98. package/dist/model/faces.js.map +1 -0
  99. package/dist/model/facesolve.d.ts +72 -0
  100. package/dist/model/facesolve.js +369 -0
  101. package/dist/model/facesolve.js.map +1 -0
  102. package/dist/model/history.d.ts +3 -2
  103. package/dist/model/history.js +17 -11
  104. package/dist/model/history.js.map +1 -1
  105. package/dist/model/images.d.ts +18 -0
  106. package/dist/model/images.js +26 -0
  107. package/dist/model/images.js.map +1 -0
  108. package/dist/model/links.d.ts +46 -0
  109. package/dist/model/links.js +73 -0
  110. package/dist/model/links.js.map +1 -0
  111. package/dist/model/markup.d.ts +46 -0
  112. package/dist/model/markup.js +71 -0
  113. package/dist/model/markup.js.map +1 -0
  114. package/dist/model/measurement.d.ts +11 -0
  115. package/dist/model/measurement.js +24 -0
  116. package/dist/model/measurement.js.map +1 -0
  117. package/dist/model/openings.d.ts +4 -0
  118. package/dist/model/openings.js +4 -0
  119. package/dist/model/openings.js.map +1 -1
  120. package/dist/model/planGeom.d.ts +72 -2
  121. package/dist/model/planGeom.js +184 -10
  122. package/dist/model/planGeom.js.map +1 -1
  123. package/dist/model/project.js +3 -2
  124. package/dist/model/project.js.map +1 -1
  125. package/dist/model/projectfile.js +2 -1
  126. package/dist/model/projectfile.js.map +1 -1
  127. package/dist/model/rooms.js +2 -2
  128. package/dist/model/rooms.js.map +1 -1
  129. package/dist/model/settings.d.ts +6 -0
  130. package/dist/model/settings.js +2 -2
  131. package/dist/model/settings.js.map +1 -1
  132. package/dist/model/sketch.d.ts +14 -0
  133. package/dist/model/sketch.js +143 -24
  134. package/dist/model/sketch.js.map +1 -1
  135. package/dist/model/smooth.d.ts +6 -0
  136. package/dist/model/smooth.js +52 -0
  137. package/dist/model/smooth.js.map +1 -0
  138. package/dist/model/store.d.ts +12 -0
  139. package/dist/model/store.js +35 -3
  140. package/dist/model/store.js.map +1 -1
  141. package/dist/model/types.d.ts +173 -8
  142. package/dist/model/wallfaces.d.ts +32 -0
  143. package/dist/model/wallfaces.js +133 -20
  144. package/dist/model/wallfaces.js.map +1 -1
  145. package/dist/model/wallshape.d.ts +15 -0
  146. package/dist/model/wallshape.js +89 -0
  147. package/dist/model/wallshape.js.map +1 -0
  148. package/dist/react/BottomBar.d.ts +9 -5
  149. package/dist/react/BottomBar.js +213 -24
  150. package/dist/react/BottomBar.js.map +1 -1
  151. package/dist/react/Buttons.d.ts +13 -2
  152. package/dist/react/Buttons.js +37 -5
  153. package/dist/react/Buttons.js.map +1 -1
  154. package/dist/react/CadEditor.d.ts +46 -3
  155. package/dist/react/CadEditor.js +77 -20
  156. package/dist/react/CadEditor.js.map +1 -1
  157. package/dist/react/ConfirmSheet.d.ts +4 -0
  158. package/dist/react/ConfirmSheet.js +31 -0
  159. package/dist/react/ConfirmSheet.js.map +1 -0
  160. package/dist/react/ConflictSheet.d.ts +2 -1
  161. package/dist/react/ConflictSheet.js +16 -8
  162. package/dist/react/ConflictSheet.js.map +1 -1
  163. package/dist/react/Drawer.d.ts +8 -2
  164. package/dist/react/Drawer.js +167 -38
  165. package/dist/react/Drawer.js.map +1 -1
  166. package/dist/react/Duck.d.ts +13 -0
  167. package/dist/react/Duck.js +45 -0
  168. package/dist/react/Duck.js.map +1 -0
  169. package/dist/react/Empty3D.d.ts +2 -0
  170. package/dist/react/Empty3D.js +39 -0
  171. package/dist/react/Empty3D.js.map +1 -0
  172. package/dist/react/FitScroll.d.ts +6 -0
  173. package/dist/react/FitScroll.js +14 -0
  174. package/dist/react/FitScroll.js.map +1 -0
  175. package/dist/react/Icon.native.d.ts +6 -0
  176. package/dist/react/Icon.native.js +4 -0
  177. package/dist/react/Icon.native.js.map +1 -1
  178. package/dist/react/Icon.web.d.ts +6 -0
  179. package/dist/react/Icon.web.js +4 -0
  180. package/dist/react/Icon.web.js.map +1 -1
  181. package/dist/react/KeypadSheet.d.ts +9 -2
  182. package/dist/react/KeypadSheet.js +174 -42
  183. package/dist/react/KeypadSheet.js.map +1 -1
  184. package/dist/react/LaserTray.js +8 -5
  185. package/dist/react/LaserTray.js.map +1 -1
  186. package/dist/react/MarkupPanel.d.ts +8 -0
  187. package/dist/react/MarkupPanel.js +67 -0
  188. package/dist/react/MarkupPanel.js.map +1 -0
  189. package/dist/react/PageThumb.native.d.ts +7 -2
  190. package/dist/react/PageThumb.native.js +19 -7
  191. package/dist/react/PageThumb.native.js.map +1 -1
  192. package/dist/react/PageThumb.web.d.ts +3 -2
  193. package/dist/react/PageThumb.web.js +4 -4
  194. package/dist/react/PageThumb.web.js.map +1 -1
  195. package/dist/react/PlanCanvas.native.d.ts +1 -1
  196. package/dist/react/PlanCanvas.native.js +134 -36
  197. package/dist/react/PlanCanvas.native.js.map +1 -1
  198. package/dist/react/PlanCanvas.web.js +16 -5
  199. package/dist/react/PlanCanvas.web.js.map +1 -1
  200. package/dist/react/Popovers.d.ts +12 -6
  201. package/dist/react/Popovers.js +53 -26
  202. package/dist/react/Popovers.js.map +1 -1
  203. package/dist/react/Rail.d.ts +24 -0
  204. package/dist/react/Rail.js +324 -34
  205. package/dist/react/Rail.js.map +1 -1
  206. package/dist/react/ShapeMenu.d.ts +7 -0
  207. package/dist/react/ShapeMenu.js +58 -0
  208. package/dist/react/ShapeMenu.js.map +1 -0
  209. package/dist/react/Sheet.d.ts +14 -4
  210. package/dist/react/Sheet.js +54 -9
  211. package/dist/react/Sheet.js.map +1 -1
  212. package/dist/react/Slider.d.ts +18 -0
  213. package/dist/react/Slider.js +27 -0
  214. package/dist/react/Slider.js.map +1 -0
  215. package/dist/react/Toast.js +14 -6
  216. package/dist/react/Toast.js.map +1 -1
  217. package/dist/react/ToolTip.d.ts +7 -0
  218. package/dist/react/ToolTip.js +43 -0
  219. package/dist/react/ToolTip.js.map +1 -0
  220. package/dist/react/TopBar.d.ts +6 -3
  221. package/dist/react/TopBar.js +48 -17
  222. package/dist/react/TopBar.js.map +1 -1
  223. package/dist/react/Tween.d.ts +7 -0
  224. package/dist/react/Tween.js +26 -0
  225. package/dist/react/Tween.js.map +1 -0
  226. package/dist/react/Tween.native.d.ts +6 -0
  227. package/dist/react/Tween.native.js +43 -0
  228. package/dist/react/Tween.native.js.map +1 -0
  229. package/dist/react/View3D.native.d.ts +4 -0
  230. package/dist/react/View3D.native.js +103 -0
  231. package/dist/react/View3D.native.js.map +1 -0
  232. package/dist/react/View3D.web.d.ts +4 -0
  233. package/dist/react/View3D.web.js +151 -0
  234. package/dist/react/View3D.web.js.map +1 -0
  235. package/dist/react/View3DHost.d.ts +143 -0
  236. package/dist/react/View3DHost.js +377 -0
  237. package/dist/react/View3DHost.js.map +1 -0
  238. package/dist/react/corners.d.ts +7 -0
  239. package/dist/react/corners.js +51 -0
  240. package/dist/react/corners.js.map +1 -0
  241. package/dist/react/easing.d.ts +15 -0
  242. package/dist/react/easing.js +10 -0
  243. package/dist/react/easing.js.map +1 -0
  244. package/dist/react/host.d.ts +7 -2
  245. package/dist/react/host.js +8 -3
  246. package/dist/react/host.js.map +1 -1
  247. package/dist/react/hostKeypad.d.ts +56 -0
  248. package/dist/react/hostKeypad.js +5 -0
  249. package/dist/react/hostKeypad.js.map +1 -0
  250. package/dist/react/motion.d.ts +77 -0
  251. package/dist/react/motion.js +173 -0
  252. package/dist/react/motion.js.map +1 -0
  253. package/dist/react/ports.js +90 -8
  254. package/dist/react/ports.js.map +1 -1
  255. package/dist/react/saving.d.ts +15 -0
  256. package/dist/react/saving.js +76 -0
  257. package/dist/react/saving.js.map +1 -0
  258. package/dist/react/store.d.ts +38 -1
  259. package/dist/react/store.js +31 -1
  260. package/dist/react/store.js.map +1 -1
  261. package/dist/react/theme.d.ts +10 -0
  262. package/dist/react/theme.js +8 -0
  263. package/dist/react/theme.js.map +1 -1
  264. package/dist/react/topRow.d.ts +22 -0
  265. package/dist/react/topRow.js +61 -0
  266. package/dist/react/topRow.js.map +1 -0
  267. package/dist/react/touch.d.ts +6 -0
  268. package/dist/react/touch.js +9 -0
  269. package/dist/react/touch.js.map +1 -0
  270. package/dist/render/ctx.d.ts +8 -0
  271. package/dist/render/draw.d.ts +33 -1
  272. package/dist/render/draw.js +593 -215
  273. package/dist/render/draw.js.map +1 -1
  274. package/dist/render/elevation.d.ts +65 -43
  275. package/dist/render/elevation.js +414 -140
  276. package/dist/render/elevation.js.map +1 -1
  277. package/dist/render/figmaIcons.d.ts +12 -0
  278. package/dist/render/figmaIcons.js +21 -0
  279. package/dist/render/figmaIcons.js.map +1 -0
  280. package/dist/render/hit.d.ts +22 -2
  281. package/dist/render/hit.js +168 -25
  282. package/dist/render/hit.js.map +1 -1
  283. package/dist/render/icons.js +69 -10
  284. package/dist/render/icons.js.map +1 -1
  285. package/dist/render/linkTile.d.ts +15 -0
  286. package/dist/render/linkTile.js +369 -0
  287. package/dist/render/linkTile.js.map +1 -0
  288. package/dist/render/markup.d.ts +11 -0
  289. package/dist/render/markup.js +112 -0
  290. package/dist/render/markup.js.map +1 -0
  291. package/dist/render/navigate.d.ts +88 -0
  292. package/dist/render/navigate.js +100 -0
  293. package/dist/render/navigate.js.map +1 -0
  294. package/dist/render/snap.d.ts +20 -1
  295. package/dist/render/snap.js +118 -25
  296. package/dist/render/snap.js.map +1 -1
  297. package/dist/render/squircle.d.ts +8 -0
  298. package/dist/render/squircle.js +71 -0
  299. package/dist/render/squircle.js.map +1 -0
  300. package/dist/render/view.d.ts +115 -2
  301. package/dist/render/view.js +244 -19
  302. package/dist/render/view.js.map +1 -1
  303. package/dist/skia/SkiaDrawCtx.d.ts +30 -2
  304. package/dist/skia/SkiaDrawCtx.js +198 -43
  305. package/dist/skia/SkiaDrawCtx.js.map +1 -1
  306. package/dist/skia/host.d.ts +18 -2
  307. package/dist/skia/host.js +46 -1
  308. package/dist/skia/host.js.map +1 -1
  309. package/dist/solver/equations.js +37 -2
  310. package/dist/solver/equations.js.map +1 -1
  311. package/dist/solver/gauss-newton.d.ts +17 -0
  312. package/dist/solver/gauss-newton.js +144 -36
  313. package/dist/solver/gauss-newton.js.map +1 -1
  314. package/dist/solver/linalg.d.ts +45 -2
  315. package/dist/solver/linalg.js +164 -10
  316. package/dist/solver/linalg.js.map +1 -1
  317. package/dist/solver/types.d.ts +5 -0
  318. package/dist/tools/annotate.d.ts +57 -1
  319. package/dist/tools/annotate.js +64 -43
  320. package/dist/tools/annotate.js.map +1 -1
  321. package/dist/tools/chamfer.d.ts +12 -0
  322. package/dist/tools/chamfer.js +114 -0
  323. package/dist/tools/chamfer.js.map +1 -0
  324. package/dist/tools/corner.d.ts +38 -0
  325. package/dist/tools/corner.js +99 -0
  326. package/dist/tools/corner.js.map +1 -0
  327. package/dist/tools/dim.d.ts +86 -9
  328. package/dist/tools/dim.js +581 -96
  329. package/dist/tools/dim.js.map +1 -1
  330. package/dist/tools/elevation.js +1 -1
  331. package/dist/tools/elevation.js.map +1 -1
  332. package/dist/tools/fillet.d.ts +9 -0
  333. package/dist/tools/fillet.js +86 -0
  334. package/dist/tools/fillet.js.map +1 -0
  335. package/dist/tools/freehand.d.ts +3 -2
  336. package/dist/tools/freehand.js +19 -12
  337. package/dist/tools/freehand.js.map +1 -1
  338. package/dist/tools/index.js +14 -4
  339. package/dist/tools/index.js.map +1 -1
  340. package/dist/tools/line.d.ts +8 -3
  341. package/dist/tools/line.js +66 -30
  342. package/dist/tools/line.js.map +1 -1
  343. package/dist/tools/modify.d.ts +13 -0
  344. package/dist/tools/modify.js +105 -25
  345. package/dist/tools/modify.js.map +1 -1
  346. package/dist/tools/openings.js +22 -15
  347. package/dist/tools/openings.js.map +1 -1
  348. package/dist/tools/registry.d.ts +8 -0
  349. package/dist/tools/registry.js +42 -2
  350. package/dist/tools/registry.js.map +1 -1
  351. package/dist/tools/select.d.ts +77 -3
  352. package/dist/tools/select.js +560 -74
  353. package/dist/tools/select.js.map +1 -1
  354. package/dist/tools/shapes.d.ts +14 -0
  355. package/dist/tools/shapes.js +154 -75
  356. package/dist/tools/shapes.js.map +1 -1
  357. package/dist/units/format.d.ts +10 -2
  358. package/dist/units/format.js +18 -3
  359. package/dist/units/format.js.map +1 -1
  360. package/dist/view3d/build.d.ts +35 -0
  361. package/dist/view3d/build.js +630 -0
  362. package/dist/view3d/build.js.map +1 -0
  363. package/dist/view3d/camera.d.ts +60 -0
  364. package/dist/view3d/camera.js +84 -0
  365. package/dist/view3d/camera.js.map +1 -0
  366. package/dist/view3d/fatLines.d.ts +11 -0
  367. package/dist/view3d/fatLines.js +83 -0
  368. package/dist/view3d/fatLines.js.map +1 -0
  369. package/dist/view3d/lines.d.ts +17 -0
  370. package/dist/view3d/lines.js +271 -0
  371. package/dist/view3d/lines.js.map +1 -0
  372. package/dist/view3d/mesh.d.ts +51 -0
  373. package/dist/view3d/mesh.js +101 -0
  374. package/dist/view3d/mesh.js.map +1 -0
  375. package/dist/view3d/poly.d.ts +16 -0
  376. package/dist/view3d/poly.js +109 -0
  377. package/dist/view3d/poly.js.map +1 -0
  378. package/dist/view3d/viewer.d.ts +86 -0
  379. package/dist/view3d/viewer.js +350 -0
  380. package/dist/view3d/viewer.js.map +1 -0
  381. package/package.json +15 -2
  382. package/src/app/edit.ts +83 -12
  383. package/src/app/main.ts +11 -5
  384. package/src/app/state.ts +38 -5
  385. package/src/data/memory.ts +2 -2
  386. package/src/data/provider.ts +15 -7
  387. package/src/data/sync.ts +87 -50
  388. package/src/editor/actions.ts +292 -83
  389. package/src/editor/confirm.ts +58 -0
  390. package/src/editor/elevation.ts +698 -478
  391. package/src/editor/face.ts +227 -0
  392. package/src/editor/files.ts +40 -4
  393. package/src/editor/gestures.ts +147 -31
  394. package/src/editor/keypad.ts +43 -11
  395. package/src/editor/keys.ts +25 -5
  396. package/src/editor/laser.ts +98 -20
  397. package/src/editor/linkActions.ts +194 -0
  398. package/src/editor/linkFollow.ts +43 -0
  399. package/src/editor/linkWrite.ts +77 -0
  400. package/src/editor/links.ts +181 -0
  401. package/src/editor/markup.ts +92 -8
  402. package/src/editor/mode.ts +83 -7
  403. package/src/editor/ports.ts +37 -6
  404. package/src/editor/project.ts +155 -26
  405. package/src/editor/selection.ts +54 -8
  406. package/src/editor/status.ts +2 -0
  407. package/src/editor/tooltips.ts +47 -0
  408. package/src/editor/view3d.ts +119 -0
  409. package/src/editor/view3dState.ts +61 -0
  410. package/src/export/dxf.ts +1 -1
  411. package/src/export/pdf.ts +15 -7
  412. package/src/export/plan.ts +119 -35
  413. package/src/index.ts +5 -0
  414. package/src/input/keyboard.ts +2 -4
  415. package/src/model/constraints.ts +13 -4
  416. package/src/model/demo.ts +3 -3
  417. package/src/model/elevation.ts +6 -16
  418. package/src/model/faces.ts +153 -0
  419. package/src/model/facesolve.ts +322 -0
  420. package/src/model/history.ts +19 -11
  421. package/src/model/images.ts +35 -0
  422. package/src/model/links.ts +77 -0
  423. package/src/model/markup.ts +80 -0
  424. package/src/model/measurement.ts +26 -0
  425. package/src/model/openings.ts +6 -0
  426. package/src/model/planGeom.ts +174 -8
  427. package/src/model/project.ts +3 -2
  428. package/src/model/projectfile.ts +2 -1
  429. package/src/model/rooms.ts +1 -1
  430. package/src/model/settings.ts +8 -2
  431. package/src/model/sketch.ts +101 -14
  432. package/src/model/smooth.ts +51 -0
  433. package/src/model/store.ts +35 -4
  434. package/src/model/types.ts +170 -8
  435. package/src/model/wallfaces.ts +126 -17
  436. package/src/model/wallshape.ts +97 -0
  437. package/src/react/BottomBar.tsx +263 -46
  438. package/src/react/Buttons.tsx +50 -9
  439. package/src/react/CadEditor.tsx +133 -30
  440. package/src/react/ConfirmSheet.tsx +38 -0
  441. package/src/react/ConflictSheet.tsx +19 -12
  442. package/src/react/Drawer.tsx +213 -110
  443. package/src/react/Duck.tsx +50 -0
  444. package/src/react/Empty3D.tsx +32 -0
  445. package/src/react/FitScroll.tsx +19 -0
  446. package/src/react/Icon.native.tsx +5 -0
  447. package/src/react/Icon.web.tsx +5 -0
  448. package/src/react/KeypadSheet.tsx +253 -60
  449. package/src/react/LaserTray.tsx +12 -8
  450. package/src/react/MarkupPanel.tsx +150 -0
  451. package/src/react/PageThumb.native.tsx +18 -7
  452. package/src/react/PageThumb.web.tsx +4 -4
  453. package/src/react/PlanCanvas.native.tsx +151 -34
  454. package/src/react/PlanCanvas.web.tsx +15 -5
  455. package/src/react/Popovers.tsx +116 -51
  456. package/src/react/Rail.tsx +360 -27
  457. package/src/react/ShapeMenu.tsx +66 -0
  458. package/src/react/Sheet.tsx +56 -11
  459. package/src/react/Slider.tsx +48 -0
  460. package/src/react/Toast.tsx +16 -9
  461. package/src/react/ToolTip.tsx +49 -0
  462. package/src/react/TopBar.tsx +97 -39
  463. package/src/react/Tween.native.tsx +36 -0
  464. package/src/react/Tween.tsx +24 -0
  465. package/src/react/View3D.native.tsx +131 -0
  466. package/src/react/View3D.web.tsx +131 -0
  467. package/src/react/View3DHost.ts +367 -0
  468. package/src/react/corners.ts +52 -0
  469. package/src/react/easing.ts +14 -0
  470. package/src/react/host.ts +8 -3
  471. package/src/react/hostKeypad.ts +66 -0
  472. package/src/react/motion.tsx +239 -0
  473. package/src/react/ports.ts +78 -9
  474. package/src/react/saving.ts +80 -0
  475. package/src/react/store.ts +35 -3
  476. package/src/react/theme.ts +8 -0
  477. package/src/react/topRow.ts +71 -0
  478. package/src/react/touch.ts +10 -0
  479. package/src/render/ctx.ts +8 -0
  480. package/src/render/draw.ts +437 -175
  481. package/src/render/elevation.ts +349 -143
  482. package/src/render/figmaIcons.ts +22 -0
  483. package/src/render/hit.ts +140 -21
  484. package/src/render/icons.ts +69 -10
  485. package/src/render/linkTile.ts +297 -0
  486. package/src/render/markup.ts +91 -0
  487. package/src/render/navigate.ts +114 -0
  488. package/src/render/snap.ts +105 -22
  489. package/src/render/squircle.ts +73 -0
  490. package/src/render/view.ts +255 -19
  491. package/src/skia/SkiaDrawCtx.ts +163 -41
  492. package/src/skia/host.ts +46 -1
  493. package/src/solver/equations.ts +37 -2
  494. package/src/solver/gauss-newton.ts +125 -28
  495. package/src/solver/linalg.ts +141 -5
  496. package/src/solver/types.ts +5 -0
  497. package/src/styles.css +91 -42
  498. package/src/tools/annotate.ts +58 -29
  499. package/src/tools/chamfer.ts +103 -0
  500. package/src/tools/corner.ts +99 -0
  501. package/src/tools/dim.ts +524 -92
  502. package/src/tools/elevation.ts +1 -1
  503. package/src/tools/fillet.ts +82 -0
  504. package/src/tools/freehand.ts +20 -16
  505. package/src/tools/index.ts +17 -4
  506. package/src/tools/line.ts +59 -23
  507. package/src/tools/modify.ts +86 -24
  508. package/src/tools/openings.ts +8 -13
  509. package/src/tools/registry.ts +27 -2
  510. package/src/tools/select.ts +424 -75
  511. package/src/tools/shapes.ts +119 -58
  512. package/src/ui/ctx.ts +2 -1
  513. package/src/ui/domPorts.ts +23 -4
  514. package/src/ui/drawer.ts +6 -26
  515. package/src/ui/elevation.ts +10 -101
  516. package/src/ui/keypad.ts +8 -14
  517. package/src/ui/laser.ts +2 -2
  518. package/src/ui/menu.ts +63 -17
  519. package/src/ui/noteinput.ts +3 -3
  520. package/src/ui/opacity.ts +108 -0
  521. package/src/ui/palette.ts +67 -12
  522. package/src/ui/rail.ts +21 -7
  523. package/src/ui/topbar.ts +4 -14
  524. package/src/units/format.ts +18 -3
  525. package/src/view3d/build.ts +607 -0
  526. package/src/view3d/camera.ts +113 -0
  527. package/src/view3d/fatLines.ts +83 -0
  528. package/src/view3d/lines.ts +258 -0
  529. package/src/view3d/mesh.ts +111 -0
  530. package/src/view3d/poly.ts +104 -0
  531. package/src/view3d/viewer.ts +348 -0
  532. package/dist/react/ElevCanvas.native.d.ts +0 -3
  533. package/dist/react/ElevCanvas.native.js +0 -58
  534. package/dist/react/ElevCanvas.native.js.map +0 -1
  535. package/dist/react/ElevCanvas.web.d.ts +0 -4
  536. package/dist/react/ElevCanvas.web.js +0 -44
  537. package/dist/react/ElevCanvas.web.js.map +0 -1
  538. package/dist/react/ElevationSheet.d.ts +0 -2
  539. package/dist/react/ElevationSheet.js +0 -38
  540. package/dist/react/ElevationSheet.js.map +0 -1
  541. package/dist/render/elevationView.d.ts +0 -3
  542. package/dist/render/elevationView.js +0 -100
  543. package/dist/render/elevationView.js.map +0 -1
  544. package/src/react/ElevCanvas.native.tsx +0 -68
  545. package/src/react/ElevCanvas.web.tsx +0 -42
  546. package/src/react/ElevationSheet.tsx +0 -88
  547. package/src/render/elevationView.ts +0 -73
@@ -1,3 +1,5 @@
1
+ import { targetImage } from '../editor/project';
2
+ import { imageById } from '../model/images';
1
3
  import { strokeToLines } from './freehand';
2
4
  import { afterEdit } from '../app/edit';
3
5
  import { ed } from '../app/state';
@@ -5,10 +7,12 @@ import { hyp } from '../model/geom';
5
7
  import { dropHistory, pushHistory } from '../model/history';
6
8
  import { uid } from '../model/ids';
7
9
  import { page, SK } from '../model/store';
8
- import type { NoteType } from '../model/types';
10
+ import { dashFor, DEFAULT_NOTE_WIDTH, NOTE_TEXT_IN, noteLook, shapeOutline } from '../model/markup';
11
+ import type { Note, NoteType } from '../model/types';
9
12
  import { cloudPath } from '../render/draw';
13
+ import { strokeMarkup, strokeOutline } from '../render/markup';
10
14
  import { TH } from '../render/theme';
11
- import { fitView, mainT, requestDraw, S, s2w, underlayH } from '../render/view';
15
+ import { fitView, mainT, requestDraw, requestOverlayDraw, S, s2w, underlayH } from '../render/view';
12
16
  import { tools, type PEv, type Tool } from './registry';
13
17
  import { hitAny } from '../render/hit';
14
18
  import { selectTool } from './select';
@@ -16,6 +20,15 @@ import { setMode } from '../editor/mode';
16
20
  import { hideNoteInput, openKeypad, setCursor, showNoteInput, toast, updateHint } from '../editor/ports';
17
21
  import { selectionHint } from '../editor/selection';
18
22
 
23
+ /** A new note in the current markup look (text takes no stroke width). */
24
+ function newNote(type: NoteType, id = ''): Note {
25
+ const n: Note = { id, type, color: ed.noteColor };
26
+ if (type !== 'text' && ed.noteWidth !== DEFAULT_NOTE_WIDTH) n.width = ed.noteWidth;
27
+ if (type === 'text' && ed.noteTextIn !== NOTE_TEXT_IN) n.ts = ed.noteTextIn;
28
+ if (ed.noteStyle !== 'solid') n.style = ed.noteStyle;
29
+ return n;
30
+ }
31
+
19
32
  /** Markup select: the draft select tool with its own state and hint. */
20
33
  export const makeAselectTool = (): Tool => Object.assign({}, selectTool, {
21
34
  st: {},
@@ -41,12 +54,9 @@ export const penTool = {
41
54
  prev() {
42
55
  const pts = this.st!.pts;
43
56
  ed.preview = (c, T) => {
44
- c.save(); c.strokeStyle = ed.noteColor; c.lineWidth = 3; c.lineCap = 'round'; c.lineJoin = 'round';
45
- c.beginPath();
46
- pts.forEach((p, i) => { const [x, y] = S(T, p[0], p[1]); i ? c.lineTo(x, y) : c.moveTo(x, y); });
47
- c.stroke(); c.restore();
57
+ strokeMarkup(c, pts.map(p => S(T, p[0], p[1])), noteLook(newNote('pen')));
48
58
  };
49
- requestDraw();
59
+ requestOverlayDraw();
50
60
  },
51
61
  up() {
52
62
  if (!this.st) return;
@@ -55,14 +65,16 @@ export const penTool = {
55
65
  if (pts.length < 2) { requestDraw(); return; }
56
66
  pushHistory();
57
67
  const id = uid('n');
58
- SK().notes.push({ id, type: 'pen', pts, color: ed.noteColor });
59
- ed.sel = [{ type: 'note', id }];
68
+ SK().notes.push({ ...newNote('pen'), id, pts });
69
+ // (drawn, not selected: nothing new is highlighted — tap it with Select to change it)
70
+ ed.sel = [];
60
71
  afterEdit();
61
72
  },
62
73
  hint() { return 'Draw freehand markup. Select a stroke to turn it into straight, constrained lines.'; },
63
74
  };
64
75
 
65
- function dragShapeTool(type: Extract<NoteType, 'arrow' | 'cloud'>, hintText: string) {
76
+ /** Markup drawn by a drag: a line or arrow from end to end, or a cloud or shape filling the box the drag makes. */
77
+ function dragShapeTool(type: Exclude<NoteType, 'pen' | 'text'>, hintText: string) {
66
78
  return {
67
79
  st: null as { a: [number, number]; b: [number, number] } | null,
68
80
  reset() { this.st = null; ed.preview = null; },
@@ -74,24 +86,27 @@ function dragShapeTool(type: Extract<NoteType, 'arrow' | 'cloud'>, hintText: str
74
86
  this.st.b = s2w(ev.sx, ev.sy);
75
87
  const { a, b } = this.st as { a: [number, number]; b: [number, number] };
76
88
  ed.preview = (c, T) => {
77
- c.save(); c.strokeStyle = ed.noteColor; c.fillStyle = ed.noteColor; c.lineWidth = type === 'cloud' ? 2.5 : 3; c.lineCap = 'round';
78
- const [x1, y1] = S(T, a[0], a[1]), [x2, y2] = S(T, b[0], b[1]);
79
- if (type === 'cloud') { cloudPath(c, Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1)); c.stroke(); }
80
- else { c.beginPath(); c.moveTo(x1, y1); c.lineTo(x2, y2); c.stroke(); }
81
- c.restore();
89
+ const look = noteLook(newNote(type)), [x1, y1] = S(T, a[0], a[1]), [x2, y2] = S(T, b[0], b[1]);
90
+ const outline = shapeOutline({ ...newNote(type), pts: [a, b] });
91
+ if (outline) strokeOutline(c, outline.map(p => S(T, p[0], p[1])), look);
92
+ else if (type === 'cloud') {
93
+ c.save(); c.strokeStyle = look.color; c.lineWidth = look.width; c.lineCap = 'round'; c.setLineDash(dashFor(look.style, look.width));
94
+ cloudPath(c, Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1)); c.stroke();
95
+ c.restore();
96
+ } else strokeMarkup(c, [[x1, y1], [x2, y2]], look);
82
97
  };
83
- requestDraw();
98
+ requestOverlayDraw();
84
99
  },
85
100
  up() {
86
101
  if (!this.st) return;
87
102
  const { a, b } = this.st as { a: [number, number]; b: [number, number] };
88
103
  this.reset!();
89
104
  const T = mainT();
90
- if (hyp(a[0] - b[0], a[1] - b[1]) * T.s < 10) { requestDraw(); if (type === 'arrow') toast('Drag to draw an arrow.'); return; }
105
+ if (hyp(a[0] - b[0], a[1] - b[1]) * T.s < 10) { requestDraw(); if (type !== 'cloud') toast('Drag to draw ' + DRAG_NAME[type] + '.', { warn: true }); return; }
91
106
  pushHistory();
92
107
  const id = uid('n');
93
- SK().notes.push({ id, type, pts: [a, b], color: ed.noteColor });
94
- ed.sel = [{ type: 'note', id }];
108
+ SK().notes.push({ ...newNote(type), id, pts: [a, b] });
109
+ ed.sel = [];
95
110
  afterEdit();
96
111
  },
97
112
  hint() { return hintText; },
@@ -100,6 +115,13 @@ function dragShapeTool(type: Extract<NoteType, 'arrow' | 'cloud'>, hintText: str
100
115
 
101
116
  export const arrowTool = dragShapeTool('arrow', 'Drag from the note toward what it points at.');
102
117
  export const cloudTool = dragShapeTool('cloud', 'Drag a box around the area to flag. Revision clouds read like a red pen on paper.');
118
+ export const markLineTool = dragShapeTool('line', 'Drag from one end of the line to the other.');
119
+ export const markRectTool = dragShapeTool('rect', 'Drag from one corner of the rectangle to the opposite one.');
120
+ export const markTriangleTool = dragShapeTool('triangle', 'Drag a box for the triangle to fill, its point at the top.');
121
+ export const markCircleTool = dragShapeTool('ellipse', 'Drag a box for the circle to fill (an oval when the box isn’t square).');
122
+
123
+ /** What a drag too short to draw anything was meant to draw ('Drag to draw …'). */
124
+ const DRAG_NAME: Record<string, string> = { arrow: 'an arrow', line: 'a line', rect: 'a rectangle', triangle: 'a triangle', ellipse: 'a circle' };
103
125
 
104
126
  export const textTool = {
105
127
  reset() { hideNoteInput(); },
@@ -112,19 +134,26 @@ export const textTool = {
112
134
  if (!txt) return;
113
135
  pushHistory();
114
136
  const id = uid('n');
115
- SK().notes.push({ id, type: 'text', x, y, text: txt, color: ed.noteColor });
116
- ed.sel = [{ type: 'note', id }];
137
+ SK().notes.push({ ...newNote('text'), id, x, y, text: txt });
138
+ ed.sel = [];
117
139
  afterEdit();
118
140
  });
119
141
  },
120
142
  hint() { return 'Tap where the note goes, type, then press Enter.'; },
121
143
  };
122
144
 
123
- /** Two-point underlay calibration: tap two points, type the real distance, the image rescales. */
145
+ /**
146
+ * Two-point image calibration: tap two points, type the real distance, the image rescales. It scales the image that
147
+ * was selected when it started (or the page's only image); the others keep their own scale.
148
+ */
124
149
  export const calibTool = {
125
- st: { pts: [] as [number, number][] },
126
- enter() { if (!page().underlay) toast('Add a photo or plan first, then calibrate it.'); },
127
- reset() { this.st = { pts: [] }; ed.preview = null; },
150
+ st: { pts: [] as [number, number][], id: null as string | null },
151
+ enter() {
152
+ const u = targetImage();
153
+ this.st = { pts: [], id: u?.id ?? null };
154
+ if (!u) toast(page().underlays.length ? 'Select the image to scale first, then Set scale.' : 'Add a photo or plan first, then calibrate it.', { warn: true });
155
+ },
156
+ reset() { this.st = { pts: [], id: this.st?.id ?? null }; ed.preview = null; },
128
157
  busy() { return this.st.pts.length > 0; },
129
158
  hover(ev: PEv) {
130
159
  if (this.st.pts.length === 1) {
@@ -134,14 +163,14 @@ export const calibTool = {
134
163
  c.save(); c.strokeStyle = TH.purple; c.lineWidth = 2; c.setLineDash([6, 4]);
135
164
  c.beginPath(); c.moveTo(x1, y1); c.lineTo(x2, y2); c.stroke(); c.restore();
136
165
  };
137
- requestDraw();
166
+ requestOverlayDraw();
138
167
  }
139
168
  },
140
169
  down() {},
141
170
  move(ev: PEv) { this.hover(ev); },
142
171
  up(ev: PEv) {
143
- const u = page().underlay;
144
- if (!u) { toast('Add a photo or plan first.'); return; }
172
+ const u = this.st.id ? imageById(this.st.id) : undefined;
173
+ if (!u) { toast(page().underlays.length ? 'Select the image to scale first, then Set scale.' : 'Add a photo or plan first.', { warn: true }); return; }
145
174
  this.st.pts.push(s2w(ev.sx, ev.sy));
146
175
  updateHint();
147
176
  if (this.st.pts.length < 2) { this.hover(ev); return; }
@@ -172,7 +201,7 @@ export function convertNote(id: string): void {
172
201
  if (!n || !n.pts) return;
173
202
  pushHistory();
174
203
  const r = strokeToLines(n.pts.map(p => ({ x: p[0], y: p[1] })));
175
- if (!r) { dropHistory(); toast('That stroke is too small to turn into lines.'); return; }
204
+ if (!r) { dropHistory(); toast('That stroke is too small to turn into lines.', { warn: true }); return; }
176
205
  sk.notes = sk.notes.filter(x => x.id !== id);
177
206
  setMode('draft');
178
207
  ed.sel = r.lines.map(id => ({ type: 'line' as const, id }));
@@ -0,0 +1,103 @@
1
+ import { afterEdit, tryAddConstraint } from '../app/edit';
2
+ import { ed } from '../app/state';
3
+ import { dropHistory, pushHistory } from '../model/history';
4
+ import { addLine } from '../model/sketch';
5
+ import { replaceSK, SK } from '../model/store';
6
+ import type { Sketch } from '../model/types';
7
+ import { requestDraw } from '../render/view';
8
+ import { solver } from '../solver';
9
+ import { fmtLen } from '../units';
10
+ import { closeKeypad, openKeypad, toast, updateCtx } from '../editor/ports';
11
+ import { cornerAt, cutCorner } from './corner';
12
+
13
+ /*
14
+ Chamfer: cut a corner off straight. Both lines stop a distance back from the corner — the same on both sides
15
+ unless it's set as unequal — and a straight cut joins them (a wall of the same thickness between two walls). A
16
+ chamfer is measured from its corner, so the corner stays as a virtual sharp (dashed lines out to it): the distance
17
+ back along one side is a driving dimension, and "equal" holds the other side to it (or a second dimension, unequal).
18
+ */
19
+
20
+ /** The corner's two lines, if it can be cut off. */
21
+ export const chamferable = (sk: Sketch, pid: string): boolean => !!cornerAt(sk, pid);
22
+
23
+ /** The most a corner can be cut back along each line. */
24
+ export function maxChamfer(sk: Sketch, pid: string): [number, number] {
25
+ const k = cornerAt(sk, pid);
26
+ return k ? [k.len1, k.len2] : [0, 0];
27
+ }
28
+
29
+ /** Show which side a distance is along while it's entered (the line selected). */
30
+ function showSide(lid: string | null): void { ed.sel = lid ? [{ type: 'line', id: lid }] : []; updateCtx(); requestDraw(); }
31
+
32
+ /** The Chamfer action: the keypad for the distance back from the corner (Unequal: one for each side), then the corner is cut off. */
33
+ export function chamferPrompt(pid: string): void {
34
+ const sk = SK(), k = cornerAt(sk, pid);
35
+ if (!k) { toast('Select a corner where two lines meet to cut it off.', { warn: true }); return; }
36
+ const most = Math.min(k.len1, k.len2), start = Math.round(Math.min(6, most * 0.5) * 16) / 16;
37
+ openKeypad({
38
+ title: 'Chamfer', sub: 'Back from the corner along each side — up to ' + fmtLen(most), initial: fmtLen(start), selectAll: true, laser: true,
39
+ extras: () => [{ label: 'Unequal', ariaLabel: 'Unequal: a distance for each side', onPress: () => { closeKeypad(); unequal(pid); } }],
40
+ onDone: d => { chamferCorner(pid, d, d); },
41
+ });
42
+ }
43
+
44
+ /** Unequal: the distance along one side (selected while it's entered), then along the other. */
45
+ function unequal(pid: string): void {
46
+ const k = cornerAt(SK(), pid);
47
+ if (!k) return;
48
+ const back = ed.sel;
49
+ showSide(k.l1);
50
+ openKeypad({
51
+ title: 'Chamfer · first side', sub: 'Along the selected line — up to ' + fmtLen(k.len1), initial: fmtLen(Math.round(Math.min(6, k.len1 * 0.5) * 16) / 16), selectAll: true, laser: true,
52
+ onClose: () => { if (ed.sel.length === 1 && ed.sel[0].id === k.l1) { ed.sel = back; updateCtx(); requestDraw(); } },
53
+ onDone: d1 => {
54
+ showSide(k.l2);
55
+ openKeypad({
56
+ title: 'Chamfer · second side', sub: 'Along the selected line — up to ' + fmtLen(k.len2), initial: fmtLen(Math.round(Math.min(6, k.len2 * 0.5) * 16) / 16), selectAll: true, laser: true,
57
+ onClose: () => { if (ed.sel.length === 1 && ed.sel[0].id === k.l2) { ed.sel = back; updateCtx(); requestDraw(); } },
58
+ onDone: d2 => { chamferCorner(pid, d1, d2); },
59
+ });
60
+ },
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Cut corner `pid` off: `d1` back along its first line, `d2` along its second (the same: an equal chamfer, held equal).
66
+ * Returns the cut's line id, or null (and says why) if it can't.
67
+ */
68
+ export function chamferCorner(pid: string, d1: number, d2: number = d1): string | null {
69
+ const sk = SK(), k = cornerAt(sk, pid);
70
+ if (!k) { toast('Select a corner where two lines meet to cut it off.', { warn: true }); return null; }
71
+ if (!(d1 > 0) || !(d2 > 0)) return null;
72
+ if (d1 >= k.len1 - 1e-6 || d2 >= k.len2 - 1e-6) {
73
+ const most = d1 === d2 ? fmtLen(Math.min(k.len1, k.len2)) : fmtLen(d1 >= k.len1 - 1e-6 ? k.len1 : k.len2);
74
+ toast('That’s more than this corner’s side: ' + most + ' at most.', { warn: true });
75
+ return null;
76
+ }
77
+ pushHistory();
78
+ const before = JSON.stringify(sk), L1 = sk.lines[k.l1], L2 = sk.lines[k.l2], wall = L1.kind === 'wall' && L2.kind === 'wall';
79
+ const cut = cutCorner(sk, k, d1, d2, (t1, t2) => {
80
+ const id = addLine(sk, t1, t2, { kind: wall ? 'wall' : 'line', constr: L1.constr && L2.constr, thick: wall ? L1.thick : undefined });
81
+ return id;
82
+ }, true);
83
+ const { c1, c2 } = cut.sharp!;
84
+ // the distance back along the first side drives it; the second is equal to it, or its own
85
+ const cons = [
86
+ ...cut.cons,
87
+ { type: 'dist' as const, a: cut.t1, b: pid, line: c1, value: d1, driving: true },
88
+ d1 === d2 ? { type: 'eq' as const, l1: c1, l2: c2 } : { type: 'dist' as const, a: cut.t2, b: pid, line: c2, value: d2, driving: true },
89
+ ];
90
+ for (const cn of cons) tryAddConstraint(cn, { silent: true });
91
+ if (!solver.solve(sk).ok) {
92
+ replaceSK(JSON.parse(before) as Sketch);
93
+ dropHistory();
94
+ toast('That corner can’t be cut off with the constraints it has.', { warn: true });
95
+ afterEdit();
96
+ return null;
97
+ }
98
+ ed.sel = [{ type: 'line', id: cut.edge }];
99
+ afterEdit();
100
+ updateCtx(); requestDraw();
101
+ toast('Corner cut off: ' + (d1 === d2 ? fmtLen(d1) + ' each side' : fmtLen(d1) + ' × ' + fmtLen(d2)));
102
+ return cut.edge;
103
+ }
@@ -0,0 +1,99 @@
1
+ import { cornerLines } from '../editor/selection';
2
+ import { hyp } from '../model/geom';
3
+ import { roomEdges } from '../model/rooms';
4
+ import { addLine, addPoint } from '../model/sketch';
5
+ import type { Constraint, Sketch, WallFace, XY } from '../model/types';
6
+
7
+ /*
8
+ Cutting a corner back, for Fillet and Chamfer: where two lines (or walls) meet, each line stops a distance short of
9
+ the corner and something new joins the two ends (an arc, a straight cut). What hangs on the lines follows: doors
10
+ and windows keep their place, level/plumb moves to the new ends, a space around the corner takes the new edge.
11
+ Whatever was measured to the corner keeps its meaning: the corner stays as a "virtual sharp" — dashed lines from
12
+ the new ends out to where the two lines would meet.
13
+ */
14
+
15
+ const norm = (x: number, y: number): XY => { const L = hyp(x, y) || 1; return { x: x / L, y: y / L }; };
16
+
17
+ /** A corner where two lines meet: the point, the two lines with their far ends, directions away from the corner and lengths, and the angle between them. */
18
+ export interface Corner {
19
+ pid: string; P: XY;
20
+ l1: string; l2: string; q1: string; q2: string;
21
+ u1: XY; u2: XY; len1: number; len2: number;
22
+ /** the angle between the lines, radians (0 < th < π) */
23
+ th: number;
24
+ }
25
+
26
+ /** The corner at `pid`, if it can be cut back: two lines meet there, not in a straight line, neither of them a wall face's own edge. */
27
+ export function cornerAt(sk: Sketch, pid: string): Corner | null {
28
+ const pair = cornerLines(sk, pid), P = sk.points[pid];
29
+ if (!pair || !P || P.anchor || pair.some(id => sk.lines[id].anchor)) return null;
30
+ const [l1, l2] = pair, L1 = sk.lines[l1], L2 = sk.lines[l2];
31
+ const q1 = L1.a === pid ? L1.b : L1.a, q2 = L2.a === pid ? L2.b : L2.a, Q1 = sk.points[q1], Q2 = sk.points[q2];
32
+ const u1 = norm(Q1.x - P.x, Q1.y - P.y), u2 = norm(Q2.x - P.x, Q2.y - P.y);
33
+ const th = Math.acos(Math.max(-1, Math.min(1, u1.x * u2.x + u1.y * u2.y)));
34
+ if (!(th > 0.5 * Math.PI / 180 && th < 179.5 * Math.PI / 180)) return null;
35
+ return { pid, P, l1, l2, q1, q2, u1, u2, len1: hyp(Q1.x - P.x, Q1.y - P.y), len2: hyp(Q2.x - P.x, Q2.y - P.y), th };
36
+ }
37
+
38
+ /** Whether anything besides its two lines hangs on the corner (dimensions, constraints, curves, door/window dimensions, a cabinet). */
39
+ function cornerUsed(sk: Sketch, k: Corner): boolean {
40
+ const { pid, l1, l2 } = k;
41
+ return Object.values(sk.cons).some(cn => cn.a === pid || cn.b === pid || cn.p === pid || (cn.type === 'angle' && [cn.l1, cn.l2].includes(l1) && [cn.l1, cn.l2].includes(l2)))
42
+ || Object.values(sk.circles).some(ci => ci.c === pid) || Object.values(sk.arcs).some(a => [a.c, a.a, a.b].includes(pid))
43
+ || Object.values(sk.openings).some(o => (o.dims || []).some(dm => 'pt' in dm.ref && dm.ref.pt === pid))
44
+ || Object.values((sk as WallFace).cabs ?? {}).some(cb => [cb.bl, cb.br, cb.tr, cb.tl].includes(pid));
45
+ }
46
+
47
+ export interface Cut {
48
+ /** the new ends of the two lines */
49
+ t1: string; t2: string;
50
+ /** what joins them */
51
+ edge: string;
52
+ /** the virtual sharp's dashed lines (new end → corner), when the corner is kept */
53
+ sharp: { c1: string; c2: string } | null;
54
+ /** constraints to add for the cut (the dashed lines staying on the lines' lines) */
55
+ cons: Constraint[];
56
+ }
57
+
58
+ /**
59
+ * Cut corner `k` back: line 1 stops `d1` short of it, line 2 `d2`, and `join(t1, t2)` makes what joins the new ends
60
+ * (returning its id). `keepSharp`: keep the corner as a virtual sharp even when nothing else hangs on it (a chamfer
61
+ * is measured from it). The caller has pushed history; it adds the returned constraints and its own, and solves.
62
+ */
63
+ export function cutCorner(sk: Sketch, k: Corner, d1: number, d2: number, join: (t1: string, t2: string) => string, keepSharp = false): Cut {
64
+ const { pid, P, l1, l2, q1, q2, u1, u2 } = k, L1 = sk.lines[l1], L2 = sk.lines[l2];
65
+ // the spaces around the corner, as they are now (their sides are found from the corner's lines)
66
+ const rooms = Object.entries(sk.rooms).map(([id, rm]) => ({ id, i: rm.loop.indexOf(pid), edges: rm.loop.includes(pid) ? roomEdges(sk, rm) : null })).filter(x => x.i >= 0 && x.edges);
67
+ const t1 = addPoint(sk, P.x + u1.x * d1, P.y + u1.y * d1), t2 = addPoint(sk, P.x + u2.x * d2, P.y + u2.y * d2);
68
+ // doors and windows keep their place along a line whose start moves
69
+ for (const o of Object.values(sk.openings)) {
70
+ if (o.line === l1 && L1.a === pid) o.at -= d1;
71
+ if (o.line === l2 && L2.a === pid) o.at -= d2;
72
+ }
73
+ // level/plumb on a line, set between the corner and its other end: between the new end and that end now
74
+ for (const cn of Object.values(sk.cons)) {
75
+ if (cn.type !== 'h' && cn.type !== 'v') continue;
76
+ if ((cn.a === pid && cn.b === q1) || (cn.b === pid && cn.a === q1)) { if (cn.a === pid) cn.a = t1; else cn.b = t1; }
77
+ else if ((cn.a === pid && cn.b === q2) || (cn.b === pid && cn.a === q2)) { if (cn.a === pid) cn.a = t2; else cn.b = t2; }
78
+ }
79
+ if (L1.a === pid) L1.a = t1; else L1.b = t1;
80
+ if (L2.a === pid) L2.a = t2; else L2.b = t2;
81
+ const keep = keepSharp || cornerUsed(sk, k);
82
+ const edge = join(t1, t2), cons: Constraint[] = [];
83
+ let sharp: Cut['sharp'] = null;
84
+ if (keep) {
85
+ const c1 = addLine(sk, t1, pid, { constr: true }), c2 = addLine(sk, t2, pid, { constr: true });
86
+ sharp = { c1, c2 };
87
+ cons.push({ type: 'col', l1, l2: c1 }, { type: 'col', l1: l2, l2: c2 });
88
+ // an angle at the corner is between the dashed lines now (they still meet there)
89
+ for (const cn of Object.values(sk.cons)) if (cn.type === 'angle' && [cn.l1, cn.l2].includes(l1) && [cn.l1, cn.l2].includes(l2)) { cn.l1 = cn.l1 === l1 ? c1 : c2; cn.l2 = cn.l2 === l2 ? c2 : c1; }
90
+ }
91
+ // spaces: the corner becomes the two new ends, with the new edge between them
92
+ for (const rm of rooms) {
93
+ const room = sk.rooms[rm.id], i = rm.i, n = room.loop.length, edges = rm.edges!;
94
+ const into = edges[(i - 1 + n) % n], tIn = into === l1 ? t1 : t2, tOut = into === l1 ? t2 : t1;
95
+ room.loop = [...room.loop.slice(0, i), tIn, tOut, ...room.loop.slice(i + 1)];
96
+ room.edges = [...edges.slice(0, i), edge, ...edges.slice(i)];
97
+ }
98
+ return { t1, t2, edge, sharp, cons };
99
+ }