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

Implemented proper parsing for graph data

parent b167d87f
No related branches found
No related tags found
1 merge request!2Implemented editor in the react framework
Pipeline #56384 failed
import { State } from "./state"; import { State } from "./state";
import * as Graph from "./graph";
import { loadGraphJson } from "../../datasets"; import { loadGraphJson } from "../../datasets";
import ForceGraph from "force-graph"; import ForceGraph from "force-graph";
import * as Interactions from "./interactions"; import * as Interactions from "./interactions";
import { setSpace, SPACE } from "../../config"; import { setSpace, SPACE } from "../../config";
import { Graph } from "./structures/graph/graph";
export var state = undefined; export let state: any = undefined;
export var graph = undefined; export let graph: Graph = undefined;
export var renderer; export let renderer: any;
window.onload = function () { window.onload = function () {
// Only execute, if corresponding dom is present // Only execute, if corresponding dom is present
...@@ -23,7 +23,7 @@ window.onload = function () { ...@@ -23,7 +23,7 @@ window.onload = function () {
loadSpace(SPACE); loadSpace(SPACE);
}; };
export function loadSpace(spaceId) { export function loadSpace(spaceId: string) {
if (state !== undefined && spaceId === SPACE) { if (state !== undefined && spaceId === SPACE) {
return; return;
} }
...@@ -31,14 +31,14 @@ export function loadSpace(spaceId) { ...@@ -31,14 +31,14 @@ export function loadSpace(spaceId) {
return loadGraphJson(SPACE).then((graphConfig) => { return loadGraphJson(SPACE).then((graphConfig) => {
state = new State(); state = new State();
graph = new Graph.Graph(graphConfig); graph = Graph.parse(graphConfig);
load(); load();
graph.restartSimulation(); // graph.restartSimulation();
}); });
} }
function extractPositions(event) { function extractPositions(event: any) {
return { return {
graph: renderer.screen2GraphCoords(event.layerX, event.layerY), graph: renderer.screen2GraphCoords(event.layerX, event.layerY),
window: { x: event.clientX, y: event.clientY }, window: { x: event.clientX, y: event.clientY },
...@@ -53,7 +53,7 @@ function load() { ...@@ -53,7 +53,7 @@ function load() {
.height(600) .height(600)
.width(width) .width(width)
.graphData(graph.data) .graphData(graph.data)
.nodeLabel(Graph.NODE_LABEL) .nodeLabel("label")
.linkColor((link) => state.linkColor(link)) .linkColor((link) => state.linkColor(link))
.nodeColor((node) => state.nodeColor(node)) .nodeColor((node) => state.nodeColor(node))
.onNodeClick((node) => state.onNodeClick(node)) .onNodeClick((node) => state.onNodeClick(node))
......
...@@ -24,9 +24,23 @@ export class Graph extends ManagedData { ...@@ -24,9 +24,23 @@ export class Graph extends ManagedData {
super(data); super(data);
this.onChangeCallbacks = []; this.onChangeCallbacks = [];
this.connectElementsToGraph();
this.prepareIds(data); this.prepareIds(data);
} }
/**
* Sets the correct graph object for all the graph elements in data.
*/
connectElementsToGraph() {
this.data.nodes.forEach((n) => (n.graph = this));
this.data.links.forEach((l) => {
l.graph = this;
l.source = this.getNode(l.sourceId);
l.target = this.getNode(l.targetId);
});
this.data.types.forEach((t) => (t.graph = this));
}
/** /**
* Intuitive getter for links. * Intuitive getter for links.
* @returns All links associated with the graph. * @returns All links associated with the graph.
...@@ -105,7 +119,11 @@ export class Graph extends ManagedData { ...@@ -105,7 +119,11 @@ export class Graph extends ManagedData {
} }
protected storableData(data: GraphData): any { protected storableData(data: GraphData): any {
let clean: GraphData; const clean: GraphData = {
nodes: [],
links: [],
types: []
};
clean.links = data.links.map((link) => link.getCleanInstance()); clean.links = data.links.map((link) => link.getCleanInstance());
clean.nodes = data.nodes.map((node) => node.getCleanInstance()); clean.nodes = data.nodes.map((node) => node.getCleanInstance());
...@@ -186,6 +204,10 @@ export class Graph extends ManagedData { ...@@ -186,6 +204,10 @@ export class Graph extends ManagedData {
return true; return true;
} }
getNode(id: number): Node {
return this.nodes.find((n) => n.id === id);
}
/** /**
* Adds a pre-created link to the graph. * Adds a pre-created link to the graph.
* @param link New link object. * @param link New link object.
...@@ -227,4 +249,52 @@ export class Graph extends ManagedData { ...@@ -227,4 +249,52 @@ export class Graph extends ManagedData {
return true; return true;
} }
public static parse(raw: any): Graph {
const data: GraphData = {
nodes: [],
links: [],
types: []
};
// Parse nodes
if (raw.nodes === undefined) {
throw new Error(
"Invalid graph data format. Could not find any nodes."
);
}
raw.nodes.forEach((rawNode: any) => {
data.nodes.push(Node.parse(rawNode));
});
// Parse links
if (raw.links === undefined) {
throw new Error(
"Invalid graph data format. Could not find any links."
);
}
raw.links.forEach((rawLink: any) => {
data.links.push(Link.parse(rawLink));
// No need to replace node ids with proper node objects, since that should be done in the graph itself. Only have to prepare valid GraphData
});
// Collect all node types
// TODO: Remove, when types are directly parsed and not just implicit
data.nodes.forEach((node) => {
const sharedType: NodeType = data.types.find(
// TODO: Use id instead, but not defined at the moment
(type) => type.name === node.type.name
);
if (sharedType !== undefined) {
node.type = sharedType; // Assign it the stored type, to make sure that it has the same reference as every other node to this type
return;
}
// Doesn't exist in list yet, so add
data.types.push(node.type);
});
return new Graph(data);
}
} }
...@@ -5,9 +5,9 @@ export class GraphElement extends SerializableItem { ...@@ -5,9 +5,9 @@ export class GraphElement extends SerializableItem {
protected isNode: boolean; protected isNode: boolean;
protected isLink: boolean; protected isLink: boolean;
protected graph: Graph; public graph: Graph;
constructor(graph: Graph) { constructor(graph: Graph = undefined) {
super(); super();
this.graph = graph; this.graph = graph;
this.isNode = false; this.isNode = false;
......
import {GraphElement} from "./graphelement"; import { GraphElement } from "./graphelement";
import {Graph} from "./graph"; import { Graph } from "./graph";
import {Node} from "./node"; import { Node } from "./node";
import {GLOBAL_PARAMS} from "../helper/serializableitem"; import { GLOBAL_PARAMS } from "../helper/serializableitem";
const LINK_PARAMS = ["source", "target", ...GLOBAL_PARAMS]; const LINK_PARAMS = ["source", "target", ...GLOBAL_PARAMS];
const LINK_SIM_PARAMS = ["index"]; const LINK_SIM_PARAMS = ["index"];
...@@ -10,17 +10,66 @@ export class Link extends GraphElement { ...@@ -10,17 +10,66 @@ export class Link extends GraphElement {
public source: Node; public source: Node;
public target: Node; public target: Node;
constructor(graph: Graph) { private _sourceId: number;
private _targetId: number;
constructor(graph: Graph = undefined) {
super(graph); super(graph);
this.isLink = true; this.isLink = true;
} }
/**
* Id of the source node.
* @returns Source id.
*/
public get sourceId(): number {
if (this.source == undefined) {
return this._sourceId;
}
return this.source.id;
}
/**
* Removes stored node object and just saves the id instead.
* @param value New source id value.
*/
public set sourceId(value: number) {
this._sourceId = value;
this.source = undefined;
}
/**
* Id of the target node.
* @returns Target id.
*/
public get targetId(): number {
if (this.target == undefined) {
return this._targetId;
}
return this.target.id;
}
/**
* Removes stored node object and just saves the id instead.
* @param value New target id value.
*/
public set targetId(value: number) {
this._targetId = value;
this.target = undefined;
}
public delete() { public delete() {
return this.graph.deleteLink(this); return this.graph.deleteLink(this);
} }
public add(graph: Graph = this.graph) { public add(graph: Graph = this.graph) {
this.graph = graph; this.graph = graph;
if (this.graph == undefined) {
return false;
}
return this.graph.addLink(this); return this.graph.addLink(this);
} }
...@@ -40,7 +89,16 @@ export class Link extends GraphElement { ...@@ -40,7 +89,16 @@ export class Link extends GraphElement {
public getCleanInstance(): any { public getCleanInstance(): any {
return { return {
...this.serialize(), ...this.serialize(),
...this.serializeProperties(LINK_SIM_PARAMS) ...this.serializeProperties(LINK_SIM_PARAMS),
}; };
} }
}
\ No newline at end of file public static parse(raw: any): Link {
const link: Link = new Link();
link.sourceId = Number(raw.source);
link.targetId = Number(raw.target);
return link;
}
}
import {Graph} from "./graph"; import { Graph } from "./graph";
import {GraphElement} from "./graphelement" import { GraphElement } from "./graphelement";
import {NodeType} from "./nodetype"; import { NodeType } from "./nodetype";
import {SerializedURL} from "../helper/serializedurl"; import { SerializedURL } from "../helper/serializedurl";
import {Link} from "./link"; import { Link } from "./link";
import {GLOBAL_PARAMS} from "../helper/serializableitem"; import { GLOBAL_PARAMS } from "../helper/serializableitem";
const NODE_PARAMS = [ const NODE_PARAMS = [
"label", "label",
...@@ -13,7 +13,7 @@ const NODE_PARAMS = [ ...@@ -13,7 +13,7 @@ const NODE_PARAMS = [
"video", "video",
"type", "type",
"banner", "banner",
...GLOBAL_PARAMS ...GLOBAL_PARAMS,
]; ];
const NODE_SIM_PARAMS = ["index", "x", "y", "vx", "vy", "fx", "fy"]; // Based on https://github.com/d3/d3-force#simulation_nodes const NODE_SIM_PARAMS = ["index", "x", "y", "vx", "vy", "fx", "fy"]; // Based on https://github.com/d3/d3-force#simulation_nodes
...@@ -26,7 +26,7 @@ export class Node extends GraphElement { ...@@ -26,7 +26,7 @@ export class Node extends GraphElement {
public video: SerializedURL; public video: SerializedURL;
public references: SerializedURL[]; public references: SerializedURL[];
constructor(graph: Graph) { constructor(graph: Graph = undefined) {
super(graph); super(graph);
this.isNode = true; this.isNode = true;
} }
...@@ -37,6 +37,10 @@ export class Node extends GraphElement { ...@@ -37,6 +37,10 @@ export class Node extends GraphElement {
public add(graph: Graph = this.graph) { public add(graph: Graph = this.graph) {
this.graph = graph; this.graph = graph;
if (this.graph == undefined) {
return false;
}
return this.graph.addNode(this); return this.graph.addNode(this);
} }
...@@ -83,7 +87,18 @@ export class Node extends GraphElement { ...@@ -83,7 +87,18 @@ export class Node extends GraphElement {
public getCleanInstance(): any { public getCleanInstance(): any {
return { return {
...this.serialize(), ...this.serialize(),
...this.serializeProperties(NODE_SIM_PARAMS) ...this.serializeProperties(NODE_SIM_PARAMS),
}; };
} }
}
\ No newline at end of file public static parse(raw: any): Node {
const node: Node = new Node();
node.id = raw.id;
node.label = raw.name;
node.description = raw.description;
node.type = NodeType.parse(raw.type);
return node;
}
}
import {GLOBAL_PARAMS, SerializableItem} from "../helper/serializableitem"; import { GLOBAL_PARAMS, SerializableItem } from "../helper/serializableitem";
import { Graph } from "./graph"; import { Graph } from "./graph";
import { GraphElement } from "./graphelement"; import { GraphElement } from "./graphelement";
...@@ -14,15 +14,24 @@ export class NodeType extends GraphElement { ...@@ -14,15 +14,24 @@ export class NodeType extends GraphElement {
public delete(): boolean { public delete(): boolean {
// TODO: Implement // TODO: Implement
throw new Error("Function \"delete()\" has not been implemented."); throw new Error('Function "delete()" has not been implemented.');
} }
public add(graph: Graph = this.graph): boolean { public add(graph: Graph = this.graph): boolean {
// TODO: Implement // TODO: Implement
throw new Error("Function \"add(graph)\" has not been implemented."); throw new Error('Function "add(graph)" has not been implemented.');
} }
public getCleanInstance(): any { public getCleanInstance(): any {
return this.serialize(); return this.serialize();
} }
}
\ No newline at end of file public static parse(raw: any): NodeType {
const type: NodeType = new NodeType();
type.name = raw;
type.color = "#ff0000"
return type;
}
}
...@@ -19,8 +19,9 @@ export class SerializableItem { ...@@ -19,8 +19,9 @@ export class SerializableItem {
/** /**
* Creates the current object based on raw, serialized data. * Creates the current object based on raw, serialized data.
* @param raw The serialized data. * @param raw The serialized data.
* @returns Parsed data in final form. Could be the finalisd object.
*/ */
public parse(raw: any) { public static parse(raw: any): any {
throw new Error("Method 'parse()' must be implemented."); throw new Error("Method 'parse()' must be implemented.");
} }
......
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