From 18c21593e619d3f94dd73c453f1624a41a0197bd Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Sun, 18 May 2025 21:41:54 +0200 Subject: [PATCH] Add caching Signed-off-by: Mark Tolmacs --- packages/element/src/utils.ts | 86 +++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/packages/element/src/utils.ts b/packages/element/src/utils.ts index 9f801fcf3..08caf82e2 100644 --- a/packages/element/src/utils.ts +++ b/packages/element/src/utils.ts @@ -30,13 +30,69 @@ type ElementShape = [LineSegment[], Curve[]]; class ElementShapeCache { private static cache = new WeakMap< ExcalidrawElement, - Map + { version: ExcalidrawElement["version"]; shapes: Map } >(); + + public static get = ( + element: T, + offset: number, + ): ElementShape | undefined => { + const record = ElementShapeCache.cache.get(element); + + if (!record) { + return undefined; + } + + const { version, shapes } = record; + + if (version !== element.version) { + ElementShapeCache.cache.delete(element); + return undefined; + } + + return shapes.get(offset); + }; + + public static set = ( + element: T, + shape: ElementShape, + offset: number, + ) => { + const record = ElementShapeCache.cache.get(element); + + if (!record) { + ElementShapeCache.cache.set(element, { + version: element.version, + shapes: new Map([[offset, shape]]), + }); + + return; + } + + const { version, shapes } = record; + + if (version !== element.version) { + ElementShapeCache.cache.set(element, { + version: element.version, + shapes: new Map([[offset, shape]]), + }); + + return; + } + + shapes.set(offset, shape); + }; } export function deconstructLinearOrFreeDrawElement( element: ExcalidrawLinearElement | ExcalidrawFreeDrawElement, ): [LineSegment[], Curve[]] { + const cachedShape = ElementShapeCache.get(element, 0); + + if (cachedShape) { + return cachedShape; + } + const ops = generateLinearCollisionShape(element) as { op: string; data: number[]; @@ -101,7 +157,10 @@ export function deconstructLinearOrFreeDrawElement( } } - return [lines, curves]; + const shape = [lines, curves] as ElementShape; + ElementShapeCache.set(element, shape, 0); + + return shape; } /** @@ -116,6 +175,12 @@ export function deconstructRectanguloidElement( element: ExcalidrawRectanguloidElement, offset: number = 0, ): [LineSegment[], Curve[]] { + const cachedShape = ElementShapeCache.get(element, offset); + + if (cachedShape) { + return cachedShape; + } + let radius = getCornerRadius( Math.min(element.width, element.height), element, @@ -231,8 +296,11 @@ export function deconstructRectanguloidElement( corners[0][0][0], ), ]; + const shape = [sides, corners.flat()] as ElementShape; - return [sides, corners.flat()]; + ElementShapeCache.set(element, shape, offset); + + return shape; } /** @@ -247,6 +315,12 @@ export function deconstructDiamondElement( element: ExcalidrawDiamondElement, offset: number = 0, ): [LineSegment[], Curve[]] { + const cachedShape = ElementShapeCache.get(element, offset); + + if (cachedShape) { + return cachedShape; + } + const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] = getDiamondPoints(element); const verticalRadius = element.roundness @@ -348,5 +422,9 @@ export function deconstructDiamondElement( ), ]; - return [sides, corners.flat()]; + const shape = [sides, corners.flat()] as ElementShape; + + ElementShapeCache.set(element, shape, offset); + + return shape; }