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.
- package/DOCS.md +20294 -0
- package/README.md +13 -1
- package/RELEASE_NOTES.md +2908 -0
- package/dist-cjs/index.d.ts +110 -23
- package/dist-cjs/index.js +3 -3
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawImage.js +0 -1
- package/dist-cjs/lib/TldrawImage.js.map +2 -2
- package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js +2 -1
- package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
- package/dist-cjs/lib/defaultEmbedDefinitions.js +3 -2
- package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
- package/dist-cjs/lib/defaultOverlayUtils.js +11 -11
- package/dist-cjs/lib/defaultOverlayUtils.js.map +2 -2
- package/dist-cjs/lib/overlays/SelectionForegroundOverlayUtil.js +76 -64
- package/dist-cjs/lib/overlays/SelectionForegroundOverlayUtil.js.map +2 -2
- package/dist-cjs/lib/overlays/ShapeHandleOverlayUtil.js +1 -1
- package/dist-cjs/lib/overlays/ShapeHandleOverlayUtil.js.map +2 -2
- package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js +107 -0
- package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js.map +7 -0
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +3 -0
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +12 -3
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/bookmarks.js +54 -6
- package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +2 -1
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/getPath.js +1 -1
- package/dist-cjs/lib/shapes/draw/getPath.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js +45 -47
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +2 -1
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/FrameShapeTool.js +23 -18
- package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js +3 -0
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +11 -7
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -11
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js +5 -1
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +31 -41
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/note/noteHelpers.js +1 -1
- package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
- package/dist-cjs/lib/shapes/note/toolStates/Pointing.js +28 -6
- package/dist-cjs/lib/shapes/note/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +3 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js +3 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/defaultStyleDefs.js +17 -3
- package/dist-cjs/lib/shapes/shared/defaultStyleDefs.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/freehand/core.js +424 -0
- package/dist-cjs/lib/shapes/shared/freehand/core.js.map +7 -0
- package/dist-cjs/lib/shapes/shared/freehand/fmt.js +106 -0
- package/dist-cjs/lib/shapes/shared/freehand/fmt.js.map +7 -0
- package/dist-cjs/lib/shapes/shared/freehand/getStroke.js +6 -6
- package/dist-cjs/lib/shapes/shared/freehand/getStroke.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/freehand/getStrokeOutlinePoints.js +320 -86
- package/dist-cjs/lib/shapes/shared/freehand/getStrokeOutlinePoints.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/freehand/getStrokePoints.js +20 -96
- package/dist-cjs/lib/shapes/shared/freehand/getStrokePoints.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/freehand/svg.js +55 -19
- package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/freehand/svgInk.js +189 -97
- package/dist-cjs/lib/shapes/shared/freehand/svgInk.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/freehand/types.js.map +1 -1
- package/dist-cjs/lib/shapes/shared/interpolate-props.js +6 -2
- package/dist-cjs/lib/shapes/shared/interpolate-props.js.map +2 -2
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js +3 -0
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/HandTool.js +16 -6
- package/dist-cjs/lib/tools/HandTool/HandTool.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js +10 -1
- package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js +84 -0
- package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js.map +7 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +12 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +4 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +107 -46
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
- package/dist-cjs/lib/ui/TldrawUi.js +4 -3
- package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
- package/dist-cjs/lib/ui/components/A11y.js +2 -0
- package/dist-cjs/lib/ui/components/A11y.js.map +2 -2
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js +14 -1
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js +0 -6
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +2 -2
- package/dist-cjs/lib/ui/components/Dialogs.js +17 -37
- package/dist-cjs/lib/ui/components/Dialogs.js.map +2 -2
- package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js +19 -4
- package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +12 -2
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +47 -70
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +25 -46
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js +1 -1
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +1 -0
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +2 -2
- package/dist-cjs/lib/ui/components/menu-items.js +7 -0
- package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js +2 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js +14 -3
- package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js +2 -0
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +2 -0
- package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js +0 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.js +1 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.js.map +2 -2
- package/dist-cjs/lib/ui/context/actions.js +70 -1
- package/dist-cjs/lib/ui/context/actions.js.map +2 -2
- package/dist-cjs/lib/ui/context/asset-urls.js +5 -8
- package/dist-cjs/lib/ui/context/asset-urls.js.map +2 -2
- package/dist-cjs/lib/ui/context/events.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js +13 -0
- package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTools.js +1 -1
- package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-cjs/lib/utils/embeds/embeds.js +4 -4
- package/dist-cjs/lib/utils/embeds/embeds.js.map +2 -2
- package/dist-cjs/lib/utils/test-helpers.js +2 -2
- package/dist-cjs/lib/utils/test-helpers.js.map +2 -2
- package/dist-cjs/lib/utils/text/richText.js +3 -1
- package/dist-cjs/lib/utils/text/richText.js.map +2 -2
- package/dist-esm/index.d.mts +110 -23
- package/dist-esm/index.mjs +5 -3
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawImage.mjs +0 -1
- package/dist-esm/lib/TldrawImage.mjs.map +2 -2
- package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs +2 -1
- package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
- package/dist-esm/lib/defaultEmbedDefinitions.mjs +3 -2
- package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
- package/dist-esm/lib/defaultOverlayUtils.mjs +11 -11
- package/dist-esm/lib/defaultOverlayUtils.mjs.map +2 -2
- package/dist-esm/lib/overlays/SelectionForegroundOverlayUtil.mjs +77 -65
- package/dist-esm/lib/overlays/SelectionForegroundOverlayUtil.mjs.map +2 -2
- package/dist-esm/lib/overlays/ShapeHandleOverlayUtil.mjs +1 -1
- package/dist-esm/lib/overlays/ShapeHandleOverlayUtil.mjs.map +2 -2
- package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs +87 -0
- package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs.map +7 -0
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +9 -1
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +15 -3
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +54 -6
- package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +3 -1
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/getPath.mjs +1 -1
- package/dist-esm/lib/shapes/draw/getPath.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs +46 -47
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +2 -1
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs +23 -18
- package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +2 -1
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs +4 -0
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +12 -7
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -11
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs +6 -1
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +32 -42
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/noteHelpers.mjs +1 -1
- package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs +29 -6
- package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +3 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +4 -3
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/defaultStyleDefs.mjs +17 -3
- package/dist-esm/lib/shapes/shared/defaultStyleDefs.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/freehand/core.mjs +404 -0
- package/dist-esm/lib/shapes/shared/freehand/core.mjs.map +7 -0
- package/dist-esm/lib/shapes/shared/freehand/fmt.mjs +86 -0
- package/dist-esm/lib/shapes/shared/freehand/fmt.mjs.map +7 -0
- package/dist-esm/lib/shapes/shared/freehand/getStroke.mjs +7 -7
- package/dist-esm/lib/shapes/shared/freehand/getStroke.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/freehand/getStrokeOutlinePoints.mjs +332 -86
- package/dist-esm/lib/shapes/shared/freehand/getStrokeOutlinePoints.mjs.map +3 -3
- package/dist-esm/lib/shapes/shared/freehand/getStrokePoints.mjs +31 -96
- package/dist-esm/lib/shapes/shared/freehand/getStrokePoints.mjs.map +3 -3
- package/dist-esm/lib/shapes/shared/freehand/svg.mjs +55 -19
- package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/freehand/svgInk.mjs +214 -99
- package/dist-esm/lib/shapes/shared/freehand/svgInk.mjs.map +3 -3
- package/dist-esm/lib/shapes/shared/interpolate-props.mjs +6 -2
- package/dist-esm/lib/shapes/shared/interpolate-props.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs +4 -0
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/HandTool.mjs +16 -6
- package/dist-esm/lib/tools/HandTool/HandTool.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs +10 -1
- package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs +64 -0
- package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs.map +7 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +12 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +4 -0
- package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +108 -46
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
- package/dist-esm/lib/ui/TldrawUi.mjs +4 -3
- package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
- package/dist-esm/lib/ui/components/A11y.mjs +2 -0
- package/dist-esm/lib/ui/components/A11y.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs +21 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs +0 -6
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Dialogs.mjs +19 -39
- package/dist-esm/lib/ui/components/Dialogs.mjs.map +2 -2
- package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs +20 -5
- package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +12 -2
- package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +47 -70
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +25 -46
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs +1 -1
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +1 -0
- package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/menu-items.mjs +7 -0
- package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiDropdownMenu.mjs +2 -1
- package/dist-esm/lib/ui/components/primitives/TldrawUiDropdownMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs +14 -3
- package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs +2 -0
- package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs +2 -0
- package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs +0 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.mjs +1 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.mjs.map +2 -2
- package/dist-esm/lib/ui/context/actions.mjs +71 -1
- package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
- package/dist-esm/lib/ui/context/asset-urls.mjs +5 -8
- package/dist-esm/lib/ui/context/asset-urls.mjs.map +2 -2
- package/dist-esm/lib/ui/context/events.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs +13 -0
- package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTools.mjs +1 -1
- package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/dist-esm/lib/utils/embeds/embeds.mjs +4 -4
- package/dist-esm/lib/utils/embeds/embeds.mjs.map +2 -2
- package/dist-esm/lib/utils/test-helpers.mjs +2 -2
- package/dist-esm/lib/utils/test-helpers.mjs.map +2 -2
- package/dist-esm/lib/utils/text/richText.mjs +3 -1
- package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
- package/package.json +11 -6
- package/src/index.ts +6 -1
- package/src/lib/TldrawImage.tsx +2 -2
- package/src/lib/bindings/arrow/ArrowBindingUtil.ts +5 -1
- package/src/lib/defaultEmbedDefinitions.ts +28 -6
- package/src/lib/defaultOverlayUtils.ts +11 -11
- package/src/lib/overlays/SelectionForegroundOverlayUtil.ts +91 -72
- package/src/lib/overlays/ShapeHandleOverlayUtil.ts +3 -1
- package/src/lib/overlays/ShapeIndicatorOverlayUtil.ts +133 -0
- package/src/lib/shapes/arrow/toolStates/Pointing.tsx +11 -1
- package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +16 -3
- package/src/lib/shapes/bookmark/bookmarks.ts +105 -15
- package/src/lib/shapes/draw/DrawShapeUtil.tsx +7 -5
- package/src/lib/shapes/draw/getPath.ts +1 -1
- package/src/lib/shapes/draw/toolStates/Drawing.ts +55 -48
- package/src/lib/shapes/embed/EmbedShapeUtil.tsx +14 -4
- package/src/lib/shapes/frame/FrameShapeTool.ts +35 -24
- package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +46 -0
- package/src/lib/shapes/geo/GeoShapeUtil.tsx +2 -1
- package/src/lib/shapes/geo/toolStates/Pointing.ts +5 -0
- package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +14 -11
- package/src/lib/shapes/image/ImageShapeUtil.tsx +7 -11
- package/src/lib/shapes/line/LineShapeTool.test.ts +41 -0
- package/src/lib/shapes/line/toolStates/Pointing.ts +12 -2
- package/src/lib/shapes/note/NoteShapeUtil.tsx +36 -51
- package/src/lib/shapes/note/noteCloning.test.ts +5 -5
- package/src/lib/shapes/note/noteHelpers.ts +1 -1
- package/src/lib/shapes/note/toolStates/Pointing.ts +40 -7
- package/src/lib/shapes/shared/PlainTextLabel.tsx +3 -3
- package/src/lib/shapes/shared/RichTextLabel.tsx +4 -3
- package/src/lib/shapes/shared/defaultStyleDefs.tsx +25 -3
- package/src/lib/shapes/shared/freehand/core.ts +547 -0
- package/src/lib/shapes/shared/freehand/fmt.ts +106 -0
- package/src/lib/shapes/shared/freehand/getStroke.ts +7 -7
- package/src/lib/shapes/shared/freehand/getStrokeOutlinePoints.ts +451 -139
- package/src/lib/shapes/shared/freehand/getStrokePoints.ts +33 -153
- package/src/lib/shapes/shared/freehand/svg.ts +72 -23
- package/src/lib/shapes/shared/freehand/svgInk.ts +281 -136
- package/src/lib/shapes/shared/freehand/types.ts +0 -1
- package/src/lib/shapes/shared/interpolate-props.ts +6 -2
- package/src/lib/shapes/text/toolStates/Pointing.ts +5 -0
- package/src/lib/tools/HandTool/HandTool.ts +19 -6
- package/src/lib/tools/HandTool/childStates/Dragging.ts +17 -1
- package/src/lib/tools/HandTool/childStates/OneFingerZooming.ts +79 -0
- package/src/lib/tools/SelectTool/childStates/Idle.ts +16 -2
- package/src/lib/tools/SelectTool/childStates/PointingShape.ts +8 -0
- package/src/lib/tools/SelectTool/childStates/Resizing.ts +137 -49
- package/src/lib/ui/TldrawUi.tsx +3 -3
- package/src/lib/ui/components/A11y.tsx +2 -0
- package/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx +32 -2
- package/src/lib/ui/components/ContextMenu/DefaultContextMenuContent.tsx +5 -6
- package/src/lib/ui/components/DefaultFollowingIndicator.tsx +2 -2
- package/src/lib/ui/components/Dialogs.tsx +20 -23
- package/src/lib/ui/components/HelperButtons/BackToContent.tsx +32 -4
- package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +10 -0
- package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +60 -74
- package/src/lib/ui/components/Minimap/MinimapManager.ts +35 -57
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.tsx +2 -2
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuContent.tsx +2 -1
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.tsx +2 -1
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuItem.tsx +3 -3
- package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +5 -0
- package/src/lib/ui/components/menu-items.tsx +9 -0
- package/src/lib/ui/components/primitives/TldrawUiDropdownMenu.tsx +3 -1
- package/src/lib/ui/components/primitives/TldrawUiIcon.tsx +15 -3
- package/src/lib/ui/components/primitives/TldrawUiInput.tsx +4 -0
- package/src/lib/ui/components/primitives/TldrawUiPopover.tsx +7 -0
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +0 -2
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.tsx +0 -1
- package/src/lib/ui/context/actions.tsx +88 -1
- package/src/lib/ui/context/asset-urls.tsx +9 -8
- package/src/lib/ui/context/events.tsx +2 -0
- package/src/lib/ui/hooks/useKeyboardShortcuts.ts +45 -7
- package/src/lib/ui/hooks/useTools.tsx +7 -1
- package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
- package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/ui.css +20 -9
- package/src/lib/utils/embeds/embeds.test.ts +14 -12
- package/src/lib/utils/embeds/embeds.ts +10 -4
- package/src/lib/utils/test-helpers.ts +3 -3
- package/src/lib/utils/text/richText.test.ts +36 -0
- package/src/lib/utils/text/richText.ts +9 -1
- package/src/test/HandTool.test.ts +93 -0
- package/src/test/ImageShapeUtil.test.ts +88 -0
- package/src/test/SelectTool.test.ts +102 -1
- package/src/test/__snapshots__/drawing.test.ts.snap +2 -1
- package/src/test/attribution.test.ts +40 -29
- package/src/test/bookmark-shapes.test.ts +256 -28
- package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
- package/src/test/commands/clipboardCallbacks.test.ts +3 -1
- package/src/test/commands/getSvgString.test.ts +30 -0
- package/src/test/commands/penmode.test.ts +97 -1
- package/src/test/commands/setCamera.test.ts +12 -0
- package/src/test/dragFromToolbar.test.ts +67 -0
- package/src/test/drawing.test.ts +48 -7
- package/src/test/frames.test.ts +72 -0
- package/src/test/freehand/corpus/presets.ts +71 -0
- package/src/test/freehand/corpus/real.ts +370 -0
- package/src/test/freehand/corpus/strokes.ts +50 -0
- package/src/test/freehand/svg.test.ts +20 -0
- package/src/test/freehand/wellformed.test.ts +30 -0
- package/src/test/long-press-create.test.ts +102 -0
- package/src/test/overlays/CollaboratorBrushOverlayUtil.test.ts +4 -4
- package/src/test/overlays/CollaboratorCursorOverlayUtil.test.ts +18 -18
- package/src/test/overlays/CollaboratorHintOverlayUtil.test.ts +7 -7
- package/src/test/overlays/CollaboratorScribbleOverlayUtil.test.ts +3 -3
- package/src/test/overlays/OverlayManager.test.ts +1 -1
- package/src/test/overlays/SelectionForegroundOverlayUtil.test.ts +58 -0
- package/src/test/overlays/ShapeHandleOverlayUtil.test.ts +2 -2
- package/src/test/overlays/ShapeIndicatorOverlayUtil.test.ts +36 -0
- package/src/test/resizing.test.ts +250 -0
- package/src/test/right-click-context-menu.test.ts +90 -0
- package/src/test/rightClickPanning.test.ts +11 -0
- package/src/test/selection-omnibus.test.ts +15 -2
- package/src/test/spatialIndex.test.ts +132 -0
- package/src/test/translating.test.ts +0 -6
- package/src/test/ui/ContextMenu.test.tsx +146 -3
- package/src/test/ui/Dialogs.test.tsx +256 -0
- package/src/test/ui/copyHoveredStyles.test.tsx +154 -0
- package/src/test/ui/keyboardShortcuts.test.tsx +266 -0
- package/tldraw.css +35 -12
- package/dist-cjs/lib/shapes/shared/freehand/setStrokePointRadii.js +0 -100
- package/dist-cjs/lib/shapes/shared/freehand/setStrokePointRadii.js.map +0 -7
- package/dist-esm/lib/shapes/shared/freehand/setStrokePointRadii.mjs +0 -80
- package/dist-esm/lib/shapes/shared/freehand/setStrokePointRadii.mjs.map +0 -7
- package/src/lib/shapes/shared/freehand/setStrokePointRadii.ts +0 -127
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
import { VecLike } from '@tldraw/editor'
|
|
2
|
+
import type { StrokeOptions, StrokePoint } from './types'
|
|
3
|
+
|
|
4
|
+
// The stroke pipeline's internal representation: one module-level set of reusable
|
|
5
|
+
// struct-of-arrays buffers instead of per-point objects. Like fmt.ts's byte buffer, the
|
|
6
|
+
// buffers are non-reentrant: each public entry point fully consumes them before returning,
|
|
7
|
+
// and the next call overwrites them.
|
|
8
|
+
//
|
|
9
|
+
// A stroke point's vector is not stored; it is derived on the fly from consecutive points
|
|
10
|
+
// (the unit vector pointing back at the predecessor). A point's distance is stored rather
|
|
11
|
+
// than derived from running lengths because `runningLength[i] - runningLength[i - 1]` is
|
|
12
|
+
// not bit-identical to the accumulated distance in floating point.
|
|
13
|
+
//
|
|
14
|
+
// Hot loops capture the array bindings into locals: the arrays never grow mid-fill
|
|
15
|
+
// (capacity is ensured up front), and locals avoid re-reading the module binding on
|
|
16
|
+
// every access.
|
|
17
|
+
|
|
18
|
+
const MIN_PRESSURE = 0.025
|
|
19
|
+
|
|
20
|
+
// This is the rate of change for simulated pressure. It could be an option.
|
|
21
|
+
const RATE_OF_PRESSURE_CHANGE = 0.275
|
|
22
|
+
|
|
23
|
+
// Default taper easings, mirroring `EASINGS.easeOutQuad` and `EASINGS.easeOutCubic`
|
|
24
|
+
// from `@tldraw/editor`.
|
|
25
|
+
const easeOutQuad = (t: number) => t * (2 - t)
|
|
26
|
+
const easeOutCubic = (t: number) => --t * t * t + 1
|
|
27
|
+
|
|
28
|
+
const { min } = Math
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------------
|
|
31
|
+
// Pipeline buffers: one slot per stroke point, filled by `ingest`, radii filled in by
|
|
32
|
+
// `computeRadii`. Callers ensure capacity before filling, so growth never copies.
|
|
33
|
+
// ---------------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
let pointCapacity = 256
|
|
36
|
+
/** Streamlined (smoothed) point coordinates. */
|
|
37
|
+
export let pointX = new Float64Array(pointCapacity)
|
|
38
|
+
export let pointY = new Float64Array(pointCapacity)
|
|
39
|
+
/** The original input coordinates (used for elbows and sharp corners). */
|
|
40
|
+
export let inputX = new Float64Array(pointCapacity)
|
|
41
|
+
export let inputY = new Float64Array(pointCapacity)
|
|
42
|
+
/** The input z (pressure channel) after clamping; kept for materializing StrokePoints. */
|
|
43
|
+
export let inputZ = new Float64Array(pointCapacity)
|
|
44
|
+
export let pressures = new Float64Array(pointCapacity)
|
|
45
|
+
export let distances = new Float64Array(pointCapacity)
|
|
46
|
+
export let runningLengths = new Float64Array(pointCapacity)
|
|
47
|
+
export let radii = new Float64Array(pointCapacity)
|
|
48
|
+
export let pointCount = 0
|
|
49
|
+
|
|
50
|
+
function ensurePointCapacity(n: number) {
|
|
51
|
+
if (n <= pointCapacity) return
|
|
52
|
+
while (pointCapacity < n) pointCapacity *= 2
|
|
53
|
+
pointX = new Float64Array(pointCapacity)
|
|
54
|
+
pointY = new Float64Array(pointCapacity)
|
|
55
|
+
inputX = new Float64Array(pointCapacity)
|
|
56
|
+
inputY = new Float64Array(pointCapacity)
|
|
57
|
+
inputZ = new Float64Array(pointCapacity)
|
|
58
|
+
pressures = new Float64Array(pointCapacity)
|
|
59
|
+
distances = new Float64Array(pointCapacity)
|
|
60
|
+
runningLengths = new Float64Array(pointCapacity)
|
|
61
|
+
radii = new Float64Array(pointCapacity)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Staging buffers for the effective input sequence in `ingest` (after stripping
|
|
65
|
+
// near-start/near-end points, the two-point interpolation, and the duplicated last
|
|
66
|
+
// point) — replaces the cloned `pts` array of the object pipeline.
|
|
67
|
+
let stageCapacity = 256
|
|
68
|
+
let stageX = new Float64Array(stageCapacity)
|
|
69
|
+
let stageY = new Float64Array(stageCapacity)
|
|
70
|
+
let stageZ = new Float64Array(stageCapacity)
|
|
71
|
+
|
|
72
|
+
function ensureStageCapacity(n: number) {
|
|
73
|
+
if (n <= stageCapacity) return
|
|
74
|
+
while (stageCapacity < n) stageCapacity *= 2
|
|
75
|
+
stageX = new Float64Array(stageCapacity)
|
|
76
|
+
stageY = new Float64Array(stageCapacity)
|
|
77
|
+
stageZ = new Float64Array(stageCapacity)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The z of a raw input point as `Vec.From` plus the pressure clamp would produce it. */
|
|
81
|
+
function zOf(p: VecLike, clampZ: boolean): number {
|
|
82
|
+
const z = p.z === undefined ? 1 : p.z
|
|
83
|
+
// Some pens or OSes report z=0 even while the pen is touching, so we clamp rather
|
|
84
|
+
// than strip to avoid removing real input.
|
|
85
|
+
return clampZ && z < MIN_PRESSURE ? MIN_PRESSURE : z
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Phase 1: ingest and streamline raw input points straight into the pipeline buffers.
|
|
90
|
+
* Mirrors what getStrokePoints used to do with per-point objects, keeping every
|
|
91
|
+
* order-sensitive step: the pressure clamp, near-start/near-end stripping, the two-point
|
|
92
|
+
* simulated-pressure interpolation, the early-noise skip, and the short-stroke pressure
|
|
93
|
+
* fixup.
|
|
94
|
+
*
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
export function ingest(rawInputPoints: VecLike[], options: StrokeOptions = {}): void {
|
|
98
|
+
const { streamline = 0.5, size = 16, simulatePressure = false } = options
|
|
99
|
+
|
|
100
|
+
pointCount = 0
|
|
101
|
+
const rawLen = rawInputPoints.length
|
|
102
|
+
if (rawLen === 0) return
|
|
103
|
+
|
|
104
|
+
// Find the interpolation level between points.
|
|
105
|
+
const t = 0.15 + (1 - streamline) * 0.85
|
|
106
|
+
|
|
107
|
+
ensureStageCapacity(rawLen + 8)
|
|
108
|
+
ensurePointCapacity(rawLen + 8)
|
|
109
|
+
|
|
110
|
+
const stX = stageX
|
|
111
|
+
const stY = stageY
|
|
112
|
+
const stZ = stageZ
|
|
113
|
+
|
|
114
|
+
const minDist2 = (size / 3) ** 2
|
|
115
|
+
const clampZ = !simulatePressure
|
|
116
|
+
|
|
117
|
+
// Strip points that are too close to the first point, accumulating the maximum
|
|
118
|
+
// pressure among them into the first point.
|
|
119
|
+
const first = rawInputPoints[0]
|
|
120
|
+
let firstZ = zOf(first, clampZ)
|
|
121
|
+
let startIdx = 1
|
|
122
|
+
while (startIdx < rawLen) {
|
|
123
|
+
const pt = rawInputPoints[startIdx]
|
|
124
|
+
const dx = pt.x - first.x
|
|
125
|
+
const dy = pt.y - first.y
|
|
126
|
+
if (dx * dx + dy * dy > minDist2) break
|
|
127
|
+
firstZ = Math.max(firstZ, zOf(pt, clampZ))
|
|
128
|
+
startIdx++
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Stage the surviving points.
|
|
132
|
+
stX[0] = first.x
|
|
133
|
+
stY[0] = first.y
|
|
134
|
+
stZ[0] = firstZ
|
|
135
|
+
let m = 1
|
|
136
|
+
for (let i = startIdx; i < rawLen; i++) {
|
|
137
|
+
const pt = rawInputPoints[i]
|
|
138
|
+
stX[m] = pt.x
|
|
139
|
+
stY[m] = pt.y
|
|
140
|
+
stZ[m] = zOf(pt, clampZ)
|
|
141
|
+
m++
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Strip points that are too close to the last point. This can consume the whole
|
|
145
|
+
// sequence, leaving just the last point.
|
|
146
|
+
let pointsRemovedFromNearEnd = 0
|
|
147
|
+
if (m > 1) {
|
|
148
|
+
const lastX = stX[m - 1]
|
|
149
|
+
const lastY = stY[m - 1]
|
|
150
|
+
let j = m - 2
|
|
151
|
+
while (j >= 0) {
|
|
152
|
+
const dx = stX[j] - lastX
|
|
153
|
+
const dy = stY[j] - lastY
|
|
154
|
+
if (dx * dx + dy * dy > minDist2) break
|
|
155
|
+
j--
|
|
156
|
+
pointsRemovedFromNearEnd++
|
|
157
|
+
}
|
|
158
|
+
if (j < m - 2) {
|
|
159
|
+
stX[j + 1] = lastX
|
|
160
|
+
stY[j + 1] = lastY
|
|
161
|
+
stZ[j + 1] = stZ[m - 1]
|
|
162
|
+
m = j + 2
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const isComplete =
|
|
167
|
+
options.last ||
|
|
168
|
+
!options.simulatePressure ||
|
|
169
|
+
(m > 1 &&
|
|
170
|
+
(stX[m - 1] - stX[m - 2]) * (stX[m - 1] - stX[m - 2]) +
|
|
171
|
+
(stY[m - 1] - stY[m - 2]) * (stY[m - 1] - stY[m - 2]) <
|
|
172
|
+
size ** 2) ||
|
|
173
|
+
pointsRemovedFromNearEnd > 0
|
|
174
|
+
|
|
175
|
+
// Add extra points between the two, to help avoid "dash" lines for strokes with
|
|
176
|
+
// tapered start and ends.
|
|
177
|
+
if (m === 2 && options.simulatePressure) {
|
|
178
|
+
const x0 = stX[0]
|
|
179
|
+
const y0 = stY[0]
|
|
180
|
+
const z0 = stZ[0]
|
|
181
|
+
const x1 = stX[1]
|
|
182
|
+
const y1 = stY[1]
|
|
183
|
+
const z1 = stZ[1]
|
|
184
|
+
for (let i = 1; i < 5; i++) {
|
|
185
|
+
const u = i / 4
|
|
186
|
+
stX[i] = x0 + (x1 - x0) * u
|
|
187
|
+
stY[i] = y0 + (y1 - y0) * u
|
|
188
|
+
stZ[i] = ((z0 + (z1 - z0)) * i) / 4
|
|
189
|
+
}
|
|
190
|
+
m = 5
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const ptX = pointX
|
|
194
|
+
const ptY = pointY
|
|
195
|
+
const inX = inputX
|
|
196
|
+
const inY = inputY
|
|
197
|
+
const inZ = inputZ
|
|
198
|
+
const press = pressures
|
|
199
|
+
const dists = distances
|
|
200
|
+
const runs = runningLengths
|
|
201
|
+
const rads = radii
|
|
202
|
+
|
|
203
|
+
// The first point needs no adjustment.
|
|
204
|
+
ptX[0] = stX[0]
|
|
205
|
+
ptY[0] = stY[0]
|
|
206
|
+
inX[0] = stX[0]
|
|
207
|
+
inY[0] = stY[0]
|
|
208
|
+
inZ[0] = stZ[0]
|
|
209
|
+
press[0] = simulatePressure ? 0.5 : stZ[0]
|
|
210
|
+
dists[0] = 0
|
|
211
|
+
runs[0] = 0
|
|
212
|
+
rads[0] = 1
|
|
213
|
+
let count = 1
|
|
214
|
+
|
|
215
|
+
if (isComplete && streamline > 0) {
|
|
216
|
+
stX[m] = stX[m - 1]
|
|
217
|
+
stY[m] = stY[m - 1]
|
|
218
|
+
stZ[m] = stZ[m - 1]
|
|
219
|
+
m++
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// We use the totalLength to keep track of the total distance, and prevX/prevY as the
|
|
223
|
+
// latest streamlined point, to calculate the next point's distance.
|
|
224
|
+
let totalLength = 0
|
|
225
|
+
let prevX = stX[0]
|
|
226
|
+
let prevY = stY[0]
|
|
227
|
+
const u = 1 - t
|
|
228
|
+
const isLast = options.last
|
|
229
|
+
|
|
230
|
+
for (let i = 1; i < m; i++) {
|
|
231
|
+
let x: number, y: number
|
|
232
|
+
if (!t || (isLast && i === m - 1)) {
|
|
233
|
+
x = stX[i]
|
|
234
|
+
y = stY[i]
|
|
235
|
+
} else {
|
|
236
|
+
x = stX[i] + (prevX - stX[i]) * u
|
|
237
|
+
y = stY[i] + (prevY - stY[i]) * u
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// If the new point is the same as the previous point, skip ahead.
|
|
241
|
+
if (Math.abs(prevX - x) < 0.0001 && Math.abs(prevY - y) < 0.0001) continue
|
|
242
|
+
|
|
243
|
+
// How far is the new point from the previous point?
|
|
244
|
+
const distance = ((y - prevY) ** 2 + (x - prevX) ** 2) ** 0.5
|
|
245
|
+
|
|
246
|
+
// Add this distance to the total "running length" of the line.
|
|
247
|
+
totalLength += distance
|
|
248
|
+
|
|
249
|
+
// At the start of the line, we wait until the new point is a certain distance
|
|
250
|
+
// away from the original point, to avoid noise.
|
|
251
|
+
if (i < 4 && totalLength < size) continue
|
|
252
|
+
|
|
253
|
+
ptX[count] = x
|
|
254
|
+
ptY[count] = y
|
|
255
|
+
inX[count] = stX[i]
|
|
256
|
+
inY[count] = stY[i]
|
|
257
|
+
inZ[count] = stZ[i]
|
|
258
|
+
press[count] = simulatePressure ? 0.5 : stZ[i]
|
|
259
|
+
dists[count] = distance
|
|
260
|
+
runs[count] = totalLength
|
|
261
|
+
rads[count] = 1
|
|
262
|
+
count++
|
|
263
|
+
prevX = x
|
|
264
|
+
prevY = y
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (totalLength < 1) {
|
|
268
|
+
let max = 0.5
|
|
269
|
+
for (let i = 0; i < count; i++) max = Math.max(max, press[i])
|
|
270
|
+
for (let i = 0; i < count; i++) press[i] = max
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
pointCount = count
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Resolve a taper option to a distance: `true` tapers over the whole stroke, `false` or
|
|
278
|
+
* `undefined` not at all.
|
|
279
|
+
*
|
|
280
|
+
* @internal
|
|
281
|
+
*/
|
|
282
|
+
export function resolveTaper(
|
|
283
|
+
taper: number | boolean | undefined,
|
|
284
|
+
size: number,
|
|
285
|
+
totalLength: number
|
|
286
|
+
): number {
|
|
287
|
+
if (!taper) return 0
|
|
288
|
+
return taper === true ? Math.max(size, totalLength) : taper
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Phase 2: compute each point's radius from its pressure, distance and running length.
|
|
293
|
+
* Same recurrences as the object pipeline, with the taper pass folded into the main
|
|
294
|
+
* radius loop.
|
|
295
|
+
*
|
|
296
|
+
* @internal
|
|
297
|
+
*/
|
|
298
|
+
export function computeRadii(options: StrokeOptions): void {
|
|
299
|
+
const {
|
|
300
|
+
size = 16,
|
|
301
|
+
thinning = 0.5,
|
|
302
|
+
simulatePressure = true,
|
|
303
|
+
easing = (t) => t,
|
|
304
|
+
start = {},
|
|
305
|
+
end = {},
|
|
306
|
+
} = options
|
|
307
|
+
|
|
308
|
+
const { easing: taperStartEase = easeOutQuad } = start
|
|
309
|
+
const { easing: taperEndEase = easeOutCubic } = end
|
|
310
|
+
|
|
311
|
+
const n = pointCount
|
|
312
|
+
const press = pressures
|
|
313
|
+
const dists = distances
|
|
314
|
+
const runs = runningLengths
|
|
315
|
+
const rads = radii
|
|
316
|
+
|
|
317
|
+
const totalLength = runs[n - 1]
|
|
318
|
+
|
|
319
|
+
if (!simulatePressure && totalLength < size) {
|
|
320
|
+
let max = 0.5
|
|
321
|
+
for (let i = 0; i < n; i++) max = Math.max(max, press[i])
|
|
322
|
+
for (let i = 0; i < n; i++) {
|
|
323
|
+
press[i] = max
|
|
324
|
+
rads[i] = size * easing(0.5 - thinning * (0.5 - max))
|
|
325
|
+
}
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Calculate initial pressure based on the average of the first n number of points.
|
|
330
|
+
// This prevents "dots" at the start of the line. Drawn lines almost always start slow!
|
|
331
|
+
let prevPressure = press[0]
|
|
332
|
+
for (let i = 0; i < n; i++) {
|
|
333
|
+
if (runs[i] > size * 5) break
|
|
334
|
+
const sp = min(1, dists[i] / size)
|
|
335
|
+
let p: number
|
|
336
|
+
if (simulatePressure) {
|
|
337
|
+
const rp = min(1, 1 - sp)
|
|
338
|
+
p = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))
|
|
339
|
+
} else {
|
|
340
|
+
p = min(1, prevPressure + (press[i] - prevPressure) * 0.5)
|
|
341
|
+
}
|
|
342
|
+
prevPressure = prevPressure + (p - prevPressure) * 0.5
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const taperStart = resolveTaper(start.taper, size, totalLength)
|
|
346
|
+
const taperEnd = resolveTaper(end.taper, size, totalLength)
|
|
347
|
+
|
|
348
|
+
const hasTaper = taperStart || taperEnd
|
|
349
|
+
|
|
350
|
+
// Now calculate pressure and radius for each point. If the point falls within a taper
|
|
351
|
+
// distance from either end, scale its radius by the smaller taper strength.
|
|
352
|
+
for (let i = 0; i < n; i++) {
|
|
353
|
+
let radius: number
|
|
354
|
+
if (thinning) {
|
|
355
|
+
let pressure = press[i]
|
|
356
|
+
const sp = min(1, dists[i] / size)
|
|
357
|
+
if (simulatePressure) {
|
|
358
|
+
// If we're simulating pressure, then do so based on the distance between the
|
|
359
|
+
// current point and the previous point, and the size of the stroke.
|
|
360
|
+
const rp = min(1, 1 - sp)
|
|
361
|
+
pressure = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))
|
|
362
|
+
} else {
|
|
363
|
+
// Otherwise, use the input pressure slightly smoothed based on the distance
|
|
364
|
+
// between the current point and the previous point.
|
|
365
|
+
pressure = min(1, prevPressure + (pressure - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))
|
|
366
|
+
}
|
|
367
|
+
radius = size * easing(0.5 - thinning * (0.5 - pressure))
|
|
368
|
+
prevPressure = pressure
|
|
369
|
+
} else {
|
|
370
|
+
radius = size / 2
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (hasTaper) {
|
|
374
|
+
const runningLength = runs[i]
|
|
375
|
+
const ts = runningLength < taperStart ? taperStartEase(runningLength / taperStart) : 1
|
|
376
|
+
const te =
|
|
377
|
+
totalLength - runningLength < taperEnd
|
|
378
|
+
? taperEndEase((totalLength - runningLength) / taperEnd)
|
|
379
|
+
: 1
|
|
380
|
+
radius = Math.max(0.01, radius * Math.min(ts, te))
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
rads[i] = radius
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ---------------------------------------------------------------------------------
|
|
388
|
+
// Track-source buffers: the (sub)sequence of stroke points the outline tracks are built
|
|
389
|
+
// from — the whole stroke for getStroke/getStrokeOutlinePoints, one elbow partition at a
|
|
390
|
+
// time for svgInk. `srcIsCap` marks points to treat as the first/last point when placing
|
|
391
|
+
// the outline (the identity check `point === first || point === last` of the object
|
|
392
|
+
// pipeline). Elbow points are loaded with their input coordinates as their point.
|
|
393
|
+
// ---------------------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
let srcCapacity = 256
|
|
396
|
+
export let srcX = new Float64Array(srcCapacity)
|
|
397
|
+
export let srcY = new Float64Array(srcCapacity)
|
|
398
|
+
export let srcZ = new Float64Array(srcCapacity)
|
|
399
|
+
export let srcInputX = new Float64Array(srcCapacity)
|
|
400
|
+
export let srcInputY = new Float64Array(srcCapacity)
|
|
401
|
+
export let srcRadius = new Float64Array(srcCapacity)
|
|
402
|
+
export let srcRunningLength = new Float64Array(srcCapacity)
|
|
403
|
+
export let srcIsCap = new Uint8Array(srcCapacity)
|
|
404
|
+
export let srcCount = 0
|
|
405
|
+
|
|
406
|
+
function ensureSrcCapacity(n: number) {
|
|
407
|
+
if (n <= srcCapacity) return
|
|
408
|
+
while (srcCapacity < n) srcCapacity *= 2
|
|
409
|
+
srcX = new Float64Array(srcCapacity)
|
|
410
|
+
srcY = new Float64Array(srcCapacity)
|
|
411
|
+
srcZ = new Float64Array(srcCapacity)
|
|
412
|
+
srcInputX = new Float64Array(srcCapacity)
|
|
413
|
+
srcInputY = new Float64Array(srcCapacity)
|
|
414
|
+
srcRadius = new Float64Array(srcCapacity)
|
|
415
|
+
srcRunningLength = new Float64Array(srcCapacity)
|
|
416
|
+
srcIsCap = new Uint8Array(srcCapacity)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** Load the track source from materialized StrokePoints. @internal */
|
|
420
|
+
export function loadSrcFromStrokePoints(strokePoints: StrokePoint[]): void {
|
|
421
|
+
const n = strokePoints.length
|
|
422
|
+
ensureSrcCapacity(n)
|
|
423
|
+
const sx = srcX
|
|
424
|
+
const sy = srcY
|
|
425
|
+
const sz = srcZ
|
|
426
|
+
const six = srcInputX
|
|
427
|
+
const siy = srcInputY
|
|
428
|
+
const sr = srcRadius
|
|
429
|
+
const srl = srcRunningLength
|
|
430
|
+
const scap = srcIsCap
|
|
431
|
+
const first = strokePoints[0]
|
|
432
|
+
const last = strokePoints[n - 1]
|
|
433
|
+
for (let i = 0; i < n; i++) {
|
|
434
|
+
const sp = strokePoints[i]
|
|
435
|
+
const point = sp.point
|
|
436
|
+
const input = sp.input
|
|
437
|
+
sx[i] = point.x
|
|
438
|
+
sy[i] = point.y
|
|
439
|
+
sz[i] = point.z
|
|
440
|
+
six[i] = input.x
|
|
441
|
+
siy[i] = input.y
|
|
442
|
+
sr[i] = sp.radius
|
|
443
|
+
srl[i] = sp.runningLength
|
|
444
|
+
scap[i] = sp === first || sp === last ? 1 : 0
|
|
445
|
+
}
|
|
446
|
+
srcCount = n
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** Load the track source from the whole pipeline. @internal */
|
|
450
|
+
export function loadSrcFromPipeline(): void {
|
|
451
|
+
const n = pointCount
|
|
452
|
+
ensureSrcCapacity(n)
|
|
453
|
+
const sx = srcX
|
|
454
|
+
const sy = srcY
|
|
455
|
+
const sz = srcZ
|
|
456
|
+
const six = srcInputX
|
|
457
|
+
const siy = srcInputY
|
|
458
|
+
const sr = srcRadius
|
|
459
|
+
const srl = srcRunningLength
|
|
460
|
+
const scap = srcIsCap
|
|
461
|
+
const ptX = pointX
|
|
462
|
+
const ptY = pointY
|
|
463
|
+
const inX = inputX
|
|
464
|
+
const inY = inputY
|
|
465
|
+
const inZ = inputZ
|
|
466
|
+
const runs = runningLengths
|
|
467
|
+
const rads = radii
|
|
468
|
+
for (let i = 0; i < n; i++) {
|
|
469
|
+
sx[i] = ptX[i]
|
|
470
|
+
sy[i] = ptY[i]
|
|
471
|
+
sz[i] = inZ[i]
|
|
472
|
+
six[i] = inX[i]
|
|
473
|
+
siy[i] = inY[i]
|
|
474
|
+
sr[i] = rads[i]
|
|
475
|
+
srl[i] = runs[i]
|
|
476
|
+
scap[i] = i === 0 || i === n - 1 ? 1 : 0
|
|
477
|
+
}
|
|
478
|
+
srcCount = n
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Load one elbow partition from the pipeline as the track source: boundary point `a`,
|
|
483
|
+
* the surviving inner points `innerStart..innerEnd`, and boundary point `b`. Elbow
|
|
484
|
+
* boundaries read the input coordinates instead of the streamlined ones. When a hard
|
|
485
|
+
* elbow's duplicated end point survived cleanup (`dupQuirk`), the inner copy of `b` is
|
|
486
|
+
* also marked as a cap point, matching the object pipeline where both array slots held
|
|
487
|
+
* the same point object.
|
|
488
|
+
*
|
|
489
|
+
* @internal
|
|
490
|
+
*/
|
|
491
|
+
export function loadSrcPartition(
|
|
492
|
+
a: number,
|
|
493
|
+
aElbow: boolean,
|
|
494
|
+
innerStart: number,
|
|
495
|
+
innerEnd: number,
|
|
496
|
+
b: number,
|
|
497
|
+
bElbow: boolean,
|
|
498
|
+
dupQuirk: boolean
|
|
499
|
+
): void {
|
|
500
|
+
ensureSrcCapacity(innerEnd - innerStart + 3)
|
|
501
|
+
const sx = srcX
|
|
502
|
+
const sy = srcY
|
|
503
|
+
const sz = srcZ
|
|
504
|
+
const six = srcInputX
|
|
505
|
+
const siy = srcInputY
|
|
506
|
+
const sr = srcRadius
|
|
507
|
+
const srl = srcRunningLength
|
|
508
|
+
const scap = srcIsCap
|
|
509
|
+
const ptX = pointX
|
|
510
|
+
const ptY = pointY
|
|
511
|
+
const inX = inputX
|
|
512
|
+
const inY = inputY
|
|
513
|
+
const inZ = inputZ
|
|
514
|
+
const runs = runningLengths
|
|
515
|
+
const rads = radii
|
|
516
|
+
|
|
517
|
+
sx[0] = aElbow ? inX[a] : ptX[a]
|
|
518
|
+
sy[0] = aElbow ? inY[a] : ptY[a]
|
|
519
|
+
sz[0] = inZ[a]
|
|
520
|
+
six[0] = inX[a]
|
|
521
|
+
siy[0] = inY[a]
|
|
522
|
+
sr[0] = rads[a]
|
|
523
|
+
srl[0] = runs[a]
|
|
524
|
+
scap[0] = 1
|
|
525
|
+
let w = 1
|
|
526
|
+
for (let i = innerStart; i <= innerEnd; i++) {
|
|
527
|
+
sx[w] = ptX[i]
|
|
528
|
+
sy[w] = ptY[i]
|
|
529
|
+
sz[w] = inZ[i]
|
|
530
|
+
six[w] = inX[i]
|
|
531
|
+
siy[w] = inY[i]
|
|
532
|
+
sr[w] = rads[i]
|
|
533
|
+
srl[w] = runs[i]
|
|
534
|
+
scap[w] = 0
|
|
535
|
+
w++
|
|
536
|
+
}
|
|
537
|
+
if (dupQuirk) scap[w - 1] = 1
|
|
538
|
+
sx[w] = bElbow ? inX[b] : ptX[b]
|
|
539
|
+
sy[w] = bElbow ? inY[b] : ptY[b]
|
|
540
|
+
sz[w] = inZ[b]
|
|
541
|
+
six[w] = inX[b]
|
|
542
|
+
siy[w] = inY[b]
|
|
543
|
+
sr[w] = rads[b]
|
|
544
|
+
srl[w] = runs[b]
|
|
545
|
+
scap[w] = 1
|
|
546
|
+
srcCount = w + 1
|
|
547
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Fast SVG path data building.
|
|
2
|
+
//
|
|
3
|
+
// Browsers don't render differences below a hundredth of a pixel, so we emit at most 2 decimal
|
|
4
|
+
// places. Stringifying doubles rounded to 2 decimals is slow (they routinely miss V8's fast
|
|
5
|
+
// float-to-string path), and concatenating many small strings costs more than the math itself.
|
|
6
|
+
// Instead we round every coordinate to an integer number of hundredths ("centi-pixels") and
|
|
7
|
+
// write decimal digits straight into a byte buffer, decoding to a string once per path.
|
|
8
|
+
//
|
|
9
|
+
// Paths use relative commands (t/q/a/l) wherever possible: deltas between consecutive points
|
|
10
|
+
// are small numbers, which keeps the path data compact. Because deltas are differences of the
|
|
11
|
+
// rounded integers, they sum back to the rounded absolute positions exactly: no drift.
|
|
12
|
+
|
|
13
|
+
let buf = new Uint8Array(65536)
|
|
14
|
+
let pos = 0
|
|
15
|
+
const decoder = new TextDecoder()
|
|
16
|
+
|
|
17
|
+
function ensure(n: number) {
|
|
18
|
+
if (pos + n > buf.length) {
|
|
19
|
+
const next = new Uint8Array(buf.length * 2)
|
|
20
|
+
next.set(buf)
|
|
21
|
+
buf = next
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Start a new path. Builders must call this before writing. */
|
|
26
|
+
export function resetPath() {
|
|
27
|
+
pos = 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Append a string of ASCII characters (command letters, arc flags) to the path. */
|
|
31
|
+
export function writeStr(s: string) {
|
|
32
|
+
ensure(s.length)
|
|
33
|
+
for (let i = 0; i < s.length; i++) {
|
|
34
|
+
buf[pos++] = s.charCodeAt(i)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Append a value given in integer hundredths, e.g. 255 as `2.55`, -30 as `-.3`, 0 as `0`.
|
|
40
|
+
* Assumes |n| < 2^31, which holds for canvas coordinates (|v| < ~21 million px).
|
|
41
|
+
*/
|
|
42
|
+
export function writeC(n: number) {
|
|
43
|
+
ensure(16)
|
|
44
|
+
if (n < 0) {
|
|
45
|
+
buf[pos++] = 45 // '-'
|
|
46
|
+
n = -n
|
|
47
|
+
}
|
|
48
|
+
const i = (n / 100) | 0
|
|
49
|
+
const f = n - i * 100
|
|
50
|
+
if (i === 0) {
|
|
51
|
+
if (f === 0) {
|
|
52
|
+
buf[pos++] = 48 // '0'
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
} else if (i < 10) {
|
|
56
|
+
buf[pos++] = 48 + i
|
|
57
|
+
} else if (i < 100) {
|
|
58
|
+
buf[pos++] = 48 + ((i / 10) | 0)
|
|
59
|
+
buf[pos++] = 48 + (i % 10)
|
|
60
|
+
} else {
|
|
61
|
+
const start = pos
|
|
62
|
+
let m = i
|
|
63
|
+
while (m > 0) {
|
|
64
|
+
buf[pos++] = 48 + (m % 10)
|
|
65
|
+
m = (m / 10) | 0
|
|
66
|
+
}
|
|
67
|
+
// digits came out least-significant first; reverse them
|
|
68
|
+
let lo = start
|
|
69
|
+
let hi = pos - 1
|
|
70
|
+
while (lo < hi) {
|
|
71
|
+
const t = buf[lo]
|
|
72
|
+
buf[lo] = buf[hi]
|
|
73
|
+
buf[hi] = t
|
|
74
|
+
lo++
|
|
75
|
+
hi--
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (f !== 0) {
|
|
79
|
+
buf[pos++] = 46 // '.'
|
|
80
|
+
const d2 = f % 10
|
|
81
|
+
buf[pos++] = 48 + ((f / 10) | 0)
|
|
82
|
+
if (d2 !== 0) {
|
|
83
|
+
buf[pos++] = 48 + d2
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Append a coordinate pair given in integer hundredths as `x,y `. */
|
|
89
|
+
export function writeCPair(nx: number, ny: number) {
|
|
90
|
+
writeC(nx)
|
|
91
|
+
buf[pos++] = 44 // ','
|
|
92
|
+
writeC(ny)
|
|
93
|
+
buf[pos++] = 32 // ' '
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Finish the path: decode everything written since `resetPath` and reset the writer. */
|
|
97
|
+
export function finishPath(): string {
|
|
98
|
+
const s = decoder.decode(buf.subarray(0, pos))
|
|
99
|
+
pos = 0
|
|
100
|
+
return s
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Round a coordinate to integer hundredths. */
|
|
104
|
+
export function toCenti(v: number): number {
|
|
105
|
+
return Math.round(v * 100)
|
|
106
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Vec, VecLike } from '@tldraw/editor'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { setStrokePointRadii } from './setStrokePointRadii'
|
|
2
|
+
import { computeRadii, ingest, loadSrcFromPipeline, pointCount } from './core'
|
|
3
|
+
import { outlineFromSrc } from './getStrokeOutlinePoints'
|
|
5
4
|
import type { StrokeOptions } from './types'
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -16,8 +15,9 @@ import type { StrokeOptions } from './types'
|
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
export function getStroke(points: VecLike[], options: StrokeOptions = {}): Vec[] {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
18
|
+
ingest(points, options)
|
|
19
|
+
if (pointCount === 0) return []
|
|
20
|
+
computeRadii(options)
|
|
21
|
+
loadSrcFromPipeline()
|
|
22
|
+
return outlineFromSrc(options)
|
|
23
23
|
}
|