From 14a5433a7069811bf0c9f04a8e0db90940d35e9a Mon Sep 17 00:00:00 2001 From: JUDIT GRESKOVITS Date: Wed, 15 Feb 2017 10:44:16 +0000 Subject: [PATCH] Add draggability to topology graph nodes --- ui/src/components/topology/graph-link.js | 2 +- ui/src/components/topology/graph-node.js | 16 ++- .../components/topology/graph-simulation.js | 65 +++++---- ui/src/components/topology/topology-graph.js | 128 ++++++++++++++---- 4 files changed, 151 insertions(+), 60 deletions(-) diff --git a/ui/src/components/topology/graph-link.js b/ui/src/components/topology/graph-link.js index 69c93646..047c6996 100644 --- a/ui/src/components/topology/graph-link.js +++ b/ui/src/components/topology/graph-link.js @@ -106,7 +106,7 @@ const GraphLink = ({ const sourceAngle = getAngleFromPoints(source, target); const sourcePosition = getPosition(sourceAngle, positions, source); const targetAngle = getAngleFromPoints(target, source); - const targetPosition = getPosition(targetAngle, positions, target, true); + const targetPosition = getPosition(targetAngle, positions, target); //, true); const arrowAngle = getAngleFromPoints(sourcePosition, targetPosition); return ( diff --git a/ui/src/components/topology/graph-node.js b/ui/src/components/topology/graph-node.js index 30cd7d80..344a6a00 100644 --- a/ui/src/components/topology/graph-node.js +++ b/ui/src/components/topology/graph-node.js @@ -45,7 +45,8 @@ const HeartCircle = styled.circle` const GraphNode = ({ data, - size + size, + onDragStart }) => { const { @@ -79,9 +80,17 @@ const GraphNode = ({ }; // const titleBbBox = {x:100, y: 30 - halfHeight}; + const onStart = (evt) => { + evt.preventDefault(); + onDragStart(evt, data.id); + }; return ( - + d.id)) - .force('collide', d3.forceCollide(nodeRadius)) - .force('center', d3.forceCenter(width/2, height/2)) - .on('tick', onTick) - .on('end', onEnd); + return ({ + simulation: d3.forceSimulation(mappedNodes) + .force('link', d3.forceLink(mappedLinks).id(d => d.id)) + .force('collide', d3.forceCollide(nodeRadius)) + .force('center', d3.forceCenter(width/2, height/2)) + .on('tick', onTick) + .on('end', onEnd), + nodes: mappedNodes, + links: mappedLinks + }); }; // TODO we need to kill the previous simulation const updateSimulation = ( simulation, - nodes, - links, nextNodes, nextLinks, + simNodes, + simLinks, nodeSize, svgSize, onTick, onEnd ) => { - // want to copy all the existing nodes that we still need and freeze them - // want to copy all the existing links we still need - // if we have any new nodes / links, we should add them - // this is going to be messy!!! maybe not so much!!! :D <3 const mappedNodes = nextNodes.map((nextNode, index) => { - const node = nodes.reduce((acc, n, i) => - nextNode.id === n.id ? n : acc ? null : acc); - return node ? { - id: node.id, - fx: node.x, - fy: node.y, + const simNode = simNodes.reduce((acc, n, i) => { + return nextNode.id === n.id ? n : acc; + }, null); + + return simNode ? { + id: simNode.id, + // fx: simNode.x, + // fy: simNode.y, index: index } : { id: nextNode.id, @@ -77,11 +79,12 @@ const updateSimulation = ( }); const mappedLinks = nextLinks.map((nextLink, index) => { - const link = links.reduce((acc, l, i) => - nextLink.source === l.source && nextLink.target === l.target ? - l : acc ? null : acc); - return link ? { - ...link + const simLink = simLinks.reduce((acc, l, i) => { + return nextLink.source === l.source && nextLink.target === l.target ? + l : acc; + }, {}); + return simLink ? { + ...simLink } : { ...nextLink }; @@ -94,12 +97,16 @@ const updateSimulation = ( const nodeRadius = rectRadius(nodeSize); - return d3.forceSimulation(mappedNodes) - .force('link', d3.forceLink(mappedLinks).id(d => d.id)) - .force('collide', d3.forceCollide(nodeRadius)) - .force('center', d3.forceCenter(width/2, height/2)) - .on('tick', onTick) - .on('end', onEnd); + return ({ + simulation: d3.forceSimulation(mappedNodes) + .force('link', d3.forceLink(mappedLinks).id(d => d.id)) + .force('collide', d3.forceCollide(nodeRadius)) + .force('center', d3.forceCenter(width/2, height/2)) + .on('tick', onTick) + .on('end', onEnd), + nodes: mappedNodes, + links: mappedLinks + }); }; module.exports = { diff --git a/ui/src/components/topology/topology-graph.js b/ui/src/components/topology/topology-graph.js index 8387ccbe..6accc436 100644 --- a/ui/src/components/topology/topology-graph.js +++ b/ui/src/components/topology/topology-graph.js @@ -29,6 +29,12 @@ const svgSize = { height: 860 }; +let dragInfo = { + dragging: false, + nodeId: null, + position: null +}; + class TopologyGraph extends React.Component { componentWillMount() { @@ -38,15 +44,17 @@ class TopologyGraph extends React.Component { links } = this.props.data; - const simulation = createSimulation( + const simulationData = createSimulation( nodes, links, nodeSize, - svgSize, - () => this.forceUpdate(), - () => this.forceUpdate() + svgSize//, + //() => this.forceUpdate(), + //() => this.forceUpdate() ); + const simulation = simulationData.simulation; + const n = Math.ceil( Math.log( simulation.alphaMin()) / Math.log( @@ -55,9 +63,7 @@ class TopologyGraph extends React.Component { simulation.tick(); } - this.setState({ - simulation: simulation - }); + this.setState(simulationData); } componentWillReceiveProps(nextProps) { @@ -68,33 +74,34 @@ class TopologyGraph extends React.Component { // otherwise, redo them bitches = by what I mean to update the simulation // try freezing exisiting ones... then adding another + const { + nodes: simNodes, + links: simLinks + } = this.state; + const { nodes: nextNodes, links: nextLinks } = nextProps.data; - const { - nodes, - links - } = this.props.data; - // this is tmp for the compare above - if(nextNodes.length !== nodes.length || nextLinks.length !== links.length) { + if(nextNodes.length !== simNodes.length || + nextLinks.length !== simLinks.length) { const simulation = this.state.simulation; - const nextSimulation = updateSimulation( + const nextSimulationData = updateSimulation( simulation, - nodes, - links, nextNodes, nextLinks, + simNodes, + simLinks, nodeSize, svgSize, () => this.forceUpdate(), () => this.forceUpdate() ); - this.setState({ - simulation: nextSimulation - }); + + const nextSimulation = nextSimulationData.simulation; + // console.log('nextSimulationData.nodes = ', nextSimulationData.nodes); const n = Math.ceil( Math.log( @@ -103,6 +110,13 @@ class TopologyGraph extends React.Component { for (var i = 0; i < n; ++i) { nextSimulation.tick(); } + + /*this.state.simulation.nodes().forEach((node, index) => { + delete node.fx; + delete node.fy; + });*/ + + this.setState(nextSimulationData); } } @@ -113,27 +127,81 @@ class TopologyGraph extends React.Component { links } = this.props.data; - const simulationNodes = this.state.simulation.nodes(); + const simulationNodes = this.state.nodes; + + const simulationNode = (nodeId) => + simulationNodes.reduce((acc, simNode, index) => { + return simNode.id === nodeId ? simNode : acc; + }, {}); const nodesData = nodes.map((node, index) => ({ ...node, - ...simulationNodes.reduce((acc, simNode, index) => - simNode.id === node.id ? simNode : acc) + ...simulationNode(node.id) })); const linksData = links.map((link, index) => ({ - source: simulationNodes.reduce((acc, simNode, index) => - simNode.id === link.source ? simNode : acc), - target: simulationNodes.reduce((acc, simNode, index) => - simNode.id === link.target ? simNode : acc) + source: simulationNode(link.source), + target: simulationNode(link.target) })); + const onDragStart = (evt, nodeId) => { + // it's this node's position that we'll need to update + dragInfo.dragging = true; + dragInfo.nodeId = nodeId; + dragInfo.position = { + x: evt.clientX, + y: evt.clientY + }; + }; + + const onDragMove = (evt) => { + + if(dragInfo.dragging) { + + const offset = { + x: evt.clientX - dragInfo.position.x, + y: evt.clientY - dragInfo.position.y + }; + + const dragNodes = simulationNodes.map((simNode, index) => { + if(simNode.id === dragInfo.nodeId) { + return ({ + ...simNode, + x: simNode.x + offset.x, + y: simNode.y + offset.y, + }); + } + return ({ + ...simNode + }); + }); + + this.setState({ + nodes: dragNodes + }); + + dragInfo.position = { + x: evt.clientX, + y: evt.clientY + }; + } + }; + + const onDragEnd = (evt) => { + dragInfo = { + dragging: false, + nodeId: null, + position: null + }; + }; + const renderedNodes = nodesData.map((n, index) => ( )); @@ -147,7 +215,13 @@ class TopologyGraph extends React.Component { )); return ( - + {renderedNodes}