tldraw 5.2.0-next.e2b8d10bf10e → 5.2.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 (420) hide show
  1. package/DOCS.md +20294 -0
  2. package/README.md +13 -1
  3. package/RELEASE_NOTES.md +2908 -0
  4. package/dist-cjs/index.d.ts +110 -23
  5. package/dist-cjs/index.js +3 -3
  6. package/dist-cjs/index.js.map +2 -2
  7. package/dist-cjs/lib/TldrawImage.js +0 -1
  8. package/dist-cjs/lib/TldrawImage.js.map +2 -2
  9. package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js +2 -1
  10. package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
  11. package/dist-cjs/lib/defaultEmbedDefinitions.js +3 -2
  12. package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
  13. package/dist-cjs/lib/defaultOverlayUtils.js +11 -11
  14. package/dist-cjs/lib/defaultOverlayUtils.js.map +2 -2
  15. package/dist-cjs/lib/overlays/SelectionForegroundOverlayUtil.js +76 -64
  16. package/dist-cjs/lib/overlays/SelectionForegroundOverlayUtil.js.map +2 -2
  17. package/dist-cjs/lib/overlays/ShapeHandleOverlayUtil.js +1 -1
  18. package/dist-cjs/lib/overlays/ShapeHandleOverlayUtil.js.map +2 -2
  19. package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js +107 -0
  20. package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js.map +7 -0
  21. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +3 -0
  22. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
  23. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +12 -3
  24. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
  25. package/dist-cjs/lib/shapes/bookmark/bookmarks.js +54 -6
  26. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
  27. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +2 -1
  28. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
  29. package/dist-cjs/lib/shapes/draw/getPath.js +1 -1
  30. package/dist-cjs/lib/shapes/draw/getPath.js.map +2 -2
  31. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js +45 -47
  32. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
  33. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +2 -1
  34. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  35. package/dist-cjs/lib/shapes/frame/FrameShapeTool.js +23 -18
  36. package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +2 -2
  37. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +1 -1
  38. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  39. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js +3 -0
  40. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
  41. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +11 -7
  42. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
  43. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -11
  44. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
  45. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js +5 -1
  46. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
  47. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +31 -41
  48. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  49. package/dist-cjs/lib/shapes/note/noteHelpers.js +1 -1
  50. package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
  51. package/dist-cjs/lib/shapes/note/toolStates/Pointing.js +28 -6
  52. package/dist-cjs/lib/shapes/note/toolStates/Pointing.js.map +2 -2
  53. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +3 -2
  54. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  55. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +3 -3
  56. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  57. package/dist-cjs/lib/shapes/shared/defaultStyleDefs.js +17 -3
  58. package/dist-cjs/lib/shapes/shared/defaultStyleDefs.js.map +2 -2
  59. package/dist-cjs/lib/shapes/shared/freehand/core.js +424 -0
  60. package/dist-cjs/lib/shapes/shared/freehand/core.js.map +7 -0
  61. package/dist-cjs/lib/shapes/shared/freehand/fmt.js +106 -0
  62. package/dist-cjs/lib/shapes/shared/freehand/fmt.js.map +7 -0
  63. package/dist-cjs/lib/shapes/shared/freehand/getStroke.js +6 -6
  64. package/dist-cjs/lib/shapes/shared/freehand/getStroke.js.map +2 -2
  65. package/dist-cjs/lib/shapes/shared/freehand/getStrokeOutlinePoints.js +320 -86
  66. package/dist-cjs/lib/shapes/shared/freehand/getStrokeOutlinePoints.js.map +3 -3
  67. package/dist-cjs/lib/shapes/shared/freehand/getStrokePoints.js +20 -96
  68. package/dist-cjs/lib/shapes/shared/freehand/getStrokePoints.js.map +3 -3
  69. package/dist-cjs/lib/shapes/shared/freehand/svg.js +55 -19
  70. package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
  71. package/dist-cjs/lib/shapes/shared/freehand/svgInk.js +189 -97
  72. package/dist-cjs/lib/shapes/shared/freehand/svgInk.js.map +3 -3
  73. package/dist-cjs/lib/shapes/shared/freehand/types.js.map +1 -1
  74. package/dist-cjs/lib/shapes/shared/interpolate-props.js +6 -2
  75. package/dist-cjs/lib/shapes/shared/interpolate-props.js.map +2 -2
  76. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js +3 -0
  77. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
  78. package/dist-cjs/lib/tools/HandTool/HandTool.js +16 -6
  79. package/dist-cjs/lib/tools/HandTool/HandTool.js.map +2 -2
  80. package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js +10 -1
  81. package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js.map +2 -2
  82. package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js +84 -0
  83. package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js.map +7 -0
  84. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +12 -0
  85. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  86. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +4 -0
  87. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
  88. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +107 -46
  89. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  90. package/dist-cjs/lib/ui/TldrawUi.js +4 -3
  91. package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
  92. package/dist-cjs/lib/ui/components/A11y.js +2 -0
  93. package/dist-cjs/lib/ui/components/A11y.js.map +2 -2
  94. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js +14 -1
  95. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js.map +2 -2
  96. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js +0 -6
  97. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js.map +2 -2
  98. package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +2 -2
  99. package/dist-cjs/lib/ui/components/Dialogs.js +17 -37
  100. package/dist-cjs/lib/ui/components/Dialogs.js.map +2 -2
  101. package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js +19 -4
  102. package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js.map +2 -2
  103. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +12 -2
  104. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +2 -2
  105. package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +47 -70
  106. package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
  107. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +25 -46
  108. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  109. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.js.map +2 -2
  110. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuContent.js.map +2 -2
  111. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.js.map +2 -2
  112. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js +1 -1
  113. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js.map +2 -2
  114. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +1 -0
  115. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +2 -2
  116. package/dist-cjs/lib/ui/components/menu-items.js +7 -0
  117. package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
  118. package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js +2 -1
  119. package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js.map +2 -2
  120. package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js +14 -3
  121. package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js.map +2 -2
  122. package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js +2 -0
  123. package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js.map +2 -2
  124. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +2 -0
  125. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js.map +2 -2
  126. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js +0 -2
  127. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
  128. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.js +1 -2
  129. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.js.map +2 -2
  130. package/dist-cjs/lib/ui/context/actions.js +70 -1
  131. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  132. package/dist-cjs/lib/ui/context/asset-urls.js +5 -8
  133. package/dist-cjs/lib/ui/context/asset-urls.js.map +2 -2
  134. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  135. package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js +13 -0
  136. package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js.map +2 -2
  137. package/dist-cjs/lib/ui/hooks/useTools.js +1 -1
  138. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  139. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  140. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
  141. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  142. package/dist-cjs/lib/ui/version.js +3 -3
  143. package/dist-cjs/lib/ui/version.js.map +1 -1
  144. package/dist-cjs/lib/utils/embeds/embeds.js +4 -4
  145. package/dist-cjs/lib/utils/embeds/embeds.js.map +2 -2
  146. package/dist-cjs/lib/utils/test-helpers.js +2 -2
  147. package/dist-cjs/lib/utils/test-helpers.js.map +2 -2
  148. package/dist-cjs/lib/utils/text/richText.js +3 -1
  149. package/dist-cjs/lib/utils/text/richText.js.map +2 -2
  150. package/dist-esm/index.d.mts +110 -23
  151. package/dist-esm/index.mjs +5 -3
  152. package/dist-esm/index.mjs.map +2 -2
  153. package/dist-esm/lib/TldrawImage.mjs +0 -1
  154. package/dist-esm/lib/TldrawImage.mjs.map +2 -2
  155. package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs +2 -1
  156. package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
  157. package/dist-esm/lib/defaultEmbedDefinitions.mjs +3 -2
  158. package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
  159. package/dist-esm/lib/defaultOverlayUtils.mjs +11 -11
  160. package/dist-esm/lib/defaultOverlayUtils.mjs.map +2 -2
  161. package/dist-esm/lib/overlays/SelectionForegroundOverlayUtil.mjs +77 -65
  162. package/dist-esm/lib/overlays/SelectionForegroundOverlayUtil.mjs.map +2 -2
  163. package/dist-esm/lib/overlays/ShapeHandleOverlayUtil.mjs +1 -1
  164. package/dist-esm/lib/overlays/ShapeHandleOverlayUtil.mjs.map +2 -2
  165. package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs +87 -0
  166. package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs.map +7 -0
  167. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +9 -1
  168. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
  169. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +15 -3
  170. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
  171. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +54 -6
  172. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
  173. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +3 -1
  174. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  175. package/dist-esm/lib/shapes/draw/getPath.mjs +1 -1
  176. package/dist-esm/lib/shapes/draw/getPath.mjs.map +2 -2
  177. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs +46 -47
  178. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
  179. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +2 -1
  180. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  181. package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs +23 -18
  182. package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +2 -2
  183. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +2 -1
  184. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  185. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs +4 -0
  186. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
  187. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +12 -7
  188. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  189. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -11
  190. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
  191. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs +6 -1
  192. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
  193. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +32 -42
  194. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  195. package/dist-esm/lib/shapes/note/noteHelpers.mjs +1 -1
  196. package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
  197. package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs +29 -6
  198. package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs.map +2 -2
  199. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +3 -2
  200. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  201. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +4 -3
  202. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  203. package/dist-esm/lib/shapes/shared/defaultStyleDefs.mjs +17 -3
  204. package/dist-esm/lib/shapes/shared/defaultStyleDefs.mjs.map +2 -2
  205. package/dist-esm/lib/shapes/shared/freehand/core.mjs +404 -0
  206. package/dist-esm/lib/shapes/shared/freehand/core.mjs.map +7 -0
  207. package/dist-esm/lib/shapes/shared/freehand/fmt.mjs +86 -0
  208. package/dist-esm/lib/shapes/shared/freehand/fmt.mjs.map +7 -0
  209. package/dist-esm/lib/shapes/shared/freehand/getStroke.mjs +7 -7
  210. package/dist-esm/lib/shapes/shared/freehand/getStroke.mjs.map +2 -2
  211. package/dist-esm/lib/shapes/shared/freehand/getStrokeOutlinePoints.mjs +332 -86
  212. package/dist-esm/lib/shapes/shared/freehand/getStrokeOutlinePoints.mjs.map +3 -3
  213. package/dist-esm/lib/shapes/shared/freehand/getStrokePoints.mjs +31 -96
  214. package/dist-esm/lib/shapes/shared/freehand/getStrokePoints.mjs.map +3 -3
  215. package/dist-esm/lib/shapes/shared/freehand/svg.mjs +55 -19
  216. package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
  217. package/dist-esm/lib/shapes/shared/freehand/svgInk.mjs +214 -99
  218. package/dist-esm/lib/shapes/shared/freehand/svgInk.mjs.map +3 -3
  219. package/dist-esm/lib/shapes/shared/interpolate-props.mjs +6 -2
  220. package/dist-esm/lib/shapes/shared/interpolate-props.mjs.map +2 -2
  221. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs +4 -0
  222. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
  223. package/dist-esm/lib/tools/HandTool/HandTool.mjs +16 -6
  224. package/dist-esm/lib/tools/HandTool/HandTool.mjs.map +2 -2
  225. package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs +10 -1
  226. package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs.map +2 -2
  227. package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs +64 -0
  228. package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs.map +7 -0
  229. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +12 -0
  230. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  231. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +4 -0
  232. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
  233. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +108 -46
  234. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  235. package/dist-esm/lib/ui/TldrawUi.mjs +4 -3
  236. package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
  237. package/dist-esm/lib/ui/components/A11y.mjs +2 -0
  238. package/dist-esm/lib/ui/components/A11y.mjs.map +2 -2
  239. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs +21 -2
  240. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs.map +2 -2
  241. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs +0 -6
  242. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs.map +2 -2
  243. package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +2 -2
  244. package/dist-esm/lib/ui/components/Dialogs.mjs +19 -39
  245. package/dist-esm/lib/ui/components/Dialogs.mjs.map +2 -2
  246. package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs +20 -5
  247. package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs.map +2 -2
  248. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +12 -2
  249. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +2 -2
  250. package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +47 -70
  251. package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
  252. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +25 -46
  253. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  254. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.mjs.map +2 -2
  255. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuContent.mjs.map +2 -2
  256. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.mjs.map +2 -2
  257. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs +1 -1
  258. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs.map +2 -2
  259. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +1 -0
  260. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +2 -2
  261. package/dist-esm/lib/ui/components/menu-items.mjs +7 -0
  262. package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
  263. package/dist-esm/lib/ui/components/primitives/TldrawUiDropdownMenu.mjs +2 -1
  264. package/dist-esm/lib/ui/components/primitives/TldrawUiDropdownMenu.mjs.map +2 -2
  265. package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs +14 -3
  266. package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs.map +2 -2
  267. package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs +2 -0
  268. package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs.map +2 -2
  269. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs +2 -0
  270. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs.map +2 -2
  271. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs +0 -2
  272. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
  273. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.mjs +1 -2
  274. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.mjs.map +2 -2
  275. package/dist-esm/lib/ui/context/actions.mjs +71 -1
  276. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  277. package/dist-esm/lib/ui/context/asset-urls.mjs +5 -8
  278. package/dist-esm/lib/ui/context/asset-urls.mjs.map +2 -2
  279. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  280. package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs +13 -0
  281. package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs.map +2 -2
  282. package/dist-esm/lib/ui/hooks/useTools.mjs +1 -1
  283. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  284. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
  285. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  286. package/dist-esm/lib/ui/version.mjs +3 -3
  287. package/dist-esm/lib/ui/version.mjs.map +1 -1
  288. package/dist-esm/lib/utils/embeds/embeds.mjs +4 -4
  289. package/dist-esm/lib/utils/embeds/embeds.mjs.map +2 -2
  290. package/dist-esm/lib/utils/test-helpers.mjs +2 -2
  291. package/dist-esm/lib/utils/test-helpers.mjs.map +2 -2
  292. package/dist-esm/lib/utils/text/richText.mjs +3 -1
  293. package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
  294. package/package.json +11 -6
  295. package/src/index.ts +6 -1
  296. package/src/lib/TldrawImage.tsx +2 -2
  297. package/src/lib/bindings/arrow/ArrowBindingUtil.ts +5 -1
  298. package/src/lib/defaultEmbedDefinitions.ts +28 -6
  299. package/src/lib/defaultOverlayUtils.ts +11 -11
  300. package/src/lib/overlays/SelectionForegroundOverlayUtil.ts +91 -72
  301. package/src/lib/overlays/ShapeHandleOverlayUtil.ts +3 -1
  302. package/src/lib/overlays/ShapeIndicatorOverlayUtil.ts +133 -0
  303. package/src/lib/shapes/arrow/toolStates/Pointing.tsx +11 -1
  304. package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +16 -3
  305. package/src/lib/shapes/bookmark/bookmarks.ts +105 -15
  306. package/src/lib/shapes/draw/DrawShapeUtil.tsx +7 -5
  307. package/src/lib/shapes/draw/getPath.ts +1 -1
  308. package/src/lib/shapes/draw/toolStates/Drawing.ts +55 -48
  309. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +14 -4
  310. package/src/lib/shapes/frame/FrameShapeTool.ts +35 -24
  311. package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +46 -0
  312. package/src/lib/shapes/geo/GeoShapeUtil.tsx +2 -1
  313. package/src/lib/shapes/geo/toolStates/Pointing.ts +5 -0
  314. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +14 -11
  315. package/src/lib/shapes/image/ImageShapeUtil.tsx +7 -11
  316. package/src/lib/shapes/line/LineShapeTool.test.ts +41 -0
  317. package/src/lib/shapes/line/toolStates/Pointing.ts +12 -2
  318. package/src/lib/shapes/note/NoteShapeUtil.tsx +36 -51
  319. package/src/lib/shapes/note/noteCloning.test.ts +5 -5
  320. package/src/lib/shapes/note/noteHelpers.ts +1 -1
  321. package/src/lib/shapes/note/toolStates/Pointing.ts +40 -7
  322. package/src/lib/shapes/shared/PlainTextLabel.tsx +3 -3
  323. package/src/lib/shapes/shared/RichTextLabel.tsx +4 -3
  324. package/src/lib/shapes/shared/defaultStyleDefs.tsx +25 -3
  325. package/src/lib/shapes/shared/freehand/core.ts +547 -0
  326. package/src/lib/shapes/shared/freehand/fmt.ts +106 -0
  327. package/src/lib/shapes/shared/freehand/getStroke.ts +7 -7
  328. package/src/lib/shapes/shared/freehand/getStrokeOutlinePoints.ts +451 -139
  329. package/src/lib/shapes/shared/freehand/getStrokePoints.ts +33 -153
  330. package/src/lib/shapes/shared/freehand/svg.ts +72 -23
  331. package/src/lib/shapes/shared/freehand/svgInk.ts +281 -136
  332. package/src/lib/shapes/shared/freehand/types.ts +0 -1
  333. package/src/lib/shapes/shared/interpolate-props.ts +6 -2
  334. package/src/lib/shapes/text/toolStates/Pointing.ts +5 -0
  335. package/src/lib/tools/HandTool/HandTool.ts +19 -6
  336. package/src/lib/tools/HandTool/childStates/Dragging.ts +17 -1
  337. package/src/lib/tools/HandTool/childStates/OneFingerZooming.ts +79 -0
  338. package/src/lib/tools/SelectTool/childStates/Idle.ts +16 -2
  339. package/src/lib/tools/SelectTool/childStates/PointingShape.ts +8 -0
  340. package/src/lib/tools/SelectTool/childStates/Resizing.ts +137 -49
  341. package/src/lib/ui/TldrawUi.tsx +3 -3
  342. package/src/lib/ui/components/A11y.tsx +2 -0
  343. package/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx +32 -2
  344. package/src/lib/ui/components/ContextMenu/DefaultContextMenuContent.tsx +5 -6
  345. package/src/lib/ui/components/DefaultFollowingIndicator.tsx +2 -2
  346. package/src/lib/ui/components/Dialogs.tsx +20 -23
  347. package/src/lib/ui/components/HelperButtons/BackToContent.tsx +32 -4
  348. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +10 -0
  349. package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +60 -74
  350. package/src/lib/ui/components/Minimap/MinimapManager.ts +35 -57
  351. package/src/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.tsx +2 -2
  352. package/src/lib/ui/components/SharePanel/DefaultPeopleMenuContent.tsx +2 -1
  353. package/src/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.tsx +2 -1
  354. package/src/lib/ui/components/SharePanel/DefaultPeopleMenuItem.tsx +3 -3
  355. package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +5 -0
  356. package/src/lib/ui/components/menu-items.tsx +9 -0
  357. package/src/lib/ui/components/primitives/TldrawUiDropdownMenu.tsx +3 -1
  358. package/src/lib/ui/components/primitives/TldrawUiIcon.tsx +15 -3
  359. package/src/lib/ui/components/primitives/TldrawUiInput.tsx +4 -0
  360. package/src/lib/ui/components/primitives/TldrawUiPopover.tsx +7 -0
  361. package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +0 -2
  362. package/src/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.tsx +0 -1
  363. package/src/lib/ui/context/actions.tsx +88 -1
  364. package/src/lib/ui/context/asset-urls.tsx +9 -8
  365. package/src/lib/ui/context/events.tsx +2 -0
  366. package/src/lib/ui/hooks/useKeyboardShortcuts.ts +45 -7
  367. package/src/lib/ui/hooks/useTools.tsx +7 -1
  368. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
  369. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
  370. package/src/lib/ui/version.ts +3 -3
  371. package/src/lib/ui.css +20 -9
  372. package/src/lib/utils/embeds/embeds.test.ts +14 -12
  373. package/src/lib/utils/embeds/embeds.ts +10 -4
  374. package/src/lib/utils/test-helpers.ts +3 -3
  375. package/src/lib/utils/text/richText.test.ts +36 -0
  376. package/src/lib/utils/text/richText.ts +9 -1
  377. package/src/test/HandTool.test.ts +93 -0
  378. package/src/test/ImageShapeUtil.test.ts +88 -0
  379. package/src/test/SelectTool.test.ts +102 -1
  380. package/src/test/__snapshots__/drawing.test.ts.snap +2 -1
  381. package/src/test/attribution.test.ts +40 -29
  382. package/src/test/bookmark-shapes.test.ts +256 -28
  383. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
  384. package/src/test/commands/clipboardCallbacks.test.ts +3 -1
  385. package/src/test/commands/getSvgString.test.ts +30 -0
  386. package/src/test/commands/penmode.test.ts +97 -1
  387. package/src/test/commands/setCamera.test.ts +12 -0
  388. package/src/test/dragFromToolbar.test.ts +67 -0
  389. package/src/test/drawing.test.ts +48 -7
  390. package/src/test/frames.test.ts +72 -0
  391. package/src/test/freehand/corpus/presets.ts +71 -0
  392. package/src/test/freehand/corpus/real.ts +370 -0
  393. package/src/test/freehand/corpus/strokes.ts +50 -0
  394. package/src/test/freehand/svg.test.ts +20 -0
  395. package/src/test/freehand/wellformed.test.ts +30 -0
  396. package/src/test/long-press-create.test.ts +102 -0
  397. package/src/test/overlays/CollaboratorBrushOverlayUtil.test.ts +4 -4
  398. package/src/test/overlays/CollaboratorCursorOverlayUtil.test.ts +18 -18
  399. package/src/test/overlays/CollaboratorHintOverlayUtil.test.ts +7 -7
  400. package/src/test/overlays/CollaboratorScribbleOverlayUtil.test.ts +3 -3
  401. package/src/test/overlays/OverlayManager.test.ts +1 -1
  402. package/src/test/overlays/SelectionForegroundOverlayUtil.test.ts +58 -0
  403. package/src/test/overlays/ShapeHandleOverlayUtil.test.ts +2 -2
  404. package/src/test/overlays/ShapeIndicatorOverlayUtil.test.ts +36 -0
  405. package/src/test/resizing.test.ts +250 -0
  406. package/src/test/right-click-context-menu.test.ts +90 -0
  407. package/src/test/rightClickPanning.test.ts +11 -0
  408. package/src/test/selection-omnibus.test.ts +15 -2
  409. package/src/test/spatialIndex.test.ts +132 -0
  410. package/src/test/translating.test.ts +0 -6
  411. package/src/test/ui/ContextMenu.test.tsx +146 -3
  412. package/src/test/ui/Dialogs.test.tsx +256 -0
  413. package/src/test/ui/copyHoveredStyles.test.tsx +154 -0
  414. package/src/test/ui/keyboardShortcuts.test.tsx +266 -0
  415. package/tldraw.css +35 -12
  416. package/dist-cjs/lib/shapes/shared/freehand/setStrokePointRadii.js +0 -100
  417. package/dist-cjs/lib/shapes/shared/freehand/setStrokePointRadii.js.map +0 -7
  418. package/dist-esm/lib/shapes/shared/freehand/setStrokePointRadii.mjs +0 -80
  419. package/dist-esm/lib/shapes/shared/freehand/setStrokePointRadii.mjs.map +0 -7
  420. package/src/lib/shapes/shared/freehand/setStrokePointRadii.ts +0 -127
@@ -1,104 +1,39 @@
1
1
  import { Vec } from "@tldraw/editor";
2
- const MIN_PRESSURE = 0.025;
2
+ import {
3
+ distances,
4
+ ingest,
5
+ inputX,
6
+ inputY,
7
+ inputZ,
8
+ pointCount,
9
+ pointX,
10
+ pointY,
11
+ pressures,
12
+ runningLengths
13
+ } from "./core.mjs";
3
14
  function getStrokePoints(rawInputPoints, options = {}) {
4
- const { streamline = 0.5, size = 16, simulatePressure = false } = options;
5
- if (rawInputPoints.length === 0) return [];
6
- const t = 0.15 + (1 - streamline) * 0.85;
7
- let pts = rawInputPoints.map(Vec.From);
8
- let pointsRemovedFromNearEnd = 0;
9
- if (!simulatePressure) {
10
- for (const pt2 of pts) {
11
- if (pt2.z < MIN_PRESSURE) {
12
- pt2.z = MIN_PRESSURE;
13
- }
14
- }
15
- }
16
- if (pts.length === 0)
17
- return [
18
- {
19
- point: Vec.From(rawInputPoints[0]),
20
- input: Vec.From(rawInputPoints[0]),
21
- pressure: simulatePressure ? 0.5 : 0.15,
22
- vector: new Vec(1, 1),
23
- distance: 0,
24
- runningLength: 0,
25
- radius: 1
26
- }
27
- ];
28
- let pt = pts[1];
29
- while (pt) {
30
- if (Vec.Dist2(pt, pts[0]) > (size / 3) ** 2) break;
31
- pts[0].z = Math.max(pts[0].z, pt.z);
32
- pts.splice(1, 1);
33
- pt = pts[1];
34
- }
35
- const last = pts.pop();
36
- pt = pts[pts.length - 1];
37
- while (pt) {
38
- if (Vec.Dist2(pt, last) > (size / 3) ** 2) break;
39
- pts.pop();
40
- pt = pts[pts.length - 1];
41
- pointsRemovedFromNearEnd++;
42
- }
43
- pts.push(last);
44
- const isComplete = options.last || !options.simulatePressure || pts.length > 1 && Vec.Dist2(pts[pts.length - 1], pts[pts.length - 2]) < size ** 2 || pointsRemovedFromNearEnd > 0;
45
- if (pts.length === 2 && options.simulatePressure) {
46
- const last2 = pts[1];
47
- pts = pts.slice(0, -1);
48
- for (let i = 1; i < 5; i++) {
49
- const next = Vec.Lrp(pts[0], last2, i / 4);
50
- next.z = (pts[0].z + (last2.z - pts[0].z)) * i / 4;
51
- pts.push(next);
52
- }
53
- }
54
- const strokePoints = [
55
- {
56
- point: pts[0],
57
- input: pts[0],
58
- pressure: simulatePressure ? 0.5 : pts[0].z,
59
- vector: new Vec(1, 1),
60
- distance: 0,
61
- runningLength: 0,
62
- radius: 1
63
- }
64
- ];
65
- let totalLength = 0;
66
- let prev = strokePoints[0];
67
- let point, distance;
68
- if (isComplete && streamline > 0) {
69
- pts.push(pts[pts.length - 1].clone());
70
- }
71
- for (let i = 1, n = pts.length; i < n; i++) {
72
- point = !t || options.last && i === n - 1 ? pts[i].clone() : pts[i].clone().lrp(prev.point, 1 - t);
73
- if (prev.point.equals(point)) continue;
74
- distance = Vec.Dist(point, prev.point);
75
- totalLength += distance;
76
- if (i < 4 && totalLength < size) {
77
- continue;
78
- }
79
- prev = {
80
- input: pts[i],
81
- // The adjusted point
15
+ ingest(rawInputPoints, options);
16
+ const n = pointCount;
17
+ const ptX = pointX;
18
+ const ptY = pointY;
19
+ const inX = inputX;
20
+ const inY = inputY;
21
+ const inZ = inputZ;
22
+ const press = pressures;
23
+ const dists = distances;
24
+ const runs = runningLengths;
25
+ const strokePoints = new Array(n);
26
+ for (let i = 0; i < n; i++) {
27
+ const input = new Vec(inX[i], inY[i], inZ[i]);
28
+ const point = i === 0 ? input : new Vec(ptX[i], ptY[i], inZ[i]);
29
+ strokePoints[i] = {
82
30
  point,
83
- // The input pressure (or .5 if not specified)
84
- pressure: simulatePressure ? 0.5 : pts[i].z,
85
- // The vector from the current point to the previous point
86
- vector: Vec.Sub(prev.point, point).uni(),
87
- // The distance between the current point and the previous point
88
- distance,
89
- // The total distance so far
90
- runningLength: totalLength,
91
- // The stroke point's radius
31
+ input,
32
+ pressure: press[i],
33
+ distance: dists[i],
34
+ runningLength: runs[i],
92
35
  radius: 1
93
36
  };
94
- strokePoints.push(prev);
95
- }
96
- if (strokePoints[1]?.vector) {
97
- strokePoints[0].vector = strokePoints[1].vector.clone();
98
- }
99
- if (totalLength < 1) {
100
- const maxPressureAmongPoints = Math.max(0.5, ...strokePoints.map((s) => s.pressure));
101
- strokePoints.forEach((s) => s.pressure = maxPressureAmongPoints);
102
37
  }
103
38
  return strokePoints;
104
39
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/shapes/shared/freehand/getStrokePoints.ts"],
4
- "sourcesContent": ["import { Vec, VecLike } from '@tldraw/editor'\nimport type { StrokeOptions, StrokePoint } from './types'\n\nconst MIN_PRESSURE = 0.025\n\n/**\n * ## getStrokePoints\n *\n * Get an array of points as objects with an adjusted point, pressure, vector, distance, and\n * runningLength.\n *\n * @param points - An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is\n * optional in both cases.\n * @param options - An object with options.\n * @public\n */\nexport function getStrokePoints(\n\trawInputPoints: VecLike[],\n\toptions: StrokeOptions = {}\n): StrokePoint[] {\n\tconst { streamline = 0.5, size = 16, simulatePressure = false } = options\n\n\t// If we don't have any points, return an empty array.\n\tif (rawInputPoints.length === 0) return []\n\n\t// Find the interpolation level between points.\n\tconst t = 0.15 + (1 - streamline) * 0.85\n\n\t// Whatever the input is, make sure that the points are in number[][].\n\tlet pts = rawInputPoints.map(Vec.From)\n\n\tlet pointsRemovedFromNearEnd = 0\n\n\tif (!simulatePressure) {\n\t\t// Clamp any zero/near-zero pressure points to a minimum value.\n\t\t// Some pens or OSes report z=0 even while the pen is touching,\n\t\t// so we clamp rather than strip to avoid removing real input.\n\t\tfor (const pt of pts) {\n\t\t\tif (pt.z < MIN_PRESSURE) {\n\t\t\t\tpt.z = MIN_PRESSURE\n\t\t\t}\n\t\t}\n\t}\n\n\tif (pts.length === 0)\n\t\treturn [\n\t\t\t{\n\t\t\t\tpoint: Vec.From(rawInputPoints[0]),\n\t\t\t\tinput: Vec.From(rawInputPoints[0]),\n\t\t\t\tpressure: simulatePressure ? 0.5 : 0.15,\n\t\t\t\tvector: new Vec(1, 1),\n\t\t\t\tdistance: 0,\n\t\t\t\trunningLength: 0,\n\t\t\t\tradius: 1,\n\t\t\t},\n\t\t]\n\n\t// Strip points that are too close to the first point.\n\tlet pt = pts[1]\n\twhile (pt) {\n\t\tif (Vec.Dist2(pt, pts[0]) > (size / 3) ** 2) break\n\t\tpts[0].z = Math.max(pts[0].z, pt.z) // Use maximum pressure\n\t\tpts.splice(1, 1)\n\t\tpt = pts[1]\n\t}\n\n\t// Strip points that are too close to the last point.\n\tconst last = pts.pop()!\n\tpt = pts[pts.length - 1]\n\twhile (pt) {\n\t\tif (Vec.Dist2(pt, last) > (size / 3) ** 2) break\n\t\tpts.pop()\n\t\tpt = pts[pts.length - 1]\n\t\tpointsRemovedFromNearEnd++\n\t}\n\tpts.push(last)\n\n\tconst isComplete =\n\t\toptions.last ||\n\t\t!options.simulatePressure ||\n\t\t(pts.length > 1 && Vec.Dist2(pts[pts.length - 1], pts[pts.length - 2]) < size ** 2) ||\n\t\tpointsRemovedFromNearEnd > 0\n\n\t// Add extra points between the two, to help avoid \"dash\" lines\n\t// for strokes with tapered start and ends. Don't mutate the\n\t// input array!\n\tif (pts.length === 2 && options.simulatePressure) {\n\t\tconst last = pts[1]\n\t\tpts = pts.slice(0, -1)\n\t\tfor (let i = 1; i < 5; i++) {\n\t\t\tconst next = Vec.Lrp(pts[0], last, i / 4)\n\t\t\tnext.z = ((pts[0].z + (last.z - pts[0].z)) * i) / 4\n\t\t\tpts.push(next)\n\t\t}\n\t}\n\n\t// The strokePoints array will hold the points for the stroke.\n\t// Start it out with the first point, which needs no adjustment.\n\tconst strokePoints: StrokePoint[] = [\n\t\t{\n\t\t\tpoint: pts[0],\n\t\t\tinput: pts[0],\n\t\t\tpressure: simulatePressure ? 0.5 : pts[0].z,\n\t\t\tvector: new Vec(1, 1),\n\t\t\tdistance: 0,\n\t\t\trunningLength: 0,\n\t\t\tradius: 1,\n\t\t},\n\t]\n\n\t// We use the totalLength to keep track of the total distance\n\tlet totalLength = 0\n\n\t// We're set this to the latest point, so we can use it to calculate\n\t// the distance and vector of the next point.\n\tlet prev = strokePoints[0]\n\n\t// Iterate through all of the points, creating StrokePoints.\n\tlet point: Vec, distance: number\n\n\tif (isComplete && streamline > 0) {\n\t\tpts.push(pts[pts.length - 1].clone())\n\t}\n\n\tfor (let i = 1, n = pts.length; i < n; i++) {\n\t\tpoint =\n\t\t\t!t || (options.last && i === n - 1) ? pts[i].clone() : pts[i].clone().lrp(prev.point, 1 - t)\n\n\t\t// If the new point is the same as the previous point, skip ahead.\n\t\tif (prev.point.equals(point)) continue\n\n\t\t// How far is the new point from the previous point?\n\t\tdistance = Vec.Dist(point, prev.point)\n\n\t\t// Add this distance to the total \"running length\" of the line.\n\t\ttotalLength += distance\n\n\t\t// At the start of the line, we wait until the new point is a\n\t\t// certain distance away from the original point, to avoid noise\n\n\t\tif (i < 4 && totalLength < size) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Create a new strokepoint (it will be the new \"previous\" one).\n\t\tprev = {\n\t\t\tinput: pts[i],\n\t\t\t// The adjusted point\n\t\t\tpoint,\n\t\t\t// The input pressure (or .5 if not specified)\n\t\t\tpressure: simulatePressure ? 0.5 : pts[i].z,\n\t\t\t// The vector from the current point to the previous point\n\t\t\tvector: Vec.Sub(prev.point, point).uni(),\n\t\t\t// The distance between the current point and the previous point\n\t\t\tdistance,\n\t\t\t// The total distance so far\n\t\t\trunningLength: totalLength,\n\t\t\t// The stroke point's radius\n\t\t\tradius: 1,\n\t\t}\n\n\t\t// Push it to the strokePoints array.\n\t\tstrokePoints.push(prev)\n\t}\n\n\t// Set the vector of the first point to be the same as the second point.\n\tif (strokePoints[1]?.vector) {\n\t\tstrokePoints[0].vector = strokePoints[1].vector.clone()\n\t}\n\n\tif (totalLength < 1) {\n\t\tconst maxPressureAmongPoints = Math.max(0.5, ...strokePoints.map((s) => s.pressure))\n\t\tstrokePoints.forEach((s) => (s.pressure = maxPressureAmongPoints))\n\t}\n\n\treturn strokePoints\n}\n"],
5
- "mappings": "AAAA,SAAS,WAAoB;AAG7B,MAAM,eAAe;AAad,SAAS,gBACf,gBACA,UAAyB,CAAC,GACV;AAChB,QAAM,EAAE,aAAa,KAAK,OAAO,IAAI,mBAAmB,MAAM,IAAI;AAGlE,MAAI,eAAe,WAAW,EAAG,QAAO,CAAC;AAGzC,QAAM,IAAI,QAAQ,IAAI,cAAc;AAGpC,MAAI,MAAM,eAAe,IAAI,IAAI,IAAI;AAErC,MAAI,2BAA2B;AAE/B,MAAI,CAAC,kBAAkB;AAItB,eAAWA,OAAM,KAAK;AACrB,UAAIA,IAAG,IAAI,cAAc;AACxB,QAAAA,IAAG,IAAI;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAEA,MAAI,IAAI,WAAW;AAClB,WAAO;AAAA,MACN;AAAA,QACC,OAAO,IAAI,KAAK,eAAe,CAAC,CAAC;AAAA,QACjC,OAAO,IAAI,KAAK,eAAe,CAAC,CAAC;AAAA,QACjC,UAAU,mBAAmB,MAAM;AAAA,QACnC,QAAQ,IAAI,IAAI,GAAG,CAAC;AAAA,QACpB,UAAU;AAAA,QACV,eAAe;AAAA,QACf,QAAQ;AAAA,MACT;AAAA,IACD;AAGD,MAAI,KAAK,IAAI,CAAC;AACd,SAAO,IAAI;AACV,QAAI,IAAI,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,OAAO,MAAM,EAAG;AAC7C,QAAI,CAAC,EAAE,IAAI,KAAK,IAAI,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC;AAClC,QAAI,OAAO,GAAG,CAAC;AACf,SAAK,IAAI,CAAC;AAAA,EACX;AAGA,QAAM,OAAO,IAAI,IAAI;AACrB,OAAK,IAAI,IAAI,SAAS,CAAC;AACvB,SAAO,IAAI;AACV,QAAI,IAAI,MAAM,IAAI,IAAI,KAAK,OAAO,MAAM,EAAG;AAC3C,QAAI,IAAI;AACR,SAAK,IAAI,IAAI,SAAS,CAAC;AACvB;AAAA,EACD;AACA,MAAI,KAAK,IAAI;AAEb,QAAM,aACL,QAAQ,QACR,CAAC,QAAQ,oBACR,IAAI,SAAS,KAAK,IAAI,MAAM,IAAI,IAAI,SAAS,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC,CAAC,IAAI,QAAQ,KACjF,2BAA2B;AAK5B,MAAI,IAAI,WAAW,KAAK,QAAQ,kBAAkB;AACjD,UAAMC,QAAO,IAAI,CAAC;AAClB,UAAM,IAAI,MAAM,GAAG,EAAE;AACrB,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC3B,YAAM,OAAO,IAAI,IAAI,IAAI,CAAC,GAAGA,OAAM,IAAI,CAAC;AACxC,WAAK,KAAM,IAAI,CAAC,EAAE,KAAKA,MAAK,IAAI,IAAI,CAAC,EAAE,MAAM,IAAK;AAClD,UAAI,KAAK,IAAI;AAAA,IACd;AAAA,EACD;AAIA,QAAM,eAA8B;AAAA,IACnC;AAAA,MACC,OAAO,IAAI,CAAC;AAAA,MACZ,OAAO,IAAI,CAAC;AAAA,MACZ,UAAU,mBAAmB,MAAM,IAAI,CAAC,EAAE;AAAA,MAC1C,QAAQ,IAAI,IAAI,GAAG,CAAC;AAAA,MACpB,UAAU;AAAA,MACV,eAAe;AAAA,MACf,QAAQ;AAAA,IACT;AAAA,EACD;AAGA,MAAI,cAAc;AAIlB,MAAI,OAAO,aAAa,CAAC;AAGzB,MAAI,OAAY;AAEhB,MAAI,cAAc,aAAa,GAAG;AACjC,QAAI,KAAK,IAAI,IAAI,SAAS,CAAC,EAAE,MAAM,CAAC;AAAA,EACrC;AAEA,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAI,GAAG,KAAK;AAC3C,YACC,CAAC,KAAM,QAAQ,QAAQ,MAAM,IAAI,IAAK,IAAI,CAAC,EAAE,MAAM,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,KAAK,OAAO,IAAI,CAAC;AAG5F,QAAI,KAAK,MAAM,OAAO,KAAK,EAAG;AAG9B,eAAW,IAAI,KAAK,OAAO,KAAK,KAAK;AAGrC,mBAAe;AAKf,QAAI,IAAI,KAAK,cAAc,MAAM;AAChC;AAAA,IACD;AAGA,WAAO;AAAA,MACN,OAAO,IAAI,CAAC;AAAA;AAAA,MAEZ;AAAA;AAAA,MAEA,UAAU,mBAAmB,MAAM,IAAI,CAAC,EAAE;AAAA;AAAA,MAE1C,QAAQ,IAAI,IAAI,KAAK,OAAO,KAAK,EAAE,IAAI;AAAA;AAAA,MAEvC;AAAA;AAAA,MAEA,eAAe;AAAA;AAAA,MAEf,QAAQ;AAAA,IACT;AAGA,iBAAa,KAAK,IAAI;AAAA,EACvB;AAGA,MAAI,aAAa,CAAC,GAAG,QAAQ;AAC5B,iBAAa,CAAC,EAAE,SAAS,aAAa,CAAC,EAAE,OAAO,MAAM;AAAA,EACvD;AAEA,MAAI,cAAc,GAAG;AACpB,UAAM,yBAAyB,KAAK,IAAI,KAAK,GAAG,aAAa,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC;AACnF,iBAAa,QAAQ,CAAC,MAAO,EAAE,WAAW,sBAAuB;AAAA,EAClE;AAEA,SAAO;AACR;",
6
- "names": ["pt", "last"]
4
+ "sourcesContent": ["import { Vec, VecLike } from '@tldraw/editor'\nimport {\n\tdistances,\n\tingest,\n\tinputX,\n\tinputY,\n\tinputZ,\n\tpointCount,\n\tpointX,\n\tpointY,\n\tpressures,\n\trunningLengths,\n} from './core'\nimport type { StrokeOptions, StrokePoint } from './types'\n\n/**\n * ## getStrokePoints\n *\n * Get an array of points as objects with an adjusted point, pressure, vector, distance, and\n * runningLength.\n *\n * @param points - An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is\n * optional in both cases.\n * @param options - An object with options.\n * @public\n */\nexport function getStrokePoints(\n\trawInputPoints: VecLike[],\n\toptions: StrokeOptions = {}\n): StrokePoint[] {\n\tingest(rawInputPoints, options)\n\n\tconst n = pointCount\n\tconst ptX = pointX\n\tconst ptY = pointY\n\tconst inX = inputX\n\tconst inY = inputY\n\tconst inZ = inputZ\n\tconst press = pressures\n\tconst dists = distances\n\tconst runs = runningLengths\n\tconst strokePoints: StrokePoint[] = new Array(n)\n\tfor (let i = 0; i < n; i++) {\n\t\tconst input = new Vec(inX[i], inY[i], inZ[i])\n\t\t// The first point needs no adjustment, so its point and input are the same vector.\n\t\tconst point = i === 0 ? input : new Vec(ptX[i], ptY[i], inZ[i])\n\t\tstrokePoints[i] = {\n\t\t\tpoint,\n\t\t\tinput,\n\t\t\tpressure: press[i],\n\t\t\tdistance: dists[i],\n\t\t\trunningLength: runs[i],\n\t\t\tradius: 1,\n\t\t}\n\t}\n\treturn strokePoints\n}\n"],
5
+ "mappings": "AAAA,SAAS,WAAoB;AAC7B;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAcA,SAAS,gBACf,gBACA,UAAyB,CAAC,GACV;AAChB,SAAO,gBAAgB,OAAO;AAE9B,QAAM,IAAI;AACV,QAAM,MAAM;AACZ,QAAM,MAAM;AACZ,QAAM,MAAM;AACZ,QAAM,MAAM;AACZ,QAAM,MAAM;AACZ,QAAM,QAAQ;AACd,QAAM,QAAQ;AACd,QAAM,OAAO;AACb,QAAM,eAA8B,IAAI,MAAM,CAAC;AAC/C,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC3B,UAAM,QAAQ,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAE5C,UAAM,QAAQ,MAAM,IAAI,QAAQ,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9D,iBAAa,CAAC,IAAI;AAAA,MACjB;AAAA,MACA;AAAA,MACA,UAAU,MAAM,CAAC;AAAA,MACjB,UAAU,MAAM,CAAC;AAAA,MACjB,eAAe,KAAK,CAAC;AAAA,MACrB,QAAQ;AAAA,IACT;AAAA,EACD;AACA,SAAO;AACR;",
6
+ "names": []
7
7
  }
@@ -1,34 +1,70 @@
1
- import { average, precise } from "@tldraw/editor";
1
+ import { finishPath, resetPath, toCenti, writeCPair, writeStr } from "./fmt.mjs";
2
2
  function getSvgPathFromStrokePoints(points, closed = false) {
3
3
  const len = points.length;
4
4
  if (len < 2) {
5
5
  return "";
6
6
  }
7
- let a = points[0].point;
8
- let b = points[1].point;
7
+ resetPath();
9
8
  if (len === 2) {
10
- return `M${precise(a)}L${precise(b)}`;
9
+ const a = points[0].point;
10
+ const b = points[1].point;
11
+ const ax = toCenti(a.x);
12
+ const ay = toCenti(a.y);
13
+ writeStr("M");
14
+ writeCPair(ax, ay);
15
+ writeStr("l");
16
+ writeCPair(toCenti(b.x) - ax, toCenti(b.y) - ay);
17
+ return finishPath();
18
+ }
19
+ const first = points[0].point;
20
+ const second = points[1].point;
21
+ const third = points[2].point;
22
+ const secondX = toCenti(second.x);
23
+ const secondY = toCenti(second.y);
24
+ const m01x = Math.round((first.x + second.x) * 50);
25
+ const m01y = Math.round((first.y + second.y) * 50);
26
+ const m12x = Math.round((second.x + third.x) * 50);
27
+ const m12y = Math.round((second.y + third.y) * 50);
28
+ let cx = m12x;
29
+ let cy = m12y;
30
+ if (closed) {
31
+ writeStr("M");
32
+ writeCPair(m01x, m01y);
33
+ writeStr("q");
34
+ writeCPair(secondX - m01x, secondY - m01y);
35
+ writeCPair(m12x - m01x, m12y - m01y);
36
+ writeStr("t");
37
+ } else {
38
+ const firstX = toCenti(first.x);
39
+ const firstY = toCenti(first.y);
40
+ writeStr("M");
41
+ writeCPair(firstX, firstY);
42
+ writeStr("q");
43
+ writeCPair(secondX - firstX, secondY - firstY);
44
+ writeCPair(m12x - firstX, m12y - firstY);
45
+ if (len > 3) writeStr("t");
11
46
  }
12
- let result = "";
13
47
  for (let i = 2, max = len - 1; i < max; i++) {
14
- a = points[i].point;
15
- b = points[i + 1].point;
16
- result += average(a, b);
48
+ const p = points[i].point;
49
+ const q = points[i + 1].point;
50
+ const mx = Math.round((p.x + q.x) * 50);
51
+ const my = Math.round((p.y + q.y) * 50);
52
+ writeCPair(mx - cx, my - cy);
53
+ cx = mx;
54
+ cy = my;
17
55
  }
56
+ const last = points[len - 1].point;
18
57
  if (closed) {
19
- return `M${average(points[0].point, points[1].point)}Q${precise(points[1].point)}${average(
20
- points[1].point,
21
- points[2].point
22
- )}T${result}${average(points[len - 1].point, points[0].point)}${average(
23
- points[0].point,
24
- points[1].point
25
- )}Z`;
58
+ const mLastX = Math.round((last.x + first.x) * 50);
59
+ const mLastY = Math.round((last.y + first.y) * 50);
60
+ writeCPair(mLastX - cx, mLastY - cy);
61
+ writeCPair(m01x - mLastX, m01y - mLastY);
62
+ writeStr("Z");
26
63
  } else {
27
- return `M${precise(points[0].point)}Q${precise(points[1].point)}${average(
28
- points[1].point,
29
- points[2].point
30
- )}${points.length > 3 ? "T" : ""}${result}L${precise(points[len - 1].point)}`;
64
+ writeStr("l");
65
+ writeCPair(toCenti(last.x) - cx, toCenti(last.y) - cy);
31
66
  }
67
+ return finishPath();
32
68
  }
33
69
  export {
34
70
  getSvgPathFromStrokePoints
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/shapes/shared/freehand/svg.ts"],
4
- "sourcesContent": ["import { average, precise } from '@tldraw/editor'\nimport { StrokePoint } from './types'\n\n/**\n * Turn an array of stroke points into a path of quadratic curves.\n *\n * @param points - The stroke points returned from perfect-freehand\n * @param closed - Whether the shape is closed\n *\n * @public\n */\nexport function getSvgPathFromStrokePoints(points: StrokePoint[], closed = false): string {\n\tconst len = points.length\n\n\tif (len < 2) {\n\t\treturn ''\n\t}\n\n\tlet a = points[0].point\n\tlet b = points[1].point\n\n\tif (len === 2) {\n\t\treturn `M${precise(a)}L${precise(b)}`\n\t}\n\n\tlet result = ''\n\n\tfor (let i = 2, max = len - 1; i < max; i++) {\n\t\ta = points[i].point\n\t\tb = points[i + 1].point\n\t\tresult += average(a, b)\n\t}\n\n\tif (closed) {\n\t\t// If closed, draw a curve from the last point to the first\n\t\treturn `M${average(points[0].point, points[1].point)}Q${precise(points[1].point)}${average(\n\t\t\tpoints[1].point,\n\t\t\tpoints[2].point\n\t\t)}T${result}${average(points[len - 1].point, points[0].point)}${average(\n\t\t\tpoints[0].point,\n\t\t\tpoints[1].point\n\t\t)}Z`\n\t} else {\n\t\t// If not closed, draw a curve starting at the first point and\n\t\t// ending at the midpoint of the last and second-last point, then\n\t\t// complete the curve with a line segment to the last point.\n\t\treturn `M${precise(points[0].point)}Q${precise(points[1].point)}${average(\n\t\t\tpoints[1].point,\n\t\t\tpoints[2].point\n\t\t)}${points.length > 3 ? 'T' : ''}${result}L${precise(points[len - 1].point)}`\n\t}\n}\n"],
5
- "mappings": "AAAA,SAAS,SAAS,eAAe;AAW1B,SAAS,2BAA2B,QAAuB,SAAS,OAAe;AACzF,QAAM,MAAM,OAAO;AAEnB,MAAI,MAAM,GAAG;AACZ,WAAO;AAAA,EACR;AAEA,MAAI,IAAI,OAAO,CAAC,EAAE;AAClB,MAAI,IAAI,OAAO,CAAC,EAAE;AAElB,MAAI,QAAQ,GAAG;AACd,WAAO,IAAI,QAAQ,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC;AAAA,EACpC;AAEA,MAAI,SAAS;AAEb,WAAS,IAAI,GAAG,MAAM,MAAM,GAAG,IAAI,KAAK,KAAK;AAC5C,QAAI,OAAO,CAAC,EAAE;AACd,QAAI,OAAO,IAAI,CAAC,EAAE;AAClB,cAAU,QAAQ,GAAG,CAAC;AAAA,EACvB;AAEA,MAAI,QAAQ;AAEX,WAAO,IAAI,QAAQ,OAAO,CAAC,EAAE,OAAO,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,QAAQ,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG;AAAA,MAClF,OAAO,CAAC,EAAE;AAAA,MACV,OAAO,CAAC,EAAE;AAAA,IACX,CAAC,IAAI,MAAM,GAAG,QAAQ,OAAO,MAAM,CAAC,EAAE,OAAO,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG;AAAA,MAC/D,OAAO,CAAC,EAAE;AAAA,MACV,OAAO,CAAC,EAAE;AAAA,IACX,CAAC;AAAA,EACF,OAAO;AAIN,WAAO,IAAI,QAAQ,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,QAAQ,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG;AAAA,MACjE,OAAO,CAAC,EAAE;AAAA,MACV,OAAO,CAAC,EAAE;AAAA,IACX,CAAC,GAAG,OAAO,SAAS,IAAI,MAAM,EAAE,GAAG,MAAM,IAAI,QAAQ,OAAO,MAAM,CAAC,EAAE,KAAK,CAAC;AAAA,EAC5E;AACD;",
4
+ "sourcesContent": ["import { finishPath, resetPath, toCenti, writeCPair, writeStr } from './fmt'\nimport { StrokePoint } from './types'\n\n/**\n * Turn an array of stroke points into a path of quadratic curves.\n *\n * The path uses relative commands with coordinates rounded to hundredths of a pixel, which\n * renders identically to the absolute 4-decimal form but is much smaller and faster to build.\n *\n * @param points - The stroke points returned from perfect-freehand\n * @param closed - Whether the shape is closed\n *\n * @public\n */\nexport function getSvgPathFromStrokePoints(points: StrokePoint[], closed = false): string {\n\tconst len = points.length\n\n\tif (len < 2) {\n\t\treturn ''\n\t}\n\n\tresetPath()\n\n\tif (len === 2) {\n\t\tconst a = points[0].point\n\t\tconst b = points[1].point\n\t\tconst ax = toCenti(a.x)\n\t\tconst ay = toCenti(a.y)\n\t\twriteStr('M')\n\t\twriteCPair(ax, ay)\n\t\twriteStr('l')\n\t\twriteCPair(toCenti(b.x) - ax, toCenti(b.y) - ay)\n\t\treturn finishPath()\n\t}\n\n\tconst first = points[0].point\n\tconst second = points[1].point\n\tconst third = points[2].point\n\n\tconst secondX = toCenti(second.x)\n\tconst secondY = toCenti(second.y)\n\t// midpoint of the first and second points\n\tconst m01x = Math.round((first.x + second.x) * 50)\n\tconst m01y = Math.round((first.y + second.y) * 50)\n\t// midpoint of the second and third points\n\tconst m12x = Math.round((second.x + third.x) * 50)\n\tconst m12y = Math.round((second.y + third.y) * 50)\n\n\t// Current position in integer hundredths\n\tlet cx = m12x\n\tlet cy = m12y\n\n\tif (closed) {\n\t\t// If closed, start at the first midpoint and draw a curve through the second point\n\t\twriteStr('M')\n\t\twriteCPair(m01x, m01y)\n\t\twriteStr('q')\n\t\twriteCPair(secondX - m01x, secondY - m01y)\n\t\twriteCPair(m12x - m01x, m12y - m01y)\n\t\twriteStr('t')\n\t} else {\n\t\t// If not closed, draw a curve starting at the first point and\n\t\t// ending at the midpoint of the second and third points.\n\t\tconst firstX = toCenti(first.x)\n\t\tconst firstY = toCenti(first.y)\n\t\twriteStr('M')\n\t\twriteCPair(firstX, firstY)\n\t\twriteStr('q')\n\t\twriteCPair(secondX - firstX, secondY - firstY)\n\t\twriteCPair(m12x - firstX, m12y - firstY)\n\t\tif (len > 3) writeStr('t')\n\t}\n\n\t// Continue the smooth quadratic chain through the midpoints of the remaining points\n\tfor (let i = 2, max = len - 1; i < max; i++) {\n\t\tconst p = points[i].point\n\t\tconst q = points[i + 1].point\n\t\tconst mx = Math.round((p.x + q.x) * 50)\n\t\tconst my = Math.round((p.y + q.y) * 50)\n\t\twriteCPair(mx - cx, my - cy)\n\t\tcx = mx\n\t\tcy = my\n\t}\n\n\tconst last = points[len - 1].point\n\n\tif (closed) {\n\t\t// Draw a curve from the last-first midpoint back to the first-second midpoint\n\t\tconst mLastX = Math.round((last.x + first.x) * 50)\n\t\tconst mLastY = Math.round((last.y + first.y) * 50)\n\t\twriteCPair(mLastX - cx, mLastY - cy)\n\t\twriteCPair(m01x - mLastX, m01y - mLastY)\n\t\twriteStr('Z')\n\t} else {\n\t\t// Complete the curve with a line segment to the last point.\n\t\twriteStr('l')\n\t\twriteCPair(toCenti(last.x) - cx, toCenti(last.y) - cy)\n\t}\n\n\treturn finishPath()\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY,WAAW,SAAS,YAAY,gBAAgB;AAc9D,SAAS,2BAA2B,QAAuB,SAAS,OAAe;AACzF,QAAM,MAAM,OAAO;AAEnB,MAAI,MAAM,GAAG;AACZ,WAAO;AAAA,EACR;AAEA,YAAU;AAEV,MAAI,QAAQ,GAAG;AACd,UAAM,IAAI,OAAO,CAAC,EAAE;AACpB,UAAM,IAAI,OAAO,CAAC,EAAE;AACpB,UAAM,KAAK,QAAQ,EAAE,CAAC;AACtB,UAAM,KAAK,QAAQ,EAAE,CAAC;AACtB,aAAS,GAAG;AACZ,eAAW,IAAI,EAAE;AACjB,aAAS,GAAG;AACZ,eAAW,QAAQ,EAAE,CAAC,IAAI,IAAI,QAAQ,EAAE,CAAC,IAAI,EAAE;AAC/C,WAAO,WAAW;AAAA,EACnB;AAEA,QAAM,QAAQ,OAAO,CAAC,EAAE;AACxB,QAAM,SAAS,OAAO,CAAC,EAAE;AACzB,QAAM,QAAQ,OAAO,CAAC,EAAE;AAExB,QAAM,UAAU,QAAQ,OAAO,CAAC;AAChC,QAAM,UAAU,QAAQ,OAAO,CAAC;AAEhC,QAAM,OAAO,KAAK,OAAO,MAAM,IAAI,OAAO,KAAK,EAAE;AACjD,QAAM,OAAO,KAAK,OAAO,MAAM,IAAI,OAAO,KAAK,EAAE;AAEjD,QAAM,OAAO,KAAK,OAAO,OAAO,IAAI,MAAM,KAAK,EAAE;AACjD,QAAM,OAAO,KAAK,OAAO,OAAO,IAAI,MAAM,KAAK,EAAE;AAGjD,MAAI,KAAK;AACT,MAAI,KAAK;AAET,MAAI,QAAQ;AAEX,aAAS,GAAG;AACZ,eAAW,MAAM,IAAI;AACrB,aAAS,GAAG;AACZ,eAAW,UAAU,MAAM,UAAU,IAAI;AACzC,eAAW,OAAO,MAAM,OAAO,IAAI;AACnC,aAAS,GAAG;AAAA,EACb,OAAO;AAGN,UAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,UAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,aAAS,GAAG;AACZ,eAAW,QAAQ,MAAM;AACzB,aAAS,GAAG;AACZ,eAAW,UAAU,QAAQ,UAAU,MAAM;AAC7C,eAAW,OAAO,QAAQ,OAAO,MAAM;AACvC,QAAI,MAAM,EAAG,UAAS,GAAG;AAAA,EAC1B;AAGA,WAAS,IAAI,GAAG,MAAM,MAAM,GAAG,IAAI,KAAK,KAAK;AAC5C,UAAM,IAAI,OAAO,CAAC,EAAE;AACpB,UAAM,IAAI,OAAO,IAAI,CAAC,EAAE;AACxB,UAAM,KAAK,KAAK,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE;AACtC,UAAM,KAAK,KAAK,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE;AACtC,eAAW,KAAK,IAAI,KAAK,EAAE;AAC3B,SAAK;AACL,SAAK;AAAA,EACN;AAEA,QAAM,OAAO,OAAO,MAAM,CAAC,EAAE;AAE7B,MAAI,QAAQ;AAEX,UAAM,SAAS,KAAK,OAAO,KAAK,IAAI,MAAM,KAAK,EAAE;AACjD,UAAM,SAAS,KAAK,OAAO,KAAK,IAAI,MAAM,KAAK,EAAE;AACjD,eAAW,SAAS,IAAI,SAAS,EAAE;AACnC,eAAW,OAAO,QAAQ,OAAO,MAAM;AACvC,aAAS,GAAG;AAAA,EACb,OAAO;AAEN,aAAS,GAAG;AACZ,eAAW,QAAQ,KAAK,CAAC,IAAI,IAAI,QAAQ,KAAK,CAAC,IAAI,EAAE;AAAA,EACtD;AAEA,SAAO,WAAW;AACnB;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,30 @@
1
- import { Vec, assert, average, precise, toDomPrecision } from "@tldraw/editor";
2
- import { getStrokeOutlineTracks } from "./getStrokeOutlinePoints.mjs";
3
- import { getStrokePoints } from "./getStrokePoints.mjs";
4
- import { setStrokePointRadii } from "./setStrokePointRadii.mjs";
1
+ import { assert } from "@tldraw/editor";
2
+ import {
3
+ computeRadii,
4
+ ingest,
5
+ inputX,
6
+ inputY,
7
+ loadSrcFromPipeline,
8
+ loadSrcPartition,
9
+ pointCount,
10
+ pointX,
11
+ pointY,
12
+ radii,
13
+ srcCount,
14
+ srcRadius,
15
+ srcX,
16
+ srcY
17
+ } from "./core.mjs";
18
+ import { finishPath, resetPath, toCenti, writeC, writeCPair, writeStr } from "./fmt.mjs";
19
+ import {
20
+ buildTracks,
21
+ trackLeftCount,
22
+ trackLeftX,
23
+ trackLeftY,
24
+ trackRightCount,
25
+ trackRightX,
26
+ trackRightY
27
+ } from "./getStrokeOutlinePoints.mjs";
5
28
  function svgInk(rawInputPoints, options = {}) {
6
29
  const { start = {}, end = {} } = options;
7
30
  const { cap: capStart = true } = start;
@@ -9,129 +32,221 @@ function svgInk(rawInputPoints, options = {}) {
9
32
  assert(!start.taper && !end.taper, "cap taper not supported here");
10
33
  assert(!start.easing && !end.easing, "cap easing not supported here");
11
34
  assert(capStart && capEnd, "cap must be true");
12
- const points = getStrokePoints(rawInputPoints, options);
13
- setStrokePointRadii(points, options);
14
- const partitions = partitionAtElbows(points);
15
- let svg = "";
16
- for (const partition of partitions) {
17
- svg += renderPartition(partition, options);
18
- }
19
- return svg;
35
+ ingest(rawInputPoints, options);
36
+ computeRadii(options);
37
+ resetPath();
38
+ partitionAtElbows(options);
39
+ return finishPath();
20
40
  }
21
- function partitionAtElbows(points) {
22
- if (points.length <= 2) return [points];
23
- const result = [];
24
- let currentPartition = [points[0]];
25
- let prevV = Vec.Sub(points[1].point, points[0].point).uni();
26
- let nextV;
27
- let dpr;
28
- let prevPoint, thisPoint, nextPoint;
29
- for (let i = 1, n = points.length; i < n - 1; i++) {
30
- prevPoint = points[i - 1];
31
- thisPoint = points[i];
32
- nextPoint = points[i + 1];
33
- nextV = Vec.Sub(nextPoint.point, thisPoint.point).uni();
34
- dpr = Vec.Dpr(prevV, nextV);
35
- prevV = nextV;
41
+ function partitionAtElbows(options) {
42
+ const n = pointCount;
43
+ if (n === 0) return;
44
+ if (n <= 2) {
45
+ loadSrcFromPipeline();
46
+ renderPartition(options, false, 0, 0);
47
+ return;
48
+ }
49
+ const ptX = pointX;
50
+ const ptY = pointY;
51
+ const rads = radii;
52
+ let a = 0;
53
+ let aElbow = false;
54
+ let hasAnchor = false;
55
+ let anchorX = 0;
56
+ let anchorY = 0;
57
+ let dx = ptX[1] - ptX[0];
58
+ let dy = ptY[1] - ptY[0];
59
+ let len = Math.sqrt(dx * dx + dy * dy);
60
+ let prevVx = dx / len;
61
+ let prevVy = dy / len;
62
+ for (let i = 1; i < n - 1; i++) {
63
+ dx = ptX[i + 1] - ptX[i];
64
+ dy = ptY[i + 1] - ptY[i];
65
+ len = Math.sqrt(dx * dx + dy * dy);
66
+ const nextVx = dx / len;
67
+ const nextVy = dy / len;
68
+ const dpr = prevVx * nextVx + prevVy * nextVy;
69
+ prevVx = nextVx;
70
+ prevVy = nextVy;
36
71
  if (dpr < -0.8) {
37
- const elbowPoint = {
38
- ...thisPoint,
39
- point: thisPoint.input
40
- };
41
- currentPartition.push(elbowPoint);
42
- result.push(cleanUpPartition(currentPartition));
43
- currentPartition = [elbowPoint];
72
+ finishPartition(a, aElbow, i, true, false, hasAnchor, anchorX, anchorY, options);
73
+ a = i;
74
+ aElbow = true;
75
+ hasAnchor = true;
76
+ anchorX = ptX[i];
77
+ anchorY = ptY[i];
44
78
  continue;
45
79
  }
46
- currentPartition.push(thisPoint);
47
80
  if (dpr > 0.7) {
48
81
  continue;
49
82
  }
50
- if ((Vec.Dist2(prevPoint.point, thisPoint.point) + Vec.Dist2(thisPoint.point, nextPoint.point)) / ((prevPoint.radius + thisPoint.radius + nextPoint.radius) / 3) ** 2 < 1.5) {
51
- currentPartition.push(thisPoint);
52
- result.push(cleanUpPartition(currentPartition));
53
- currentPartition = [thisPoint];
83
+ const pdx = ptX[i] - ptX[i - 1];
84
+ const pdy = ptY[i] - ptY[i - 1];
85
+ const ndx = ptX[i + 1] - ptX[i];
86
+ const ndy = ptY[i + 1] - ptY[i];
87
+ const meanRadius = (rads[i - 1] + rads[i] + rads[i + 1]) / 3;
88
+ if ((pdx * pdx + pdy * pdy + ndx * ndx + ndy * ndy) / (meanRadius * meanRadius) < 1.5) {
89
+ finishPartition(a, aElbow, i, false, true, hasAnchor, anchorX, anchorY, options);
90
+ a = i;
91
+ aElbow = false;
92
+ hasAnchor = false;
54
93
  continue;
55
94
  }
56
95
  }
57
- currentPartition.push(points[points.length - 1]);
58
- result.push(cleanUpPartition(currentPartition));
59
- return result;
96
+ finishPartition(a, aElbow, n - 1, false, false, hasAnchor, anchorX, anchorY, options);
60
97
  }
61
- function cleanUpPartition(partition) {
62
- const startPoint = partition[0];
63
- let nextPoint;
64
- while (partition.length > 2) {
65
- nextPoint = partition[1];
66
- if (Vec.Dist2(startPoint.point, nextPoint.point) < ((startPoint.radius + nextPoint.radius) / 2 * 0.5) ** 2) {
67
- partition.splice(1, 1);
98
+ function finishPartition(a, aElbow, b, bElbow, bDup, hasAnchor, anchorX, anchorY, options) {
99
+ const ptX = pointX;
100
+ const ptY = pointY;
101
+ const rads = radii;
102
+ const len = b - a + 1 + (bDup ? 1 : 0);
103
+ let s = 0;
104
+ let e = 0;
105
+ const startX = aElbow ? inputX[a] : ptX[a];
106
+ const startY = aElbow ? inputY[a] : ptY[a];
107
+ const startRadius = rads[a];
108
+ while (len - s > 2) {
109
+ const i = a + 1 + s;
110
+ const dx = startX - ptX[i];
111
+ const dy = startY - ptY[i];
112
+ if (dx * dx + dy * dy < ((startRadius + rads[i]) / 2 * 0.5) ** 2) {
113
+ hasAnchor = true;
114
+ anchorX = ptX[i];
115
+ anchorY = ptY[i];
116
+ s++;
68
117
  } else {
69
118
  break;
70
119
  }
71
120
  }
72
- const endPoint = partition[partition.length - 1];
73
- let prevPoint;
74
- while (partition.length > 2) {
75
- prevPoint = partition[partition.length - 2];
76
- if (Vec.Dist2(endPoint.point, prevPoint.point) < ((endPoint.radius + prevPoint.radius) / 2 * 0.5) ** 2) {
77
- partition.splice(partition.length - 2, 1);
121
+ const endX = bElbow ? inputX[b] : ptX[b];
122
+ const endY = bElbow ? inputY[b] : ptY[b];
123
+ const endRadius = rads[b];
124
+ while (len - s - e > 2) {
125
+ const i = bDup ? b - e : b - 1 - e;
126
+ const dx = endX - ptX[i];
127
+ const dy = endY - ptY[i];
128
+ if (dx * dx + dy * dy < ((endRadius + rads[i]) / 2 * 0.5) ** 2) {
129
+ e++;
78
130
  } else {
79
131
  break;
80
132
  }
81
133
  }
82
- if (partition.length > 1) {
83
- partition[0] = {
84
- ...partition[0],
85
- vector: Vec.Sub(partition[0].point, partition[1].point).uni()
86
- };
87
- partition[partition.length - 1] = {
88
- ...partition[partition.length - 1],
89
- vector: Vec.Sub(
90
- partition[partition.length - 2].point,
91
- partition[partition.length - 1].point
92
- ).uni()
93
- };
94
- }
95
- return partition;
134
+ const innerStart = a + 1 + s;
135
+ const innerEnd = bDup ? b - e : b - 1 - e;
136
+ loadSrcPartition(a, aElbow, innerStart, innerEnd, b, bElbow, bDup && e === 0);
137
+ renderPartition(options, hasAnchor, anchorX, anchorY);
138
+ }
139
+ function writeCirclePath(cx, cy, r) {
140
+ const ncx = toCenti(cx);
141
+ const ncy = toCenti(cy);
142
+ const nr = toCenti(r);
143
+ writeStr("M ");
144
+ writeC(ncx);
145
+ writeStr(" ");
146
+ writeC(ncy);
147
+ writeStr(" m -");
148
+ writeC(nr);
149
+ writeStr(", 0 a ");
150
+ writeC(nr);
151
+ writeStr(",");
152
+ writeC(nr);
153
+ writeStr(" 0 1,1 ");
154
+ writeC(nr * 2);
155
+ writeStr(",0 a ");
156
+ writeC(nr);
157
+ writeStr(",");
158
+ writeC(nr);
159
+ writeStr(" 0 1,1 -");
160
+ writeC(nr * 2);
161
+ writeStr(",0");
96
162
  }
97
- function circlePath(cx, cy, r) {
98
- return "M " + cx + " " + cy + " m -" + r + ", 0 a " + r + "," + r + " 0 1,1 " + r * 2 + ",0 a " + r + "," + r + " 0 1,1 -" + r * 2 + ",0";
163
+ function writeCapArc(nr, dx, dy) {
164
+ writeStr("a");
165
+ writeC(nr);
166
+ writeStr(",");
167
+ writeC(nr);
168
+ writeStr(" 0 0 1 ");
169
+ writeCPair(dx, dy);
99
170
  }
100
- function renderPartition(strokePoints, options = {}) {
101
- if (strokePoints.length === 0) return "";
102
- if (strokePoints.length === 1) {
103
- return circlePath(strokePoints[0].point.x, strokePoints[0].point.y, strokePoints[0].radius);
171
+ function renderPartition(options, hasAnchor, anchorX, anchorY) {
172
+ const n = srcCount;
173
+ if (n === 0) return;
174
+ if (n === 1) {
175
+ writeCirclePath(srcX[0], srcY[0], srcRadius[0]);
176
+ return;
104
177
  }
105
- const { left, right } = getStrokeOutlineTracks(strokePoints, options);
106
- right.reverse();
107
- let svg = `M${precise(left[0])}T`;
108
- for (let i = 1; i < left.length; i++) {
109
- svg += average(left[i - 1], left[i]);
178
+ buildTracks(options, hasAnchor, anchorX, anchorY);
179
+ const lxs = trackLeftX;
180
+ const lys = trackLeftY;
181
+ const rxs = trackRightX;
182
+ const rys = trackRightY;
183
+ let cx = toCenti(lxs[0]);
184
+ let cy = toCenti(lys[0]);
185
+ writeStr("M");
186
+ writeCPair(cx, cy);
187
+ writeStr("t");
188
+ let prevX = lxs[0];
189
+ let prevY = lys[0];
190
+ for (let i = 1; i < trackLeftCount; i++) {
191
+ const ptX = lxs[i];
192
+ const ptY = lys[i];
193
+ const mx = Math.round((prevX + ptX) * 50);
194
+ const my = Math.round((prevY + ptY) * 50);
195
+ writeCPair(mx - cx, my - cy);
196
+ cx = mx;
197
+ cy = my;
198
+ prevX = ptX;
199
+ prevY = ptY;
110
200
  }
111
201
  {
112
- const point = strokePoints[strokePoints.length - 1];
113
- const radius = point.radius;
114
- const direction = point.vector.clone().per().neg();
115
- const arcStart = Vec.Add(point.point, Vec.Mul(direction, radius));
116
- const arcEnd = Vec.Add(point.point, Vec.Mul(direction, -radius));
117
- svg += `${precise(arcStart)}A${toDomPrecision(radius)},${toDomPrecision(
118
- radius
119
- )} 0 0 1 ${precise(arcEnd)}T`;
202
+ const pointX2 = srcX[n - 1];
203
+ const pointY2 = srcY[n - 1];
204
+ const radius = srcRadius[n - 1];
205
+ const vdx = srcX[n - 2] - pointX2;
206
+ const vdy = srcY[n - 2] - pointY2;
207
+ const vlen = Math.sqrt(vdx * vdx + vdy * vdy);
208
+ const dx = -vdy / vlen * radius;
209
+ const dy = vdx / vlen * radius;
210
+ const asx = toCenti(pointX2 + dx);
211
+ const asy = toCenti(pointY2 + dy);
212
+ const aex = toCenti(pointX2 - dx);
213
+ const aey = toCenti(pointY2 - dy);
214
+ writeCPair(asx - cx, asy - cy);
215
+ writeCapArc(toCenti(radius), aex - asx, aey - asy);
216
+ writeStr("t");
217
+ cx = aex;
218
+ cy = aey;
120
219
  }
121
- for (let i = 1; i < right.length; i++) {
122
- svg += average(right[i - 1], right[i]);
220
+ prevX = rxs[trackRightCount - 1];
221
+ prevY = rys[trackRightCount - 1];
222
+ for (let i = trackRightCount - 2; i >= 0; i--) {
223
+ const ptX = rxs[i];
224
+ const ptY = rys[i];
225
+ const mx = Math.round((prevX + ptX) * 50);
226
+ const my = Math.round((prevY + ptY) * 50);
227
+ writeCPair(mx - cx, my - cy);
228
+ cx = mx;
229
+ cy = my;
230
+ prevX = ptX;
231
+ prevY = ptY;
123
232
  }
124
233
  {
125
- const point = strokePoints[0];
126
- const radius = point.radius;
127
- const direction = point.vector.clone().per();
128
- const arcStart = Vec.Add(point.point, Vec.Mul(direction, radius));
129
- const arcEnd = Vec.Add(point.point, Vec.Mul(direction, -radius));
130
- svg += `${precise(arcStart)}A${toDomPrecision(radius)},${toDomPrecision(
131
- radius
132
- )} 0 0 1 ${precise(arcEnd)}Z`;
234
+ const pointX2 = srcX[0];
235
+ const pointY2 = srcY[0];
236
+ const radius = srcRadius[0];
237
+ const vdx = pointX2 - srcX[1];
238
+ const vdy = pointY2 - srcY[1];
239
+ const vlen = Math.sqrt(vdx * vdx + vdy * vdy);
240
+ const dx = vdy / vlen * radius;
241
+ const dy = -vdx / vlen * radius;
242
+ const asx = toCenti(pointX2 + dx);
243
+ const asy = toCenti(pointY2 + dy);
244
+ const aex = toCenti(pointX2 - dx);
245
+ const aey = toCenti(pointY2 - dy);
246
+ writeCPair(asx - cx, asy - cy);
247
+ writeCapArc(toCenti(radius), aex - asx, aey - asy);
248
+ writeStr("Z");
133
249
  }
134
- return svg;
135
250
  }
136
251
  export {
137
252
  svgInk