-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
96b52b6
commit ae2aa23
Showing
8 changed files
with
4,614 additions
and
13,002 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
226 changes: 92 additions & 134 deletions
226
components/tools/toolComp/ScaffoldNetworkWholeGraph.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,141 +1,99 @@ | ||
import "@react-sigma/core/lib/react-sigma.min.css"; | ||
import getNodeProgramImage from "sigma/rendering/webgl/programs/node.image"; | ||
import { | ||
SigmaContainer, | ||
useRegisterEvents, | ||
useSetSettings, | ||
useSigma, | ||
} from "@react-sigma/core"; | ||
import { useEffect, useState } from "react"; | ||
import { Attributes } from "graphology-types"; | ||
import { circular } from "graphology-layout"; | ||
import { subgraph } from "graphology-operators"; | ||
|
||
const GraphEvents: React.FC = () => { | ||
const registerEvents = useRegisterEvents(); | ||
const sigma = useSigma(); | ||
|
||
const [draggedNode, setDraggedNode] = useState<string | null>(null); | ||
const [hoveredNode, setHoveredNode] = useState<string | null>(null); | ||
|
||
const setSettings = useSetSettings(); | ||
import React, { useEffect, useRef } from 'react'; | ||
import * as d3 from 'd3'; | ||
|
||
const ScaffoldNetworkWholeGraph = ({ graph, imageSize = 120 }) => { | ||
const svgRef = useRef(null); | ||
|
||
useEffect(() => { | ||
setSettings({ | ||
nodeReducer: (node, data) => { | ||
const graph = sigma.getGraph(); | ||
const newData: Attributes = { | ||
...data, | ||
highlighted: data.highlighted || false, | ||
}; | ||
|
||
if (hoveredNode) { | ||
if ( | ||
node === hoveredNode || | ||
graph.neighbors(hoveredNode).includes(node) | ||
) { | ||
newData.highlighted = true; | ||
newData.size = 40; | ||
(newData.type = "image"), (newData.image = "/logo.svg"); | ||
} else { | ||
newData.color = "#E2E2E2"; | ||
newData.highlighted = false; | ||
} | ||
} | ||
return newData; | ||
}, | ||
edgeReducer: (edge, data) => { | ||
const graph = sigma.getGraph(); | ||
const newData = { ...data, hidden: false }; | ||
|
||
if (hoveredNode && !graph.extremities(edge).includes(hoveredNode)) { | ||
newData.hidden = true; | ||
} | ||
return newData; | ||
}, | ||
}); | ||
const width = 928; | ||
const height = 680; | ||
|
||
|
||
const graph_ex = graph.export(); | ||
const { nodes, links } = { | ||
nodes: graph_ex.nodes.map(n => ({ id: n.key, ...n.attributes })), | ||
links: graph_ex.edges | ||
} | ||
const simulation = d3.forceSimulation(nodes) | ||
.force("link", d3.forceLink(links).id(d => d.id)) | ||
.force("charge", d3.forceManyBody()) | ||
.force("x", d3.forceX()) | ||
.force("y", d3.forceY()); | ||
|
||
const svg = d3.select(svgRef.current) | ||
.attr("width", width) | ||
.attr("height", height) | ||
.attr("viewBox", [-width / 2, -height / 2, width, height]) | ||
.attr("style", "max-width: 100%; height: auto;") | ||
.call(d3.zoom().on("zoom", function (event) { | ||
svg.attr("transform", event.transform); | ||
})); | ||
|
||
const link = svg.append("g") | ||
.attr("stroke", "#999") | ||
.attr("stroke-opacity", 0.5) | ||
.selectAll("line") | ||
.data(links) | ||
.attr("stroke", d => d.color) | ||
.join("line") | ||
.attr("stroke-width", 0.5) | ||
|
||
const node = svg.append("g") | ||
.selectAll("image") | ||
.data(nodes) | ||
.join("image") | ||
.attr("xlink:href", d => d.image) // Function to get image URL | ||
.attr("width", imageSize * 0.10) // Adjust image width | ||
.attr("height", imageSize * 0.10); // Adjust image height | ||
|
||
// Register the events | ||
registerEvents({ | ||
enterNode: (event) => setHoveredNode(event.node), | ||
leaveNode: () => setHoveredNode(null), | ||
downNode: (e) => { | ||
setDraggedNode(e.node); | ||
sigma.getGraph().setNodeAttribute(e.node, "highlighted", true); | ||
}, | ||
mouseup: (e) => { | ||
if (draggedNode) { | ||
setDraggedNode(null); | ||
sigma.getGraph().removeNodeAttribute(draggedNode, "highlighted"); | ||
} | ||
}, | ||
mousedown: (e) => { | ||
// Disable the autoscale at the first down interaction | ||
if (!sigma.getCustomBBox()) sigma.setCustomBBox(sigma.getBBox()); | ||
}, | ||
mousemove: (e) => { | ||
if (draggedNode) { | ||
// Get new position of node | ||
const pos = sigma.viewportToGraph(e); | ||
sigma.getGraph().setNodeAttribute(draggedNode, "x", pos.x); | ||
sigma.getGraph().setNodeAttribute(draggedNode, "y", pos.y); | ||
|
||
// Prevent sigma to move camera: | ||
e.preventSigmaDefault(); | ||
e.original.preventDefault(); | ||
e.original.stopPropagation(); | ||
} | ||
}, | ||
touchup: (e) => { | ||
if (draggedNode) { | ||
setDraggedNode(null); | ||
sigma.getGraph().removeNodeAttribute(draggedNode, "highlighted"); | ||
} | ||
}, | ||
touchdown: (e) => { | ||
// Disable the autoscale at the first down interaction | ||
if (!sigma.getCustomBBox()) sigma.setCustomBBox(sigma.getBBox()); | ||
}, | ||
touchmove: (e) => { | ||
if (draggedNode) { | ||
// Get new position of node | ||
// @ts-ignore | ||
const pos = sigma.viewportToGraph(e); | ||
sigma.getGraph().setNodeAttribute(draggedNode, "x", pos.x); | ||
sigma.getGraph().setNodeAttribute(draggedNode, "y", pos.y); | ||
|
||
// Prevent sigma to move camera: | ||
// @ts-ignore | ||
e.preventSigmaDefault(); | ||
e.original.preventDefault(); | ||
e.original.stopPropagation(); | ||
} | ||
}, | ||
node.append("title") | ||
.text(d => d.smiles); | ||
|
||
|
||
// Add a drag behavior. | ||
node.call(d3.drag() | ||
.on("start", dragstarted) | ||
.on("drag", dragged) | ||
.on("end", dragended)); | ||
|
||
// Set the position attributes of links and nodes each time the simulation ticks. | ||
simulation.on("tick", () => { | ||
link | ||
.attr("x1", d => d.source.x) | ||
.attr("y1", d => d.source.y) | ||
.attr("x2", d => d.target.x) | ||
.attr("y2", d => d.target.y); | ||
|
||
node | ||
.attr("x", d => d.x) | ||
.attr("y", d => d.y); | ||
}); | ||
}, [hoveredNode, setSettings, sigma]); | ||
|
||
return null; | ||
// Reheat the simulation when drag starts, and fix the subject position. | ||
function dragstarted(event) { | ||
if (!event.active) simulation.alphaTarget(0.3).restart(); | ||
event.subject.fx = event.subject.x; | ||
event.subject.fy = event.subject.y; | ||
} | ||
function dragged(event) { | ||
event.subject.fx = event.x; | ||
event.subject.fy = event.y; | ||
} | ||
function dragended(event) { | ||
if (!event.active) simulation.alphaTarget(0); | ||
event.subject.fx = null; | ||
event.subject.fy = null; | ||
} | ||
|
||
// When this cell is re-run, stop the previous simulation. (This doesn’t | ||
// really matter since the target alpha is zero and the simulation will | ||
// stop naturally, but it’s a good practice.) | ||
// // invalidation.then(() => simulation.stop()); | ||
|
||
|
||
}, [graph]); | ||
|
||
return <svg ref={svgRef} width="100%" height="80vh" />; | ||
}; | ||
|
||
export default function ScaffoldNetworkWholeGraph({ graph }) { | ||
const sub = subgraph(graph, function (_, attr) { | ||
return attr.nodeType === "whole"; | ||
}); | ||
const positions = circular(sub, { scale: 100 }); | ||
circular.assign(graph, positions); | ||
|
||
return ( | ||
<div> | ||
<SigmaContainer | ||
style={{ height: "600px", width: "100%" }} | ||
graph={graph} | ||
settings={{ | ||
nodeProgramClasses: { image: getNodeProgramImage() }, | ||
defaultNodeType: "image", | ||
}} | ||
> | ||
<GraphEvents /> | ||
</SigmaContainer> | ||
</div> | ||
); | ||
} | ||
export default ScaffoldNetworkWholeGraph; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.