Topo2: Scale links when zooming in and out
Topo2: Limit icon scaling

Change-Id: I9b203183ebfe25ae9993a9ee8813608969867849
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 b19d469..27ce6c8 100644
--- a/web/gui/src/main/webapp/app/fw/svg/zoom.js
+++ b/web/gui/src/main/webapp/app/fw/svg/zoom.js
@@ -93,7 +93,7 @@
                 function adjustZoomLayer(translate, scale) {
                     settings.zoomLayer.attr('transform',
                         'translate(' + translate + ')scale(' + scale + ')');
-                    settings.zoomCallback();
+                    settings.zoomCallback(translate, scale);
                 }
 
                 zoomer = {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.js b/web/gui/src/main/webapp/app/view/topo2/topo2.js
index 363cec5..5c8332c 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.js
@@ -25,7 +25,7 @@
 
     // references to injected services
     var $scope, $log, fs, mast, ks, zs,
-        gs, sus, ps, t2es, t2fs, t2is, t2bcs, t2kcs, t2ms, t2mcs;
+        gs, sus, ps, t2es, t2fs, t2is, t2bcs, t2kcs, t2ms, t2mcs, t2zs;
 
     // DOM elements
     var ovtopo2, svg, defs, zoomLayer, forceG;
@@ -63,14 +63,12 @@
             tr = zoomer.translate();
 
         ps.setPrefs('topo_zoom', { tx: tr[0], ty: tr[1], sc: sc });
-
-        // keep the map lines constant width while zooming
-        // mapG.style('stroke-width', (2.0 / sc) + 'px');
     }
 
     function setUpZoom() {
         zoomLayer = svg.append('g').attr('id', 'topo-zoomlayer');
-        zoomer = zs.createZoomer({
+
+        zoomer = t2zs.createZoomer({
             svg: svg,
             zoomLayer: zoomLayer,
             zoomEnabled: zoomEnabled,
@@ -88,14 +86,14 @@
         'WebSocketService', 'PrefsService', 'ThemeService',
         'Topo2EventService', 'Topo2ForceService', 'Topo2InstanceService',
         'Topo2BreadcrumbService', 'Topo2KeyCommandService', 'Topo2MapService',
-        'Topo2MapConfigService', 'Topo2SummaryPanelService',
+        'Topo2MapConfigService', 'Topo2SummaryPanelService', 'Topo2ZoomService',
 
         function (_$scope_, _$log_, _$loc_,
             _fs_, _mast_, _ks_, _zs_,
             _gs_, _ms_, _sus_, _flash_,
             _wss_, _ps_, _th_,
             _t2es_, _t2fs_, _t2is_, _t2bcs_, _t2kcs_, _t2ms_, _t2mcs_,
-            summaryPanel
+            summaryPanel, _t2zs_
         ) {
 
             var params = _$loc_.search(),
@@ -131,6 +129,7 @@
             t2kcs = _t2kcs_;
             t2ms = _t2ms_;
             t2mcs = _t2mcs_;
+            t2zs = _t2zs_;
 
             // capture selected intent parameters (if they are set in the
             //  query string) so that the traffic overlay can highlight
@@ -189,7 +188,6 @@
                     // Now the map has load and we have a projection we can
                     // get the info from the server
                     t2es.start();
-
                 }
             );
 
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 a4aa096..33c4183 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -194,14 +194,14 @@
         var allNodes = t2rs.regionNodes();
         angular.forEach(allNodes, function (node) {
             node.update();
-        })
+        });
     }
 
     angular.module('ovTopo2')
     .factory('Topo2ForceService',
         ['$log', 'WebSocketService', 'Topo2InstanceService', 'Topo2RegionService',
-        'Topo2LayoutService', 'Topo2ViewService', 'Topo2BreadcrumbService',
-        function (_$log_, _wss_, _t2is_, _t2rs_, _t2ls_, _t2vs_, _t2bcs_) {
+        'Topo2LayoutService', 'Topo2ViewService', 'Topo2BreadcrumbService', 'Topo2ZoomService',
+        function (_$log_, _wss_, _t2is_, _t2rs_, _t2ls_, _t2vs_, _t2bcs_, zoomService) {
 
             $log = _$log_;
             wss = _wss_;
@@ -211,6 +211,19 @@
             t2vs = _t2vs_;
             t2bcs = _t2bcs_;
 
+            var onZoom = function () {
+                var nodes = [].concat(
+                        t2rs.regionNodes(),
+                        t2rs.regionLinks()
+                    );
+
+                angular.forEach(nodes, function (node) {
+                    node.setScale();
+                });
+            };
+
+            zoomService.addZoomEventListener(onZoom);
+
             return {
 
                 init: init,
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 74fe8a2..c62a935 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
@@ -23,7 +23,7 @@
     'use strict';
 
     var $log;
-    var Collection, Model, ts, sus;
+    var Collection, Model, ts, sus, t2zs;
 
     var linkLabelOffset = '0.35em';
 
@@ -171,7 +171,9 @@
                         rect = el.append('rect'),
                         text = el.append('text').text(d.num);
 
-                    rect.attr(rectAroundText(el))
+                    var rectSize = rectAroundText(el);
+
+                    rect.attr(rectSize)
                         .attr('rx', 2)
                         .attr('ry', 2);
 
@@ -211,7 +213,6 @@
                     type = this.get('type'),
                     online = this.online(),
                     modeCls = this.expected() ? 'inactive' : 'not-permitted',
-                    lw = 1.2,
                     delay = immediate ? 0 : 1000;
 
                 // NOTE: understand why el is sometimes undefined on addLink events...
@@ -230,7 +231,7 @@
                     }
                     el.transition()
                         .duration(delay)
-                        .attr('stroke-width', linkScale(lw))
+                        .attr('stroke-width', linkScale(widthRatio))
                         .attr('stroke', linkConfig[th].baseColor);
                 }
             },
@@ -243,6 +244,10 @@
                 if (this.get('type') === 'hostLink') {
                     // sus.visible(link, api.showHosts());
                 }
+            },
+            setScale: function () {
+                var width = linkScale(widthRatio / t2zs.scale());
+                this.el.style('stroke-width', width + 'px');
             }
         });
 
@@ -256,13 +261,14 @@
     angular.module('ovTopo2')
     .factory('Topo2LinkService',
         ['$log', 'Topo2Collection', 'Topo2Model',
-        'ThemeService', 'SvgUtilService',
+        'ThemeService', 'SvgUtilService', 'Topo2ZoomService',
 
-            function (_$log_, _Collection_, _Model_, _ts_, _sus_) {
+            function (_$log_, _Collection_, _Model_, _ts_, _sus_, _t2zs_) {
 
                 $log = _$log_;
                 ts = _ts_;
                 sus = _sus_;
+                t2zs = _t2zs_;
                 Collection = _Collection_;
                 Model = _Model_;
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js b/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
index 09e6ef0..109a694 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
@@ -29,6 +29,8 @@
     var nearDist = 15;
 
     var devIconDim = 36,
+        devIconDimMin = 20,
+        devIconDimMax = 40,
         labelPad = 5,
         textPad = 5,
         halfDevIcon = devIconDim / 2;
@@ -143,9 +145,9 @@
     .factory('Topo2NodeModel',
         ['Topo2Model', 'FnService', 'RandomService', 'Topo2PrefsService',
         'SvgUtilService', 'IconService', 'ThemeService',
-        'Topo2MapConfigService',
+        'Topo2MapConfigService', 'Topo2ZoomService',
         function (Model, _fn_, _RandomService_, _ps_, _sus_, _is_, _ts_,
-            _t2mcs_) {
+            _t2mcs_, zoomService) {
 
             randomService = _RandomService_;
             ts = _ts_;
@@ -268,6 +270,20 @@
                     this.el = d3.select(el);
                     this.render();
                 },
+                setScale: function () {
+
+                    var dim = devIconDim,
+                        multipler = 1;
+
+                    if (dim * zoomService.scale() < devIconDimMin) {
+                        multipler = devIconDimMin / (dim * zoomService.scale());
+                    } else if (dim * zoomService.scale() > devIconDimMax) {
+                        multipler = devIconDimMax / (dim * zoomService.scale());
+                    }
+
+
+                    this.el.selectAll('*').style('transform', 'scale(' + multipler + ')');
+                },
                 render: function () {
                     var node = this.el,
                         glyphId = this.icon(this.get('type')),
@@ -289,6 +305,8 @@
                     if (this.events) {
                         this.setUpEvents();
                     }
+
+                    this.setScale();
                 }
             });
         }]
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Zoom.js b/web/gui/src/main/webapp/app/view/topo2/topo2Zoom.js
new file mode 100644
index 0000000..772699a
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Zoom.js
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Topology Breadcrumb Module.
+ Module that renders the breadcrumbs for regions
+ */
+
+(function () {
+
+    'use strict';
+
+    var zs, ps;
+    var zoomer;
+    var zoomEventListeners = [];
+
+    function createZoomer(options) {
+        var settings = angular.extend({}, options, {
+            zoomCallback: zoomCallback
+        });
+
+        zoomer = zs.createZoomer(settings);
+        return zoomer;
+    }
+
+    function zoomCallback() {
+        var sc = zoomer.scale(),
+            tr = zoomer.translate();
+
+        ps.setPrefs('topo_zoom', { tx: tr[0], ty: tr[1], sc: sc });
+
+        angular.forEach(zoomEventListeners, function (ev) {
+            ev(zoomer);
+        });
+    }
+
+    function findZoomEventListener(ev) {
+        for (var i = 0, l = zoomEventListeners.length; i < l; i++) {
+            if (zoomEventListeners[i] === ev) return i;
+        }
+
+        return -1;
+    }
+
+    function addZoomEventListener(callback) {
+        zoomEventListeners.push(callback);
+    }
+
+    function removeZoomEventListener(callback) {
+
+        var evIndex = findZoomEventListener(callback);
+
+        if (evIndex !== -1) {
+            zoomEventListeners.splice(evIndex);
+        }
+    }
+
+    function scale() {
+        return zoomer.scale();
+    }
+
+    angular.module('ovTopo2')
+    .factory('Topo2ZoomService',
+        ['ZoomService', 'PrefsService',
+        function (_zs_, _ps_) {
+
+            zs = _zs_;
+            ps = _ps_;
+
+            return {
+                createZoomer: createZoomer,
+                addZoomEventListener: addZoomEventListener,
+                removeZoomEventListener: removeZoomEventListener,
+
+                scale: scale
+            };
+        }]);
+})();
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 2831781..f5fd009 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -126,7 +126,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>
@@ -153,10 +153,11 @@
     <script src="app/view/topo2/topo2SubRegion.js"></script>
     <script src="app/view/topo2/topo2Theme.js"></script>
     <script src="app/view/topo2/topo2View.js"></script>
+    <script src="app/view/topo2/topo2Zoom.js"></script>
     <link rel="stylesheet" href="app/view/topo2/topo2.css">
-    <link rel="stylesheet" href="app/view/topo2/topo2-theme.css">
+    <link rel="stylesheet" href="app/view/topo2/topo2-theme.css">-->
 
-    <script src="app/view/topoX/topoX.js"></script>
+    <!-- <script src="app/view/topoX/topoX.js"></script>
     <script src="app/view/topoX/topoXEvent.js"></script>
     <script src="app/view/topoX/topoXForce.js"></script>
     <link rel="stylesheet" href="app/view/topoX/topoX.css">