From 982ef521b8d43dd67adf955b428483b13f4dd04c Mon Sep 17 00:00:00 2001 From: aarkue Date: Wed, 29 May 2024 19:25:20 +0200 Subject: [PATCH] Polished OCELGraphViewer (Zoom + Download Button, Adjust D3 Forces) --- frontend/src/routes/OcelGraphViewer.tsx | 288 ++++++++++++++---------- 1 file changed, 171 insertions(+), 117 deletions(-) diff --git a/frontend/src/routes/OcelGraphViewer.tsx b/frontend/src/routes/OcelGraphViewer.tsx index 7921e48..eab1977 100644 --- a/frontend/src/routes/OcelGraphViewer.tsx +++ b/frontend/src/routes/OcelGraphViewer.tsx @@ -8,7 +8,8 @@ import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { downloadURL } from "@/lib/download-url"; import type { OCELGraphOptions } from "@/types/generated/OCELGraphOptions"; import type { OCELEvent, OCELObject } from "@/types/ocel"; -import { useContext, useMemo, useRef, useState } from "react"; +import { ImageIcon } from "@radix-ui/react-icons"; +import { useContext, useEffect, useMemo, useRef, useState } from "react"; import ForceGraph2D, { type ForceGraphMethods, type LinkObject, @@ -16,6 +17,12 @@ import ForceGraph2D, { } from "react-force-graph-2d"; import toast from "react-hot-toast"; import { LuClipboardCopy } from "react-icons/lu"; +import { + MdOutlineZoomInMap +} from "react-icons/md"; + +import AutoSizer from "react-virtualized-auto-sizer"; + type GraphNode = (OCELEvent | OCELObject) & { neighbors?: GraphNode[]; links?: GraphLink[]; @@ -31,7 +38,10 @@ type GraphData = { }; export default function OcelGraphViewer() { const ocelInfo = useContext(OcelInfoContext); - const [graphData, setGraphData] = useState(); + const [graphData, setGraphData] = useState({ + nodes: [], + links: [], + }); const data = useMemo(() => { const gData = graphData; @@ -58,6 +68,12 @@ export default function OcelGraphViewer() { return gData; }, [graphData]); + useEffect(() => { + setTimeout(() => { + graphRef.current?.zoomToFit(200,100); + },300); + }, [data]); + const [highlightNodes, setHighlightNodes] = useState(new Set()); const [highlightLinks, setHighlightLinks] = useState(new Set()); @@ -105,10 +121,35 @@ export default function OcelGraphViewer() { return (

OCEL Graph

- + { + console.log(graphRef.current?.d3Force); + graphRef.current!.d3Force("link")!.distance(10); + + if (gd === undefined) { + setGraphData({ nodes: [], links: [] }); + } else { + setGraphData(gd); + } + }} + />
+ {data !== undefined && ( - "#d6d6d6"} - backgroundColor="white" - linkWidth={(link) => (highlightLinks.has(link) ? 5 : 2)} - linkDirectionalParticles={4} - linkDirectionalParticleWidth={(link) => - highlightLinks.has(link) ? 4 : 0 - } - onNodeHover={handleNodeHover} - onLinkHover={handleLinkHover} - nodeLabel={(x) => - `
${ - x.id - }
${ - x.type - } (${"time" in x ? "Event" : "Object"})
` - } - nodeCanvasObject={(node, ctx) => { - if (node.x === undefined || node.y === undefined) { - return; - } - const isFirstNode = node.id === graphData?.nodes[0].id; - let width = 4; - let height = 4; - const fillStyle = isFirstNode ? node.color : node.color + "a4"; - ctx.lineWidth = isFirstNode ? 0.2 : 0.2; - ctx.strokeStyle = highlightNodes.has(node) - ? "black" - : isFirstNode - ? "#707070" - : node.color; - if ("time" in node) { - width = 7; - height = 7; - ctx.beginPath(); - ctx.fillStyle = "white"; - ctx.roundRect( - node.x - width / 2, - node.y - height / 2, - width, - height, - 0.2, - ); - ctx.fill(); - ctx.fillStyle = fillStyle; - ctx.roundRect( - node.x - width / 2, - node.y - height / 2, - width, - height, - 0.2, - ); - ctx.fill(); - ctx.stroke(); - node.__bckgDimensions = [2 * width, 2 * height]; // save for nodePointerAreaPaint - } else { - ctx.beginPath(); - ctx.fillStyle = "white"; - ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false); - ctx.fill(); - ctx.fillStyle = fillStyle; - ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false); - ctx.fill(); - ctx.stroke(); - node.__bckgDimensions = [2 * width, 2 * height]; // save for nodePointerAreaPaint - } + + {({ height, width }) => ( + "#d6d6d6"} + backgroundColor="white" + linkWidth={(link) => (highlightLinks.has(link) ? 5 : 2)} + linkDirectionalParticleColor={() => "#556166"} + linkDirectionalParticles={4} + linkDirectionalParticleWidth={(link) => + highlightLinks.has(link) ? 4 : 0 + } + onNodeHover={handleNodeHover} + onLinkHover={handleLinkHover} + nodeLabel={(x) => + `
${ + x.id + }
${ + x.type + } (${"time" in x ? "Event" : "Object"})
` + } + nodeCanvasObject={(node, ctx) => { + if (node.x === undefined || node.y === undefined) { + return; + } + const isFirstNode = node.id === graphData?.nodes[0].id; + let width = 4; + let height = 4; + const fillStyle = isFirstNode + ? node.color + : node.color + "a4"; + ctx.lineWidth = isFirstNode ? 0.4 : 0.2; + ctx.strokeStyle = highlightNodes.has(node) + ? "black" + : isFirstNode + ? "#515151" + : node.color; + if ("time" in node) { + width = 7; + height = 7; + ctx.beginPath(); + ctx.fillStyle = "white"; + ctx.roundRect( + node.x - width / 2, + node.y - height / 2, + width, + height, + 0.2, + ); + ctx.fill(); + ctx.fillStyle = fillStyle; + ctx.roundRect( + node.x - width / 2, + node.y - height / 2, + width, + height, + 0.2, + ); + ctx.fill(); + ctx.stroke(); + node.__bckgDimensions = [2 * width, 2 * height]; // save for nodePointerAreaPaint + } else { + ctx.beginPath(); + ctx.fillStyle = "white"; + ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false); + ctx.fill(); + ctx.fillStyle = fillStyle; + ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false); + ctx.fill(); + ctx.stroke(); + node.__bckgDimensions = [2 * width, 2 * height]; // save for nodePointerAreaPaint + } - // Web browser used in Tauri under Linux butchers this text terribly >:( - // Maybe because of the very small font size? + // Web browser used in Tauri under Linux butchers this text terribly >:( + // Maybe because of the very small font size? - // if ((window as any).__TAURI__ === undefined) { - let fontSize = 1; - ctx.font = `${fontSize}px Sans-Serif`; - const label = node.id; - const maxLength = 13; - const text = - label.length > maxLength - ? label.substring(0, maxLength - 3) + "..." - : label; - ctx.fillStyle = "black"; + // if ((window as any).__TAURI__ === undefined) { + let fontSize = 1; + ctx.font = `${fontSize}px Sans-Serif`; + const label = node.id; + const maxLength = 13; + const text = + label.length > maxLength + ? label.substring(0, maxLength - 3) + "..." + : label; + ctx.fillStyle = "black"; - ctx.textAlign = "center"; - ctx.textBaseline = "bottom"; - ctx.fillText(text, node.x, node.y); - fontSize = 0.8; - ctx.font = `${fontSize}px Sans-Serif`; - ctx.fillStyle = "#3f3f3f"; - const typeText = - node.type.length > maxLength - ? node.type.substring(0, maxLength - 3) + "..." - : node.type; - ctx.fillText(typeText, node.x, node.y + 1.5 * fontSize); - // } - }} - nodePointerAreaPaint={(node, color, ctx) => { - if (node.x === undefined || node.y === undefined) { - return; - } - ctx.fillStyle = color; - const bckgDimensions: [number, number] = node.__bckgDimensions; - Boolean(bckgDimensions) && - ctx.fillRect( - node.x - bckgDimensions[0] / 2, - node.y - bckgDimensions[1] / 2, - ...bckgDimensions, - ); - }} - onNodeClick={async (node) => { - await navigator.clipboard.writeText(node.id); - toast("Copied ID to clipboard!", { icon: }); - }} - graphData={data} - /> + ctx.textAlign = "center"; + ctx.textBaseline = "bottom"; + ctx.fillText(text, node.x, node.y); + fontSize = 0.8; + ctx.font = `${fontSize}px Sans-Serif`; + ctx.fillStyle = "#3f3f3f"; + const typeText = + node.type.length > maxLength + ? node.type.substring(0, maxLength - 3) + "..." + : node.type; + ctx.fillText(typeText, node.x, node.y + 1.5 * fontSize); + // } + }} + nodePointerAreaPaint={(node, color, ctx) => { + if (node.x === undefined || node.y === undefined) { + return; + } + ctx.fillStyle = color; + const bckgDimensions: [number, number] = + node.__bckgDimensions; + Boolean(bckgDimensions) && + ctx.fillRect( + node.x - bckgDimensions[0] / 2, + node.y - bckgDimensions[1] / 2, + ...bckgDimensions, + ); + }} + onNodeClick={async (node) => { + await navigator.clipboard.writeText(node.id); + toast("Copied ID to clipboard!", { + icon: , + }); + }} + /> + )} +
)}
@@ -334,6 +387,7 @@ function GraphOptions({