blob: 708ff91729102f5285891bb4be1c58a3843d0c97 [file] [log] [blame]
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Simon Huntfb8ea1f2015-02-24 21:38:09 -08003 *
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
Simon Hunt5c1a9382016-06-01 19:35:35 -070026 var $log, fs, sus, ts, flash, tss, tps, tov;
Simon Huntfb8ea1f2015-02-24 21:38:09 -080027
Simon Hunt0c6b2d32015-03-26 17:46:29 -070028 // internal state
Simon Huntfb8ea1f2015-02-24 21:38:09 -080029 var api,
Simon Hunt1a5301e2015-02-25 15:31:25 -080030 td3,
Simon Huntfb8ea1f2015-02-24 21:38:09 -080031 network,
Simon Hunt0c6b2d32015-03-26 17:46:29 -070032 showPorts = true, // enable port highlighting by default
33 enhancedLink = null, // the link over which the mouse is hovering
Prince Pereira46c82d42016-09-19 13:30:50 +053034 selectedLinks = {}; // the links which are already selected
Simon Huntfb8ea1f2015-02-24 21:38:09 -080035
36 // SVG elements;
Simon Hunt9e2104c2015-02-26 10:48:59 -080037 var svg;
38
Simon Huntfb8ea1f2015-02-24 21:38:09 -080039
40 // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ========
41
Simon Hunt0c6b2d32015-03-26 17:46:29 -070042 function getLogicalMousePosition(container) {
43 var m = d3.mouse(container),
Simon Hunt9e2104c2015-02-26 10:48:59 -080044 sc = api.zoomer.scale(),
45 tr = api.zoomer.translate(),
46 mx = (m[0] - tr[0]) / sc,
47 my = (m[1] - tr[1]) / sc;
Simon Hunt0c6b2d32015-03-26 17:46:29 -070048 return {x: mx, y: my};
Simon Huntfb8ea1f2015-02-24 21:38:09 -080049 }
50
Simon Hunt5aac2fc2015-06-09 12:34:07 -070051
52 function sq(x) { return x * x; }
53
54 function mdist(p, m) {
55 return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
56 }
57
58 function prox(dist) {
59 return dist / api.zoomer.scale();
60 }
61
62 function computeNearestNode(mouse) {
63 var proximity = prox(30),
Simon Hunt0c6b2d32015-03-26 17:46:29 -070064 nearest = null,
65 minDist;
Simon Huntfb8ea1f2015-02-24 21:38:09 -080066
Simon Hunt5aac2fc2015-06-09 12:34:07 -070067 if (network.nodes.length) {
68 minDist = proximity * 2;
Simon Huntfb8ea1f2015-02-24 21:38:09 -080069
Simon Hunt5aac2fc2015-06-09 12:34:07 -070070 network.nodes.forEach(function (d) {
71 var dist;
72
73 if (!api.showHosts() && d.class === 'host') {
74 return; // skip hidden hosts
75 }
76
77 dist = mdist({x: d.x, y: d.y}, mouse);
78 if (dist < minDist && dist < proximity) {
79 minDist = dist;
80 nearest = d;
81 }
82 });
Simon Hunt9e2104c2015-02-26 10:48:59 -080083 }
Simon Hunt5aac2fc2015-06-09 12:34:07 -070084 return nearest;
85 }
86
87
88 function computeNearestLink(mouse) {
89 var proximity = prox(30),
90 nearest = null,
91 minDist;
Simon Hunt9e2104c2015-02-26 10:48:59 -080092
Simon Huntfb8ea1f2015-02-24 21:38:09 -080093 function pdrop(line, mouse) {
94 var x1 = line.x1,
95 y1 = line.y1,
96 x2 = line.x2,
97 y2 = line.y2,
98 x3 = mouse.x,
99 y3 = mouse.y,
100 k = ((y2-y1) * (x3-x1) - (x2-x1) * (y3-y1)) /
101 (sq(y2-y1) + sq(x2-x1)),
102 x4 = x3 - k * (y2-y1),
103 y4 = y3 + k * (x2-x1);
104 return {x:x4, y:y4};
105 }
106
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800107 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;
Simon Hunt5f361082015-02-25 11:36:38 -0800112 // line intersects, but are we close enough?
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800113 return mdist(p, m) <= proximity;
114 }
115
116 if (network.links.length) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800117 minDist = proximity * 2;
118
119 network.links.forEach(function (d) {
120 if (!api.showHosts() && d.type() === 'hostLink') {
121 return; // skip hidden host links
122 }
123
Kavitha Alagesan21314f02016-11-17 11:59:11 +0530124 if(d.position != null)
125 {
126 var line = d.position,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800127 point = pdrop(line, mouse),
128 hit = lineHit(line, point, mouse),
129 dist;
Kavitha Alagesan21314f02016-11-17 11:59:11 +0530130 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800131
132 if (hit) {
133 dist = mdist(point, mouse);
134 if (dist < minDist) {
135 minDist = dist;
136 nearest = d;
137 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800138 }
139 });
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800140 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700141 return nearest;
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800142 }
143
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700144 function enhanceLink(ldata) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800145 // if the new link is same as old link, do nothing
146 if (enhancedLink && ldata && enhancedLink.key === ldata.key) return;
147
148 // first, unenhance the currently enhanced link
149 if (enhancedLink) {
150 unenhance(enhancedLink);
151 }
152 enhancedLink = ldata;
153 if (enhancedLink) {
154 enhance(enhancedLink);
155 }
156 }
157
158 function unenhance(d) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800159 // guard against link element not set
160 if (d.el) {
161 d.el.classed('enhanced', false);
162 }
Simon Hunt1a5301e2015-02-25 15:31:25 -0800163 api.portLabelG().selectAll('.portLabel').remove();
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800164 }
165
166 function enhance(d) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800167 var data = [],
168 point;
169
170 // guard against link element not set
171 if (!d.el) return;
172
Simon Huntd5264122015-02-25 10:17:43 -0800173 d.el.classed('enhanced', true);
Simon Hunt1a5301e2015-02-25 15:31:25 -0800174
Simon Hunt8ae474b2015-02-25 15:39:14 -0800175 // Define port label data objects.
176 // NOTE: src port is absent in the case of host-links.
177
Simon Hunt969b3c92015-02-25 18:11:31 -0800178 point = locatePortLabel(d);
179 angular.extend(point, {
Simon Hunt8ae474b2015-02-25 15:39:14 -0800180 id: 'topo-port-tgt',
Simon Hunt969b3c92015-02-25 18:11:31 -0800181 num: d.tgtPort
182 });
183 data.push(point);
Simon Hunt8ae474b2015-02-25 15:39:14 -0800184
185 if (d.srcPort) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800186 point = locatePortLabel(d, 1);
187 angular.extend(point, {
Simon Hunt1a5301e2015-02-25 15:31:25 -0800188 id: 'topo-port-src',
Simon Hunt969b3c92015-02-25 18:11:31 -0800189 num: d.srcPort
Simon Hunt8ae474b2015-02-25 15:39:14 -0800190 });
Simon Hunt969b3c92015-02-25 18:11:31 -0800191 data.push(point);
Simon Hunt8ae474b2015-02-25 15:39:14 -0800192 }
Simon Hunt1a5301e2015-02-25 15:31:25 -0800193
194 td3.applyPortLabels(data, api.portLabelG());
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800195 }
196
Simon Hunt969b3c92015-02-25 18:11:31 -0800197 function locatePortLabel(link, src) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700198 var offset = 32,
199 pos = link.position,
200 nearX = src ? pos.x1 : pos.x2,
Steven Burrowsaefd5862016-10-19 14:20:46 -0500201 nearY = src ? pos.y1 : pos.y2,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700202 farX = src ? pos.x2 : pos.x1,
Steven Burrowsaefd5862016-10-19 14:20:46 -0500203 farY = src ? pos.y2 : pos.y1;
Simon Hunt969b3c92015-02-25 18:11:31 -0800204
205 function dist(x, y) { return Math.sqrt(x*x + y*y); }
206
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700207 var dx = farX - nearX,
208 dy = farY - nearY,
Simon Hunt969b3c92015-02-25 18:11:31 -0800209 k = offset / dist(dx, dy);
210
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700211 return {x: k * dx + nearX, y: k * dy + nearY};
Simon Hunt969b3c92015-02-25 18:11:31 -0800212 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800213
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700214 function selectLink(ldata) {
215 // if the new link is same as old link, do nothing
Prince Pereira46c82d42016-09-19 13:30:50 +0530216 if (d3.event.shiftKey && ldata.el.classed('selected')) {
217 unselLink(ldata);
218 return;
219 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700220
Prince Pereira46c82d42016-09-19 13:30:50 +0530221 if (d3.event.shiftKey && !ldata.el.classed('selected')) {
222 selLink(ldata);
223 return;
224 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700225
Prince Pereira46c82d42016-09-19 13:30:50 +0530226 tss.deselectAll();
227
Kavitha Alagesan21314f02016-11-17 11:59:11 +0530228 if(ldata != null)
229 {
Prince Pereira46c82d42016-09-19 13:30:50 +0530230
Kavitha Alagesan21314f02016-11-17 11:59:11 +0530231 if (!ldata.el.classed('selected')) {
232 selLink(ldata);
233 return;
234 }
235
236 if (ldata.el.classed('selected')) {
237 unselLink(ldata);
238 return;
239 }
Prince Pereira46c82d42016-09-19 13:30:50 +0530240 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700241 }
242
243 function unselLink(d) {
244 // guard against link element not set
245 if (d.el) {
246 d.el.classed('selected', false);
Prince Pereira46c82d42016-09-19 13:30:50 +0530247 delete selectedLinks[d.key];
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700248 }
249 }
250
251 function selLink(d) {
252 // guard against link element not set
253 if (!d.el) return;
254
255 d.el.classed('selected', true);
Prince Pereira46c82d42016-09-19 13:30:50 +0530256 selectedLinks[d.key] = {key : d};
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700257
Simon Hunt5c1a9382016-06-01 19:35:35 -0700258 tps.displayLink(d, tov.hooks.modifyLinkData);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700259 tps.displaySomething();
260 }
261
262 // ====== MOUSE EVENT HANDLERS ======
263
264 function mouseMoveHandler() {
265 var mp = getLogicalMousePosition(this),
266 link = computeNearestLink(mp);
267 enhanceLink(link);
268 }
269
270 function mouseClickHandler() {
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700271 var mp, link, node;
Prince Pereira46c82d42016-09-19 13:30:50 +0530272 if (!d3.event.shiftKey) {
273 deselectAllLinks();
274 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700275
276 if (!tss.clickConsumed()) {
277 mp = getLogicalMousePosition(this);
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700278 node = computeNearestNode(mp);
279 if (node) {
280 $log.debug('found nearest node:', node.labels[1]);
281 tss.selectObject(node);
282 } else {
283 link = computeNearestLink(mp);
284 selectLink(link);
Prince Pereira46c82d42016-09-19 13:30:50 +0530285 tss.selectObject(link);
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700286 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700287 }
288 }
289
290
291 // ======================
292
Simon Huntfcbde892015-04-16 12:05:28 -0700293 function togglePorts(x) {
294 var kev = (x === 'keyev'),
295 on = kev ? !showPorts : !!x,
296 what = on ? 'Enable' : 'Disable',
297 handler = on ? mouseMoveHandler : null;
Simon Hunt9e2104c2015-02-26 10:48:59 -0800298
Simon Huntfcbde892015-04-16 12:05:28 -0700299 showPorts = on;
Simon Hunt9e2104c2015-02-26 10:48:59 -0800300
Simon Huntfcbde892015-04-16 12:05:28 -0700301 if (!on) {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700302 enhanceLink(null);
Simon Hunt9e2104c2015-02-26 10:48:59 -0800303 }
304 svg.on('mousemove', handler);
305 flash.flash(what + ' port highlighting');
Simon Huntfcbde892015-04-16 12:05:28 -0700306 return on;
Simon Hunt9e2104c2015-02-26 10:48:59 -0800307 }
308
Prince Pereira46c82d42016-09-19 13:30:50 +0530309 function deselectAllLinks() {
310
311 if (Object.keys(selectedLinks).length > 0) {
312 network.links.forEach(function (d) {
313 if (selectedLinks[d.key]) {
314 unselLink(d);
315 }
316 });
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700317 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700318 }
319
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800320 // ==========================
321 // Module definition
322
323 angular.module('ovTopo')
324 .factory('TopoLinkService',
Simon Hunt9e2104c2015-02-26 10:48:59 -0800325 ['$log', 'FnService', 'SvgUtilService', 'ThemeService', 'FlashService',
Simon Hunt5c1a9382016-06-01 19:35:35 -0700326 'TopoSelectService', 'TopoPanelService', 'TopoOverlayService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800327
Simon Hunt5c1a9382016-06-01 19:35:35 -0700328 function (_$log_, _fs_, _sus_, _ts_, _flash_, _tss_, _tps_, _tov_) {
Simon Hunt9e2104c2015-02-26 10:48:59 -0800329 $log = _$log_;
330 fs = _fs_;
331 sus = _sus_;
332 ts = _ts_;
333 flash = _flash_;
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700334 tss = _tss_;
335 tps = _tps_;
Simon Hunt5c1a9382016-06-01 19:35:35 -0700336 tov = _tov_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800337
Simon Hunt9e2104c2015-02-26 10:48:59 -0800338 function initLink(_api_, _td3_) {
339 api = _api_;
340 td3 = _td3_;
341 svg = api.svg;
342 network = api.network;
Bri Prebilic Coled8745462015-06-01 16:08:57 -0700343 if (showPorts && !fs.isMobile()) {
Simon Hunt9e2104c2015-02-26 10:48:59 -0800344 svg.on('mousemove', mouseMoveHandler);
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800345 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700346 svg.on('click', mouseClickHandler);
Simon Hunt9e2104c2015-02-26 10:48:59 -0800347 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800348
Simon Hunt9e2104c2015-02-26 10:48:59 -0800349 function destroyLink() {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700350 // unconditionally remove any event handlers
Simon Hunt9e2104c2015-02-26 10:48:59 -0800351 svg.on('mousemove', null);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700352 svg.on('click', null);
Simon Hunt9e2104c2015-02-26 10:48:59 -0800353 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800354
Simon Hunt9e2104c2015-02-26 10:48:59 -0800355 return {
356 initLink: initLink,
357 destroyLink: destroyLink,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700358 togglePorts: togglePorts,
Prince Pereira46c82d42016-09-19 13:30:50 +0530359 deselectAllLinks: deselectAllLinks
Simon Hunt9e2104c2015-02-26 10:48:59 -0800360 };
361 }]);
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800362}());