aboutsummaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/scene/ShapeCache.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/excalidraw/scene/ShapeCache.ts')
-rw-r--r--packages/excalidraw/scene/ShapeCache.ts86
1 files changed, 86 insertions, 0 deletions
diff --git a/packages/excalidraw/scene/ShapeCache.ts b/packages/excalidraw/scene/ShapeCache.ts
new file mode 100644
index 0000000..39d388a
--- /dev/null
+++ b/packages/excalidraw/scene/ShapeCache.ts
@@ -0,0 +1,86 @@
+import type { Drawable } from "roughjs/bin/core";
+import { RoughGenerator } from "roughjs/bin/generator";
+import type {
+ ExcalidrawElement,
+ ExcalidrawSelectionElement,
+} from "../element/types";
+import { elementWithCanvasCache } from "../renderer/renderElement";
+import { _generateElementShape } from "./Shape";
+import type { ElementShape, ElementShapes } from "./types";
+import { COLOR_PALETTE } from "../colors";
+import type { AppState, EmbedsValidationStatus } from "../types";
+
+export class ShapeCache {
+ private static rg = new RoughGenerator();
+ private static cache = new WeakMap<ExcalidrawElement, ElementShape>();
+
+ /**
+ * Retrieves shape from cache if available. Use this only if shape
+ * is optional and you have a fallback in case it's not cached.
+ */
+ public static get = <T extends ExcalidrawElement>(element: T) => {
+ return ShapeCache.cache.get(
+ element,
+ ) as T["type"] extends keyof ElementShapes
+ ? ElementShapes[T["type"]] | undefined
+ : ElementShape | undefined;
+ };
+
+ public static set = <T extends ExcalidrawElement>(
+ element: T,
+ shape: T["type"] extends keyof ElementShapes
+ ? ElementShapes[T["type"]]
+ : Drawable,
+ ) => ShapeCache.cache.set(element, shape);
+
+ public static delete = (element: ExcalidrawElement) =>
+ ShapeCache.cache.delete(element);
+
+ public static destroy = () => {
+ ShapeCache.cache = new WeakMap();
+ };
+
+ /**
+ * Generates & caches shape for element if not already cached, otherwise
+ * returns cached shape.
+ */
+ public static generateElementShape = <
+ T extends Exclude<ExcalidrawElement, ExcalidrawSelectionElement>,
+ >(
+ element: T,
+ renderConfig: {
+ isExporting: boolean;
+ canvasBackgroundColor: AppState["viewBackgroundColor"];
+ embedsValidationStatus: EmbedsValidationStatus;
+ } | null,
+ ) => {
+ // when exporting, always regenerated to guarantee the latest shape
+ const cachedShape = renderConfig?.isExporting
+ ? undefined
+ : ShapeCache.get(element);
+
+ // `null` indicates no rc shape applicable for this element type,
+ // but it's considered a valid cache value (= do not regenerate)
+ if (cachedShape !== undefined) {
+ return cachedShape;
+ }
+
+ elementWithCanvasCache.delete(element);
+
+ const shape = _generateElementShape(
+ element,
+ ShapeCache.rg,
+ renderConfig || {
+ isExporting: false,
+ canvasBackgroundColor: COLOR_PALETTE.white,
+ embedsValidationStatus: null,
+ },
+ ) as T["type"] extends keyof ElementShapes
+ ? ElementShapes[T["type"]]
+ : Drawable | null;
+
+ ShapeCache.cache.set(element, shape);
+
+ return shape;
+ };
+}