@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
@@ -3,46 +3,78 @@ import { ed, type Snap } from '../app/state';
3
3
  import { circum, hyp, normAng } from '../model/geom';
4
4
  import { pushHistory } from '../model/history';
5
5
  import { uid } from '../model/ids';
6
- import { addLine, addPoint } from '../model/sketch';
6
+ import { addLine, addPoint, WALL_THICK } from '../model/sketch';
7
+ import { settings } from '../model/settings';
8
+ import { gridSteps } from '../model/planGeom';
7
9
  import { SK } from '../model/store';
8
10
  import type { XY } from '../model/types';
9
11
  import { chip } from '../render/draw';
10
12
  import { snapAt } from '../render/snap';
11
13
  import { TH } from '../render/theme';
12
- import { requestDraw, S } from '../render/view';
14
+ import { mainT, requestDraw, requestOverlayDraw, S } from '../render/view';
13
15
  import { fmtLen } from '../units';
14
- import { applyPending, commitPoint, lengthPreview, PT, rememberHover, type PEv } from './registry';
16
+ import { anchorMark, applyPending, commitPoint, lengthPreview, PT, rememberHover, wallBand, type PEv } from './registry';
15
17
  import { toast, updateHint } from '../editor/ports';
18
+ import { wallBounds } from '../editor/elevation';
19
+ import { drawsWalls } from '../editor/mode';
20
+ import { splitWallsAtEnds } from './modify';
16
21
 
17
22
  const wasDrag = (down: { sx: number; sy: number } | null, ev: PEv) => !!down && hyp(ev.sx - down.sx, ev.sy - down.sy) > 12;
18
23
 
24
+ /**
25
+ * A circle's radius out to a snap: with Snap to grid, a whole number of the grid's steps (round sizes, as its centre
26
+ * and the grid's points are) — unless the edge snapped onto something, which it then goes through.
27
+ */
28
+ function radiusTo(c: XY, s: Snap): number {
29
+ const r = hyp(s.x - c.x, s.y - c.y);
30
+ if (!settings.snapGrid || (s.kind !== 'grid' && s.kind !== 'free')) return r;
31
+ const st = gridSteps(mainT().s).minor;
32
+ return Math.max(st, Math.round(r / st) * st);
33
+ }
34
+
35
+ /** A new circle's or arc's kind: a wall with the Walls toggle on (never a guide), otherwise a line or a guide. */
36
+ const asDrawn = () => (drawsWalls() ? { constr: false, kind: 'wall' as const, thick: WALL_THICK } : { constr: ed.drawConstr });
37
+
38
+ /**
39
+ * Rectangle: two opposite corners, tapped one then the other or dragged from one to the other. As with the arc, each
40
+ * corner is marked as it's placed — the first the moment a finger lands and still once it lifts, the second under the
41
+ * finger as it goes down — so on a touch screen, where nothing hovers between taps, it's clear where it starts.
42
+ */
19
43
  export const rectTool = {
20
- st: { p1: null as Snap | null, down: null as { sx: number; sy: number } | null, anchor: null as Snap | null },
21
- reset() { this.st = { p1: null, down: null, anchor: null }; ed.preview = null; ed.snapState = null; },
44
+ st: { p1: null as Snap | null, down: null as { sx: number; sy: number } | null, anchor: null as Snap | null, pressing: false },
45
+ reset() { this.st = { p1: null, down: null, anchor: null, pressing: false }; ed.preview = null; ed.snapState = null; },
22
46
  busy() { return !!this.st.p1; },
23
47
  hover(ev: PEv) {
24
48
  ed.snapState = snapAt(ev.sx, ev.sy, { touch: ev.touch });
25
49
  rememberHover(ed.snapState);
50
+ const pressing = this.st.pressing, p2: XY = { x: ed.snapState.x, y: ed.snapState.y };
26
51
  // first corner: the one already placed, or (press-and-drag) where the drag started
27
52
  const p1 = this.st.p1 ?? (wasDrag(this.st.down, ev) ? this.st.anchor : null);
28
- if (p1) {
29
- const p2 = { x: ed.snapState.x, y: ed.snapState.y };
30
- ed.preview = (c, T) => {
31
- const [x1, y1] = S(T, p1.x, p1.y), [x2, y2] = S(T, p2.x, p2.y);
53
+ // (nothing yet: a mark only once a corner is being placed — a mouse just moving about gets the snap marker)
54
+ if (!p1 && !pressing) { ed.preview = null; requestOverlayDraw(); return; }
55
+ ed.preview = (c, T) => {
56
+ if (p1) {
57
+ const [x1, y1] = S(T, p1.x, p1.y), [x2, y2] = S(T, p2.x, p2.y), wall = drawsWalls();
58
+ const box = [Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1)] as const;
59
+ if (wall) wallBand(c, T, () => c.rect(...box));
32
60
  c.save(); c.strokeStyle = TH.blue; c.lineWidth = 2;
33
- if (ed.drawConstr) c.setLineDash([6, 5]);
34
- c.strokeRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1));
61
+ if (ed.drawConstr && !wall) c.setLineDash([6, 5]);
62
+ c.strokeRect(...box);
35
63
  c.restore();
36
64
  lengthPreview(c, T, { x: p1.x, y: Math.max(p1.y, p2.y) }, { x: p2.x, y: Math.max(p1.y, p2.y) });
37
65
  lengthPreview(c, T, { x: Math.max(p1.x, p2.x), y: p1.y }, { x: Math.max(p1.x, p2.x), y: p2.y });
38
- };
39
- }
40
- requestDraw();
66
+ anchorMark(c, x1, y1);
67
+ }
68
+ // the corner going down: marked under the finger as it's placed
69
+ if (pressing) { const [x, y] = S(T, p2.x, p2.y); anchorMark(c, x, y); }
70
+ };
71
+ requestOverlayDraw();
41
72
  },
42
- down(ev: PEv) { this.st.down = { sx: ev.sx, sy: ev.sy }; if (!this.st.p1) this.st.anchor = snapAt(ev.sx, ev.sy, { touch: ev.touch }); this.hover(ev); },
73
+ down(ev: PEv) { this.st.pressing = true; this.st.down = { sx: ev.sx, sy: ev.sy }; if (!this.st.p1) this.st.anchor = snapAt(ev.sx, ev.sy, { touch: ev.touch }); this.hover(ev); },
43
74
  move(ev: PEv) { this.hover(ev); },
44
75
  up(ev: PEv) {
45
76
  const st = this.st, dragged = wasDrag(st.down, ev);
77
+ st.pressing = false;
46
78
  const s = snapAt(ev.sx, ev.sy, { touch: ev.touch });
47
79
  if (!st.p1) {
48
80
  st.p1 = dragged ? st.anchor ?? snapAt(st.down!.sx, st.down!.sy, { touch: ev.touch }) : s;
@@ -55,12 +87,16 @@ export const rectTool = {
55
87
  const a = commitPoint(s1), c = commitPoint(s);
56
88
  const pa = PT(a.id), pc = PT(c.id);
57
89
  const b = addPoint(sk, pc.x, pa.y), d = addPoint(sk, pa.x, pc.y);
58
- const o = { constr: ed.drawConstr };
59
- const L = [addLine(sk, a.id, b, o), addLine(sk, b, c.id, o), addLine(sk, c.id, d, o), addLine(sk, d, a.id, o)];
90
+ // (walls with the Walls toggle on; a wall is never a guide)
91
+ const wall = drawsWalls(), o = { kind: wall ? 'wall' as const : 'line' as const, constr: !wall && ed.drawConstr };
92
+ const sides = [addLine(sk, a.id, b, o), addLine(sk, b, c.id, o), addLine(sk, c.id, d, o), addLine(sk, d, a.id, o)];
93
+ // (a wall corner placed partway along another wall splits that one there)
94
+ if (wall) for (const id of sides) splitWallsAtEnds(sk, id);
60
95
  applyPending(a.cons); applyPending(c.cons);
61
96
  for (const k of [{ type: 'h' as const, a: a.id, b }, { type: 'v' as const, a: b, b: c.id }, { type: 'h' as const, a: d, b: c.id }, { type: 'v' as const, a: a.id, b: d }]) tryAddConstraint(k, { silent: true });
62
97
  this.reset();
63
- ed.sel = L.map(id => ({ type: 'line' as const, id }));
98
+ // (drawn, not selected: nothing new is highlighted — tap it to select it)
99
+ ed.sel = [];
64
100
  afterEdit();
65
101
  },
66
102
  hint() { return this.st.p1 ? 'Tap the opposite corner.' : 'Tap or drag two opposite corners. Edges stay square.'; },
@@ -75,27 +111,27 @@ export const circleTool = {
75
111
  // centre: the one already placed, or (press-and-drag) where the drag started
76
112
  const c0 = this.st.c ?? (this.st.down ? this.st.anchor : null);
77
113
  if (c0) {
78
- const edge = { x: ed.snapState.x, y: ed.snapState.y };
79
- const r = hyp(edge.x - c0.x, edge.y - c0.y);
114
+ const r = radiusTo(c0, ed.snapState), raw = hyp(ed.snapState.x - c0.x, ed.snapState.y - c0.y) || 1;
115
+ // (the edge where the circle is: toward the pointer, at the radius it'll have)
116
+ const edge = { x: c0.x + ((ed.snapState.x - c0.x) * r) / raw, y: c0.y + ((ed.snapState.y - c0.y) * r) / raw };
80
117
  ed.preview = (c, T) => {
81
- const [x, y] = S(T, c0.x, c0.y), [ex, ey] = S(T, edge.x, edge.y);
118
+ const [x, y] = S(T, c0.x, c0.y), [ex, ey] = S(T, edge.x, edge.y), wall = drawsWalls();
119
+ if (wall) wallBand(c, T, () => c.arc(x, y, Math.max(0.5, r * T.s), 0, Math.PI * 2));
82
120
  c.save();
83
121
  c.strokeStyle = TH.blue; c.lineWidth = 2;
84
- if (ed.drawConstr) c.setLineDash([6, 5]);
122
+ if (ed.drawConstr && !wall) c.setLineDash([6, 5]);
85
123
  c.beginPath(); c.arc(x, y, Math.max(0.5, r * T.s), 0, Math.PI * 2); c.stroke();
86
124
  // radius from the centre to the pointer
87
125
  c.setLineDash([4, 4]); c.lineWidth = 1.2;
88
126
  c.beginPath(); c.moveTo(x, y); c.lineTo(ex, ey); c.stroke();
89
127
  c.setLineDash([]);
90
- // centre marker: crosshair + dot, always visible
91
- c.lineWidth = 1.5;
92
- c.beginPath(); c.moveTo(x - 9, y); c.lineTo(x + 9, y); c.moveTo(x, y - 9); c.lineTo(x, y + 9); c.stroke();
93
- c.beginPath(); c.arc(x, y, 3.5, 0, Math.PI * 2); c.fillStyle = TH.blue; c.fill();
94
128
  c.restore();
129
+ // centre marker: crosshair + dot, always visible
130
+ anchorMark(c, x, y);
95
131
  chip(c, 'Ø ' + fmtLen(2 * r), x, y - r * T.s - 16, { bg: '#fff', fg: '#0066FF', border: ['#0066FF', 1.5] });
96
132
  };
97
133
  } else ed.preview = null;
98
- requestDraw();
134
+ requestOverlayDraw();
99
135
  },
100
136
  down(ev: PEv) { this.st.down = { sx: ev.sx, sy: ev.sy }; if (!this.st.c) this.st.anchor = snapAt(ev.sx, ev.sy, { touch: ev.touch }); this.hover(ev); },
101
137
  move(ev: PEv) { this.hover(ev); },
@@ -105,60 +141,85 @@ export const circleTool = {
105
141
  st.c = dragged ? st.anchor ?? snapAt(st.down!.sx, st.down!.sy, { touch: ev.touch }) : s;
106
142
  if (!dragged) { this.hover(ev); return; }
107
143
  }
108
- const r = hyp(s.x - st.c!.x, s.y - st.c!.y);
144
+ let r = radiusTo(st.c!, s);
145
+ // in the wall view, a circle stays inside the wall
146
+ const b = wallBounds(), o = st.c!;
147
+ if (b) r = Math.min(r, o.x - b.x0, b.x1 - o.x, o.y - b.y0, b.y1 - o.y);
109
148
  if (r < 0.25) { this.reset(); return; }
110
149
  pushHistory();
111
150
  const cp = commitPoint(st.c!);
112
151
  applyPending(cp.cons);
113
152
  const id = uid('ci');
114
- SK().circles[id] = { c: cp.id, r, constr: ed.drawConstr };
153
+ SK().circles[id] = { c: cp.id, r, ...asDrawn() };
115
154
  if (s.kind === 'point') tryAddConstraint({ type: 'oncircle', p: s.ref!, c: id }, { silent: true });
116
155
  this.reset();
117
- ed.sel = [{ type: 'circle', id }];
156
+ ed.sel = [];
118
157
  afterEdit();
119
158
  },
120
159
  hint() { return this.st.c ? 'Tap the edge, or use Diameter after.' : 'Tap the center, then the edge. Good for columns and core holes.'; },
121
160
  };
122
161
 
123
162
  /** Three-point arc: start, end, then a point it passes through. */
163
+ /**
164
+ * Arc: where it starts, where it ends, then a point it passes through. Each point is marked as it's placed — the
165
+ * start the moment a finger lands, the end beside it, the two joined by a dashed line once both are down — so on a
166
+ * touch screen, where nothing hovers between taps, it's always clear what's placed and where the arc will span. The
167
+ * third press bends the arc through the finger from the moment it lands: a tap places it there, a drag reshapes it
168
+ * until the finger lifts.
169
+ */
124
170
  export const arcTool = {
125
- st: { pts: [] as Snap[] },
126
- reset() { this.st = { pts: [] }; ed.preview = null; ed.snapState = null; },
171
+ st: { pts: [] as Snap[], pressing: false },
172
+ reset() { this.st = { pts: [], pressing: false }; ed.preview = null; ed.snapState = null; },
127
173
  busy() { return this.st.pts.length > 0; },
128
174
  hover(ev: PEv) {
129
175
  ed.snapState = snapAt(ev.sx, ev.sy, { touch: ev.touch });
130
- const p = this.st.pts, m: XY = { x: ed.snapState.x, y: ed.snapState.y };
131
- ed.preview = p.length
132
- ? (c, T) => {
133
- c.save(); c.strokeStyle = TH.blue; c.lineWidth = 2; c.beginPath();
134
- if (p.length === 1) {
135
- const [x1, y1] = S(T, p[0].x, p[0].y), [x2, y2] = S(T, m.x, m.y);
136
- c.setLineDash([4, 4]); c.moveTo(x1, y1); c.lineTo(x2, y2);
137
- } else {
138
- const o = circum(p[0], p[1], m);
139
- if (o) {
140
- const r = hyp(p[0].x - o.x, p[0].y - o.y);
141
- let t0 = Math.atan2(p[0].y - o.y, p[0].x - o.x), t1 = Math.atan2(p[1].y - o.y, p[1].x - o.x);
142
- const tm = Math.atan2(m.y - o.y, m.x - o.x);
143
- if (normAng(tm - t0) > normAng(t1 - t0)) { const t = t0; t0 = t1; t1 = t; }
144
- const [x, y] = S(T, o.x, o.y);
145
- c.arc(x, y, r * T.s, -t0, -t1, true);
146
- }
147
- }
148
- c.stroke(); c.restore();
176
+ const p = this.st.pts, m: XY = { x: ed.snapState.x, y: ed.snapState.y }, pressing = this.st.pressing;
177
+ // (nothing yet: a mark only once a point is being placed — a mouse just moving about gets the snap marker)
178
+ if (!p.length && !pressing) { ed.preview = null; requestOverlayDraw(); return; }
179
+ // the arc through the pointer, once both ends are placed (null while it's in line with them)
180
+ const o = p.length === 2 ? circum(p[0], p[1], m) : null;
181
+ ed.preview = (c, T) => {
182
+ const at = (q: XY) => S(T, q.x, q.y);
183
+ // the arc through the pointer, on screen (as a wall, over the band it'll fill)
184
+ let bend: (() => void) | null = null;
185
+ if (p.length === 2 && o) {
186
+ const r = hyp(p[0].x - o.x, p[0].y - o.y);
187
+ let t0 = Math.atan2(p[0].y - o.y, p[0].x - o.x), t1 = Math.atan2(p[1].y - o.y, p[1].x - o.x);
188
+ const tm = Math.atan2(m.y - o.y, m.x - o.x);
189
+ if (normAng(tm - t0) > normAng(t1 - t0)) { const t = t0; t0 = t1; t1 = t; }
190
+ const [x, y] = at(o);
191
+ bend = () => c.arc(x, y, r * T.s, -t0, -t1, true);
192
+ if (drawsWalls()) wallBand(c, T, bend);
193
+ }
194
+ c.save(); c.strokeStyle = TH.blue; c.lineWidth = 2; c.beginPath();
195
+ if (p.length === 1) {
196
+ // the start to the pointer: where the end will go
197
+ const [x1, y1] = at(p[0]), [x2, y2] = at(m);
198
+ c.setLineDash([4, 4]); c.moveTo(x1, y1); c.lineTo(x2, y2);
199
+ } else if (bend) {
200
+ bend();
201
+ } else if (p.length === 2) {
202
+ // both ends placed, no bend yet: the span it'll bend across
203
+ const [x1, y1] = at(p[0]), [x2, y2] = at(p[1]);
204
+ c.setLineDash([4, 4]); c.moveTo(x1, y1); c.lineTo(x2, y2);
149
205
  }
150
- : null;
151
- requestDraw();
206
+ c.stroke(); c.restore();
207
+ for (const q of p) { const [x, y] = at(q); anchorMark(c, x, y); }
208
+ // the point going down: marked under the finger as it's placed (not the third: that's the arc itself)
209
+ if (pressing && p.length < 2) { const [x, y] = at(m); anchorMark(c, x, y); }
210
+ };
211
+ requestOverlayDraw();
152
212
  },
153
- down(ev: PEv) { this.hover(ev); },
213
+ down(ev: PEv) { this.st.pressing = true; this.hover(ev); },
154
214
  move(ev: PEv) { this.hover(ev); },
155
215
  up(ev: PEv) {
216
+ this.st.pressing = false;
156
217
  const s = snapAt(ev.sx, ev.sy, { touch: ev.touch });
157
218
  this.st.pts.push(s);
158
219
  if (this.st.pts.length < 3) { this.hover(ev); updateHint(); return; }
159
220
  const [p0, p1, pm] = this.st.pts;
160
221
  const o = circum(p0, p1, pm);
161
- if (!o) { toast('Those points are in a line — pick a point off the line.', { warn: true }); this.st.pts.pop(); return; }
222
+ if (!o) { toast('Those points are in a line — pick a point off the line.', { warn: true }); this.st.pts.pop(); this.hover(ev); return; }
162
223
  pushHistory();
163
224
  let a = commitPoint(p0), b = commitPoint(p1);
164
225
  const t0 = Math.atan2(PT(a.id).y - o.y, PT(a.id).x - o.x), t1 = Math.atan2(PT(b.id).y - o.y, PT(b.id).x - o.x), tm = Math.atan2(pm.y - o.y, pm.x - o.x);
@@ -166,11 +227,11 @@ export const arcTool = {
166
227
  const sk = SK();
167
228
  const cid = addPoint(sk, o.x, o.y);
168
229
  const id = uid('ar');
169
- sk.arcs[id] = { c: cid, a: a.id, b: b.id, constr: ed.drawConstr };
230
+ sk.arcs[id] = { c: cid, a: a.id, b: b.id, ...asDrawn() };
170
231
  applyPending(a.cons); applyPending(b.cons);
171
232
  this.reset();
172
- ed.sel = [{ type: 'arc', id }];
233
+ ed.sel = [];
173
234
  afterEdit();
174
235
  },
175
- hint() { return ['Tap where the arc starts.', 'Tap where the arc ends.', 'Tap a point the arc passes through.'][this.st.pts.length]; },
236
+ hint() { return ['Tap where the arc starts.', 'Tap where the arc ends.', 'Tap where the arc passes through, or drag to bend it.'][this.st.pts.length]; },
176
237
  };
package/src/ui/ctx.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ed } from '../app/state';
2
+ import { markupTarget } from '../editor/markup';
2
3
  import { applyCtxAction, barItems, deleteSelection, plusAction, plusLabel, plusVisible } from '../editor/actions';
3
4
  import { selInfo, selectionHint } from '../editor/selection';
4
5
  import { $ } from './dom';
@@ -17,7 +18,7 @@ export function updateCtx(): void {
17
18
  const b = document.createElement('button');
18
19
  b.className = 'cb' + (it.on ? ' on' : '') + (it.locked ? ' locked' : '');
19
20
  b.innerHTML = (it.ic === 'color'
20
- ? `<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8" fill="${ed.noteColor}" stroke="#fff" stroke-width="2"/></svg>`
21
+ ? `<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8" fill="${markupTarget().color}" stroke="#fff" stroke-width="2"/></svg>`
21
22
  : icon(it.ic)) + '<span>' + it.label + '</span>';
22
23
  b.setAttribute('aria-label', it.label);
23
24
  if (it.on != null) b.setAttribute('aria-pressed', String(!!it.on));
@@ -8,17 +8,29 @@ import { renderTray } from './laser';
8
8
  import { doSave, download } from './menu';
9
9
  import { $ } from './dom';
10
10
  import { hideNoteInput, showNoteInput } from './noteinput';
11
- import { closePopovers, openPalette } from './palette';
11
+ import { closePopovers, openPalette, refreshPalette } from './palette';
12
+ import { closeOpacity, refreshOpacity, toggleOpacity, toggleRotation } from './opacity';
12
13
  import { renderModes, updateRail } from './rail';
13
14
  import { updateHint, updateStatus } from './status';
14
15
  import { toast } from './toast';
15
16
  import { updateTopBar } from './topbar';
16
17
  import { cvs } from '../render/view';
18
+ import { confirmCancel, confirmChoose, confirmSeq, confirmSpec } from '../editor/confirm';
17
19
 
18
20
  /* The browser DOM interface as the editor's ports (see editor/ports.ts). */
19
21
 
22
+ /** This interface has no sheet for the editor's questions (editor/confirm.ts): the browser's own — OK the first choice. */
23
+ function askInBrowser(): void {
24
+ const c = confirmSpec(), seq = confirmSeq();
25
+ if (!c) return;
26
+ setTimeout(() => {
27
+ if (confirmSeq() !== seq || confirmSpec() !== c) return;
28
+ if (window.confirm(c.title + (c.msg ? '\n\n' + c.msg : ''))) confirmChoose(0); else confirmCancel();
29
+ }, 0);
30
+ }
31
+
20
32
  const REFRESH: Record<ChromePart, () => void> = {
21
- ctx: updateCtx,
33
+ ctx: () => { updateCtx(); updateRail(); refreshPalette(); refreshOpacity(); },
22
34
  hint: updateHint,
23
35
  status: updateStatus,
24
36
  rail: updateRail,
@@ -28,6 +40,7 @@ const REFRESH: Record<ChromePart, () => void> = {
28
40
  topbar: () => { updateTopBar(); updateCrumb(); },
29
41
  keypad: kpSync,
30
42
  tray: renderTray,
43
+ confirm: askInBrowser,
31
44
  };
32
45
 
33
46
  /** Open the Spaces tab and put the cursor in the space's name, once the drawer has slid in. */
@@ -50,12 +63,18 @@ export const domPorts: EditorPorts = {
50
63
  showNoteInput,
51
64
  hideNoteInput,
52
65
  openPalette: anchor => { if (anchor) openPalette(anchor as HTMLElement); },
66
+ toggleImageOpacity: anchor => toggleOpacity(anchor as HTMLElement | undefined),
67
+ toggleImageRotation: anchor => toggleRotation(anchor as HTMLElement | undefined),
68
+ // (this interface has no link tiles: no host links)
69
+ toggleTileSize: () => {},
53
70
  editRoomName,
54
- setCursor: (cursor, where) => { (where === 'elevation' ? $<HTMLCanvasElement>('#elevCv') : cvs.cv).style.cursor = cursor; },
71
+ setCursor: cursor => { cvs.cv.style.cursor = cursor; },
72
+ // (this page's chrome stays put while a gesture moves)
73
+ interacting() {},
55
74
  dismissPopovers: () => {
56
75
  const open = $('#menu').classList.contains('show') || $('#palette').classList.contains('show');
57
76
  closePopovers();
58
- return open;
77
+ return closeOpacity() || open;
59
78
  },
60
79
  dismissConflict: () => {
61
80
  if (!cfOpen()) return false;
package/src/ui/drawer.ts CHANGED
@@ -1,12 +1,11 @@
1
- import { addPage, calibrateUnderlay, deletePage, deletePending, duplicatePage, focusSpace, LAYER_ROWS, layerOn, removeUnderlay, renamePage, renameProject, renameSpace, setUnderlayOpacity, spaceSchedule, switchPage as switchPageIn, toggleLayer, toggleUnderlayLock, type LayerKey } from '../editor/project';
2
- import { settings } from '../model/settings';
1
+ import { addPage, deletePage, deletePending, duplicatePage, focusSpace, renamePage, renameProject, renameSpace, spaceSchedule, switchPage as switchPageIn } from '../editor/project';
3
2
  import { allPages, page, store } from '../model/store';
4
3
  import { drawThumb } from '../render/draw';
5
4
  import { fmtArea, fmtLen } from '../units';
6
5
  import { $, esc } from './dom';
7
6
  import { onSessionReset } from '../model/session';
8
7
 
9
- type Tab = 'pages' | 'layers' | 'rooms';
8
+ type Tab = 'pages' | 'rooms';
10
9
  let drawerTab: Tab = 'pages';
11
10
  let drawerT: ReturnType<typeof setTimeout> | undefined;
12
11
  onSessionReset(() => { drawerTab = 'pages'; clearTimeout(drawerT); });
@@ -19,6 +18,7 @@ export function openDrawer(tab?: Tab): void {
19
18
 
20
19
  export function closeDrawer(): void {
21
20
  $('#drawer').classList.remove('show');
21
+ $('#btnLayers').setAttribute('aria-expanded', 'false');
22
22
  }
23
23
 
24
24
  export function refreshDrawerSoon(): void {
@@ -33,7 +33,7 @@ export function switchPage(id: string): void {
33
33
  updateCrumb(); renderDrawer();
34
34
  }
35
35
 
36
- /** Project → Page navigator, layer switches, and the space schedule. */
36
+ /** Project → Page navigator and the space schedule (tabs). */
37
37
  export function renderDrawer(): void {
38
38
  document.querySelectorAll<HTMLElement>('.tabs button').forEach(b => b.setAttribute('aria-selected', String(b.dataset.tab === drawerTab)));
39
39
  const body = $('#dbody'), project = store.project;
@@ -54,27 +54,6 @@ export function renderDrawer(): void {
54
54
  $<HTMLInputElement>('#pgName').onchange = e => { renamePage((e.target as HTMLInputElement).value); updateCrumb(); renderDrawer(); };
55
55
  $('#pgDup').onclick = () => { duplicatePage(); updateCrumb(); renderDrawer(); };
56
56
  $('#pgDel').onclick = () => { deletePage(); updateCrumb(); renderDrawer(); };
57
- } else if (drawerTab === 'layers') {
58
- const ul = page().underlay;
59
- const sw = (key: string, label: string, sub: string, val: boolean) =>
60
- `<div class="row"><div>${label}${sub ? `<small>${sub}</small>` : ''}</div><button class="switch" role="switch" aria-label="${label}" aria-checked="${val}" data-sw="${key}"></button></div>`;
61
- let h = sw('underlay', 'Photo or plan underlay', ul ? 'Traced image under the drawing' : 'None on this page', settings.layers.underlay);
62
- if (ul) h += `<div class="row"><div>Image opacity</div><input type="range" id="ulOp" min="10" max="100" value="${Math.round((ul.opacity ?? 0.6) * 100)}"></div><div class="thispage"><button class="smallbtn" id="ulLock">${ul.locked ? 'Unlock image' : 'Lock image'}</button><button class="smallbtn" id="ulCal">Set scale</button><button class="smallbtn" id="ulDel" style="color:#FFB4AE">Remove image</button></div>`;
63
- h += LAYER_ROWS.map(([k, label, sub]) => sw(k, label, sub, layerOn(k))).join('');
64
- body.innerHTML = h;
65
- body.querySelectorAll<HTMLElement>('[data-sw]').forEach(b => (b.onclick = () => {
66
- const k = b.dataset.sw as LayerKey;
67
- toggleLayer(k);
68
- b.setAttribute('aria-checked', String(layerOn(k)));
69
- }));
70
- const op = document.querySelector<HTMLInputElement>('#ulOp');
71
- if (op) op.oninput = () => setUnderlayOpacity(+op.value / 100);
72
- const ul2 = document.querySelector<HTMLElement>('#ulLock');
73
- if (ul2) ul2.onclick = () => { toggleUnderlayLock(); renderDrawer(); };
74
- const uc = document.querySelector<HTMLElement>('#ulCal');
75
- if (uc) uc.onclick = () => { closeDrawer(); calibrateUnderlay(); };
76
- const ud = document.querySelector<HTMLElement>('#ulDel');
77
- if (ud) ud.onclick = () => { removeUnderlay(); renderDrawer(); };
78
57
  } else {
79
58
  const sched = spaceSchedule(), total = sched.total;
80
59
  if (!sched.rows.length) { body.innerHTML = '<p class="empty">No spaces on this page yet. Close a loop of walls or lines, or pick the Space tool and tap inside a closed shape.</p>'; return; }
@@ -101,6 +80,7 @@ export function initDrawer(): void {
101
80
  $('#drawerClose').onclick = closeDrawer;
102
81
  $('#btnPages').onclick = () => openDrawer('pages');
103
82
  $('#crumb').onclick = () => openDrawer('pages');
104
- $('#btnLayers').onclick = () => { if ($('#drawer').classList.contains('show') && drawerTab === 'layers') closeDrawer(); else openDrawer('layers'); };
83
+ // the stack: pages and spaces (tabs), or closed if that's what's open
84
+ $('#btnProject').onclick = () => { if ($('#drawer').classList.contains('show')) closeDrawer(); else openDrawer(drawerTab); };
105
85
  document.querySelectorAll<HTMLElement>('.tabs button').forEach(b => (b.onclick = () => { drawerTab = b.dataset.tab as Tab; renderDrawer(); }));
106
86
  }
@@ -1,108 +1,26 @@
1
- import { redo, undo } from '../app/edit';
2
- import {
3
- closeElevation, deleteSelectedElevDim, editSelected, elev, elevAfterRender, elevBar, ELEV_RAIL, elevOpen, elevPointerCancel,
4
- elevPointerDown, elevPointerLeave, elevPointerMove, elevPointerUp, elevSyncFrame, elevTitle, elevTrashEnabled, elevWheel,
5
- fitElevation, flipElevation, setElevTool, type ElevTool,
6
- } from '../editor/elevation';
7
- import type { PointerIn } from '../editor/gestures';
1
+ import { closeElevation, elevOpen, elevSyncFrame, elevTitle, fitElevation, flipElevation } from '../editor/elevation';
8
2
  import { onSessionReset } from '../model/session';
9
- import type { ElevAct } from '../render/elevation';
10
- import { drawElevationView } from '../render/elevationView';
11
3
  import { $ } from './dom';
12
- import { icon } from './icons';
13
4
  import { bindTip } from './tip';
14
5
 
15
- /* The wall elevation sheet in the DOM. The view's state, tools and rules live in editor/elevation.ts; this shows the
16
- sheet while the editor has a wall open, draws it, and builds its toolbar and bar. */
6
+ /* The wall view in the DOM: the plan's own canvas, rail and bar edit the wall's face (editor/elevation.ts); this shows
7
+ the header over the top bar — back to the plan, the wall and the side it's seen from, Flip and Fit. */
17
8
 
18
- let shown = false, railTool: ElevTool | null = null;
19
- onSessionReset(() => { shown = false; railTool = null; });
9
+ let shown = false;
10
+ onSessionReset(() => { shown = false; });
20
11
 
21
- /** Redraw the elevation (and its title and bar) from the live drawing; show or hide the sheet to match. */
12
+ /** Show or hide the wall view's header, and keep its title up to date. */
22
13
  export function renderElevation(): void {
23
- const sheet = $('#elev');
14
+ const sheet = $('#elev'), modes = document.querySelector<HTMLElement>('.modes');
24
15
  if (!elevOpen()) {
25
- if (shown) {
26
- shown = false;
27
- sheet.classList.remove('show');
28
- sheet.setAttribute('aria-hidden', 'true');
29
- ($('#cv') as HTMLCanvasElement).focus({ preventScroll: true });
30
- }
16
+ if (shown) { shown = false; sheet.classList.remove('show'); sheet.setAttribute('aria-hidden', 'true'); if (modes) modes.hidden = false; }
31
17
  return;
32
18
  }
33
- const opening = !shown;
34
- if (opening) { shown = true; sheet.classList.add('show'); sheet.setAttribute('aria-hidden', 'false'); }
19
+ if (!shown) { shown = true; sheet.classList.add('show'); sheet.setAttribute('aria-hidden', 'false'); if (modes) modes.hidden = true; }
35
20
  if (!elevSyncFrame()) return;
36
- if (railTool !== elev.tool) {
37
- railTool = elev.tool;
38
- $('#elevRail').querySelectorAll<HTMLElement>('[data-tool]').forEach(b => b.classList.toggle('on', b.dataset.tool === elev.tool));
39
- $<HTMLCanvasElement>('#elevCv').style.cursor = elev.tool === 'select' ? '' : 'crosshair';
40
- }
41
- const cv = $<HTMLCanvasElement>('#elevCv'), box = cv.parentElement!.getBoundingClientRect(), dpr = window.devicePixelRatio || 1;
42
- elev.W = box.width; elev.H = box.height;
43
- if (cv.width !== Math.round(elev.W * dpr) || cv.height !== Math.round(elev.H * dpr)) { cv.width = Math.round(elev.W * dpr); cv.height = Math.round(elev.H * dpr); }
44
- const c = cv.getContext('2d')!;
45
- c.setTransform(dpr, 0, 0, dpr, 0, 0);
46
- const on = elevAfterRender(drawElevationView(c));
47
- ($('#elevTrash') as HTMLButtonElement).disabled = !elevTrashEnabled(on);
48
- renderBar(on ? on.act : null);
49
21
  const t = elevTitle();
50
22
  $('#elevTitle').textContent = t.title;
51
23
  $('#elevSub').textContent = t.sub;
52
- cv.setAttribute('aria-label', 'Elevation of the wall, ' + t.sub.toLowerCase());
53
- if (opening) $<HTMLButtonElement>('#elevClose').focus({ preventScroll: true });
54
- }
55
-
56
- function renderBar(dimAct: ElevAct | null): void {
57
- const bar = $('#elevBar'), acts = $('#elevActs'), m = elevBar(dimAct);
58
- acts.innerHTML = '';
59
- bar.hidden = !m;
60
- ($('#elevAdd') as HTMLElement).hidden = !!dimAct && dimAct.k === 'len';
61
- if (!m) return;
62
- $('#elevSel').textContent = m.label;
63
- for (const a of m.actions) {
64
- const b = document.createElement('button');
65
- b.className = 'cb'; b.innerHTML = icon(a.icon) + '<span>' + a.label + '</span>'; b.setAttribute('aria-label', a.label); b.onclick = a.run;
66
- acts.appendChild(b);
67
- }
68
- }
69
-
70
- function buildElevRail(): void {
71
- const rail = $('#elevRail');
72
- rail.innerHTML = '';
73
- const add = (id: string, label: string, key: string, ic: string, fn: () => void) => {
74
- const b = document.createElement('button');
75
- b.dataset.tool = id; b.setAttribute('aria-label', label); b.innerHTML = icon(ic); b.onclick = fn;
76
- bindTip(b, label, key);
77
- rail.appendChild(b);
78
- };
79
- for (const it of ELEV_RAIL) {
80
- if (it === '|') { const d = document.createElement('div'); d.className = 'div'; rail.appendChild(d); continue; }
81
- const [id, label, key, ic] = it;
82
- add(id, label, key, ic, () => setElevTool(id));
83
- }
84
- add('undo', 'Undo', '⌘Z', 'undo', () => undo());
85
- add('redo', 'Redo', '⇧⌘Z', 'redo', () => redo());
86
- }
87
-
88
- function bindGestures(cv: HTMLCanvasElement): void {
89
- const pin = (e: PointerEvent): PointerIn => {
90
- const r = cv.getBoundingClientRect();
91
- return {
92
- id: e.pointerId, kind: e.pointerType, x: e.clientX - r.left, y: e.clientY - r.top, t: performance.now(),
93
- button: e.button, shift: e.shiftKey, mod: e.metaKey || e.ctrlKey, cx: e.clientX, cy: e.clientY,
94
- };
95
- };
96
- cv.addEventListener('pointerdown', e => { cv.setPointerCapture(e.pointerId); elevPointerDown(pin(e)); });
97
- cv.addEventListener('pointermove', e => elevPointerMove(pin(e)));
98
- cv.addEventListener('pointerup', e => elevPointerUp(pin(e)));
99
- cv.addEventListener('pointercancel', e => elevPointerCancel(pin(e)));
100
- cv.addEventListener('pointerleave', e => elevPointerLeave(e.pointerType));
101
- cv.addEventListener('wheel', e => {
102
- e.preventDefault();
103
- const r = cv.getBoundingClientRect();
104
- elevWheel(e.clientX - r.left, e.clientY - r.top, e.deltaY);
105
- }, { passive: false });
106
24
  }
107
25
 
108
26
  export function initElevation(): void {
@@ -111,14 +29,5 @@ export function initElevation(): void {
111
29
  $('#elevFit').onclick = fitElevation;
112
30
  bindTip($('#elevClose'), 'Back to the plan', 'Esc');
113
31
  bindTip($('#elevFlip'), 'Look from the other side', '');
114
- bindTip($('#elevFit'), 'Fit to screen', '');
115
- bindGestures($<HTMLCanvasElement>('#elevCv'));
116
- buildElevRail();
117
- $('#elevAdd').innerHTML = icon('plus');
118
- $('#elevAdd').onclick = editSelected;
119
- $('#elevTrash').onclick = deleteSelectedElevDim;
120
- bindTip($('#elevTrash'), 'Delete the selected door/window — or free a locked dimension', 'Del');
121
- bindTip($('#elevAdd'), 'Change this measurement', '');
122
- window.addEventListener('resize', () => { if (elevOpen()) fitElevation(); });
123
- matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => setTimeout(renderElevation));
32
+ bindTip($('#elevFit'), 'Fit to screen', 'F');
124
33
  }
package/src/ui/keypad.ts CHANGED
@@ -1,15 +1,13 @@
1
1
  import { ed } from '../app/state';
2
2
  import {
3
- FRACTIONS, fractionRowShown, fractionText, keyLayout, keypadBackspace, keypadDeleteLast, keypadEnd, keypadFresh, keypadInsert,
4
- keypadOpen, keypadPreview, keypadSetReading, keypadSetText, keypadSpec, keypadStart, keypadTakeLaser as takeLaserReading,
3
+ FRACTIONS, fractionRowShown, fractionText, keyLayout, keySpan, keypadBackspace, keypadDeleteLast, keypadEnd, keypadFresh, keypadInsert,
4
+ keypadOpen, keypadPreview, keypadSetText, keypadSpec, keypadStart, keypadTakeLaser as takeLaserReading,
5
5
  keypadApply, keypadText, keypadTyped, parenText,
6
6
  } from '../editor/keypad';
7
7
  import type { KeypadSpec } from '../editor/ports';
8
8
  import { cvs, requestDraw } from '../render/view';
9
9
  import { closeConflict, cfOpen } from './conflict';
10
10
  import { $ } from './dom';
11
- import { icon } from './icons';
12
- import { laser, takeReading } from './laser';
13
11
  import { markSheetOpened } from './sheets';
14
12
 
15
13
  /* The fraction keypad in the DOM (Board Optimizer style). Its state and rules live in editor/keypad.ts. */
@@ -53,23 +51,19 @@ export function openKeypad(o: KeypadSpec): void {
53
51
  const b = document.createElement('button');
54
52
  b.className = 'key';
55
53
  if (k === '⌫') {
56
- b.classList.add('alt');
54
+ b.classList.add('del');
57
55
  b.setAttribute('aria-label', 'Backspace');
58
- b.textContent = '⌫';
56
+ b.textContent = 'Del';
59
57
  b.onclick = () => { keypadBackspace(); kpSync(); };
60
58
  } else if (k === 'space') {
61
59
  b.classList.add('alt'); b.textContent = 'space'; b.onclick = () => kpInsert(' ');
62
- } else if (k === 'laser') {
63
- b.classList.add('laser');
64
- b.innerHTML = icon('bt', 18) + 'Laser';
65
- b.disabled = !laser.on || o.laser === false;
66
- b.setAttribute('aria-label', 'Use laser reading');
67
- b.onclick = () => { const v = takeReading(true); if (v != null) { keypadSetReading(v); kpSync(); } };
68
60
  } else if (k === '( )') {
69
61
  b.classList.add('alt'); b.textContent = '( )';
70
62
  b.onclick = () => kpInsert(parenText());
71
63
  } else {
72
- if (k === "'" || k === '"') b.classList.add('alt');
64
+ // digits slate; units and symbols a lighter slate; the 0 two keys wide in lengths
65
+ if (!/^\d$/.test(k)) b.classList.add('alt');
66
+ if (keySpan(k, o.numeric) === 3) b.classList.add('wide');
73
67
  b.textContent = k;
74
68
  b.onclick = () => kpInsert(k);
75
69
  }
@@ -91,7 +85,7 @@ export function openKeypad(o: KeypadSpec): void {
91
85
  };
92
86
  refresh();
93
87
  $('#kp').classList.add('show');
94
- $('#backdrop').classList.add('show');
88
+ if (!o.modeless) $('#backdrop').classList.add('show');
95
89
  markSheetOpened();
96
90
  kpPreview();
97
91
  if (!isTouchUI()) { inp.focus({ preventScroll: true }); if (o.selectAll) inp.select(); }
package/src/ui/laser.ts CHANGED
@@ -41,8 +41,8 @@ function startChipDrag(e: PointerEvent, r: Reading, el: HTMLElement): void {
41
41
  const x0 = e.clientX, y0 = e.clientY;
42
42
  const lineUnder = (ev: PointerEvent) => {
43
43
  const cr = cvs.cv.getBoundingClientRect();
44
- const h = hitAny(ev.clientX - cr.left, ev.clientY - cr.top, { touch: true, only: 'geom' });
45
- return h && h.type === 'line' ? h : null;
44
+ const h = hitAny(ev.clientX - cr.left, ev.clientY - cr.top, { touch: true, only: 'geom', faces: true });
45
+ return h && (h.type === 'line' || h.type === 'wface') ? h : null;
46
46
  };
47
47
  const mv = (ev: PointerEvent) => {
48
48
  if (!moved && hyp(ev.clientX - x0, ev.clientY - y0) > 6) {