diff --git a/site/components/InteractiveGraphics/InteractiveGraphics.tsx b/site/components/InteractiveGraphics/InteractiveGraphics.tsx index de014e1..dc4db27 100644 --- a/site/components/InteractiveGraphics/InteractiveGraphics.tsx +++ b/site/components/InteractiveGraphics/InteractiveGraphics.tsx @@ -421,65 +421,177 @@ 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( + const allFilteredLines = useMemo( () => - filterAndLimit(graphics.lines, filterLines).sort( + filterObjects(graphics.lines, filterLines).sort( (a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0) || a.originalIndex - b.originalIndex, ), - [graphics.lines, filterLines, objectLimit], + [graphics.lines, filterLines], + ) + const allFilteredInfiniteLines = useMemo( + () => filterObjects(graphics.infiniteLines, filterLayerAndStep), + [graphics.infiniteLines, filterLayerAndStep], + ) + const allFilteredRects = useMemo( + () => sortRectsByArea(filterObjects(graphics.rects, filterRects)), + [graphics.rects, filterRects], + ) + const allFilteredPolygons = useMemo( + () => filterObjects(graphics.polygons, filterPolygons), + [graphics.polygons, filterPolygons], + ) + const allFilteredPoints = useMemo( + () => filterObjects(graphics.points, filterPoints), + [graphics.points, filterPoints], + ) + const allFilteredCircles = useMemo( + () => filterObjects(graphics.circles, filterCircles), + [graphics.circles, filterCircles], + ) + const allFilteredTexts = useMemo( + () => filterObjects(graphics.texts, filterTexts), + [graphics.texts, filterTexts], + ) + const allFilteredArrows = useMemo( + () => filterObjects(graphics.arrows, filterArrows), + [graphics.arrows, filterArrows], + ) + + const totalFilteredObjects = + allFilteredInfiniteLines.length + + allFilteredLines.length + + allFilteredRects.length + + allFilteredPolygons.length + + allFilteredPoints.length + + allFilteredCircles.length + + allFilteredTexts.length + + allFilteredArrows.length + const normalizedObjectLimit = + typeof objectLimit === "number" && Number.isFinite(objectLimit) + ? Math.max(0, Math.floor(objectLimit)) + : undefined + const isLimitReached = + normalizedObjectLimit !== undefined && + totalFilteredObjects > normalizedObjectLimit + const visibleObjectKeys = useMemo(() => { + if (!isLimitReached || normalizedObjectLimit === undefined) return null + + const orderedObjects = [ + ...allFilteredArrows.map((obj) => ({ + type: "arrow", + originalIndex: obj.originalIndex, + })), + ...allFilteredInfiniteLines.map((obj) => ({ + type: "infinite-line", + originalIndex: obj.originalIndex, + })), + ...allFilteredLines.map((obj) => ({ + type: "line", + originalIndex: obj.originalIndex, + })), + ...allFilteredRects.map((obj) => ({ + type: "rect", + originalIndex: obj.originalIndex, + })), + ...allFilteredPolygons.map((obj) => ({ + type: "polygon", + originalIndex: obj.originalIndex, + })), + ...allFilteredCircles.map((obj) => ({ + type: "circle", + originalIndex: obj.originalIndex, + })), + ...allFilteredTexts.map((obj) => ({ + type: "text", + originalIndex: obj.originalIndex, + })), + ...allFilteredPoints.map((obj) => ({ + type: "point", + originalIndex: obj.originalIndex, + })), + ] + + const limitedObjects = + normalizedObjectLimit === 0 + ? [] + : orderedObjects.slice(-normalizedObjectLimit) + + return new Set( + limitedObjects.map((obj) => `${obj.type}:${obj.originalIndex}`), + ) + }, [ + allFilteredArrows, + allFilteredCircles, + allFilteredInfiniteLines, + allFilteredLines, + allFilteredPoints, + allFilteredPolygons, + allFilteredRects, + allFilteredTexts, + isLimitReached, + normalizedObjectLimit, + ]) + const shouldRenderObject = useCallback( + (type: string, obj: { originalIndex: number }) => + !visibleObjectKeys || + visibleObjectKeys.has(`${type}:${obj.originalIndex}`), + [visibleObjectKeys], + ) + + const filteredArrows = useMemo( + () => allFilteredArrows.filter((obj) => shouldRenderObject("arrow", obj)), + [allFilteredArrows, shouldRenderObject], ) const filteredInfiniteLines = useMemo( - () => filterAndLimit(graphics.infiniteLines, filterLayerAndStep), - [graphics.infiniteLines, filterLayerAndStep, objectLimit], + () => + allFilteredInfiniteLines.filter((obj) => + shouldRenderObject("infinite-line", obj), + ), + [allFilteredInfiniteLines, shouldRenderObject], + ) + const filteredLines = useMemo( + () => allFilteredLines.filter((obj) => shouldRenderObject("line", obj)), + [allFilteredLines, shouldRenderObject], ) const filteredRects = useMemo( - () => sortRectsByArea(filterAndLimit(graphics.rects, filterRects)), - [graphics.rects, filterRects, objectLimit], + () => allFilteredRects.filter((obj) => shouldRenderObject("rect", obj)), + [allFilteredRects, shouldRenderObject], ) const filteredPolygons = useMemo( - () => filterAndLimit(graphics.polygons, filterPolygons), - [graphics.polygons, filterPolygons, objectLimit], - ) - const filteredPoints = useMemo( - () => filterAndLimit(graphics.points, filterPoints), - [graphics.points, filterPoints, objectLimit], + () => + allFilteredPolygons.filter((obj) => shouldRenderObject("polygon", obj)), + [allFilteredPolygons, shouldRenderObject], ) const filteredCircles = useMemo( - () => filterAndLimit(graphics.circles, filterCircles), - [graphics.circles, filterCircles, objectLimit], + () => allFilteredCircles.filter((obj) => shouldRenderObject("circle", obj)), + [allFilteredCircles, shouldRenderObject], ) const filteredTexts = useMemo( - () => filterAndLimit(graphics.texts, filterTexts), - [graphics.texts, filterTexts, objectLimit], + () => allFilteredTexts.filter((obj) => shouldRenderObject("text", obj)), + [allFilteredTexts, shouldRenderObject], ) - const filteredArrows = useMemo( - () => filterAndLimit(graphics.arrows, filterArrows), - [graphics.arrows, filterArrows, objectLimit], + const filteredPoints = useMemo( + () => allFilteredPoints.filter((obj) => shouldRenderObject("point", obj)), + [allFilteredPoints, shouldRenderObject], ) - - const totalFilteredObjects = - filteredInfiniteLines.length + - filteredLines.length + - filteredRects.length + - filteredPolygons.length + - filteredPoints.length + - filteredCircles.length + - filteredTexts.length + - filteredArrows.length - const isLimitReached = objectLimit && totalFilteredObjects > objectLimit + const objectLimitWarning = isLimitReached ? ( + + Display limited to {normalizedObjectLimit} objects. Received:{" "} + {totalFilteredObjects}. + + ) : null return (
@@ -552,15 +664,12 @@ export const InteractiveGraphics = ({ /> Show last step - {isLimitReached && ( - - Display limited to {objectLimit} objects. Received:{" "} - {totalFilteredObjects}. - - )} + {objectLimitWarning}
)} + {maxStep <= 0 && objectLimitWarning} +