Topo2: Fix layout issues

Change-Id: Ief3772b9755216e44d0fe005c625e0090d8fb2b7
diff --git a/web/gui/src/main/webapp/app/fw/svg/zoom.js b/web/gui/src/main/webapp/app/fw/svg/zoom.js
index 27ce6c8..587c78d 100644
--- a/web/gui/src/main/webapp/app/fw/svg/zoom.js
+++ b/web/gui/src/main/webapp/app/fw/svg/zoom.js
@@ -69,6 +69,7 @@
                     fail = false,
                     zoomer;
 
+
                 if (!settings.svg) {
                     $log.error(cz + 'No "svg" (svg tag)' + d3s);
                     fail = true;
@@ -121,6 +122,10 @@
 
                 // apply the zoom behavior to the SVG element
                 settings.svg && settings.svg.call(zoom);
+
+                // Remove zoom on double click (prevents a
+                // false zoom navigating regions)
+                settings.svg.on("dblclick.zoom", null);
                 return zoomer;
             }
 
diff --git a/web/gui/src/main/webapp/app/view/topo/topoLink.js b/web/gui/src/main/webapp/app/view/topo/topoLink.js
index bfe0df5..ca2982c 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoLink.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoLink.js
@@ -195,9 +195,9 @@
         var offset = 32,
             pos = link.position,
             nearX = src ? pos.x1 : pos.x2,
-            nearY = src ? pos.y1 : pos.y2,
+            nearY = src ? pos.y2 : pos.y1,
             farX = src ? pos.x2 : pos.x1,
-            farY = src ? pos.y2 : pos.y1;
+            farY = src ? pos.y1 : pos.y2;
 
         function dist(x, y) { return Math.sqrt(x*x + y*y); }
 
diff --git a/web/gui/src/main/webapp/app/view/topo/topoModel.js b/web/gui/src/main/webapp/app/view/topo/topoModel.js
index 7679fe1..0b4fd2c 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoModel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoModel.js
@@ -248,6 +248,7 @@
             //logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
             return null;
         }
+
         return {
             source: srcNode,
             target: dstNode
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
index 89eb020..b6ed850 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -183,9 +183,6 @@
     function newDim(_dim_) {
         dim = _dim_;
         t2vs.newDim(dim);
-        // force.size(dim);
-        // tms.newDim(dim);
-        t2ls.setDimensions();
     }
 
     // ========================== Main Service Definition
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
index 4cd86cc..9ffcbe0 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
@@ -110,30 +110,41 @@
         link = linkG.selectAll('.link');
         linkLabelG.selectAll('.linkLabel');
         node = nodeG.selectAll('.node');
-
-        _svg_.on('mousemove', mouseMoveHandler);
     }
 
     function createForceLayout() {
 
+        var regionLinks = t2rs.regionLinks(),
+            regionNodes = t2rs.regionNodes();
+
         force = d3.layout.force()
             .size(t2vs.getDimensions())
-            .nodes(t2rs.regionNodes())
-            .links(t2rs.regionLinks())
-            .gravity(settings.gravity)
-            .friction(settings.friction)
             .charge(settings.charge._def_)
             .linkDistance(settings.linkDistance._def_)
-            .linkStrength(settings.linkStrength._def_)
-            .on('tick', tick);
+            .on("tick", tick);
+
+        force
+            .nodes(t2rs.regionNodes())
+            .links(regionLinks)
+            .start();
+
+        link = linkG.selectAll('.link')
+            .data(regionLinks, function (d) { return d.get('key'); });
+
+        node = nodeG.selectAll('.node')
+            .data(regionNodes, function (d) { return d.get('id'); });
 
         drag = sus.createDragBehavior(force,
-            t2ss.selectObject, atDragEnd, dragEnabled, clickEnabled);
+          t2ss.selectObject, atDragEnd, dragEnabled, clickEnabled);
 
-        start();
         update();
     }
 
+    // predicate that indicates when clicking is active
+    function clickEnabled() {
+        return true;
+    }
+
     function zoomingOrPanning(ev) {
         return ev.metaKey || ev.altKey;
     }
@@ -178,21 +189,21 @@
         });
     }
 
-    // predicate that indicates when clicking is active
-    function clickEnabled() {
-        return true;
-    }
-
     function tick() {
-        // guard against null (which can happen when our view pages out)...
-        if (node && node.size()) {
-            node.attr(tickStuff.nodeAttr);
-        }
-        if (link && link.size()) {
-            link.call(calcPosition)
-                .attr(tickStuff.linkAttr);
-            // t2d3.applyNumLinkLabels(linkNums, numLinkLabelsG);
-        }
+        link
+            .attr("x1", function (d) { return d.source.x; })
+            .attr("y1", function (d) { return d.source.y; })
+            .attr("x2", function (d) { return d.target.x; })
+            .attr("y2", function (d) { return d.target.y; });
+
+        node
+            .attr({
+                transform: function (d) {
+                    var dx = isNaN(d.x) ? 0 : d.x,
+                        dy = isNaN(d.y) ? 0 : d.y;
+                    return sus.translate(dx, dy);
+                }
+            });
     }
 
     function update() {
@@ -305,7 +316,6 @@
     }
 
     function getDefaultPos(link) {
-
         return {
             x1: link.get('source').x,
             y1: link.get('source').y,
@@ -324,100 +334,6 @@
         force.start();
     }
 
-    // Mouse Events
-    function mouseMoveHandler() {
-        var mp = getLogicalMousePosition(this),
-            link = computeNearestLink(mp);
-
-        if (highlightedLink) {
-            highlightedLink.unenhance();
-            highlightedLink = null;
-        }
-
-        if (link) {
-            link.enhance();
-            highlightedLink = link;
-        }
-    }
-
-    // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ========
-
-    function getLogicalMousePosition(container) {
-        var m = d3.mouse(container),
-            sc = uplink.zoomer().scale(),
-            tr = uplink.zoomer().translate(),
-            mx = (m[0] - tr[0]) / sc,
-            my = (m[1] - tr[1]) / sc;
-        return { x: mx, y: my };
-    }
-
-    function sq(x) {
-        return x * x;
-    }
-
-    function mdist(p, m) {
-        return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
-    }
-
-    function prox(dist) {
-        return dist / uplink.zoomer().scale();
-    }
-
-    function computeNearestLink(mouse) {
-        var proximity = prox(30),
-            nearest = null,
-            minDist,
-            regionLinks = t2rs.regionLinks();
-
-        function pdrop(line, mouse) {
-
-            var x1 = line.x1,
-                y1 = line.y1,
-                x2 = line.x2,
-                y2 = line.y2,
-                x3 = mouse.x,
-                y3 = mouse.y,
-                k = ((y2 - y1) * (x3 - x1) - (x2 - x1) * (y3 - y1)) /
-                    (sq(y2 - y1) + sq(x2 - x1)),
-                x4 = x3 - k * (y2 - y1),
-                y4 = y3 + k * (x2 - x1);
-            return { x: x4, y: y4 };
-        }
-
-        function lineHit(line, p, m) {
-            if (p.x < line.x1 && p.x < line.x2) return false;
-            if (p.x > line.x1 && p.x > line.x2) return false;
-            if (p.y < line.y1 && p.y < line.y2) return false;
-            if (p.y > line.y1 && p.y > line.y2) return false;
-            // line intersects, but are we close enough?
-            return mdist(p, m) <= proximity;
-        }
-
-        if (regionLinks.length) {
-            minDist = proximity * 2;
-
-            regionLinks.forEach(function (d) {
-                // if (!api.showHosts() && d.type() === 'hostLink') {
-                //     return; // skip hidden host links
-                // }
-
-                var line = d.get('position'),
-                    point = pdrop(line, mouse),
-                    hit = lineHit(line, point, mouse),
-                    dist;
-
-                if (hit) {
-                    dist = mdist(point, mouse);
-                    if (dist < minDist) {
-                        minDist = dist;
-                        nearest = d;
-                    }
-                }
-            });
-        }
-        return nearest;
-    }
-
     angular.module('ovTopo2')
     .factory('Topo2LayoutService',
         [
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
index 415a21e..66e3bb5 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
@@ -32,7 +32,8 @@
             .range([widthRatio, 12 * widthRatio])
             .clamp(true),
         allLinkTypes = 'direct indirect optical tunnel UiDeviceLink',
-        allLinkSubTypes = 'inactive not-permitted';
+        allLinkSubTypes = 'inactive not-permitted',
+        labelDim = 30;
 
     // configuration
     var linkConfig = {
@@ -93,6 +94,7 @@
 
     function linkEndPoints(srcId, dstId) {
 
+        var allNodes = this.region.nodes();
         var sourceNode = this.region.findNodeById(srcId);
         var targetNode = this.region.findNodeById(dstId);
 
@@ -102,8 +104,10 @@
             return null;
         }
 
-        this.source = sourceNode.toJSON();
-        this.target = targetNode.toJSON();
+        this.source = allNodes.indexOf(sourceNode);
+        this.target = allNodes.indexOf(targetNode);
+        this.sourceNode = sourceNode;
+        this.targetNode = targetNode;
 
         return {
             source: sourceNode,
@@ -162,9 +166,10 @@
 
                     var entering = d3.select('#topo-portLabels')
                         .selectAll('.portLabel')
-                        .data(data).enter().append('g')
+                        .data(data)
+                        .enter().append('g')
                         .classed('portLabel', true)
-                        .attr('id', function (d) { return d.id; });
+                        .attr('id', function (d) { return d.id; })
 
                     entering.each(function (d) {
                         var el = d3.select(this),
@@ -181,7 +186,10 @@
                             .attr('text-anchor', 'middle');
 
                         el.attr('transform', sus.translate(d.x, d.y));
+
                     });
+
+                    this.setScale();
                 }
             },
             unenhance: function () {
@@ -189,12 +197,16 @@
                 d3.select('#topo-portLabels').selectAll('.portLabel').remove();
             },
             locatePortLabel: function (src) {
-                var offset = 32,
-                    pos = this.get('position'),
-                    nearX = src ? pos.x1 : pos.x2,
-                    nearY = src ? pos.y1 : pos.y2,
-                    farX = src ? pos.x2 : pos.x1,
-                    farY = src ? pos.y2 : pos.y1;
+
+                var offset = 32 / (labelDim * t2zs.scale()),
+                    sourceX = this.get('position').x1,
+                    sourceY = this.get('position').y1,
+                    targetX = this.get('position').x2,
+                    targetY = this.get('position').y2,
+                    nearX = src ? sourceX : targetX,
+                    nearY = src ? sourceY : targetY,
+                    farX = src ? targetX : sourceX,
+                    farY = src ? targetY : sourceY;
 
                 function dist(x, y) {
                     return Math.sqrt(x * x + y * y);
@@ -202,7 +214,7 @@
 
                 var dx = farX - nearX,
                     dy = farY - nearY,
-                    k = offset / dist(dx, dy);
+                    k = (32 * offset) / dist(dx, dy);
 
                 return { x: k * dx + nearX, y: k * dy + nearY };
             },
@@ -232,8 +244,9 @@
                     }
                     el.transition()
                         .duration(delay)
-                        .attr('stroke-width', linkScale(widthRatio))
                         .attr('stroke', linkConfig[th].baseColor);
+
+                    this.setScale();
                 }
             },
             onEnter: function (el) {
@@ -242,13 +255,26 @@
                 this.el = link;
                 this.restyleLinkElement();
 
+                // TODO: Needs improving - originally this was calculated
+                // from mouse position.
+                this.el.on('mouseover', this.enhance.bind(this));
+                this.el.on('mouseout', this.unenhance.bind(this));
+
                 if (this.get('type') === 'hostLink') {
                     // sus.visible(link, api.showHosts());
                 }
             },
             setScale: function () {
-                var width = linkScale(widthRatio / t2zs.scale());
+                var width = linkScale(widthRatio) / t2zs.scale();
                 this.el.style('stroke-width', width + 'px');
+
+                var labelScale = labelDim / (labelDim * t2zs.scale());
+
+                d3.select('#topo-portLabels')
+                    .selectAll('.portLabel')
+                    .selectAll('*')
+                    .style('transform', 'scale(' + labelScale + ')');
+
             },
             update: function () {
                 if (this.el.classed('enhanced')) {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
index 6becc49..c3acff9 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
@@ -34,7 +34,8 @@
     function addRegion(data) {
 
         var RegionModel = Model.extend({
-            findNodeById: findNodeById
+            findNodeById: findNodeById,
+            nodes: regionNodes
         });
 
         region = new RegionModel({
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index b848f36..0f91449 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -127,7 +127,7 @@
     <link rel="stylesheet" href="app/fw/widget/table-theme.css">
 
     <!-- Under development for Region support. -->
-    <!--<script src="app/view/topo2/topo2.js"></script>
+    <!-- <script src="app/view/topo2/topo2.js"></script>
     <script src="app/view/topo2/topo2Breadcrumb.js"></script>
     <script src="app/view/topo2/topo2Collection.js"></script>
     <script src="app/view/topo2/topo2D3.js"></script>