Simon Hunt

GUI -- TopoView - Initial work for implementing link selection.

- a step in the direction for showing port numbers.

Change-Id: I313782374c82b87b6d426e88519a5ab7c072a622
...@@ -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>
......