import React from 'react'; import { Renderer } from 'core/renderers'; import { classes, distance } from 'common/util'; import styles from './GraphRenderer.module.scss'; class GraphRenderer extends Renderer { constructor(props) { super(props); this.elementRef = React.createRef(); this.selectedNode = null; this.togglePan(true); this.toggleZoom(true); } handleMouseDown(e) { super.handleMouseDown(e); const coords = this.computeCoords(e); const { nodes, dimensions } = this.props.data; const { nodeRadius } = dimensions; this.selectedNode = nodes.find(node => distance(coords, node) <= nodeRadius); } handleMouseMove(e) { if (this.selectedNode) { const { x, y } = this.computeCoords(e); const node = this.props.data.findNode(this.selectedNode.id); node.x = x; node.y = y; this.refresh(); } else { super.handleMouseMove(e); } } computeCoords(e) { const svg = this.elementRef.current; const s = svg.createSVGPoint(); s.x = e.clientX; s.y = e.clientY; const { x, y } = s.matrixTransform(svg.getScreenCTM().inverse()); return { x, y }; } renderData() { const { nodes, edges, isDirected, isWeighted, dimensions } = this.props.data; const { baseWidth, baseHeight, nodeRadius, arrowGap, nodeWeightGap, edgeWeightGap } = dimensions; const viewBox = [ (this.centerX - baseWidth / 2) * this.zoom, (this.centerY - baseHeight / 2) * this.zoom, baseWidth * this.zoom, baseHeight * this.zoom, ]; return ( { edges.sort((a, b) => a.visitedCount - b.visitedCount).map(edge => { const { source, target, weight, visitedCount, selectedCount } = edge; const sourceNode = this.props.data.findNode(source); const targetNode = this.props.data.findNode(target); if (!sourceNode || !targetNode) return undefined; const { x: sx, y: sy } = sourceNode; let { x: ex, y: ey } = targetNode; const mx = (sx + ex) / 2; const my = (sy + ey) / 2; const dx = ex - sx; const dy = ey - sy; const degree = Math.atan2(dy, dx) / Math.PI * 180; if (isDirected) { const length = Math.sqrt(dx * dx + dy * dy); if (length !== 0) { ex = sx + dx / length * (length - nodeRadius - arrowGap); ey = sy + dy / length * (length - nodeRadius - arrowGap); } } return ( { isWeighted && {this.toString(weight)} } ); }) } { nodes.map(node => { const { id, x, y, weight, visitedCount, selectedCount } = node; return ( {id} { isWeighted && {this.toString(weight)} } ); }) } ); } } export default GraphRenderer;