From fd98b3c43e3ec746ee8c4c6e877ddfc98c73f0aa Mon Sep 17 00:00:00 2001 From: Maximilian Giller <m.giller@tu-bs.de> Date: Thu, 30 Jun 2022 16:26:03 +0200 Subject: [PATCH] Drag and drop connect nodes mechanic --- src/editor/js/components/editor.tsx | 38 +++++++++++++++++- src/editor/js/structures/graph/graph.ts | 51 +++++++++++++++++++++++++ src/editor/js/structures/graph/node.ts | 29 ++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/editor/js/components/editor.tsx b/src/editor/js/components/editor.tsx index bbc0cce..cf7c59b 100644 --- a/src/editor/js/components/editor.tsx +++ b/src/editor/js/components/editor.tsx @@ -21,8 +21,14 @@ type clickPosition = { graph: { x: number; y: number }; window: { x: number; y: number }; }; +type positionTranslate = { + x: number; + y: number; + z: number; +}; export class Editor extends React.PureComponent<propTypes, stateTypes> { + private maxDistanceToConnect = 15; private defaultWarmupTicks = 100; private warmupTicks = 100; private renderer: any; @@ -31,6 +37,7 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> { super(props); this.loadGraph = this.loadGraph.bind(this); this.loadSpace = this.loadSpace.bind(this); + this.extractPositions = this.extractPositions.bind(this); this.handleNodeClick = this.handleNodeClick.bind(this); this.onHistoryChange = this.onHistoryChange.bind(this); this.handleEngineStop = this.handleEngineStop.bind(this); @@ -41,7 +48,8 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> { this.handleNodeCanvasObject = this.handleNodeCanvasObject.bind(this); this.handleLinkCanvasObject = this.handleLinkCanvasObject.bind(this); this.handleBackgroundClick = this.handleBackgroundClick.bind(this); - this.extractPositions = this.extractPositions.bind(this); + this.handleNodeDrag = this.handleNodeDrag.bind(this); + this.handleNodeDragEnd = this.handleNodeDragEnd.bind(this); this.renderer = React.createRef(); @@ -308,6 +316,32 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> { return undefined; } + private handleNodeDrag(node: Node, translate: positionTranslate) { + this.setState({ + selectedNode: node, + }); + + const closest = this.state.graph.getClosestOtherNode(node); + + // Is close enough for new link? + if (closest.distance > this.maxDistanceToConnect) { + return; + } + + // Does link already exist? + if (node.neighbors.includes(closest.node)) { + return; + } + + // Add link + node.connect(closest.node); + this.forceUpdate(); + } + + private handleNodeDragEnd(node: Node, translate: positionTranslate) { + return; + } + private handleEngineStop() { // Only do something on first stop for each graph if (this.warmupTicks <= 0) { @@ -355,6 +389,8 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> { nodeCanvasObjectMode={"after"} linkCanvasObject={this.handleLinkCanvasObject} linkCanvasObjectMode={"replace"} + onNodeDrag={this.handleNodeDrag} + onNodeDragEnd={this.handleNodeDragEnd} onBackgroundClick={(event) => this.handleBackgroundClick( event, diff --git a/src/editor/js/structures/graph/graph.ts b/src/editor/js/structures/graph/graph.ts index 0a562e0..b8dc963 100644 --- a/src/editor/js/structures/graph/graph.ts +++ b/src/editor/js/structures/graph/graph.ts @@ -287,6 +287,57 @@ export class Graph extends ManagedData { return true; } + /** + * Calculates the pythagoras distance. + * @param nodeA One node. + * @param nodeB The other node. + * @returns Distance between both nodes. + */ + private nodeDistance(nodeA: Node, nodeB: Node): number { + const a = nodeA as any; + const b = nodeB as any; + + const xDistance = Math.abs(a.x - b.x); + const yDistance = Math.abs(a.y - b.y); + + return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2)); + } + + /** + * Goes over all nodes and finds the closest node based on distance, that is not the given reference node. + * @param referenceNode Reference node to get closest other node to. + * @returns Closest node and distance. Undefined, if no closest node can be found. + */ + public getClosestOtherNode(referenceNode: Node): { + node: Node; + distance: number; + } { + if (referenceNode == undefined || this.nodes.length < 2) { + return undefined; + } + + // Iterate over all nodes, keep the one with the shortest distance + let closestDistance: number = undefined; + let closestNode: Node = undefined; + this.nodes.forEach((node) => { + if (node.equals(referenceNode)) { + return; // Don't compare to itself + } + + const currentDistance = this.nodeDistance(node, referenceNode); + + if ( + closestDistance == undefined || + closestDistance > currentDistance + ) { + closestDistance = currentDistance; + closestNode = node; + } + }); + + return { node: closestNode, distance: closestDistance }; + } + public static parse(raw: any): Graph { return new Graph(this.parseData(raw)); } diff --git a/src/editor/js/structures/graph/node.ts b/src/editor/js/structures/graph/node.ts index fcae0f0..426feec 100644 --- a/src/editor/js/structures/graph/node.ts +++ b/src/editor/js/structures/graph/node.ts @@ -85,6 +85,35 @@ export class Node extends GraphElement { return links; } + /** + * Calculates a list of all connected nodes to the current node. + * @returns Array containing all connected nodes. + */ + public get neighbors(): Node[] { + const nodes: Node[] = []; + + this.links.forEach((link) => { + // Find "other" node + let otherNode = link.source; + if (this.equals(otherNode)) { + otherNode = link.target; + } + + // Still undefined? + if (otherNode == undefined) { + // Link apparently not properly set up + return; + } + + // Add to list if doesn't exist + if (!nodes.includes(otherNode)) { + nodes.push(otherNode); + } + }); + + return nodes; + } + /** * Connects a given node to itself. Only works if they are in the same graph. * @param node Other node to connect. -- GitLab