From f8c505efff3bca2db499ced7344aff30c712c244 Mon Sep 17 00:00:00 2001
From: Matthias Konitzny <konitzny@ibr.cs.tu-bs.de>
Date: Fri, 9 Sep 2022 09:41:42 +0200
Subject: [PATCH] Simplified node, link and nodetype creation

---
 src/common/graph.ts    | 155 ++++++++++++++++-------------------------
 src/common/link.ts     |   4 +-
 src/common/node.ts     |  13 +---
 src/common/nodetype.ts |   8 +++
 4 files changed, 74 insertions(+), 106 deletions(-)

diff --git a/src/common/graph.ts b/src/common/graph.ts
index ee23da5..8399ce6 100644
--- a/src/common/graph.ts
+++ b/src/common/graph.ts
@@ -55,25 +55,29 @@ export class Graph
             this.initialized = false;
             return;
         }
+        this.reset();
 
         Object.assign(this, data);
 
-        this.nameToObjectGroup = new Map<string, NodeType>();
         this.objectGroups.forEach((group) =>
             this.nameToObjectGroup.set(group.name, group)
         );
-
-        this.idToNode = new Map<number, Node>();
         this.nodes.forEach((node) => {
             this.idToNode.set(node.id, node);
         });
-
-        this.idToLink = new Map<number, Link>();
         this.links.forEach((link) => {
             this.idToLink.set(link.id, link);
         });
     }
 
+    private reset() {
+        this.nodes = [];
+        this.links = [];
+        this.nameToObjectGroup = new Map<string, NodeType>();
+        this.idToNode = new Map<number, Node>();
+        this.idToLink = new Map<number, Link>();
+    }
+
     public toJSONSerializableObject(): GraphData {
         return {
             nodes: this.nodes.map((node) => node.toJSONSerializableObject()),
@@ -95,57 +99,24 @@ export class Graph
     }
 
     public fromSerializedObject(data: GraphData | SimGraphData): Graph {
-        let objectGroups: Array<NodeType>;
+        this.reset();
 
         if (data.objectGroups === undefined) {
-            objectGroups = this.createObjectGroupsFromStrings(data.nodes);
+            this.createObjectGroupsFromStrings(data.nodes);
         } else {
-            objectGroups = this.createObjectGroupsFromObjects(
-                data.objectGroups
+            data.objectGroups.forEach((group) =>
+                this.createObjectGroup(group.name, group.color)
             );
         }
 
-        this.nameToObjectGroup = new Map<string, NodeType>();
-        objectGroups.forEach((group) =>
-            this.nameToObjectGroup.set(group.name, group)
-        );
-
-        this.nodes = this.createNodes(data.nodes);
-        this.idToNode = new Map<number, Node>();
-        this.nodes.forEach((node) => {
-            this.idToNode.set(node.id, node);
-        });
-
-        this.links = data.links.map((link, i) => {
-            const l = new Link();
-            l.id = i;
-            l.source = this.idToNode.get(link.source);
-            l.target = this.idToNode.get(link.target);
-            return l;
-        });
-        this.idToLink = new Map<number, Link>();
-        this.links.forEach((link) => {
-            this.idToLink.set(link.id, link);
-        });
+        data.nodes.forEach((node) => this.createNode(node));
+        data.links.forEach((link) => this.createLink(link.source, link.target));
 
         this.updateNodeData();
 
         return this;
     }
 
-    private createNodes(nodeJSONData: NodeData[]): Array<Node> {
-        const nodes: Array<Node> = [];
-        for (const nodeData of nodeJSONData) {
-            const node = new Node();
-            node.fromSerializedObject(nodeData);
-            node.type = this.nameToObjectGroup.get(nodeData.type);
-            node.neighbors = [];
-            node.links = [];
-            nodes.push(node);
-        }
-        return nodes;
-    }
-
     private createObjectGroupsFromStrings(nodes: NodeData[]): Array<NodeType> {
         const objectGroups: NodeType[] = [];
         const nodeClasses: string[] = [];
@@ -153,26 +124,14 @@ export class Graph
         const nodeTypes = [...new Set(nodeClasses)].map((c) => String(c));
 
         for (let i = 0; i < nodeTypes.length; i++) {
-            const nodeType = new NodeType();
-            nodeType.fromSerializedObject({
-                id: i,
-                name: nodeTypes[i],
-                color: Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length],
-            });
+            this.createObjectGroup(
+                nodeTypes[i],
+                Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length]
+            );
         }
         return objectGroups;
     }
 
-    private createObjectGroupsFromObjects(
-        groups: NodeTypeData[]
-    ): Array<NodeType> {
-        return groups.map((group) => {
-            const t = new NodeType();
-            t.fromSerializedObject(group);
-            return t;
-        });
-    }
-
     /**
      * Updates the graph data structure to contain additional values.
      * Creates a 'neighbors' and 'links' array for each node object.
@@ -197,53 +156,61 @@ export class Graph
         return this.idToNode.get(id);
     }
 
-    private checkNode(node: Node) {
-        for (const neighbor of node.neighbors) {
-            if (this.idToNode.get(neighbor.id) === undefined) {
-                return false;
-            }
-        }
-
-        for (const link of node.links) {
-            if (this.idToLink.get(link.id) === undefined) {
-                return false;
-            }
-        }
-        return node.isInitialized();
+    private addNode(node: Node) {
+        node.id = this.nodes.length;
+        this.nodes.push(node);
+        this.idToNode.set(node.id, node);
     }
 
-    private checkLink(link: Link) {
-        return (
-            link.isInitialized() &&
-            !(
-                this.idToNode.get(link.source.id) === undefined ||
-                this.idToNode.get(link.target.id) === undefined
-            )
-        );
+    public createNode(data?: NodeData): Node {
+        const node = new Node(this);
+        node.fromSerializedObject(data);
+        node.type = this.nameToObjectGroup.get(data.type);
+        node.neighbors = [];
+        node.links = [];
+        this.addNode(node);
+        return node;
     }
 
-    public addNode(node: Node) {
-        node.id = this.nodes.length;
+    public createLink(source: number, target: number): Link {
+        if (source === target) {
+            console.warn(
+                "Attempting to create a link where source equals target"
+            );
+            return;
+        }
 
-        if (!this.checkNode(node)) {
-            return false;
+        const sourceNode = this.idToNode.get(source);
+        const targetNode = this.idToNode.get(target);
+
+        if (sourceNode === undefined || targetNode === undefined) {
+            console.warn("Tried to create a link between nonexisting nodes!");
+            return;
         }
 
-        this.nodes.push(node);
-        this.idToNode.set(node.id, node);
-        return true;
+        const link = new Link(sourceNode, targetNode, this);
+        sourceNode.links.push(link);
+        targetNode.links.push(link);
+        this.addLink(link);
+        return link;
     }
 
-    public addLink(link: Link) {
-        link.id = this.links.length;
+    public createObjectGroup(name?: string, color?: string): NodeType {
+        const group = new NodeType(name, color, this);
+        this.addObjectGroup(group);
+        return group;
+    }
 
-        if (!this.checkLink(link)) {
-            return false;
-        }
+    private addObjectGroup(group: NodeType) {
+        group.id = this.objectGroups.length;
+        this.objectGroups.push(group);
+        this.nameToObjectGroup.set(group.name, group); // TODO: Replace with id
+    }
 
+    private addLink(link: Link) {
+        link.id = this.links.length;
         this.links.push(link);
         this.idToLink.set(link.id, link);
-        return true;
     }
 
     public deleteLink(id: number) {
diff --git a/src/common/link.ts b/src/common/link.ts
index 78d6324..100894e 100644
--- a/src/common/link.ts
+++ b/src/common/link.ts
@@ -35,8 +35,10 @@ export class Link
     // These parameters will be added by the force graph implementation
     public index?: number;
 
-    constructor(graph: Graph = undefined) {
+    constructor(source?: Node, target?: Node, graph?: Graph) {
         super(0, graph);
+        this.source = source;
+        this.target = target;
     }
 
     /**
diff --git a/src/common/node.ts b/src/common/node.ts
index 076293d..fa06cf2 100644
--- a/src/common/node.ts
+++ b/src/common/node.ts
@@ -82,7 +82,7 @@ export class Node
     public fx?: number;
     public fy?: number;
 
-    constructor(graph: Graph = undefined) {
+    constructor(graph?: Graph) {
         super(0, graph);
         this.neighbors = [];
         this.links = [];
@@ -127,16 +127,7 @@ export class Node
             throw new Error("The connected nodes are not on the same graph!");
         }
 
-        const link = new Link(this.graph);
-
-        link.source = this;
-        link.target = node;
-
-        if (this.graph.addLink(link)) {
-            this.neighbors.push(node);
-            node.neighbors.push(this);
-            return link;
-        }
+        return this.graph.createLink(this.id, node.id);
     }
 
     public toJSONSerializableObject(): NodeData {
diff --git a/src/common/nodetype.ts b/src/common/nodetype.ts
index c160ec0..c9be115 100644
--- a/src/common/nodetype.ts
+++ b/src/common/nodetype.ts
@@ -1,4 +1,6 @@
 import { GraphElement } from "./graphelement";
+import { Node } from "./node";
+import { Graph } from "./graph";
 
 export interface NodeTypeData {
     id: number;
@@ -14,6 +16,12 @@ export class NodeType
     public name: string;
     public color: string;
 
+    constructor(name?: string, color?: string, graph?: Graph) {
+        super(0, graph);
+        this.name = name;
+        this.color = color;
+    }
+
     toJSONSerializableObject(): NodeTypeData {
         return { id: this.id, name: this.name, color: this.color };
     }
-- 
GitLab