@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,5 +1,5 @@
1
1
  import type {
2
- Skia as SkiaApi, SkCanvas, SkColor, SkFont, SkImage, SkPaint, SkPathBuilder, SkTypeface,
2
+ Skia as SkiaApi, SkCanvas, SkColor, SkFont, SkImage, SkPaint, SkPath, SkPathBuilder, SkTypeface,
3
3
  } from '@shopify/react-native-skia/lib/typescript/src/skia/types';
4
4
  import type { DrawCtx } from '../render/ctx';
5
5
 
@@ -72,38 +72,77 @@ export function parseFont(font: string): { weight: number; size: number } {
72
72
  return { weight: w, size: Number(m[2]) };
73
73
  }
74
74
 
75
- interface FontPair { main: SkFont; fb: SkFont | null }
75
+ interface FontPair { main: SkFont; fb: SkFont | null; metrics: { ascent: number; descent: number; size: number } }
76
+ interface Run { text: string; font: SkFont; width: number }
76
77
 
78
+ /** Caches stay small: a drawing has a few dozen styles and labels; past this they start again. */
79
+ const CACHE_MAX = 2000;
80
+ const remember = <K, V>(m: Map<K, V>, k: K, v: V): V => { if (m.size >= CACHE_MAX) m.clear(); m.set(k, v); return v; };
81
+
82
+ /*
83
+ Every call into Skia crosses from JavaScript to native code on a device, and every object it makes has to be
84
+ collected again — at 120 frames a second that is what makes drawing lag. So one context is kept for frame after
85
+ frame (begin() points it at the next frame's canvas): paints are made once per distinct style and reused, fonts,
86
+ colours, font metrics and text runs (with their widths) are cached, and one path builder is reset rather than remade.
87
+ Cached paints are never changed after they're made.
88
+ */
77
89
  export class SkiaDrawCtx implements DrawCtx {
78
90
  private st: Style = initialStyle();
79
91
  private stack: Style[] = [];
80
92
  private path: SkPathBuilder;
81
93
  private hasPoint = false;
94
+ /** by CSS font string */
82
95
  private fonts = new Map<string, FontPair>();
83
96
  private colors = new Map<string, SkColor>();
97
+ private paints = new Map<string, SkPaint>();
98
+ /** by CSS font string, then text */
99
+ private textRuns = new Map<string, Map<string, Run[]>>();
100
+ /** the paints for the current style, until a style change (most draws in a row share one: no key to work out) */
101
+ private fillPaint: SkPaint | null = null;
102
+ private strokePaint: SkPaint | null = null;
103
+ /** the current path as Skia draws it, made once while it doesn't change (a fill and a stroke of it share it) */
104
+ private built: SkPath | null = null;
105
+ /** shapes by key, each made once at the origin (see shape), and the current path when it's one moved into place */
106
+ private shapes = new Map<string, SkPath>();
107
+ private placed: { path: SkPath; x: number; y: number; draw: (c: DrawCtx, x: number, y: number) => void } | null = null;
84
108
 
85
109
  constructor(
86
110
  private readonly Skia: SkiaApi,
87
- private readonly canvas: SkCanvas,
111
+ private canvas: SkCanvas,
88
112
  private readonly typefaces: SkiaFonts,
89
113
  private readonly imageOf: SkiaImageSource = img => (img && typeof (img as SkImage).width === 'function' ? (img as SkImage) : null),
90
114
  ) {
91
115
  this.path = Skia.PathBuilder.Make();
92
116
  }
93
117
 
118
+ /** Draw the next frame on `canvas`, keeping the caches: the drawing state starts fresh, as on a new canvas. */
119
+ begin(canvas: SkCanvas): this {
120
+ this.canvas = canvas;
121
+ this.st = initialStyle();
122
+ this.stack = [];
123
+ this.path.reset();
124
+ this.hasPoint = false;
125
+ this.fillPaint = this.strokePaint = null;
126
+ this.built = null; this.placed = null;
127
+ return this;
128
+ }
129
+
130
+ /** The typefaces this context draws text with (a new set needs a new context). */
131
+ get typefaceSet(): SkiaFonts { return this.typefaces; }
132
+
94
133
  // ---- style state (scoped by save/restore, as in the browser canvas) ----
95
134
  get fillStyle(): unknown { return this.st.fillStyle; }
96
- set fillStyle(v: unknown) { this.st.fillStyle = String(v); }
135
+ set fillStyle(v: unknown) { const c = String(v); if (c !== this.st.fillStyle) { this.st.fillStyle = c; this.fillPaint = null; } }
97
136
  get strokeStyle(): unknown { return this.st.strokeStyle; }
98
- set strokeStyle(v: unknown) { this.st.strokeStyle = String(v); }
137
+ set strokeStyle(v: unknown) { const c = String(v); if (c !== this.st.strokeStyle) { this.st.strokeStyle = c; this.strokePaint = null; } }
99
138
  get lineWidth() { return this.st.lineWidth; }
100
- set lineWidth(v: number) { if (v > 0 && Number.isFinite(v)) this.st.lineWidth = v; }
139
+ set lineWidth(v: number) { if (v > 0 && Number.isFinite(v) && v !== this.st.lineWidth) { this.st.lineWidth = v; this.strokePaint = null; } }
101
140
  get lineCap() { return this.st.lineCap; }
102
- set lineCap(v: DrawCtx['lineCap']) { this.st.lineCap = v; }
141
+ set lineCap(v: DrawCtx['lineCap']) { if (v !== this.st.lineCap) { this.st.lineCap = v; this.strokePaint = null; } }
103
142
  get lineJoin() { return this.st.lineJoin; }
104
- set lineJoin(v: DrawCtx['lineJoin']) { this.st.lineJoin = v; }
143
+ set lineJoin(v: DrawCtx['lineJoin']) { if (v !== this.st.lineJoin) { this.st.lineJoin = v; this.strokePaint = null; } }
105
144
  get globalAlpha() { return this.st.globalAlpha; }
106
- set globalAlpha(v: number) { if (v >= 0 && v <= 1) this.st.globalAlpha = v; }
145
+ set globalAlpha(v: number) { if (v >= 0 && v <= 1 && v !== this.st.globalAlpha) { this.st.globalAlpha = v; this.fillPaint = this.strokePaint = null; } }
107
146
  get font() { return this.st.font; }
108
147
  set font(v: string) { this.st.font = v; }
109
148
  get textAlign() { return this.st.textAlign; }
@@ -123,26 +162,44 @@ export class SkiaDrawCtx implements DrawCtx {
123
162
  const s = this.stack.pop();
124
163
  if (!s) return;
125
164
  this.st = s;
165
+ this.fillPaint = this.strokePaint = null;
126
166
  this.canvas.restore();
127
167
  }
128
168
  translate(x: number, y: number): void { this.canvas.translate(x, y); }
129
169
  rotate(angle: number): void { this.canvas.rotate(deg(angle), 0, 0); }
170
+ scale(x: number, y: number): void { this.canvas.scale(x, y); }
130
171
  setLineDash(segments: number[]): void {
131
172
  if (segments.some(v => !(v >= 0) || !Number.isFinite(v))) return;
132
173
  // as the browser: an odd list repeats to make it even
133
174
  this.st.dash = segments.length % 2 ? [...segments, ...segments] : segments.slice();
175
+ this.strokePaint = null;
134
176
  }
135
177
 
136
178
  // ---- paths ----
137
- beginPath(): void { this.path = this.Skia.PathBuilder.Make(); this.hasPoint = false; }
138
- moveTo(x: number, y: number): void { this.path.moveTo(x, y); this.hasPoint = true; }
179
+ beginPath(): void { this.path.reset(); this.hasPoint = false; this.built = null; this.placed = null; }
180
+ /** About to add to the path: it changes (made again when drawn), and a shape moved into place becomes path commands. */
181
+ private changing(): void {
182
+ this.built = null;
183
+ const pl = this.placed;
184
+ if (pl) { this.placed = null; pl.draw(this, pl.x, pl.y); this.built = null; }
185
+ }
186
+ moveTo(x: number, y: number): void { this.changing(); this.path.moveTo(x, y); this.hasPoint = true; }
139
187
  lineTo(x: number, y: number): void {
188
+ this.changing();
140
189
  if (!this.hasPoint) this.path.moveTo(x, y); else this.path.lineTo(x, y);
141
190
  this.hasPoint = true;
142
191
  }
143
- closePath(): void { this.path.close(); }
192
+ closePath(): void { this.changing(); this.path.close(); }
193
+ bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void {
194
+ this.changing();
195
+ // (as the browser: with no current point, the first control point is where it starts)
196
+ if (!this.hasPoint) this.path.moveTo(cp1x, cp1y);
197
+ this.path.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
198
+ this.hasPoint = true;
199
+ }
144
200
  arc(x: number, y: number, r: number, startAngle: number, endAngle: number, counterclockwise = false): void {
145
201
  if (!(r >= 0)) return;
202
+ this.changing();
146
203
  const oval = this.Skia.XYWHRect(x - r, y - r, 2 * r, 2 * r);
147
204
  const sweep = arcSweep(startAngle, endAngle, counterclockwise);
148
205
  // the browser draws a line from the current point to the arc's start; Skia's arcToOval does the same
@@ -155,16 +212,19 @@ export class SkiaDrawCtx implements DrawCtx {
155
212
  this.hasPoint = true;
156
213
  }
157
214
  arcTo(x1: number, y1: number, x2: number, y2: number, r: number): void {
215
+ this.changing();
158
216
  if (!this.hasPoint) this.path.moveTo(x1, y1);
159
217
  this.path.arcToTangent(x1, y1, x2, y2, r);
160
218
  this.hasPoint = true;
161
219
  }
162
220
  rect(x: number, y: number, w: number, h: number): void {
221
+ this.changing();
163
222
  this.path.addRect(this.Skia.XYWHRect(x, y, w, h));
164
223
  this.path.moveTo(x, y);
165
224
  this.hasPoint = true;
166
225
  }
167
226
  roundRect(x: number, y: number, w: number, h: number, r: number): void {
227
+ this.changing();
168
228
  if (w < 0) { x += w; w = -w; }
169
229
  if (h < 0) { y += h; h = -h; }
170
230
  const rr = Math.max(0, Math.min(r, w / 2, h / 2));
@@ -172,9 +232,39 @@ export class SkiaDrawCtx implements DrawCtx {
172
232
  this.path.moveTo(x, y);
173
233
  this.hasPoint = true;
174
234
  }
175
- fill(): void { this.canvas.drawPath(this.path.build(), this.paint(false)); }
176
- stroke(): void { this.canvas.drawPath(this.path.build(), this.paint(true)); }
177
- clip(): void { this.canvas.clipPath(this.path.build(), INTERSECT, true); }
235
+ /**
236
+ * The path is a shape kept by key, made the first time (on a builder of its own, at the origin): its top left at x, y.
237
+ * Chips and badges — hundreds a frame — each took a few dozen calls into native code to describe; now a few to place.
238
+ */
239
+ shape(key: string, x: number, y: number, draw: (c: DrawCtx, x: number, y: number) => void): void {
240
+ let p = this.shapes.get(key);
241
+ if (!p) {
242
+ const outer = this.path, had = this.hasPoint;
243
+ this.path = this.Skia.PathBuilder.Make();
244
+ this.hasPoint = false; this.placed = null; this.built = null;
245
+ draw(this, 0, 0);
246
+ p = this.path.build();
247
+ this.path = outer; this.hasPoint = had;
248
+ remember(this.shapes, key, p);
249
+ }
250
+ this.path.reset();
251
+ this.hasPoint = false; this.built = null;
252
+ this.placed = { path: p, x, y, draw };
253
+ }
254
+ fill(): void { this.drawCurrent(this.paint(false)); }
255
+ stroke(): void { this.drawCurrent(this.paint(true)); }
256
+ clip(): void {
257
+ if (this.placed) this.changing();
258
+ this.canvas.clipPath(this.built ??= this.path.build(), INTERSECT, true);
259
+ }
260
+ private drawCurrent(paint: SkPaint): void {
261
+ const pl = this.placed;
262
+ if (!pl) { this.canvas.drawPath(this.built ??= this.path.build(), paint); return; }
263
+ this.canvas.save();
264
+ this.canvas.translate(pl.x, pl.y);
265
+ this.canvas.drawPath(pl.path, paint);
266
+ this.canvas.restore();
267
+ }
178
268
 
179
269
  // ---- shapes, text, images ----
180
270
  fillRect(x: number, y: number, w: number, h: number): void { this.canvas.drawRect(this.Skia.XYWHRect(x, y, w, h), this.paint(false)); }
@@ -182,32 +272,37 @@ export class SkiaDrawCtx implements DrawCtx {
182
272
 
183
273
  measureText(text: string): { width: number } {
184
274
  const f = this.fontPair();
185
- return { width: this.runs(text, f).reduce((w, r) => w + r.font.getTextWidth(r.text), 0) };
275
+ return { width: this.runs(text, f).reduce((w, r) => w + r.width, 0) };
186
276
  }
187
277
 
188
278
  fillText(text: string, x: number, y: number): void {
189
279
  if (!text) return;
190
280
  const f = this.fontPair(), runs = this.runs(text, f), paint = this.paint(false);
191
- const width = runs.reduce((w, r) => w + r.font.getTextWidth(r.text), 0);
281
+ const width = runs.reduce((w, r) => w + r.width, 0);
192
282
  const a = this.st.textAlign;
193
283
  let tx = a === 'center' ? x - width / 2 : a === 'right' || a === 'end' ? x - width : x;
194
- const m = f.main.getMetrics(), b = this.st.textBaseline;
195
- const ty = b === 'middle' ? y + middleShift(m.ascent, m.descent, f.main.getSize())
284
+ const m = f.metrics, b = this.st.textBaseline;
285
+ const ty = b === 'middle' ? y + middleShift(m.ascent, m.descent, m.size)
196
286
  : b === 'top' || b === 'hanging' ? y - m.ascent
197
287
  : b === 'bottom' || b === 'ideographic' ? y - m.descent
198
288
  : y;
199
289
  for (const r of runs) {
200
290
  this.canvas.drawText(r.text, tx, ty, paint, r.font);
201
- tx += r.font.getTextWidth(r.text);
291
+ tx += r.width;
202
292
  }
203
293
  }
204
294
 
205
295
  drawImage(image: unknown, x: number, y: number, w: number, h: number): void {
206
296
  const img = this.imageOf(image);
207
297
  if (!img) return;
208
- const p = this.Skia.Paint();
209
- p.setAntiAlias(true);
210
- p.setAlphaf(this.st.globalAlpha);
298
+ const key = 'image|' + this.st.globalAlpha;
299
+ let p = this.paints.get(key);
300
+ if (!p) {
301
+ p = this.Skia.Paint();
302
+ p.setAntiAlias(true);
303
+ p.setAlphaf(this.st.globalAlpha);
304
+ remember(this.paints, key, p);
305
+ }
211
306
  this.canvas.drawImageRect(img, this.Skia.XYWHRect(0, 0, img.width(), img.height()), this.Skia.XYWHRect(x, y, w, h), p);
212
307
  }
213
308
 
@@ -219,10 +314,26 @@ export class SkiaDrawCtx implements DrawCtx {
219
314
  return c;
220
315
  }
221
316
 
317
+ /** The paint for the current style: made the first time a style is used, the same object every time after. */
222
318
  private paint(stroke: boolean): SkPaint {
223
- const s = this.st, p = this.Skia.Paint();
319
+ const same = stroke ? this.strokePaint : this.fillPaint;
320
+ if (same) return same;
321
+ const p = this.paintFor(stroke);
322
+ if (stroke) this.strokePaint = p; else this.fillPaint = p;
323
+ return p;
324
+ }
325
+
326
+ private paintFor(stroke: boolean): SkPaint {
327
+ const s = this.st, css = stroke ? s.strokeStyle : s.fillStyle;
328
+ const dashed = stroke && s.dash.length > 0 && s.dash.some(v => v > 0);
329
+ const key = stroke
330
+ ? 's|' + css + '|' + s.globalAlpha + '|' + s.lineWidth + '|' + s.lineCap + '|' + s.lineJoin + (dashed ? '|' + s.dash.join(',') : '')
331
+ : 'f|' + css + '|' + s.globalAlpha;
332
+ const hit = this.paints.get(key);
333
+ if (hit) return hit;
334
+ const p = this.Skia.Paint();
224
335
  p.setAntiAlias(true);
225
- const c = this.color(stroke ? s.strokeStyle : s.fillStyle);
336
+ const c = this.color(css);
226
337
  p.setColor(c);
227
338
  p.setAlphaf(c[3] * s.globalAlpha);
228
339
  if (stroke) {
@@ -230,36 +341,47 @@ export class SkiaDrawCtx implements DrawCtx {
230
341
  p.setStrokeWidth(s.lineWidth);
231
342
  p.setStrokeCap(CAP[s.lineCap]);
232
343
  p.setStrokeJoin(JOIN[s.lineJoin]);
233
- if (s.dash.length && s.dash.some(v => v > 0)) p.setPathEffect(this.Skia.PathEffect.MakeDash(s.dash, 0));
344
+ if (dashed) p.setPathEffect(this.Skia.PathEffect.MakeDash(s.dash, 0));
234
345
  }
235
- return p;
346
+ return remember(this.paints, key, p);
236
347
  }
237
348
 
238
349
  private fontPair(): FontPair {
239
- const { weight, size } = parseFont(this.st.font), key = weight + '|' + size;
350
+ const key = this.st.font;
240
351
  let f = this.fonts.get(key);
241
352
  if (!f) {
353
+ const { weight, size } = parseFont(key);
242
354
  const ws = Object.keys(this.typefaces.weights).map(Number);
243
355
  const near = ws.sort((a, b) => Math.abs(a - weight) - Math.abs(b - weight))[0];
356
+ const main = this.Skia.Font(this.typefaces.weights[near]!, size), m = main.getMetrics();
244
357
  f = {
245
- main: this.Skia.Font(this.typefaces.weights[near]!, size),
358
+ main,
246
359
  fb: this.typefaces.fallback ? this.Skia.Font(this.typefaces.fallback, size) : null,
360
+ metrics: { ascent: m.ascent, descent: m.descent, size: main.getSize() },
247
361
  };
248
- this.fonts.set(key, f);
362
+ remember(this.fonts, key, f);
249
363
  }
250
364
  return f;
251
365
  }
252
366
 
253
- /** Split text into runs the main font can draw and runs that need the fallback. */
254
- private runs(text: string, f: FontPair): { text: string; font: SkFont }[] {
255
- if (!f.fb) return [{ text, font: f.main }];
256
- const chars = Array.from(text), ids = f.main.getGlyphIDs(text, chars.length);
257
- if (!ids.includes(0)) return [{ text, font: f.main }];
258
- const out: { text: string; font: SkFont }[] = [];
259
- chars.forEach((ch, i) => {
260
- const font = ids[i] === 0 ? f.fb! : f.main, last = out[out.length - 1];
261
- if (last && last.font === font) last.text += ch; else out.push({ text: ch, font });
262
- });
263
- return out;
367
+ /** Split text into runs the main font can draw and runs that need the fallback, with their widths (cached). */
368
+ private runs(text: string, f: FontPair): Run[] {
369
+ const css = this.st.font;
370
+ let byText = this.textRuns.get(css);
371
+ if (!byText) this.textRuns.set(css, (byText = new Map()));
372
+ const hit = byText.get(text);
373
+ if (hit) return hit;
374
+ const out: Run[] = [];
375
+ const ids = f.fb ? f.main.getGlyphIDs(text, Array.from(text).length) : null;
376
+ if (!ids || !ids.includes(0)) out.push({ text, font: f.main, width: 0 });
377
+ else {
378
+ Array.from(text).forEach((ch, i) => {
379
+ const font = ids[i] === 0 ? f.fb! : f.main, last = out[out.length - 1];
380
+ if (last && last.font === font) last.text += ch; else out.push({ text: ch, font, width: 0 });
381
+ });
382
+ }
383
+ for (const r of out) r.width = r.font.getTextWidth(r.text);
384
+ // (kept: measuring and drawing a label again — every frame — asks native code nothing)
385
+ return remember(byText, text, out);
264
386
  }
265
387
  }
package/src/skia/host.ts CHANGED
@@ -44,7 +44,20 @@ export const skiaImageLoader: ImageLoader = (src, onLoad) => {
44
44
  const imageOf = (img: unknown): SkImage | null => (img as SkiaImage | null)?.sk ?? null;
45
45
 
46
46
  /** A drawing context on a Skia canvas, with the current typefaces and Skia-loaded pictures. */
47
- export const skiaCtx = (canvas: SkCanvas): SkiaDrawCtx => new SkiaDrawCtx(Skia, canvas, currentFonts(), imageOf);
47
+ /** One context for frame after frame (its paints, fonts and text caches are reused); a new one if the typefaces change. */
48
+ let shared: SkiaDrawCtx | null = null;
49
+ export function skiaCtx(canvas: SkCanvas): SkiaDrawCtx {
50
+ const fonts = currentFonts();
51
+ if (!shared || shared.typefaceSet !== fonts) shared = new SkiaDrawCtx(Skia, canvas, fonts, imageOf);
52
+ return shared.begin(canvas);
53
+ }
54
+ /** A second one, for drawing on two canvases at once (recordLayers). */
55
+ let second: SkiaDrawCtx | null = null;
56
+ function skiaCtx2(canvas: SkCanvas): SkiaDrawCtx {
57
+ const fonts = currentFonts();
58
+ if (!second || second.typefaceSet !== fonts) second = new SkiaDrawCtx(Skia, canvas, fonts, imageOf);
59
+ return second.begin(canvas);
60
+ }
48
61
 
49
62
  /** Record w × h of drawing as a picture; also returns what `draw` returned (e.g. the tappable chips). */
50
63
  export function recordPicture<T>(w: number, h: number, draw: (ctx: SkiaDrawCtx) => T): { picture: SkPicture; result: T } {
@@ -54,6 +67,19 @@ export function recordPicture<T>(w: number, h: number, draw: (ctx: SkiaDrawCtx)
54
67
  return { picture: rec.finishRecordingAsPicture(), result };
55
68
  }
56
69
 
70
+ /**
71
+ * Record a frame as two pictures at once: `moving`, which a host moves and scales with the view (drawn `margin` px
72
+ * past each edge of the w × h screen, so moved a little it shows no edge), and `fixed`, what stays put on the screen.
73
+ * Also returns what `draw` returned.
74
+ */
75
+ export function recordLayers<T>(w: number, h: number, margin: number, draw: (moving: SkiaDrawCtx, fixed: SkiaDrawCtx) => T): { moving: SkPicture; fixed: SkPicture; result: T } {
76
+ const recM = Skia.PictureRecorder(), recF = Skia.PictureRecorder();
77
+ const m = recM.beginRecording(Skia.XYWHRect(-margin, -margin, w + 2 * margin, h + 2 * margin));
78
+ const f = recF.beginRecording(Skia.XYWHRect(0, 0, w, h));
79
+ const result = draw(skiaCtx(m), skiaCtx2(f));
80
+ return { moving: recM.finishRecordingAsPicture(), fixed: recF.finishRecordingAsPicture(), result };
81
+ }
82
+
57
83
  /** Draw w × h into an image and encode it as a PNG data URL (null if no surface could be made). */
58
84
  export function renderPng(w: number, h: number, draw: (ctx: SkiaDrawCtx) => void): string | null {
59
85
  const surface = Skia.Surface.Make(w, h);
@@ -62,3 +88,22 @@ export function renderPng(w: number, h: number, draw: (ctx: SkiaDrawCtx) => void
62
88
  surface.flush();
63
89
  return 'data:image/png;base64,' + surface.makeImageSnapshot().encodeToBase64();
64
90
  }
91
+
92
+ /**
93
+ * Measuring text without drawing (picking a note, its handles): a context of its own with the current typefaces, so
94
+ * widths match the drawing's exactly. Its canvas is never drawn on.
95
+ */
96
+ let measurer: SkiaDrawCtx | null = null;
97
+ function measureCtx(): SkiaDrawCtx {
98
+ const fonts = currentFonts();
99
+ if (!measurer || measurer.typefaceSet !== fonts) {
100
+ const canvas = Skia.PictureRecorder().beginRecording(Skia.XYWHRect(0, 0, 1, 1));
101
+ measurer = new SkiaDrawCtx(Skia, canvas, fonts, imageOf);
102
+ }
103
+ return measurer;
104
+ }
105
+ export const skiaTextMeasure = {
106
+ get font(): string { return measureCtx().font; },
107
+ set font(v: string) { measureCtx().font = v; },
108
+ measureText: (s: string): { width: number } => measureCtx().measureText(s),
109
+ };
@@ -1,5 +1,6 @@
1
1
  import { hyp } from '../model/geom';
2
2
  import { curveInfo } from '../model/sketch';
3
+ import { faceEnds, faceSpan } from '../model/wallfaces';
3
4
  import type { Constraint, Sketch } from '../model/types';
4
5
 
5
6
  export type Vec = Float64Array;
@@ -51,6 +52,20 @@ export function consEqs(sk: Sketch, c: Constraint, P: PFn, D: DFn, RV: RVFn, RD:
51
52
  // on the side the centre started on (sign), so it never flips across the line
52
53
  const l = L(c.l), r = rad(c.c);
53
54
  if (!l || !r) break;
55
+ // an arc that ends on one of the line's ends (a fillet, a smooth join): tangent there is its radius to that end
56
+ // standing square to the line. (The distance form has no slope at that point, so the solver couldn't tell a
57
+ // second such tangent from one that's already true.)
58
+ const ci = curveInfo(sk, c.c);
59
+ const joint = ci && ci.kind === 'arc' ? [ci.o.a, ci.o.b].find(p => p === l.a || p === l.b) : undefined;
60
+ if (ci && joint) {
61
+ const o = ci.o.c;
62
+ out.push([[...D(l.a), ...D(l.b), ...D(o), ...D(joint)], X => {
63
+ const a = P(X, l.a), b = P(X, l.b), m = P(X, o), e = P(X, joint);
64
+ const ux = b[0] - a[0], uy = b[1] - a[1], vx = e[0] - m[0], vy = e[1] - m[1];
65
+ return (ux * vx + uy * vy) / ((hyp(ux, uy) || 1e-12) * (hyp(vx, vy) || 1e-12));
66
+ }]);
67
+ break;
68
+ }
54
69
  const s = c.sign || 1;
55
70
  out.push([[...D(l.a), ...D(l.b), ...D(r.c), ...r.deps], X => {
56
71
  const o = P(X, r.c), a = P(X, l.a), b = P(X, l.b), n = hyp(b[0] - a[0], b[1] - a[1]) || 1e-12;
@@ -109,6 +124,19 @@ export function consEqs(sk: Sketch, c: Constraint, P: PFn, D: DFn, RV: RVFn, RD:
109
124
  break;
110
125
  }
111
126
  case 'dist': {
127
+ const fw = c.face && c.line && (!c.kind || c.kind === 'al') ? L(c.line) : undefined;
128
+ if (fw && fw.kind === 'wall') {
129
+ // along one face of a wall, corner to corner: the walls at its ends cut it short (an inside corner) or carry it
130
+ // on (an outside one), so they're part of it too
131
+ const ends = faceEnds(sk, c.line!), side = c.face!;
132
+ const deps = [...D(fw.a), ...D(fw.b), ...[...ends.a, ...ends.b].flatMap(e => D(e.far ?? e.arc!.c))];
133
+ out.push([deps, X => {
134
+ const at = (id: string) => { const q = P(X, id); return { x: q[0], y: q[1] }; };
135
+ const f = faceSpan(at, at(fw.a), at(fw.b), fw.thick, side, ends);
136
+ return (f ? f.s1 - f.s0 : 0) - c.value!;
137
+ }]);
138
+ break;
139
+ }
112
140
  const deps = [...D(c.a!), ...D(c.b!)], s = c.sign || 1;
113
141
  if (c.kind === 'h') out.push([deps, X => (P(X, c.b!)[0] - P(X, c.a!)[0]) * s - c.value!]);
114
142
  else if (c.kind === 'v') out.push([deps, X => (P(X, c.b!)[1] - P(X, c.a!)[1]) * s - c.value!]);
@@ -119,16 +147,23 @@ export function consEqs(sk: Sketch, c: Constraint, P: PFn, D: DFn, RV: RVFn, RD:
119
147
  const l = L(c.l);
120
148
  if (!l) break;
121
149
  const s = c.sign || 1;
150
+ // between walls' faces: the centreline distance, less half of each wall's thickness for a face toward the other
151
+ // (more, for a far face) — read when solved, so a wall made thicker keeps the faces where they were measured
152
+ const k = c.faces, w2 = L(c.l2);
153
+ const faceOff = () => (k ? (k[0] * (l.kind === 'wall' ? l.thick : 0) + k[1] * (w2 && w2.kind === 'wall' ? w2.thick : 0)) / 2 : 0);
122
154
  out.push([[...D(c.p!), ...D(l.a), ...D(l.b)], X => {
123
155
  const p = P(X, c.p!), a = P(X, l.a), b = P(X, l.b), dx = b[0] - a[0], dy = b[1] - a[1], n = hyp(dx, dy) || 1e-12;
124
- return (s * ((b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]))) / n - c.value!;
156
+ return (s * ((b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]))) / n - faceOff() - c.value!;
125
157
  }]);
126
158
  break;
127
159
  }
128
160
  case 'diam': {
129
161
  const r = rad(c.c);
130
162
  if (!r) break;
131
- out.push([r.deps, X => 2 * r.f(X) - c.value!]);
163
+ // across a curved wall's face: the centreline's, plus its thickness for the outer face (less, the inner)
164
+ const w = sk.circles[c.c!] ?? sk.arcs[c.c!];
165
+ const faceOff = () => (c.face && w && w.kind === 'wall' ? c.face * (w.thick || 4.5) : 0);
166
+ out.push([r.deps, X => 2 * r.f(X) + faceOff() - c.value!]);
132
167
  break;
133
168
  }
134
169
  case 'angle': {