@tldraw/editor 4.5.2 → 4.6.0-canary.4ec045c286e1
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/dist-cjs/index.d.ts +37 -6
- package/dist-cjs/index.js +6 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +7 -5
- package/dist-cjs/lib/TldrawEditor.js.map +3 -3
- package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js +3 -2
- package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +8 -5
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
- package/dist-cjs/lib/config/TLSessionStateSnapshot.js +8 -5
- package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +2 -2
- package/dist-cjs/lib/config/TLUserPreferences.js +3 -2
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/config/createTLStore.js +1 -0
- package/dist-cjs/lib/config/createTLStore.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +52 -16
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +62 -6
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +4 -3
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +5 -0
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +2 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +3 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/exports/FontEmbedder.js +9 -8
- package/dist-cjs/lib/exports/FontEmbedder.js.map +2 -2
- package/dist-cjs/lib/exports/StyleEmbedder.js +27 -15
- package/dist-cjs/lib/exports/StyleEmbedder.js.map +3 -3
- package/dist-cjs/lib/exports/domUtils.js +15 -0
- package/dist-cjs/lib/exports/domUtils.js.map +2 -2
- package/dist-cjs/lib/exports/embedMedia.js +15 -12
- package/dist-cjs/lib/exports/embedMedia.js.map +2 -2
- package/dist-cjs/lib/exports/exportToSvg.js +8 -7
- package/dist-cjs/lib/exports/exportToSvg.js.map +2 -2
- package/dist-cjs/lib/exports/getSvgAsImage.js +181 -29
- package/dist-cjs/lib/exports/getSvgAsImage.js.map +3 -3
- package/dist-cjs/lib/exports/getSvgJsx.js +21 -9
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/globals/environment.js +4 -3
- package/dist-cjs/lib/globals/environment.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +13 -11
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +3 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useScreenBounds.js +10 -6
- package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
- package/dist-cjs/lib/hooks/useViewportHeight.js +13 -11
- package/dist-cjs/lib/hooks/useViewportHeight.js.map +3 -3
- package/dist-cjs/lib/license/Watermark.js +10 -0
- package/dist-cjs/lib/license/Watermark.js.map +2 -2
- package/dist-cjs/lib/primitives/Vec.js +35 -22
- package/dist-cjs/lib/primitives/Vec.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Arc2d.js +6 -13
- package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Circle2d.js +31 -2
- package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +9 -0
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js +9 -0
- package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Edge2d.js +32 -18
- package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +12 -0
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Polyline2d.js +51 -12
- package/dist-cjs/lib/primitives/geometry/Polyline2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Stadium2d.js +12 -0
- package/dist-cjs/lib/primitives/geometry/Stadium2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry.bench.js +133 -0
- package/dist-cjs/lib/primitives/geometry/geometry.bench.js.map +7 -0
- package/dist-cjs/lib/primitives/intersect.js +16 -15
- package/dist-cjs/lib/primitives/intersect.js.map +2 -2
- package/dist-cjs/lib/primitives/utils.js +0 -1
- package/dist-cjs/lib/primitives/utils.js.map +2 -2
- package/dist-cjs/lib/utils/browserCanvasMaxSize.js +3 -2
- package/dist-cjs/lib/utils/browserCanvasMaxSize.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js +15 -2
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +37 -6
- package/dist-esm/index.mjs +8 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +7 -5
- package/dist-esm/lib/TldrawEditor.mjs.map +3 -3
- package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs +2 -1
- package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +8 -5
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
- package/dist-esm/lib/config/TLSessionStateSnapshot.mjs +8 -5
- package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs +3 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/config/createTLStore.mjs +1 -0
- package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +53 -17
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +64 -6
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +4 -3
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +5 -0
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +2 -2
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +3 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/exports/FontEmbedder.mjs +9 -8
- package/dist-esm/lib/exports/FontEmbedder.mjs.map +2 -2
- package/dist-esm/lib/exports/StyleEmbedder.mjs +29 -16
- package/dist-esm/lib/exports/StyleEmbedder.mjs.map +3 -3
- package/dist-esm/lib/exports/domUtils.mjs +15 -0
- package/dist-esm/lib/exports/domUtils.mjs.map +2 -2
- package/dist-esm/lib/exports/embedMedia.mjs +16 -13
- package/dist-esm/lib/exports/embedMedia.mjs.map +2 -2
- package/dist-esm/lib/exports/exportToSvg.mjs +8 -7
- package/dist-esm/lib/exports/exportToSvg.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgAsImage.mjs +181 -29
- package/dist-esm/lib/exports/getSvgAsImage.mjs.map +3 -3
- package/dist-esm/lib/exports/getSvgJsx.mjs +21 -9
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/globals/environment.mjs +4 -3
- package/dist-esm/lib/globals/environment.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +13 -11
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +3 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useScreenBounds.mjs +10 -6
- package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
- package/dist-esm/lib/hooks/useViewportHeight.mjs +13 -11
- package/dist-esm/lib/hooks/useViewportHeight.mjs.map +3 -3
- package/dist-esm/lib/license/Watermark.mjs +10 -0
- package/dist-esm/lib/license/Watermark.mjs.map +2 -2
- package/dist-esm/lib/primitives/Vec.mjs +35 -22
- package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs +6 -13
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs +31 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +9 -0
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs +9 -0
- package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Edge2d.mjs +32 -18
- package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +13 -1
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Polyline2d.mjs +51 -12
- package/dist-esm/lib/primitives/geometry/Polyline2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Stadium2d.mjs +13 -1
- package/dist-esm/lib/primitives/geometry/Stadium2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/geometry.bench.mjs +132 -0
- package/dist-esm/lib/primitives/geometry/geometry.bench.mjs.map +7 -0
- package/dist-esm/lib/primitives/intersect.mjs +17 -16
- package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
- package/dist-esm/lib/primitives/utils.mjs +0 -1
- package/dist-esm/lib/primitives/utils.mjs.map +2 -2
- package/dist-esm/lib/utils/browserCanvasMaxSize.mjs +3 -2
- package/dist-esm/lib/utils/browserCanvasMaxSize.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs +15 -2
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +3 -0
- package/src/lib/TldrawEditor.tsx +7 -5
- package/src/lib/components/default-components/CanvasShapeIndicators.tsx +2 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +1 -1
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +8 -5
- package/src/lib/config/TLSessionStateSnapshot.ts +8 -5
- package/src/lib/config/TLUserPreferences.ts +3 -2
- package/src/lib/config/createTLStore.ts +3 -0
- package/src/lib/editor/Editor.ts +53 -15
- package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +7 -6
- package/src/lib/editor/managers/FocusManager/FocusManager.ts +10 -7
- package/src/lib/editor/managers/FontManager/FontManager.test.ts +1 -0
- package/src/lib/editor/managers/FontManager/FontManager.ts +4 -3
- package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +16 -0
- package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +7 -2
- package/src/lib/editor/managers/TextManager/TextManager.test.ts +4 -5
- package/src/lib/editor/managers/TextManager/TextManager.ts +2 -2
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +3 -2
- package/src/lib/editor/types/misc-types.ts +8 -2
- package/src/lib/exports/FontEmbedder.ts +10 -9
- package/src/lib/exports/StyleEmbedder.ts +33 -15
- package/src/lib/exports/domUtils.ts +20 -0
- package/src/lib/exports/embedMedia.ts +23 -17
- package/src/lib/exports/exportToSvg.tsx +8 -7
- package/src/lib/exports/getSvgAsImage.ts +292 -32
- package/src/lib/exports/getSvgJsx.test.ts +103 -101
- package/src/lib/exports/getSvgJsx.tsx +33 -10
- package/src/lib/globals/environment.ts +4 -3
- package/src/lib/hooks/useCanvasEvents.ts +2 -3
- package/src/lib/hooks/useDocumentEvents.ts +16 -11
- package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +3 -3
- package/src/lib/hooks/useScreenBounds.ts +10 -6
- package/src/lib/hooks/useViewportHeight.ts +13 -11
- package/src/lib/license/Watermark.tsx +10 -0
- package/src/lib/primitives/Vec.ts +51 -24
- package/src/lib/primitives/geometry/Arc2d.ts +10 -15
- package/src/lib/primitives/geometry/Circle2d.ts +40 -2
- package/src/lib/primitives/geometry/CubicBezier2d.ts +10 -0
- package/src/lib/primitives/geometry/CubicSpline2d.ts +10 -0
- package/src/lib/primitives/geometry/Edge2d.ts +41 -18
- package/src/lib/primitives/geometry/Ellipse2d.ts +14 -1
- package/src/lib/primitives/geometry/Polyline2d.ts +60 -12
- package/src/lib/primitives/geometry/Stadium2d.ts +14 -1
- package/src/lib/primitives/geometry/geometry.bench.ts +179 -0
- package/src/lib/primitives/intersect.ts +27 -27
- package/src/lib/primitives/utils.ts +4 -4
- package/src/lib/test/TestEditor.ts +1 -0
- package/src/lib/utils/browserCanvasMaxSize.ts +4 -2
- package/src/lib/utils/dom.ts +34 -2
- package/src/version.ts +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useLayoutEffect, useState } from 'react'
|
|
2
|
+
import { useMaybeEditor } from './useEditor'
|
|
2
3
|
|
|
3
4
|
/*!
|
|
4
5
|
* BSD License: https://github.com/outline/rich-markdown-editor/blob/main/LICENSE
|
|
@@ -12,26 +13,27 @@ import { useLayoutEffect, useState } from 'react'
|
|
|
12
13
|
*/
|
|
13
14
|
/** @public */
|
|
14
15
|
export function useViewportHeight(): number {
|
|
15
|
-
const
|
|
16
|
+
const editor = useMaybeEditor()
|
|
17
|
+
const win = editor?.getContainerWindow() ?? window
|
|
18
|
+
const vv = win.visualViewport
|
|
16
19
|
const [height, setHeight] = useState<number>(() =>
|
|
17
|
-
|
|
20
|
+
vv ? vv.height + vv.offsetTop : win.innerHeight
|
|
18
21
|
)
|
|
19
22
|
|
|
20
23
|
useLayoutEffect(() => {
|
|
24
|
+
const win = editor?.getContainerWindow() ?? window
|
|
21
25
|
const handleResize = () => {
|
|
22
|
-
const
|
|
23
|
-
setHeight(() =>
|
|
24
|
-
visualViewport ? visualViewport.height + visualViewport.offsetTop : window.innerHeight
|
|
25
|
-
)
|
|
26
|
+
const vv = win.visualViewport
|
|
27
|
+
setHeight(() => (vv ? vv.height + vv.offsetTop : win.innerHeight))
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
win.visualViewport?.addEventListener('resize', handleResize)
|
|
31
|
+
win.visualViewport?.addEventListener('scroll', handleResize)
|
|
30
32
|
|
|
31
33
|
return () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
win.visualViewport?.removeEventListener('resize', handleResize)
|
|
35
|
+
win.visualViewport?.removeEventListener('scroll', handleResize)
|
|
34
36
|
}
|
|
35
|
-
}, [])
|
|
37
|
+
}, [editor])
|
|
36
38
|
return height
|
|
37
39
|
}
|
|
@@ -191,6 +191,16 @@ To remove the watermark, please purchase a license at tldraw.dev.
|
|
|
191
191
|
height: 32px;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
.tl-container[dir='rtl'] .${className} {
|
|
195
|
+
right: auto;
|
|
196
|
+
left: max(var(--tl-space-2), env(safe-area-inset-left));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.tl-container[dir='rtl'] .${className}[data-mobile='true'] {
|
|
200
|
+
border-radius: 0px 4px 4px 0px;
|
|
201
|
+
left: max(-2px, calc(env(safe-area-inset-left) - 2px));
|
|
202
|
+
}
|
|
203
|
+
|
|
194
204
|
.${className}[data-unlicensed='true'] > button {
|
|
195
205
|
font-size: 100px;
|
|
196
206
|
position: absolute;
|
|
@@ -430,32 +430,58 @@ export class Vec {
|
|
|
430
430
|
* @param P - A point not on the line to test.
|
|
431
431
|
*/
|
|
432
432
|
static NearestPointOnLineThroughPoint(A: VecLike, u: VecLike, P: VecLike): Vec {
|
|
433
|
-
|
|
433
|
+
// Inlined: t = Vec.Sub(P, A).pry(u), return Vec.Mul(u, t).add(A)
|
|
434
|
+
const t = (P.x - A.x) * u.x + (P.y - A.y) * u.y
|
|
435
|
+
return new Vec(A.x + u.x * t, A.y + u.y * t)
|
|
434
436
|
}
|
|
435
437
|
|
|
436
438
|
static NearestPointOnLineSegment(A: VecLike, B: VecLike, P: VecLike, clamp = true): Vec {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
+
// Parametric projection of P onto segment AB.
|
|
440
|
+
// Inlined: d = Vec.Sub(B, A); t = Vec.Sub(P, A).pry(d) / d.len(); return Vec.Lrp(A, B, t)
|
|
441
|
+
const dx = B.x - A.x
|
|
442
|
+
const dy = B.y - A.y
|
|
443
|
+
const d2 = dx * dx + dy * dy
|
|
439
444
|
|
|
440
|
-
|
|
441
|
-
|
|
445
|
+
if (d2 === 0) return Vec.From(A)
|
|
446
|
+
|
|
447
|
+
let t = ((P.x - A.x) * dx + (P.y - A.y) * dy) / d2
|
|
442
448
|
|
|
443
449
|
if (clamp) {
|
|
444
|
-
if (
|
|
445
|
-
if (
|
|
446
|
-
if (C.y < Math.min(A.y, B.y)) return Vec.Cast(A.y < B.y ? A : B)
|
|
447
|
-
if (C.y > Math.max(A.y, B.y)) return Vec.Cast(A.y > B.y ? A : B)
|
|
450
|
+
if (t < 0) t = 0
|
|
451
|
+
else if (t > 1) t = 1
|
|
448
452
|
}
|
|
449
453
|
|
|
450
|
-
return
|
|
454
|
+
return new Vec(A.x + t * dx, A.y + t * dy)
|
|
451
455
|
}
|
|
452
456
|
|
|
453
457
|
static DistanceToLineThroughPoint(A: VecLike, u: VecLike, P: VecLike): number {
|
|
454
|
-
|
|
458
|
+
// Inlined: Vec.Dist(P, Vec.NearestPointOnLineThroughPoint(A, u, P))
|
|
459
|
+
// Uses |cross(P-A, u)| which equals the perpendicular distance when u is a unit vector.
|
|
460
|
+
const dx = P.x - A.x
|
|
461
|
+
const dy = P.y - A.y
|
|
462
|
+
return Math.abs(dx * u.y - dy * u.x)
|
|
455
463
|
}
|
|
456
464
|
|
|
457
465
|
static DistanceToLineSegment(A: VecLike, B: VecLike, P: VecLike, clamp = true): number {
|
|
458
|
-
|
|
466
|
+
// Inlined: Vec.Dist(P, Vec.NearestPointOnLineSegment(A, B, P, clamp))
|
|
467
|
+
// Computes the nearest point via parametric t-projection then returns the scalar distance,
|
|
468
|
+
// avoiding the intermediate Vec allocation that NearestPointOnLineSegment would create.
|
|
469
|
+
const dx = B.x - A.x
|
|
470
|
+
const dy = B.y - A.y
|
|
471
|
+
const d2 = dx * dx + dy * dy
|
|
472
|
+
|
|
473
|
+
if (d2 === 0) return Vec.Dist(A, P)
|
|
474
|
+
|
|
475
|
+
let t = ((P.x - A.x) * dx + (P.y - A.y) * dy) / d2
|
|
476
|
+
|
|
477
|
+
if (clamp) {
|
|
478
|
+
if (t < 0) t = 0
|
|
479
|
+
else if (t > 1) t = 1
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const nx = A.x + t * dx - P.x
|
|
483
|
+
const ny = A.y + t * dy - P.y
|
|
484
|
+
return Math.sqrt(nx * nx + ny * ny)
|
|
459
485
|
}
|
|
460
486
|
|
|
461
487
|
static Snap(A: VecLike, step = 1) {
|
|
@@ -476,6 +502,10 @@ export class Vec {
|
|
|
476
502
|
return isNaN(A.x) || isNaN(A.y)
|
|
477
503
|
}
|
|
478
504
|
|
|
505
|
+
static IsFinite(A: VecLike): boolean {
|
|
506
|
+
return Number.isFinite(A.x) && Number.isFinite(A.y)
|
|
507
|
+
}
|
|
508
|
+
|
|
479
509
|
/**
|
|
480
510
|
* Get the angle from position A to position B.
|
|
481
511
|
*/
|
|
@@ -488,14 +518,11 @@ export class Vec {
|
|
|
488
518
|
* two vectors, between -π and π. The sign indicates direction of angle.
|
|
489
519
|
*/
|
|
490
520
|
static AngleBetween(A: VecLike, B: VecLike): number {
|
|
521
|
+
// p = dot(A, B); n = |A| * |B| (uses x*x instead of Math.pow(x, 2))
|
|
491
522
|
const p = A.x * B.x + A.y * B.y
|
|
492
|
-
const n = Math.sqrt(
|
|
493
|
-
(Math.pow(A.x, 2) + Math.pow(A.y, 2)) * (Math.pow(B.x, 2) + Math.pow(B.y, 2))
|
|
494
|
-
)
|
|
523
|
+
const n = Math.sqrt((A.x * A.x + A.y * A.y) * (B.x * B.x + B.y * B.y))
|
|
495
524
|
const sign = A.x * B.y - A.y * B.x < 0 ? -1 : 1
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
return angle
|
|
525
|
+
return sign * Math.acos(clamp(p / n, -1, 1))
|
|
499
526
|
}
|
|
500
527
|
|
|
501
528
|
/**
|
|
@@ -506,7 +533,8 @@ export class Vec {
|
|
|
506
533
|
* @returns The interpolated point.
|
|
507
534
|
*/
|
|
508
535
|
static Lrp(A: VecLike, B: VecLike, t: number): Vec {
|
|
509
|
-
|
|
536
|
+
// Inlined: Vec.Sub(B, A).mul(t).add(A) — note: only interpolates x/y, not z.
|
|
537
|
+
return new Vec(A.x + (B.x - A.x) * t, A.y + (B.y - A.y) * t)
|
|
510
538
|
}
|
|
511
539
|
|
|
512
540
|
static Med(A: VecLike, B: VecLike): Vec {
|
|
@@ -604,14 +632,15 @@ export class Vec {
|
|
|
604
632
|
* @param A - The first point.
|
|
605
633
|
* @param B - The second point.
|
|
606
634
|
* @param steps - The number of points to return.
|
|
635
|
+
* @param ease - The easing to use.
|
|
607
636
|
*/
|
|
608
|
-
static PointsBetween(A: VecModel, B: VecModel, steps = 6): Vec[] {
|
|
637
|
+
static PointsBetween(A: VecModel, B: VecModel, steps = 6, ease = EASINGS.easeInQuad): Vec[] {
|
|
609
638
|
const results: Vec[] = []
|
|
610
639
|
|
|
611
640
|
for (let i = 0; i < steps; i++) {
|
|
612
|
-
const t =
|
|
641
|
+
const t = ease(i / (steps - 1))
|
|
613
642
|
const point = Vec.Lrp(A, B, t)
|
|
614
|
-
point.z = Math.min(1, 0.5 + Math.abs(0.5 -
|
|
643
|
+
point.z = Math.min(1, 0.5 + Math.abs(0.5 - EASINGS.easeInOutQuad(t)) * 0.65)
|
|
615
644
|
results.push(point)
|
|
616
645
|
}
|
|
617
646
|
|
|
@@ -622,5 +651,3 @@ export class Vec {
|
|
|
622
651
|
return new Vec(Math.round(A.x / gridSize) * gridSize, Math.round(A.y / gridSize) * gridSize)
|
|
623
652
|
}
|
|
624
653
|
}
|
|
625
|
-
|
|
626
|
-
const ease = (t: number) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t)
|
|
@@ -57,21 +57,16 @@ export class Arc2d extends Geometry2d {
|
|
|
57
57
|
if (t <= 0) return A
|
|
58
58
|
if (t >= 1) return B
|
|
59
59
|
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
dist = d
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (!nearest) throw Error('nearest point not found')
|
|
74
|
-
return nearest
|
|
60
|
+
// Inlined: Vec.Sub(point, _center).uni().mul(radius).add(_center)
|
|
61
|
+
// When t is in (0,1), the nearest point is the radial projection of point onto the arc.
|
|
62
|
+
// Previously this also checked min-distance against A and B, but that's unnecessary when
|
|
63
|
+
// t is already in range — the radial projection is always closer.
|
|
64
|
+
const dx = point.x - _center.x
|
|
65
|
+
const dy = point.y - _center.y
|
|
66
|
+
const len = Math.sqrt(dx * dx + dy * dy)
|
|
67
|
+
if (len === 0) return A
|
|
68
|
+
const scale = radius / len
|
|
69
|
+
return new Vec(_center.x + dx * scale, _center.y + dy * scale)
|
|
75
70
|
}
|
|
76
71
|
|
|
77
72
|
hitTestLineSegment(A: VecLike, B: VecLike): boolean {
|
|
@@ -44,9 +44,47 @@ export class Circle2d extends Geometry2d {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
nearestPoint(point: VecLike): Vec {
|
|
47
|
+
// Inlined: Vec.Sub(point, _center).uni().mul(radius).add(_center)
|
|
48
|
+
// Computes direction from center to point, normalizes, scales by radius, offsets by center.
|
|
47
49
|
const { _center, _radius: radius } = this
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
const dx = point.x - _center.x
|
|
51
|
+
const dy = point.y - _center.y
|
|
52
|
+
const len = Math.sqrt(dx * dx + dy * dy)
|
|
53
|
+
if (len === 0) return new Vec(_center.x + radius, _center.y)
|
|
54
|
+
const scale = radius / len
|
|
55
|
+
return new Vec(_center.x + dx * scale, _center.y + dy * scale)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override distanceToPoint(point: VecLike, hitInside = false): number {
|
|
59
|
+
// Inlined: Math.abs(Vec.Dist(point, _center) - radius)
|
|
60
|
+
// Computes distance from point to center, then subtracts radius for edge distance.
|
|
61
|
+
// Returns negative when inside a filled circle to indicate containment.
|
|
62
|
+
const { _center, _radius: radius } = this
|
|
63
|
+
const dx = point.x - _center.x
|
|
64
|
+
const dy = point.y - _center.y
|
|
65
|
+
const dist = Math.sqrt(dx * dx + dy * dy)
|
|
66
|
+
const distToEdge = dist - radius
|
|
67
|
+
if (distToEdge < 0 && (this.isFilled || hitInside)) {
|
|
68
|
+
return distToEdge
|
|
69
|
+
}
|
|
70
|
+
return Math.abs(distToEdge)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override hitTestPoint(point: VecLike, margin = 0, hitInside = false): boolean {
|
|
74
|
+
// Equivalent to: dist = Vec.Dist(point, _center); return dist within [radius - margin, radius + margin]
|
|
75
|
+
// Uses squared distances throughout to avoid any sqrt calls.
|
|
76
|
+
const { _center, _radius: radius } = this
|
|
77
|
+
const dx = point.x - _center.x
|
|
78
|
+
const dy = point.y - _center.y
|
|
79
|
+
const dist2 = dx * dx + dy * dy
|
|
80
|
+
if ((this.isFilled || hitInside) && dist2 <= radius * radius) {
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
const outerR = radius + margin
|
|
84
|
+
if (dist2 > outerR * outerR) return false
|
|
85
|
+
const innerR = radius - margin
|
|
86
|
+
if (innerR <= 0) return true
|
|
87
|
+
return dist2 >= innerR * innerR
|
|
50
88
|
}
|
|
51
89
|
|
|
52
90
|
hitTestLineSegment(A: VecLike, B: VecLike, distance = 0): boolean {
|
|
@@ -69,6 +69,16 @@ export class CubicBezier2d extends Polyline2d {
|
|
|
69
69
|
return nearest
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
override distanceToPoint(point: VecLike, _hitInside = false): number {
|
|
73
|
+
const { segments } = this
|
|
74
|
+
let minDist = Infinity
|
|
75
|
+
for (let i = 0; i < segments.length; i++) {
|
|
76
|
+
const d = segments[i].distanceToPoint(point)
|
|
77
|
+
if (d < minDist) minDist = d
|
|
78
|
+
}
|
|
79
|
+
return minDist
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
getSvgPathData(first = true) {
|
|
73
83
|
const { _a: a, _b: b, _c: c, _d: d } = this
|
|
74
84
|
return `${first ? `M ${a.toFixed()} ` : ``} C${b.toFixed()} ${c.toFixed()} ${d.toFixed()}`
|
|
@@ -75,6 +75,16 @@ export class CubicSpline2d extends Geometry2d {
|
|
|
75
75
|
return nearest
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
override distanceToPoint(point: VecLike, _hitInside = false): number {
|
|
79
|
+
const { segments } = this
|
|
80
|
+
let minDist = Infinity
|
|
81
|
+
for (let i = 0; i < segments.length; i++) {
|
|
82
|
+
const d = segments[i].distanceToPoint(point)
|
|
83
|
+
if (d < minDist) minDist = d
|
|
84
|
+
}
|
|
85
|
+
return minDist
|
|
86
|
+
}
|
|
87
|
+
|
|
78
88
|
hitTestLineSegment(A: VecLike, B: VecLike): boolean {
|
|
79
89
|
return this.segments.some((segment) => segment.hitTestLineSegment(A, B))
|
|
80
90
|
}
|
|
@@ -5,9 +5,9 @@ import { Geometry2d } from './Geometry2d'
|
|
|
5
5
|
export class Edge2d extends Geometry2d {
|
|
6
6
|
private _start: Vec
|
|
7
7
|
private _end: Vec
|
|
8
|
-
private
|
|
9
|
-
private
|
|
10
|
-
private
|
|
8
|
+
private _dx: number
|
|
9
|
+
private _dy: number
|
|
10
|
+
private _len2: number
|
|
11
11
|
|
|
12
12
|
constructor(config: { start: Vec; end: Vec }) {
|
|
13
13
|
super({ ...config, isClosed: false, isFilled: false })
|
|
@@ -16,13 +16,14 @@ export class Edge2d extends Geometry2d {
|
|
|
16
16
|
this._start = start
|
|
17
17
|
this._end = end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
19
|
+
// Precomputed segment delta and squared length (replaces Vec.Sub(end, start) and Vec.Len2)
|
|
20
|
+
this._dx = end.x - start.x
|
|
21
|
+
this._dy = end.y - start.y
|
|
22
|
+
this._len2 = this._dx * this._dx + this._dy * this._dy
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
override getLength() {
|
|
25
|
-
return this.
|
|
26
|
+
return Math.sqrt(this._len2)
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
override getVertices(): Vec[] {
|
|
@@ -30,17 +31,39 @@ export class Edge2d extends Geometry2d {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
override nearestPoint(point: VecLike): Vec {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
// Inlined: Vec.NearestPointOnLineSegment(start, end, point)
|
|
35
|
+
// Uses precomputed dx/dy/len2 and parametric t-clamping instead of Vec allocations.
|
|
36
|
+
const { _start: start, _end: end, _dx: dx, _dy: dy, _len2: len2 } = this
|
|
37
|
+
if (len2 === 0) return start
|
|
38
|
+
|
|
39
|
+
const t = ((point.x - start.x) * dx + (point.y - start.y) * dy) / len2
|
|
40
|
+
if (t <= 0) return start
|
|
41
|
+
if (t >= 1) return end
|
|
42
|
+
return new Vec(start.x + dx * t, start.y + dy * t)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
override distanceToPoint(point: VecLike, _hitInside = false): number {
|
|
46
|
+
// Inlined: Vec.Dist(point, this.nearestPoint(point))
|
|
47
|
+
// Finds nearest point via parametric t-projection then returns scalar distance directly,
|
|
48
|
+
// avoiding the Vec allocation that nearestPoint would create.
|
|
49
|
+
const { _start: start, _end: end, _dx: dx, _dy: dy, _len2: len2 } = this
|
|
50
|
+
if (len2 === 0) return Vec.Dist(point, start)
|
|
51
|
+
|
|
52
|
+
const t = ((point.x - start.x) * dx + (point.y - start.y) * dy) / len2
|
|
53
|
+
let nx: number, ny: number
|
|
54
|
+
if (t <= 0) {
|
|
55
|
+
nx = start.x
|
|
56
|
+
ny = start.y
|
|
57
|
+
} else if (t >= 1) {
|
|
58
|
+
nx = end.x
|
|
59
|
+
ny = end.y
|
|
60
|
+
} else {
|
|
61
|
+
nx = start.x + dx * t
|
|
62
|
+
ny = start.y + dy * t
|
|
63
|
+
}
|
|
64
|
+
const ex = point.x - nx
|
|
65
|
+
const ey = point.y - ny
|
|
66
|
+
return Math.sqrt(ex * ex + ey * ey)
|
|
44
67
|
}
|
|
45
68
|
|
|
46
69
|
getSvgPathData(first = true) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Box } from '../Box'
|
|
2
2
|
import { Vec, VecLike } from '../Vec'
|
|
3
|
-
import { PI, PI2, clamp, perimeterOfEllipse } from '../utils'
|
|
3
|
+
import { PI, PI2, clamp, perimeterOfEllipse, pointInPolygon } from '../utils'
|
|
4
4
|
import { Edge2d } from './Edge2d'
|
|
5
5
|
import { Geometry2d, Geometry2dOptions } from './Geometry2d'
|
|
6
6
|
import { getVerticesCountForArcLength } from './geometry-constants'
|
|
@@ -89,6 +89,19 @@ export class Ellipse2d extends Geometry2d {
|
|
|
89
89
|
return nearest
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
override distanceToPoint(point: VecLike, hitInside = false): number {
|
|
93
|
+
const { edges } = this
|
|
94
|
+
let minDist = Infinity
|
|
95
|
+
for (let i = 0; i < edges.length; i++) {
|
|
96
|
+
const d = edges[i].distanceToPoint(point)
|
|
97
|
+
if (d < minDist) minDist = d
|
|
98
|
+
}
|
|
99
|
+
if (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {
|
|
100
|
+
return -minDist
|
|
101
|
+
}
|
|
102
|
+
return minDist
|
|
103
|
+
}
|
|
104
|
+
|
|
92
105
|
hitTestLineSegment(A: VecLike, B: VecLike): boolean {
|
|
93
106
|
return this.edges.some((edge) => edge.hitTestLineSegment(A, B))
|
|
94
107
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Vec, VecLike } from '../Vec'
|
|
2
|
+
import { pointInPolygon } from '../utils'
|
|
2
3
|
import { Edge2d } from './Edge2d'
|
|
3
4
|
import { Geometry2d, Geometry2dOptions } from './Geometry2d'
|
|
4
5
|
|
|
@@ -45,21 +46,68 @@ export class Polyline2d extends Geometry2d {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
nearestPoint(A: VecLike): Vec {
|
|
49
|
+
// Inlined: for each segment, Edge2d.nearestPoint(A) + Vec.Dist2(result, A), pick closest.
|
|
50
|
+
// Inlines the per-segment nearest-point math to avoid N Edge2d.nearestPoint Vec allocations;
|
|
51
|
+
// only allocates a single Vec at the end for the best result.
|
|
52
|
+
const { vertices } = this
|
|
53
|
+
let bestX = vertices[0].x
|
|
54
|
+
let bestY = vertices[0].y
|
|
55
|
+
let bestDist2 = (A.x - bestX) * (A.x - bestX) + (A.y - bestY) * (A.y - bestY)
|
|
56
|
+
|
|
57
|
+
const limit = this.isClosed ? vertices.length : vertices.length - 1
|
|
58
|
+
for (let i = 0; i < limit; i++) {
|
|
59
|
+
const start = vertices[i]
|
|
60
|
+
const end = vertices[(i + 1) % vertices.length]
|
|
61
|
+
const dx = end.x - start.x
|
|
62
|
+
const dy = end.y - start.y
|
|
63
|
+
const len2 = dx * dx + dy * dy
|
|
64
|
+
|
|
65
|
+
let nx: number, ny: number
|
|
66
|
+
if (len2 === 0) {
|
|
67
|
+
nx = start.x
|
|
68
|
+
ny = start.y
|
|
69
|
+
} else {
|
|
70
|
+
const t = ((A.x - start.x) * dx + (A.y - start.y) * dy) / len2
|
|
71
|
+
if (t <= 0) {
|
|
72
|
+
nx = start.x
|
|
73
|
+
ny = start.y
|
|
74
|
+
} else if (t >= 1) {
|
|
75
|
+
nx = end.x
|
|
76
|
+
ny = end.y
|
|
77
|
+
} else {
|
|
78
|
+
nx = start.x + dx * t
|
|
79
|
+
ny = start.y + dy * t
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const ex = A.x - nx
|
|
84
|
+
const ey = A.y - ny
|
|
85
|
+
const d2 = ex * ex + ey * ey
|
|
86
|
+
if (d2 < bestDist2) {
|
|
87
|
+
bestX = nx
|
|
88
|
+
bestY = ny
|
|
89
|
+
bestDist2 = d2
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return new Vec(bestX, bestY)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
override hitTestPoint(point: VecLike, margin = 0, hitInside = false): boolean {
|
|
97
|
+
return this.distanceToPoint(point, hitInside) <= margin
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
override distanceToPoint(point: VecLike, hitInside = false): number {
|
|
48
101
|
const { segments } = this
|
|
49
|
-
let
|
|
50
|
-
let dist = Infinity
|
|
51
|
-
let p: Vec // current point on segment
|
|
52
|
-
let d: number // distance from A to p
|
|
102
|
+
let minDist = Infinity
|
|
53
103
|
for (let i = 0; i < segments.length; i++) {
|
|
54
|
-
|
|
55
|
-
d =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
104
|
+
const d = segments[i].distanceToPoint(point)
|
|
105
|
+
if (d < minDist) minDist = d
|
|
106
|
+
}
|
|
107
|
+
if (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {
|
|
108
|
+
return -minDist
|
|
60
109
|
}
|
|
61
|
-
|
|
62
|
-
return nearest
|
|
110
|
+
return minDist
|
|
63
111
|
}
|
|
64
112
|
|
|
65
113
|
hitTestLineSegment(A: VecLike, B: VecLike, distance = 0): boolean {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Box } from '../Box'
|
|
2
2
|
import { Vec, VecLike } from '../Vec'
|
|
3
|
-
import { PI } from '../utils'
|
|
3
|
+
import { PI, pointInPolygon } from '../utils'
|
|
4
4
|
import { Arc2d } from './Arc2d'
|
|
5
5
|
import { Edge2d } from './Edge2d'
|
|
6
6
|
import { Geometry2d, Geometry2dOptions } from './Geometry2d'
|
|
@@ -83,6 +83,19 @@ export class Stadium2d extends Geometry2d {
|
|
|
83
83
|
return nearest
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
override distanceToPoint(point: VecLike, hitInside = false): number {
|
|
87
|
+
const { _a: a, _b: b, _c: c, _d: d } = this
|
|
88
|
+
let minDist = Infinity
|
|
89
|
+
for (const part of [a, b, c, d]) {
|
|
90
|
+
const dist = part.distanceToPoint(point)
|
|
91
|
+
if (dist < minDist) minDist = dist
|
|
92
|
+
}
|
|
93
|
+
if (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {
|
|
94
|
+
return -minDist
|
|
95
|
+
}
|
|
96
|
+
return minDist
|
|
97
|
+
}
|
|
98
|
+
|
|
86
99
|
hitTestLineSegment(A: VecLike, B: VecLike): boolean {
|
|
87
100
|
const { _a: a, _b: b, _c: c, _d: d } = this
|
|
88
101
|
return [a, b, c, d].some((edge) => edge.hitTestLineSegment(A, B))
|