GUI -- TopoView - Initial work for implementing link selection.
- a step in the direction for showing port numbers.
Change-Id: I313782374c82b87b6d426e88519a5ab7c072a622
diff --git a/web/gui/src/main/resources/core/js.html b/web/gui/src/main/resources/core/js.html
index 581b190..dde2271 100644
--- a/web/gui/src/main/resources/core/js.html
+++ b/web/gui/src/main/resources/core/js.html
@@ -5,6 +5,7 @@
<script src="app/view/topo/topoFilter.js"></script>
<script src="app/view/topo/topoForce.js"></script>
<script src="app/view/topo/topoInst.js"></script>
+<script src="app/view/topo/topoLink.js"></script>
<script src="app/view/topo/topoModel.js"></script>
<script src="app/view/topo/topoOblique.js"></script>
<script src="app/view/topo/topoPanel.js"></script>
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 998f442..2aebee8 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -232,6 +232,7 @@
showNoDevs: showNoDevs,
projection: function () { return projection; },
zoomLayer: function () { return zoomLayer; },
+ zoomer: function () { return zoomer; },
opacifyMap: opacifyMap,
sendEvent: _tes_.sendEvent
};
@@ -287,7 +288,7 @@
);
forceG = zoomLayer.append('g').attr('id', 'topo-force');
- tfs.initForce(forceG, uplink, dim);
+ tfs.initForce(svg, forceG, uplink, dim);
tis.initInst({ showMastership: tfs.showMastership });
tps.initPanels({ sendEvent: tes.sendEvent });
tes.openSock();
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
index d97111f..2509290 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -23,7 +23,7 @@
'use strict';
// injected refs
- var $log, fs, sus, is, ts, flash, tis, tms, td3, tss, tts, tos, fltr,
+ var $log, fs, sus, is, ts, flash, tis, tms, td3, tss, tts, tos, fltr, tls,
icfg, uplink;
// configuration
@@ -728,15 +728,25 @@
};
}
+ function mkLinkApi(svg, forceG, uplink) {
+ return {
+ svg: svg,
+ forceG: forceG,
+ zoomer: uplink.zoomer(),
+ network: network,
+ showHosts: function () { return showHosts; }
+ };
+ }
+
angular.module('ovTopo')
.factory('TopoForceService',
['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
'FlashService', 'TopoInstService', 'TopoModelService',
'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
- 'TopoObliqueService', 'TopoFilterService',
+ 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_,
- _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_) {
+ _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
$log = _$log_;
fs = _fs_;
sus = _sus_;
@@ -750,6 +760,7 @@
tts = _tts_;
tos = _tos_;
fltr = _fltr_;
+ tls = _tls_;
icfg = is.iconConfig();
@@ -762,7 +773,7 @@
// uplink is the api from the main topo source file
// dim is the initial dimensions of the SVG as [w,h]
// opts are, well, optional :)
- function initForce(forceG, _uplink_, _dim_, opts) {
+ function initForce(svg, forceG, _uplink_, _dim_, opts) {
uplink = _uplink_;
dim = _dim_;
@@ -774,6 +785,7 @@
tts.initTraffic(mkTrafficApi(uplink));
tos.initOblique(mkObliqueApi(uplink, fltr));
fltr.initFilter(mkFilterApi(uplink), d3.select('#mast-right'));
+ tls.initLink(mkLinkApi(svg, forceG, uplink));
settings = angular.extend({}, defaultSettings, opts);
@@ -808,6 +820,7 @@
}
function destroyForce() {
+ tls.destroyLink();
fltr.destroyFilter();
tos.destroyOblique();
tts.destroyTraffic();
diff --git a/web/gui/src/main/webapp/app/view/topo/topoLink.js b/web/gui/src/main/webapp/app/view/topo/topoLink.js
new file mode 100644
index 0000000..6dd4338
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo/topoLink.js
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2015 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 Link Module.
+ Functions for highlighting/selecting links
+ */
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, fs, sus, ts;
+
+ var api,
+ network,
+ enhancedLink = null; // the link which the mouse is hovering over
+
+ // SVG elements;
+ var svg, mouseG;
+
+
+ // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ========
+
+ function setupMouse(forceG, zoomer) {
+ $log.debug('set up mouse handlers for mouse move');
+ mouseG = forceG.append('g').attr('id', 'topo-mouse');
+ //mouseG.append('circle')
+ // .attr({
+ // r: 5,
+ // opacity: 0
+ // })
+ // .style('fill', 'red');
+
+ svg.on('mouseenter', function () {
+ //$log.log('M--ENTER');
+ //mouseG.selectAll('circle').attr('opacity', 1);
+ })
+ .on('mouseleave', function () {
+ //$log.log('M--LEAVE');
+ //mouseG.selectAll('circle').attr('opacity', 0);
+ })
+ .on('mousemove', function () {
+ var m = d3.mouse(this),
+ sc = zoomer.scale(),
+ tr = zoomer.translate(),
+ mx = (m[0] - tr[0]) / sc,
+ my = (m[1] - tr[1]) / sc;
+
+ //$log.log('M--MOVE', m);
+
+ //mouseG.selectAll('circle')
+ // .attr({
+ // cx: mx,
+ // cy: my
+ // });
+ updatePerps({x: mx, y: my}, zoomer);
+ });
+ }
+
+ function updatePerps(mouse, zoomer) {
+ var proximity = 30 / zoomer.scale(),
+ perpData, perps, nearest, minDist;
+
+ function sq(x) { return x * x; }
+
+ 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 mdist(p, m) {
+ return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
+ }
+
+ function lineSeg(d) {
+ return {
+ x1: d.source.x,
+ y1: d.source.y,
+ x2: d.target.x,
+ y2: d.target.y
+ };
+ }
+
+ 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 bisects, but are we close enough?
+ return mdist(p, m) <= proximity;
+ }
+
+ if (network.links.length) {
+ perpData = [];
+ nearest = null;
+ minDist = proximity * 2;
+
+ network.links.forEach(function (d) {
+ if (!api.showHosts() && d.type() === 'hostLink') {
+ return; // skip hidden host links
+ }
+
+ var line = lineSeg(d),
+ point = pdrop(line, mouse),
+ hit = lineHit(line, point, mouse),
+ dist;
+
+ if (hit) {
+ dist = mdist(point, mouse);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = d;
+ }
+ /*
+ perpData.push({
+ key: d.key,
+ x1: mouse.x,
+ y1: mouse.y,
+ x2: point.x,
+ y2: point.y
+ });
+ */
+ }
+ });
+
+ /*
+ perps = mouseG.selectAll('line')
+ .data(perpData, function (d) { return d.key; })
+ .attr({
+ x1: function (d) { return d.x1; },
+ y1: function (d) { return d.y1; },
+ x2: function (d) { return d.x2; },
+ y2: function (d) { return d.y2; }
+ });
+
+ perps.enter().append('line')
+ .attr({
+ x1: function (d) { return d.x1; },
+ y1: function (d) { return d.y1; },
+ x2: function (d) { return d.x2; },
+ y2: function (d) { return d.y2; }
+ })
+ .style('stroke-width', 2)
+ .style('stroke', 'limegreen');
+
+ perps.exit().remove();
+ */
+
+ enhanceNearestLink(nearest);
+ }
+ }
+
+
+ function enhanceNearestLink(ldata) {
+ // if the new link is same as old link, do nothing
+ if (enhancedLink && ldata && enhancedLink.key === ldata.key) return;
+
+ // first, unenhance the currently enhanced link
+ if (enhancedLink) {
+ unenhance(enhancedLink);
+ }
+ enhancedLink = ldata;
+ if (enhancedLink) {
+ enhance(enhancedLink);
+ }
+ }
+
+ function unenhance(d) {
+ d.el.style('stroke', '#666');
+ $log.debug('UN-enhancing link: ', d.key);
+ }
+
+ function enhance(d) {
+ d.el.style('stroke', 'gold');
+ $log.debug('enhancing link: ', d.key);
+ }
+
+
+
+
+ // ==========================
+ // Module definition
+
+ angular.module('ovTopo')
+ .factory('TopoLinkService',
+ ['$log', 'FnService', 'SvgUtilService', 'ThemeService',
+
+ function (_$log_, _fs_, _sus_, _ts_) {
+ $log = _$log_;
+ fs = _fs_;
+ sus = _sus_;
+ ts = _ts_;
+
+ function initLink(_api_) {
+ api = _api_;
+ svg = api.svg;
+ network = api.network;
+ setupMouse(api.forceG, api.zoomer);
+ }
+
+ function destroyLink() {
+ svg.on('mouseenter', null)
+ .on('mouseleave', null)
+ .on('mousemove', null);
+ }
+
+ return {
+ initLink: initLink,
+ destroyLink: destroyLink
+ };
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 530bfa9..5f7632c 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -91,6 +91,7 @@
<script src="app/view/topo/topoFilter.js"></script>
<script src="app/view/topo/topoForce.js"></script>
<script src="app/view/topo/topoInst.js"></script>
+ <script src="app/view/topo/topoLink.js"></script>
<script src="app/view/topo/topoModel.js"></script>
<script src="app/view/topo/topoOblique.js"></script>
<script src="app/view/topo/topoPanel.js"></script>