GUI -- TopoView - Initial work for implementing link selection.
- a step in the direction for showing port numbers. Change-Id: I313782374c82b87b6d426e88519a5ab7c072a622
Showing
5 changed files
with
256 additions
and
5 deletions
| ... | @@ -5,6 +5,7 @@ | ... | @@ -5,6 +5,7 @@ |
| 5 | <script src="app/view/topo/topoFilter.js"></script> | 5 | <script src="app/view/topo/topoFilter.js"></script> |
| 6 | <script src="app/view/topo/topoForce.js"></script> | 6 | <script src="app/view/topo/topoForce.js"></script> |
| 7 | <script src="app/view/topo/topoInst.js"></script> | 7 | <script src="app/view/topo/topoInst.js"></script> |
| 8 | +<script src="app/view/topo/topoLink.js"></script> | ||
| 8 | <script src="app/view/topo/topoModel.js"></script> | 9 | <script src="app/view/topo/topoModel.js"></script> |
| 9 | <script src="app/view/topo/topoOblique.js"></script> | 10 | <script src="app/view/topo/topoOblique.js"></script> |
| 10 | <script src="app/view/topo/topoPanel.js"></script> | 11 | <script src="app/view/topo/topoPanel.js"></script> | ... | ... |
| ... | @@ -232,6 +232,7 @@ | ... | @@ -232,6 +232,7 @@ |
| 232 | showNoDevs: showNoDevs, | 232 | showNoDevs: showNoDevs, |
| 233 | projection: function () { return projection; }, | 233 | projection: function () { return projection; }, |
| 234 | zoomLayer: function () { return zoomLayer; }, | 234 | zoomLayer: function () { return zoomLayer; }, |
| 235 | + zoomer: function () { return zoomer; }, | ||
| 235 | opacifyMap: opacifyMap, | 236 | opacifyMap: opacifyMap, |
| 236 | sendEvent: _tes_.sendEvent | 237 | sendEvent: _tes_.sendEvent |
| 237 | }; | 238 | }; |
| ... | @@ -287,7 +288,7 @@ | ... | @@ -287,7 +288,7 @@ |
| 287 | ); | 288 | ); |
| 288 | 289 | ||
| 289 | forceG = zoomLayer.append('g').attr('id', 'topo-force'); | 290 | forceG = zoomLayer.append('g').attr('id', 'topo-force'); |
| 290 | - tfs.initForce(forceG, uplink, dim); | 291 | + tfs.initForce(svg, forceG, uplink, dim); |
| 291 | tis.initInst({ showMastership: tfs.showMastership }); | 292 | tis.initInst({ showMastership: tfs.showMastership }); |
| 292 | tps.initPanels({ sendEvent: tes.sendEvent }); | 293 | tps.initPanels({ sendEvent: tes.sendEvent }); |
| 293 | tes.openSock(); | 294 | tes.openSock(); | ... | ... |
| ... | @@ -23,7 +23,7 @@ | ... | @@ -23,7 +23,7 @@ |
| 23 | 'use strict'; | 23 | 'use strict'; |
| 24 | 24 | ||
| 25 | // injected refs | 25 | // injected refs |
| 26 | - var $log, fs, sus, is, ts, flash, tis, tms, td3, tss, tts, tos, fltr, | 26 | + var $log, fs, sus, is, ts, flash, tis, tms, td3, tss, tts, tos, fltr, tls, |
| 27 | icfg, uplink; | 27 | icfg, uplink; |
| 28 | 28 | ||
| 29 | // configuration | 29 | // configuration |
| ... | @@ -728,15 +728,25 @@ | ... | @@ -728,15 +728,25 @@ |
| 728 | }; | 728 | }; |
| 729 | } | 729 | } |
| 730 | 730 | ||
| 731 | + function mkLinkApi(svg, forceG, uplink) { | ||
| 732 | + return { | ||
| 733 | + svg: svg, | ||
| 734 | + forceG: forceG, | ||
| 735 | + zoomer: uplink.zoomer(), | ||
| 736 | + network: network, | ||
| 737 | + showHosts: function () { return showHosts; } | ||
| 738 | + }; | ||
| 739 | + } | ||
| 740 | + | ||
| 731 | angular.module('ovTopo') | 741 | angular.module('ovTopo') |
| 732 | .factory('TopoForceService', | 742 | .factory('TopoForceService', |
| 733 | ['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService', | 743 | ['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService', |
| 734 | 'FlashService', 'TopoInstService', 'TopoModelService', | 744 | 'FlashService', 'TopoInstService', 'TopoModelService', |
| 735 | 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService', | 745 | 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService', |
| 736 | - 'TopoObliqueService', 'TopoFilterService', | 746 | + 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService', |
| 737 | 747 | ||
| 738 | function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, | 748 | function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, |
| 739 | - _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_) { | 749 | + _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) { |
| 740 | $log = _$log_; | 750 | $log = _$log_; |
| 741 | fs = _fs_; | 751 | fs = _fs_; |
| 742 | sus = _sus_; | 752 | sus = _sus_; |
| ... | @@ -750,6 +760,7 @@ | ... | @@ -750,6 +760,7 @@ |
| 750 | tts = _tts_; | 760 | tts = _tts_; |
| 751 | tos = _tos_; | 761 | tos = _tos_; |
| 752 | fltr = _fltr_; | 762 | fltr = _fltr_; |
| 763 | + tls = _tls_; | ||
| 753 | 764 | ||
| 754 | icfg = is.iconConfig(); | 765 | icfg = is.iconConfig(); |
| 755 | 766 | ||
| ... | @@ -762,7 +773,7 @@ | ... | @@ -762,7 +773,7 @@ |
| 762 | // uplink is the api from the main topo source file | 773 | // uplink is the api from the main topo source file |
| 763 | // dim is the initial dimensions of the SVG as [w,h] | 774 | // dim is the initial dimensions of the SVG as [w,h] |
| 764 | // opts are, well, optional :) | 775 | // opts are, well, optional :) |
| 765 | - function initForce(forceG, _uplink_, _dim_, opts) { | 776 | + function initForce(svg, forceG, _uplink_, _dim_, opts) { |
| 766 | uplink = _uplink_; | 777 | uplink = _uplink_; |
| 767 | dim = _dim_; | 778 | dim = _dim_; |
| 768 | 779 | ||
| ... | @@ -774,6 +785,7 @@ | ... | @@ -774,6 +785,7 @@ |
| 774 | tts.initTraffic(mkTrafficApi(uplink)); | 785 | tts.initTraffic(mkTrafficApi(uplink)); |
| 775 | tos.initOblique(mkObliqueApi(uplink, fltr)); | 786 | tos.initOblique(mkObliqueApi(uplink, fltr)); |
| 776 | fltr.initFilter(mkFilterApi(uplink), d3.select('#mast-right')); | 787 | fltr.initFilter(mkFilterApi(uplink), d3.select('#mast-right')); |
| 788 | + tls.initLink(mkLinkApi(svg, forceG, uplink)); | ||
| 777 | 789 | ||
| 778 | settings = angular.extend({}, defaultSettings, opts); | 790 | settings = angular.extend({}, defaultSettings, opts); |
| 779 | 791 | ||
| ... | @@ -808,6 +820,7 @@ | ... | @@ -808,6 +820,7 @@ |
| 808 | } | 820 | } |
| 809 | 821 | ||
| 810 | function destroyForce() { | 822 | function destroyForce() { |
| 823 | + tls.destroyLink(); | ||
| 811 | fltr.destroyFilter(); | 824 | fltr.destroyFilter(); |
| 812 | tos.destroyOblique(); | 825 | tos.destroyOblique(); |
| 813 | tts.destroyTraffic(); | 826 | tts.destroyTraffic(); | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +/* | ||
| 18 | + ONOS GUI -- Topology Link Module. | ||
| 19 | + Functions for highlighting/selecting links | ||
| 20 | + */ | ||
| 21 | + | ||
| 22 | +(function () { | ||
| 23 | + 'use strict'; | ||
| 24 | + | ||
| 25 | + // injected refs | ||
| 26 | + var $log, fs, sus, ts; | ||
| 27 | + | ||
| 28 | + var api, | ||
| 29 | + network, | ||
| 30 | + enhancedLink = null; // the link which the mouse is hovering over | ||
| 31 | + | ||
| 32 | + // SVG elements; | ||
| 33 | + var svg, mouseG; | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ======== | ||
| 37 | + | ||
| 38 | + function setupMouse(forceG, zoomer) { | ||
| 39 | + $log.debug('set up mouse handlers for mouse move'); | ||
| 40 | + mouseG = forceG.append('g').attr('id', 'topo-mouse'); | ||
| 41 | + //mouseG.append('circle') | ||
| 42 | + // .attr({ | ||
| 43 | + // r: 5, | ||
| 44 | + // opacity: 0 | ||
| 45 | + // }) | ||
| 46 | + // .style('fill', 'red'); | ||
| 47 | + | ||
| 48 | + svg.on('mouseenter', function () { | ||
| 49 | + //$log.log('M--ENTER'); | ||
| 50 | + //mouseG.selectAll('circle').attr('opacity', 1); | ||
| 51 | + }) | ||
| 52 | + .on('mouseleave', function () { | ||
| 53 | + //$log.log('M--LEAVE'); | ||
| 54 | + //mouseG.selectAll('circle').attr('opacity', 0); | ||
| 55 | + }) | ||
| 56 | + .on('mousemove', function () { | ||
| 57 | + var m = d3.mouse(this), | ||
| 58 | + sc = zoomer.scale(), | ||
| 59 | + tr = zoomer.translate(), | ||
| 60 | + mx = (m[0] - tr[0]) / sc, | ||
| 61 | + my = (m[1] - tr[1]) / sc; | ||
| 62 | + | ||
| 63 | + //$log.log('M--MOVE', m); | ||
| 64 | + | ||
| 65 | + //mouseG.selectAll('circle') | ||
| 66 | + // .attr({ | ||
| 67 | + // cx: mx, | ||
| 68 | + // cy: my | ||
| 69 | + // }); | ||
| 70 | + updatePerps({x: mx, y: my}, zoomer); | ||
| 71 | + }); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + function updatePerps(mouse, zoomer) { | ||
| 75 | + var proximity = 30 / zoomer.scale(), | ||
| 76 | + perpData, perps, nearest, minDist; | ||
| 77 | + | ||
| 78 | + function sq(x) { return x * x; } | ||
| 79 | + | ||
| 80 | + function pdrop(line, mouse) { | ||
| 81 | + var x1 = line.x1, | ||
| 82 | + y1 = line.y1, | ||
| 83 | + x2 = line.x2, | ||
| 84 | + y2 = line.y2, | ||
| 85 | + x3 = mouse.x, | ||
| 86 | + y3 = mouse.y, | ||
| 87 | + k = ((y2-y1) * (x3-x1) - (x2-x1) * (y3-y1)) / | ||
| 88 | + (sq(y2-y1) + sq(x2-x1)), | ||
| 89 | + x4 = x3 - k * (y2-y1), | ||
| 90 | + y4 = y3 + k * (x2-x1); | ||
| 91 | + return {x:x4, y:y4}; | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + function mdist(p, m) { | ||
| 95 | + return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y)); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + function lineSeg(d) { | ||
| 99 | + return { | ||
| 100 | + x1: d.source.x, | ||
| 101 | + y1: d.source.y, | ||
| 102 | + x2: d.target.x, | ||
| 103 | + y2: d.target.y | ||
| 104 | + }; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + function lineHit(line, p, m) { | ||
| 108 | + if (p.x < line.x1 && p.x < line.x2) return false; | ||
| 109 | + if (p.x > line.x1 && p.x > line.x2) return false; | ||
| 110 | + if (p.y < line.y1 && p.y < line.y2) return false; | ||
| 111 | + if (p.y > line.y1 && p.y > line.y2) return false; | ||
| 112 | + // line bisects, but are we close enough? | ||
| 113 | + return mdist(p, m) <= proximity; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + if (network.links.length) { | ||
| 117 | + perpData = []; | ||
| 118 | + nearest = null; | ||
| 119 | + minDist = proximity * 2; | ||
| 120 | + | ||
| 121 | + network.links.forEach(function (d) { | ||
| 122 | + if (!api.showHosts() && d.type() === 'hostLink') { | ||
| 123 | + return; // skip hidden host links | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + var line = lineSeg(d), | ||
| 127 | + point = pdrop(line, mouse), | ||
| 128 | + hit = lineHit(line, point, mouse), | ||
| 129 | + dist; | ||
| 130 | + | ||
| 131 | + if (hit) { | ||
| 132 | + dist = mdist(point, mouse); | ||
| 133 | + if (dist < minDist) { | ||
| 134 | + minDist = dist; | ||
| 135 | + nearest = d; | ||
| 136 | + } | ||
| 137 | + /* | ||
| 138 | + perpData.push({ | ||
| 139 | + key: d.key, | ||
| 140 | + x1: mouse.x, | ||
| 141 | + y1: mouse.y, | ||
| 142 | + x2: point.x, | ||
| 143 | + y2: point.y | ||
| 144 | + }); | ||
| 145 | + */ | ||
| 146 | + } | ||
| 147 | + }); | ||
| 148 | + | ||
| 149 | + /* | ||
| 150 | + perps = mouseG.selectAll('line') | ||
| 151 | + .data(perpData, function (d) { return d.key; }) | ||
| 152 | + .attr({ | ||
| 153 | + x1: function (d) { return d.x1; }, | ||
| 154 | + y1: function (d) { return d.y1; }, | ||
| 155 | + x2: function (d) { return d.x2; }, | ||
| 156 | + y2: function (d) { return d.y2; } | ||
| 157 | + }); | ||
| 158 | + | ||
| 159 | + perps.enter().append('line') | ||
| 160 | + .attr({ | ||
| 161 | + x1: function (d) { return d.x1; }, | ||
| 162 | + y1: function (d) { return d.y1; }, | ||
| 163 | + x2: function (d) { return d.x2; }, | ||
| 164 | + y2: function (d) { return d.y2; } | ||
| 165 | + }) | ||
| 166 | + .style('stroke-width', 2) | ||
| 167 | + .style('stroke', 'limegreen'); | ||
| 168 | + | ||
| 169 | + perps.exit().remove(); | ||
| 170 | + */ | ||
| 171 | + | ||
| 172 | + enhanceNearestLink(nearest); | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + | ||
| 177 | + function enhanceNearestLink(ldata) { | ||
| 178 | + // if the new link is same as old link, do nothing | ||
| 179 | + if (enhancedLink && ldata && enhancedLink.key === ldata.key) return; | ||
| 180 | + | ||
| 181 | + // first, unenhance the currently enhanced link | ||
| 182 | + if (enhancedLink) { | ||
| 183 | + unenhance(enhancedLink); | ||
| 184 | + } | ||
| 185 | + enhancedLink = ldata; | ||
| 186 | + if (enhancedLink) { | ||
| 187 | + enhance(enhancedLink); | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + function unenhance(d) { | ||
| 192 | + d.el.style('stroke', '#666'); | ||
| 193 | + $log.debug('UN-enhancing link: ', d.key); | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + function enhance(d) { | ||
| 197 | + d.el.style('stroke', 'gold'); | ||
| 198 | + $log.debug('enhancing link: ', d.key); | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + | ||
| 202 | + | ||
| 203 | + | ||
| 204 | + // ========================== | ||
| 205 | + // Module definition | ||
| 206 | + | ||
| 207 | + angular.module('ovTopo') | ||
| 208 | + .factory('TopoLinkService', | ||
| 209 | + ['$log', 'FnService', 'SvgUtilService', 'ThemeService', | ||
| 210 | + | ||
| 211 | + function (_$log_, _fs_, _sus_, _ts_) { | ||
| 212 | + $log = _$log_; | ||
| 213 | + fs = _fs_; | ||
| 214 | + sus = _sus_; | ||
| 215 | + ts = _ts_; | ||
| 216 | + | ||
| 217 | + function initLink(_api_) { | ||
| 218 | + api = _api_; | ||
| 219 | + svg = api.svg; | ||
| 220 | + network = api.network; | ||
| 221 | + setupMouse(api.forceG, api.zoomer); | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + function destroyLink() { | ||
| 225 | + svg.on('mouseenter', null) | ||
| 226 | + .on('mouseleave', null) | ||
| 227 | + .on('mousemove', null); | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + return { | ||
| 231 | + initLink: initLink, | ||
| 232 | + destroyLink: destroyLink | ||
| 233 | + }; | ||
| 234 | + }]); | ||
| 235 | +}()); |
| ... | @@ -91,6 +91,7 @@ | ... | @@ -91,6 +91,7 @@ |
| 91 | <script src="app/view/topo/topoFilter.js"></script> | 91 | <script src="app/view/topo/topoFilter.js"></script> |
| 92 | <script src="app/view/topo/topoForce.js"></script> | 92 | <script src="app/view/topo/topoForce.js"></script> |
| 93 | <script src="app/view/topo/topoInst.js"></script> | 93 | <script src="app/view/topo/topoInst.js"></script> |
| 94 | + <script src="app/view/topo/topoLink.js"></script> | ||
| 94 | <script src="app/view/topo/topoModel.js"></script> | 95 | <script src="app/view/topo/topoModel.js"></script> |
| 95 | <script src="app/view/topo/topoOblique.js"></script> | 96 | <script src="app/view/topo/topoOblique.js"></script> |
| 96 | <script src="app/view/topo/topoPanel.js"></script> | 97 | <script src="app/view/topo/topoPanel.js"></script> | ... | ... |
-
Please register or login to post a comment