Skip to content
Snippets Groups Projects
Commit 3af06eb5 authored by Maximilian Giller's avatar Maximilian Giller :squid:
Browse files

Moved some variables to state and tried to implemented custom node and link...

Moved some variables to state and tried to implemented custom node and link rendering, but doesn't get called for some reason
parent e5dbcded
No related branches found
No related tags found
1 merge request!2Implemented editor in the react framework
Pipeline #56692 passed
import React from "react";
import { State } from "../state";
import * as Interactions from "../interactions";
import { Graph } from "../structures/graph/graph";
import { loadGraphJson } from "../../../datasets";
......@@ -9,19 +8,20 @@ import "./editor.css";
import ReactForceGraph2d from "react-force-graph-2d";
import { Node } from "../structures/graph/node";
import { HistoryNavigator } from "./historynavigator";
import { GraphElement } from "../structures/graph/graphelement";
type propTypes = any;
type stateTypes = {
graph: Graph;
visibleLabels: boolean;
selectedNode: Node;
keys: { [name: string]: boolean };
};
export class Editor extends React.PureComponent<propTypes, stateTypes> {
private defaultWarmupTicks = 100;
private warmupTicks = 100;
private keyStates: { [name: string]: boolean } = {};
private selectedNode: Node;
constructor(props: propTypes) {
super(props);
this.loadGraph = this.loadGraph.bind(this);
......@@ -32,10 +32,16 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
this.forceUpdate = this.forceUpdate.bind(this);
this.isHighlighted = this.isHighlighted.bind(this);
this.handleNodeCanvasObject = this.handleNodeCanvasObject.bind(this);
this.handleLinkCanvasObject = this.handleLinkCanvasObject.bind(this);
// Set as new state
this.state = {
graph: undefined,
visibleLabels: true,
selectedNode: undefined,
keys: {},
};
Interactions.initInteractions();
......@@ -72,9 +78,6 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
return false;
}
// .linkColor((link: any) => Editor.globalState.linkColor(link))
// .nodeColor((node: any) => Editor.globalState.nodeColor(node))
// .onNodeClick((node: any) => Editor.globalState.onNodeClick(node))
// .onNodeDragEnd((node: any, translate: any) =>
// Editor.globalState.onNodeDragEnd(node, translate)
// )
......@@ -91,18 +94,6 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
// this.extractPositions(event)
// )
// )
// .nodeCanvasObjectMode((node: any) =>
// Editor.globalState.nodeCanvasObjectMode(node)
// )
// .nodeCanvasObject((node: any, ctx: any, globalScale: any) =>
// Editor.globalState.nodeCanvasObject(node, ctx, globalScale)
// )
// .linkCanvasObjectMode((link: any) =>
// Editor.globalState.linkCanvasObjectMode(link)
// )
// .linkCanvasObject((link: any, ctx: any, globalScale: any) =>
// Editor.globalState.linkCanvasObject(link, ctx, globalScale)
// )
// .onLinkClick((link: any) => Editor.globalState.onLinkClick(link));
// Set as new state
......@@ -122,22 +113,62 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
private handleKeyDown(event: KeyboardEvent) {
const key: string = event.key;
this.keyStates[key] = true;
const keys = this.state.keys;
keys[key] = true;
this.setState({
keys: keys,
});
}
private handleKeyUp(event: KeyboardEvent) {
const key: string = event.key;
this.keyStates[key] = false;
const keys = this.state.keys;
keys[key] = false;
this.setState({
keys: keys,
});
}
/**
* Propagates the changed state of the graph.
*/
private onHistoryChange() {
this.selectedNode = undefined;
this.setState({
selectedNode: undefined,
});
this.forceUpdate();
}
/**
* Should a given element be highlighted in rendering or not.
* @param element Element that should, or should not be highlighted.
* @returns True, if element should be highlighted.
*/
private isHighlighted(element: GraphElement): boolean {
if (this.state.selectedNode == undefined) {
// Default to false if nothing selected.
return false;
}
if (element.node) {
// Is node
return element.equals(this.state.selectedNode);
} else if (element.link) {
// Is link
// Is it one of the adjacent links?
const found = this.state.selectedNode.links.find(
this.state.selectedNode.equals
);
return found !== undefined;
} else {
return false;
}
}
/**
* Calculates the corresponding coordinates for a click event for easier further processing.
* @param event The corresponding click event.
......@@ -157,14 +188,100 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
}
private handleNodeClick(node: Node) {
if (this.keyStates["Control"]) {
if (this.state.keys["Control"]) {
node.delete();
} else {
this.selectedNode = node;
this.setState({
selectedNode: node,
});
}
this.forceUpdate();
}
private handleNodeCanvasObject(node: any, ctx: any, globalScale: any) {
// add ring just for highlighted nodes
if (this.isHighlighted(node)) {
ctx.beginPath();
ctx.arc(node.x, node.y, 4 * 0.6, 0, 2 * Math.PI, false);
ctx.fillStyle = "red";
ctx.fill();
}
// Draw image
const imageSize = 12;
if (node.icon !== undefined) {
const img = new Image();
img.src = node.icon.link;
ctx.drawImage(
img,
node.x - imageSize / 2,
node.y - imageSize / 2,
imageSize,
imageSize
);
}
// Draw label
if (this.state.visibleLabels) {
const label = node.label;
const fontSize = 11 / globalScale;
ctx.font = `${fontSize}px Sans-Serif`;
const textWidth = ctx.measureText(label).width;
const bckgDimensions = [textWidth, fontSize].map(
(n) => n + fontSize * 0.2
); // some padding
const nodeHeightOffset = imageSize / 3 + bckgDimensions[1];
ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
ctx.fillRect(
node.x - bckgDimensions[0] / 2,
node.y - bckgDimensions[1] / 2 + nodeHeightOffset,
...bckgDimensions
);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "white";
ctx.fillText(label, node.x, node.y + nodeHeightOffset);
}
// TODO: Render label as always visible
}
private handleLinkCanvasObject(link: any, ctx: any): any {
// Links already initialized?
if (link.source.x === undefined) {
return undefined;
}
// Draw gradient link
const gradient = ctx.createLinearGradient(
link.source.x,
link.source.y,
link.target.x,
link.target.y
);
// Have reversed colors
// Color at source node referencing the target node and vice versa
gradient.addColorStop("0", link.target.type.color);
gradient.addColorStop("1", link.source.type.color);
ctx.beginPath();
ctx.moveTo(link.source.x, link.source.y);
ctx.lineTo(link.target.x, link.target.y);
ctx.strokeStyle = gradient;
ctx.stroke();
// Only render strokes on last link
// var lastLink = graph.data[Graph.GRAPH_LINKS][graph.data[Graph.GRAPH_LINKS].length - 1];
// if (link === lastLink) {
// ctx.stroke();
// }
return undefined;
}
private handleEngineStop() {
// Only do something on first stop for each graph
if (this.warmupTicks <= 0) {
......@@ -192,7 +309,7 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
/>
<hr />
<NodeDetails
selectedNode={this.selectedNode}
selectedNode={this.state.selectedNode}
allTypes={
this.state.graph ? this.state.graph.types : []
}
......@@ -207,6 +324,10 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
cooldownTicks={0}
warmupTicks={this.warmupTicks}
onEngineStop={this.handleEngineStop}
nodeCanvasObject={this.handleNodeCanvasObject}
nodeCanvasObjectMode={"after"}
linkCanvasObject={this.handleLinkCanvasObject}
linkCanvasObjectMode={"replace"}
/>
) : undefined}
</div>
......
import Tool from "./tools/tool";
import UndoTool from "./tools/undotool";
import RedoTool from "./tools/redotool";
import SelectTool from "./tools/selecttool";
import DeleteTool from "./tools/deletetool";
import AddNodeTool from "./tools/addnodetool";
import ConnectTool from "./tools/connecttool";
import SettingsTool from "./tools/settingstool";
import SaveTool from "./tools/savetool";
import Toolbar from "./toolbar";
import { DRAG_THRESHOLD_2D } from "../../config";
import { GraphElement } from "./structures/graph/graphelement";
import { Node } from "./structures/graph/node";
export const TOOLS = {
undo: new UndoTool("undo"),
redo: new RedoTool("redo"),
select: new SelectTool("select"),
delete: new DeleteTool("delete"),
addnode: new AddNodeTool("addnode"),
connect: new ConnectTool("connect"),
settings: new SettingsTool("settings"),
save: new SaveTool("save"),
};
export const CONTEXT = {
node: "node",
link: "link",
mixed: "mixed",
nothing: "nothing",
};
export class State extends Tool {
display: Toolbar;
tool: Tool;
previousTool: Tool;
selectedItem: GraphElement;
selectedItems: Set<GraphElement>;
itemsContext: any;
labelVisible: boolean;
keyStates: any;
constructor() {
super("State");
this.display = new Toolbar(TOOLS);
this.tool = undefined;
this.setTool(TOOLS.select);
// Shared variables
this.selectedItem = undefined;
this.selectedItems = new Set();
this.itemsContext = CONTEXT.nothing;
this.labelVisible = true;
this.keyStates = {};
}
setTool(tool: Tool) {
if (this.tool === tool) {
return;
}
if (this.tool !== undefined) {
this.tool.deactivateTool(tool);
}
this.previousTool = this.tool;
this.tool = tool;
this.display.setSelectedTool(tool);
if (this.tool !== undefined) {
this.tool.activateTool();
}
}
setSelectedItem(item: GraphElement) {
this.selectedItem = item;
}
addSelectedItem(item: GraphElement) {
this.selectedItems.add(item);
}
addSelectedItems(items: GraphElement) {
Object.values(items).forEach((item) => {
this.selectedItems.add(item);
});
}
removeSelectedItem(item: GraphElement) {
this.selectedItems.delete(item);
}
clearSelectedItems() {
this.selectedItems.clear();
this.itemsContext = CONTEXT.nothing;
}
onNodeClick(node: GraphElement) {
this.tool.onNodeClick(node);
}
onNodeDragEnd(node: any, translate: any) {
// Handle as click event, if drag distance under a certain threshold
const distanceDragged = Math.sqrt(
Math.pow(translate.x, 2) + Math.pow(translate.y, 2)
);
if (distanceDragged < DRAG_THRESHOLD_2D) {
this.onNodeClick(node);
return;
} else {
this.tool.onNodeDragEnd(node, translate);
}
}
onLinkClick(link: any) {
this.tool.onLinkClick(link);
}
linkColor(link: any) {
return "red";
}
nodeColor(node: any) {
return "black";
}
onKeyDown(key: any) {
const id = this.getKeyId(key);
const previous = this.keyStates[id];
this.keyStates[id] = true;
if (previous !== true) {
this.tool.onKeyDown(key);
}
}
onKeyUp(key: any) {
const id = this.getKeyId(key);
const previous = this.keyStates[id];
this.keyStates[id] = false;
if (previous !== false) {
this.tool.onKeyUp(key);
}
}
getKeyId(key: any) {
return key.keyCode;
}
nodeCanvasObject(node: any, ctx: any, globalScale: any) {
const toolValue = this.tool.nodeCanvasObject(node, ctx);
if (toolValue !== undefined) {
return toolValue;
}
// TODO: Clean up function
// add ring just for highlighted nodes
if (this.selectedItem === node || this.selectedItems.has(node)) {
ctx.beginPath();
ctx.arc(node.x, node.y, 4 * 0.6, 0, 2 * Math.PI, false);
ctx.fillStyle = this.selectedItem === node ? "red" : "white";
ctx.fill();
}
// Draw image
const imageSize = 12;
if (node.icon !== undefined) {
const img = new Image();
img.src = node.icon.link;
ctx.drawImage(
img,
node.x - imageSize / 2,
node.y - imageSize / 2,
imageSize,
imageSize
);
}
// Draw label
if (this.labelVisible) {
const label = node.label;
const fontSize = 11 / globalScale;
ctx.font = `${fontSize}px Sans-Serif`;
const textWidth = ctx.measureText(label).width;
const bckgDimensions = [textWidth, fontSize].map(
(n) => n + fontSize * 0.2
); // some padding
const nodeHeightOffset = imageSize / 3 + bckgDimensions[1];
ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
ctx.fillRect(
node.x - bckgDimensions[0] / 2,
node.y - bckgDimensions[1] / 2 + nodeHeightOffset,
...bckgDimensions
);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "white";
ctx.fillText(label, node.x, node.y + nodeHeightOffset);
}
// TODO: Render label as always visible
}
nodePointerAreaPaint(node: any, color: any, ctx: any) {
const toolValue = this.tool.nodePointerAreaPaint(node, color, ctx);
if (toolValue !== undefined) {
return toolValue;
}
const imageSize = 12;
ctx.fillStyle = color;
ctx.fillRect(
node.x - imageSize / 2,
node.y - imageSize / 2,
imageSize,
imageSize
); // draw square as pointer trap
}
nodeCanvasObjectMode(node: any) {
const toolValue = this.tool.nodeCanvasObjectMode(node);
if (toolValue !== undefined) {
return toolValue;
}
return "after";
}
linkCanvasObjectMode(link: any) {
const toolValue = this.tool.linkCanvasObjectMode(link);
if (toolValue !== undefined) {
return toolValue;
}
return "replace";
}
linkCanvasObject(link: any, ctx: any, globalScale: any) {
const toolValue = this.tool.linkCanvasObject(link, ctx);
if (toolValue !== undefined) {
return toolValue;
}
// Links already initialized?
if (link.source.x === undefined) {
return undefined;
}
// Draw gradient link
const gradient = ctx.createLinearGradient(
link.source.x,
link.source.y,
link.target.x,
link.target.y
);
// Have reversed colors
// Color at source node referencing the target node and vice versa
gradient.addColorStop("0", link.target.type.color);
gradient.addColorStop("1", link.source.type.color);
ctx.beginPath();
ctx.moveTo(link.source.x, link.source.y);
ctx.lineTo(link.target.x, link.target.y);
ctx.strokeStyle = gradient;
ctx.stroke();
// Only render strokes on last link
// var lastLink = graph.data[Graph.GRAPH_LINKS][graph.data[Graph.GRAPH_LINKS].length - 1];
// if (link === lastLink) {
// ctx.stroke();
// }
return undefined;
}
linkWidth(link: any) {
const toolValue = this.tool.linkWidth(link);
if (toolValue !== undefined) {
return toolValue;
}
return this.isLinkHighlighted(link) ? 2 : 1;
}
linkDirectionalParticles() {
const toolValue = this.tool.linkDirectionalParticles();
if (toolValue !== undefined) {
return toolValue;
}
return 4;
}
linkDirectionalParticleWidth(link: any) {
const toolValue = this.tool.linkDirectionalParticleWidth(link);
if (toolValue !== undefined) {
return toolValue;
}
return this.isLinkHighlighted(link) ? 4 : 0;
}
onBackgroundClick(event: any, positions: any) {
this.tool.onBackgroundClick(event, positions);
}
redraw() {
this.display.setSelectedTool(this.tool);
}
isLinkHighlighted(link: any) {
return false; // Code after that is causing issues
if (this.selectedItem === link) {
return true;
}
if (this.selectedItem.node) {
return (this.selectedItem as Node).links.includes(link);
}
return false;
}
setLabelVisibility(visibility: boolean) {
this.labelVisible = visibility;
}
}
// export const TOOLS = {
// undo: new UndoTool("undo"),
// redo: new RedoTool("redo"),
// select: new SelectTool("select"),
// delete: new DeleteTool("delete"),
// addnode: new AddNodeTool("addnode"),
// connect: new ConnectTool("connect"),
// settings: new SettingsTool("settings"),
// save: new SaveTool("save"),
// };
// export class State extends Tool {
// onNodeDragEnd(node: any, translate: any) {
// // Handle as click event, if drag distance under a certain threshold
// const distanceDragged = Math.sqrt(
// Math.pow(translate.x, 2) + Math.pow(translate.y, 2)
// );
// if (distanceDragged < DRAG_THRESHOLD_2D) {
// this.onNodeClick(node);
// return;
// } else {
// this.tool.onNodeDragEnd(node, translate);
// }
// }
// onKeyDown(key: any) {
// const previous = this.keyStates[id];
// if (previous !== true) {
// this.tool.onKeyDown(key);
// }
// }
// onKeyUp(key: any) {
// const previous = this.keyStates[id];
// if (previous !== false) {
// this.tool.onKeyUp(key);
// }
// }
// nodePointerAreaPaint(node: any, color: any, ctx: any) {
// const toolValue = this.tool.nodePointerAreaPaint(node, color, ctx);
// if (toolValue !== undefined) {
// return toolValue;
// }
// const imageSize = 12;
// ctx.fillStyle = color;
// ctx.fillRect(
// node.x - imageSize / 2,
// node.y - imageSize / 2,
// imageSize,
// imageSize
// ); // draw square as pointer trap
// }
// linkWidth(link: any) {
// const toolValue = this.tool.linkWidth(link);
// if (toolValue !== undefined) {
// return toolValue;
// }
// return this.isLinkHighlighted(link) ? 2 : 1;
// }
// linkDirectionalParticles() {
// const toolValue = this.tool.linkDirectionalParticles();
// if (toolValue !== undefined) {
// return toolValue;
// }
// return 4;
// }
// linkDirectionalParticleWidth(link: any) {
// const toolValue = this.tool.linkDirectionalParticleWidth(link);
// if (toolValue !== undefined) {
// return toolValue;
// }
// return this.isLinkHighlighted(link) ? 4 : 0;
// }
// onBackgroundClick(event: any, positions: any) {
// this.tool.onBackgroundClick(event, positions);
// }
// }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment