GUI2 added in the layout topo overlay

Change-Id: I9960f95ae726a5af9950771ed67bcfc9d172e267
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/models/force-directed-graph.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/models/force-directed-graph.ts
index 24dd029..b4de906 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/models/force-directed-graph.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/models/force-directed-graph.ts
@@ -20,17 +20,16 @@
 import {LogService} from 'gui2-fw-lib';
 
 const FORCES = {
-    LINKS: 1 / 50,
     COLLISION: 1,
     GRAVITY: 0.4,
     FRICTION: 0.7
 };
 
 const CHARGES = {
-    device: -80,
-    host: -200,
-    region: -80,
-    _def_: -120
+    device: -800,
+    host: -2000,
+    region: -800,
+    _def_: -1200
 };
 
 const LINK_DISTANCE = {
@@ -41,10 +40,12 @@
     _def_: 50,
 };
 
+/**
+ * note: key is link.type
+ * range: {0.0 ... 1.0}
+ */
 const LINK_STRENGTH = {
-    // note: key is link.type
-    // range: {0.0 ... 1.0}
-    _def_: 0.1
+    _def_: 0.5
 };
 
 export interface Options {
@@ -55,87 +56,68 @@
 /**
  * The inspiration for this approach comes from
  * https://medium.com/netscape/visualizing-data-with-angular-and-d3-209dde784aeb
+ *
+ * Do yourself a favour and read https://d3indepth.com/force-layout/
  */
 export class ForceDirectedGraph {
     public ticker: EventEmitter<d3.Simulation<Node, Link>> = new EventEmitter();
     public simulation: d3.Simulation<any, any>;
-
+    public canvasOptions: Options;
     public nodes: Node[] = [];
     public links: Link[] = [];
 
     constructor(options: Options, public log: LogService) {
-        this.initSimulation(options);
+        this.canvasOptions = options;
+        const ticker = this.ticker;
+
+        // Creating the force simulation and defining the charges
+        this.simulation = d3.forceSimulation()
+            .force('charge',
+                d3.forceManyBody().strength(this.charges.bind(this)))
+            // .distanceMin(100).distanceMax(500))
+            .force('gravity',
+                d3.forceManyBody().strength(FORCES.GRAVITY))
+            .force('friction',
+                d3.forceManyBody().strength(FORCES.FRICTION))
+            .force('center',
+                d3.forceCenter(this.canvasOptions.width / 2, this.canvasOptions.height / 2))
+            .force('x', d3.forceX())
+            .force('y', d3.forceY())
+            .on('tick', () => {
+                ticker.emit(this.simulation); // ForceSvgComponent.ngOnInit listens
+            });
+
     }
 
-    initNodes() {
-        if (!this.simulation) {
-            throw new Error('simulation was not initialized yet');
-        }
-
+    /**
+     * Assigning updated node and restarting the simulation
+     * Setting alpha to 0.3 and it will count down to alphaTarget=0
+     */
+    public reinitSimulation() {
         this.simulation.nodes(this.nodes);
-    }
-
-    initLinks() {
-        if (!this.simulation) {
-            throw new Error('simulation was not initialized yet');
-        }
-
-        // Initializing the links force simulation
-        this.simulation.force('links',
+        this.simulation.force('link',
             d3.forceLink(this.links)
                 .strength(this.strength.bind(this))
                 .distance(this.distance.bind(this))
         );
+        this.simulation.alpha(0.3).restart();
     }
 
-    charges(node) {
+    charges(node: Node) {
         const nodeType = node.nodeType;
         return CHARGES[nodeType] || CHARGES._def_;
     }
 
-    distance(node) {
-        const nodeType = node.nodeType;
-        return LINK_DISTANCE[nodeType] || LINK_DISTANCE._def_;
+    distance(link: Link) {
+        const linkType = link.type;
+        this.log.debug('Link type', linkType, LINK_DISTANCE[linkType]);
+        return LINK_DISTANCE[linkType] || LINK_DISTANCE._def_;
     }
 
-    strength(node) {
-        const nodeType = node.nodeType;
-        return LINK_STRENGTH[nodeType] || LINK_STRENGTH._def_;
-    }
-
-    initSimulation(options: Options) {
-        if (!options || !options.width || !options.height) {
-            throw new Error('missing options when initializing simulation');
-        }
-
-        /** Creating the simulation */
-        if (!this.simulation) {
-            const ticker = this.ticker;
-
-            // Creating the force simulation and defining the charges
-            this.simulation = d3.forceSimulation()
-                .force('charge',
-                    d3.forceManyBody().strength(this.charges.bind(this)))
-                        // .distanceMin(100).distanceMax(500))
-                .force('gravity',
-                    d3.forceManyBody().strength(FORCES.GRAVITY))
-                .force('friction',
-                    d3.forceManyBody().strength(FORCES.FRICTION));
-
-            // Connecting the d3 ticker to an angular event emitter
-            this.simulation.on('tick', function () {
-                ticker.emit(this);
-            });
-
-            this.initNodes();
-            // this.initLinks();
-        }
-
-        /** Updating the central force of the simulation */
-        this.simulation.force('centers', d3.forceCenter(options.width / 2, options.height / 2));
-
-        /** Restarting the simulation internal timer */
-        this.simulation.restart();
+    strength(link: Link) {
+        const linkType = link.type;
+        this.log.debug('Link type', linkType, LINK_STRENGTH[linkType]);
+        return LINK_STRENGTH[linkType] || LINK_STRENGTH._def_;
     }
 
     stopSimulation() {
@@ -143,8 +125,8 @@
         this.log.debug('Simulation stopped');
     }
 
-    restartSimulation() {
-        this.simulation.restart();
-        this.log.debug('Simulation restarted');
+    public restartSimulation(alpha: number = 0.3) {
+        this.simulation.alpha(alpha).restart();
+        this.log.debug('Simulation restarted. Alpha:', alpha);
     }
 }