From 7c73557b091c010c4c5ec14edea07e087be80edc Mon Sep 17 00:00:00 2001 From: Asim Arshad Date: Sun, 10 May 2026 22:57:29 +0100 Subject: [PATCH] Enforce global object limit --- .../InteractiveGraphics.tsx | 135 +++++++++++------- .../limitFilteredObjects.ts | 49 +++++++ tests/limitFilteredObjects.test.ts | 68 +++++++++ 3 files changed, 204 insertions(+), 48 deletions(-) create mode 100644 site/components/InteractiveGraphics/limitFilteredObjects.ts create mode 100644 tests/limitFilteredObjects.test.ts diff --git a/site/components/InteractiveGraphics/InteractiveGraphics.tsx b/site/components/InteractiveGraphics/InteractiveGraphics.tsx index de014e1..10a0ebd 100644 --- a/site/components/InteractiveGraphics/InteractiveGraphics.tsx +++ b/site/components/InteractiveGraphics/InteractiveGraphics.tsx @@ -21,6 +21,7 @@ import { ContextMenu } from "./ContextMenu" import { InfiniteLine } from "./InfiniteLine" import { InteractiveState } from "./InteractiveState" import { Line } from "./Line" +import { limitFilteredObjects } from "./limitFilteredObjects" import { Marker, MarkerPoint } from "./Marker" import { Point } from "./Point" import { Polygon } from "./Polygon" @@ -421,65 +422,102 @@ export const InteractiveGraphics = ({ filterLayerAndStep, }) - const filterAndLimit = ( + const filterObjects = ( objects: T[] | undefined, filterFn: (obj: T) => boolean, ): (T & { originalIndex: number })[] => { if (!objects) return [] - const filtered = objects + return objects .map((obj, index) => ({ ...obj, originalIndex: index })) .filter(filterFn) - return objectLimit ? filtered.slice(-objectLimit) : filtered } - const filteredLines = useMemo( - () => - filterAndLimit(graphics.lines, filterLines).sort( - (a, b) => - (a.zIndex ?? 0) - (b.zIndex ?? 0) || - a.originalIndex - b.originalIndex, - ), - [graphics.lines, filterLines, objectLimit], + const filteredLinesBeforeLimit = useMemo( + () => filterObjects(graphics.lines, filterLines), + [graphics.lines, filterLines], ) - const filteredInfiniteLines = useMemo( - () => filterAndLimit(graphics.infiniteLines, filterLayerAndStep), - [graphics.infiniteLines, filterLayerAndStep, objectLimit], + const filteredInfiniteLinesBeforeLimit = useMemo( + () => filterObjects(graphics.infiniteLines, filterLayerAndStep), + [graphics.infiniteLines, filterLayerAndStep], ) - const filteredRects = useMemo( - () => sortRectsByArea(filterAndLimit(graphics.rects, filterRects)), - [graphics.rects, filterRects, objectLimit], + const filteredRectsBeforeLimit = useMemo( + () => filterObjects(graphics.rects, filterRects), + [graphics.rects, filterRects], ) - const filteredPolygons = useMemo( - () => filterAndLimit(graphics.polygons, filterPolygons), - [graphics.polygons, filterPolygons, objectLimit], + const filteredPolygonsBeforeLimit = useMemo( + () => filterObjects(graphics.polygons, filterPolygons), + [graphics.polygons, filterPolygons], ) - const filteredPoints = useMemo( - () => filterAndLimit(graphics.points, filterPoints), - [graphics.points, filterPoints, objectLimit], + const filteredPointsBeforeLimit = useMemo( + () => filterObjects(graphics.points, filterPoints), + [graphics.points, filterPoints], ) - const filteredCircles = useMemo( - () => filterAndLimit(graphics.circles, filterCircles), - [graphics.circles, filterCircles, objectLimit], + const filteredCirclesBeforeLimit = useMemo( + () => filterObjects(graphics.circles, filterCircles), + [graphics.circles, filterCircles], ) - const filteredTexts = useMemo( - () => filterAndLimit(graphics.texts, filterTexts), - [graphics.texts, filterTexts, objectLimit], + const filteredTextsBeforeLimit = useMemo( + () => filterObjects(graphics.texts, filterTexts), + [graphics.texts, filterTexts], ) - const filteredArrows = useMemo( - () => filterAndLimit(graphics.arrows, filterArrows), - [graphics.arrows, filterArrows, objectLimit], + const filteredArrowsBeforeLimit = useMemo( + () => filterObjects(graphics.arrows, filterArrows), + [graphics.arrows, filterArrows], + ) + + const { + groups: limitedFilteredObjects, + totalFilteredObjects, + isLimitReached, + } = useMemo( + () => + limitFilteredObjects( + { + arrows: filteredArrowsBeforeLimit, + infiniteLines: filteredInfiniteLinesBeforeLimit, + lines: filteredLinesBeforeLimit, + rects: filteredRectsBeforeLimit, + polygons: filteredPolygonsBeforeLimit, + circles: filteredCirclesBeforeLimit, + texts: filteredTextsBeforeLimit, + points: filteredPointsBeforeLimit, + }, + objectLimit, + ), + [ + filteredArrowsBeforeLimit, + filteredInfiniteLinesBeforeLimit, + filteredLinesBeforeLimit, + filteredRectsBeforeLimit, + filteredPolygonsBeforeLimit, + filteredCirclesBeforeLimit, + filteredTextsBeforeLimit, + filteredPointsBeforeLimit, + objectLimit, + ], ) - const totalFilteredObjects = - filteredInfiniteLines.length + - filteredLines.length + - filteredRects.length + - filteredPolygons.length + - filteredPoints.length + - filteredCircles.length + - filteredTexts.length + - filteredArrows.length - const isLimitReached = objectLimit && totalFilteredObjects > objectLimit + const filteredLines = useMemo( + () => + limitedFilteredObjects.lines + .slice() + .sort( + (a, b) => + (a.zIndex ?? 0) - (b.zIndex ?? 0) || + a.originalIndex - b.originalIndex, + ), + [limitedFilteredObjects.lines], + ) + const filteredInfiniteLines = limitedFilteredObjects.infiniteLines + const filteredRects = useMemo( + () => sortRectsByArea(limitedFilteredObjects.rects), + [limitedFilteredObjects.rects], + ) + const filteredPolygons = limitedFilteredObjects.polygons + const filteredPoints = limitedFilteredObjects.points + const filteredCircles = limitedFilteredObjects.circles + const filteredTexts = limitedFilteredObjects.texts + const filteredArrows = limitedFilteredObjects.arrows return (
@@ -552,15 +590,16 @@ export const InteractiveGraphics = ({ /> Show last step - {isLimitReached && ( - - Display limited to {objectLimit} objects. Received:{" "} - {totalFilteredObjects}. - - )}
)} + {isLimitReached && ( + + Display limited to {objectLimit} objects. Received:{" "} + {totalFilteredObjects}. + + )} +