blob: ec4cc53365b24363be9b26923fabb0116a45e254 [file] [log] [blame]
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001/*
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
Simon Hunt0c6b2d32015-03-26 17:46:29 -070026 var $log, fs, sus, ts, flash, tss, tps;
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
34 selectedLink = null; // the link which is currently 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 Hunt9e2104c2015-02-26 10:48:59 -080051 function computeNearestLink(mouse) {
52 var proximity = 30 / api.zoomer.scale(),
Simon Hunt0c6b2d32015-03-26 17:46:29 -070053 nearest = null,
54 minDist;
Simon Huntfb8ea1f2015-02-24 21:38:09 -080055
56 function sq(x) { return x * x; }
57
Simon Hunt9e2104c2015-02-26 10:48:59 -080058 function mdist(p, m) {
59 return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
60 }
61
Simon Huntfb8ea1f2015-02-24 21:38:09 -080062 function pdrop(line, mouse) {
63 var x1 = line.x1,
64 y1 = line.y1,
65 x2 = line.x2,
66 y2 = line.y2,
67 x3 = mouse.x,
68 y3 = mouse.y,
69 k = ((y2-y1) * (x3-x1) - (x2-x1) * (y3-y1)) /
70 (sq(y2-y1) + sq(x2-x1)),
71 x4 = x3 - k * (y2-y1),
72 y4 = y3 + k * (x2-x1);
73 return {x:x4, y:y4};
74 }
75
Simon Huntfb8ea1f2015-02-24 21:38:09 -080076 function lineSeg(d) {
77 return {
78 x1: d.source.x,
79 y1: d.source.y,
80 x2: d.target.x,
81 y2: d.target.y
82 };
83 }
84
85 function lineHit(line, p, m) {
86 if (p.x < line.x1 && p.x < line.x2) return false;
87 if (p.x > line.x1 && p.x > line.x2) return false;
88 if (p.y < line.y1 && p.y < line.y2) return false;
89 if (p.y > line.y1 && p.y > line.y2) return false;
Simon Hunt5f361082015-02-25 11:36:38 -080090 // line intersects, but are we close enough?
Simon Huntfb8ea1f2015-02-24 21:38:09 -080091 return mdist(p, m) <= proximity;
92 }
93
94 if (network.links.length) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -080095 minDist = proximity * 2;
96
97 network.links.forEach(function (d) {
98 if (!api.showHosts() && d.type() === 'hostLink') {
99 return; // skip hidden host links
100 }
101
102 var line = lineSeg(d),
103 point = pdrop(line, mouse),
104 hit = lineHit(line, point, mouse),
105 dist;
106
107 if (hit) {
108 dist = mdist(point, mouse);
109 if (dist < minDist) {
110 minDist = dist;
111 nearest = d;
112 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800113 }
114 });
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800115 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700116 return nearest;
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800117 }
118
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700119 function enhanceLink(ldata) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800120 // if the new link is same as old link, do nothing
121 if (enhancedLink && ldata && enhancedLink.key === ldata.key) return;
122
123 // first, unenhance the currently enhanced link
124 if (enhancedLink) {
125 unenhance(enhancedLink);
126 }
127 enhancedLink = ldata;
128 if (enhancedLink) {
129 enhance(enhancedLink);
130 }
131 }
132
133 function unenhance(d) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800134 // guard against link element not set
135 if (d.el) {
136 d.el.classed('enhanced', false);
137 }
Simon Hunt1a5301e2015-02-25 15:31:25 -0800138 api.portLabelG().selectAll('.portLabel').remove();
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800139 }
140
141 function enhance(d) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800142 var data = [],
143 point;
144
145 // guard against link element not set
146 if (!d.el) return;
147
Simon Huntd5264122015-02-25 10:17:43 -0800148 d.el.classed('enhanced', true);
Simon Hunt1a5301e2015-02-25 15:31:25 -0800149
Simon Hunt8ae474b2015-02-25 15:39:14 -0800150 // Define port label data objects.
151 // NOTE: src port is absent in the case of host-links.
152
Simon Hunt969b3c92015-02-25 18:11:31 -0800153 point = locatePortLabel(d);
154 angular.extend(point, {
Simon Hunt8ae474b2015-02-25 15:39:14 -0800155 id: 'topo-port-tgt',
Simon Hunt969b3c92015-02-25 18:11:31 -0800156 num: d.tgtPort
157 });
158 data.push(point);
Simon Hunt8ae474b2015-02-25 15:39:14 -0800159
160 if (d.srcPort) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800161 point = locatePortLabel(d, 1);
162 angular.extend(point, {
Simon Hunt1a5301e2015-02-25 15:31:25 -0800163 id: 'topo-port-src',
Simon Hunt969b3c92015-02-25 18:11:31 -0800164 num: d.srcPort
Simon Hunt8ae474b2015-02-25 15:39:14 -0800165 });
Simon Hunt969b3c92015-02-25 18:11:31 -0800166 data.push(point);
Simon Hunt8ae474b2015-02-25 15:39:14 -0800167 }
Simon Hunt1a5301e2015-02-25 15:31:25 -0800168
169 td3.applyPortLabels(data, api.portLabelG());
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800170 }
171
Simon Hunt969b3c92015-02-25 18:11:31 -0800172 function locatePortLabel(link, src) {
173 var near = src ? 'source' : 'target',
174 far = src ? 'target' : 'source',
175 ln = link[near],
176 lf = link[far],
177 offset = 32;
178
179 function dist(x, y) { return Math.sqrt(x*x + y*y); }
180
181 var dx = lf.x - ln.x,
182 dy = lf.y - ln.y,
183 k = offset / dist(dx, dy);
184
185 return {x: k * dx + ln.x, y: k * dy + ln.y};
186 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800187
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700188
189 function selectLink(ldata) {
190 // if the new link is same as old link, do nothing
191 if (selectedLink && ldata && selectedLink.key === ldata.key) return;
192
193 // make sure no nodes are selected
194 tss.deselectAll();
195
196 // first, unenhance the currently enhanced link
197 if (selectedLink) {
198 unselLink(selectedLink);
199 }
200 selectedLink = ldata;
201 if (selectedLink) {
202 selLink(selectedLink);
203 }
204 }
205
206 function unselLink(d) {
207 // guard against link element not set
208 if (d.el) {
209 d.el.classed('selected', false);
210 }
211 }
212
213 function selLink(d) {
214 // guard against link element not set
215 if (!d.el) return;
216
217 d.el.classed('selected', true);
218
219 tps.displayLink(d);
220 tps.displaySomething();
221 }
222
223 // ====== MOUSE EVENT HANDLERS ======
224
225 function mouseMoveHandler() {
226 var mp = getLogicalMousePosition(this),
227 link = computeNearestLink(mp);
228 enhanceLink(link);
229 }
230
231 function mouseClickHandler() {
232 var mp, link;
233
234 if (!tss.clickConsumed()) {
235 mp = getLogicalMousePosition(this);
236 link = computeNearestLink(mp);
237 selectLink(link);
238 }
239 }
240
241
242 // ======================
243
Simon Huntfcbde892015-04-16 12:05:28 -0700244 function togglePorts(x) {
245 var kev = (x === 'keyev'),
246 on = kev ? !showPorts : !!x,
247 what = on ? 'Enable' : 'Disable',
248 handler = on ? mouseMoveHandler : null;
Simon Hunt9e2104c2015-02-26 10:48:59 -0800249
Simon Huntfcbde892015-04-16 12:05:28 -0700250 showPorts = on;
Simon Hunt9e2104c2015-02-26 10:48:59 -0800251
Simon Huntfcbde892015-04-16 12:05:28 -0700252 if (!on) {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700253 enhanceLink(null);
Simon Hunt9e2104c2015-02-26 10:48:59 -0800254 }
255 svg.on('mousemove', handler);
256 flash.flash(what + ' port highlighting');
Simon Huntfcbde892015-04-16 12:05:28 -0700257 return on;
Simon Hunt9e2104c2015-02-26 10:48:59 -0800258 }
259
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700260 function deselectLink() {
261 if (selectedLink) {
262 unselLink(selectedLink);
263 selectedLink = null;
264 return true;
265 }
266 return false;
267 }
268
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800269 // ==========================
270 // Module definition
271
272 angular.module('ovTopo')
273 .factory('TopoLinkService',
Simon Hunt9e2104c2015-02-26 10:48:59 -0800274 ['$log', 'FnService', 'SvgUtilService', 'ThemeService', 'FlashService',
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700275 'TopoSelectService', 'TopoPanelService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800276
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700277 function (_$log_, _fs_, _sus_, _ts_, _flash_, _tss_, _tps_) {
Simon Hunt9e2104c2015-02-26 10:48:59 -0800278 $log = _$log_;
279 fs = _fs_;
280 sus = _sus_;
281 ts = _ts_;
282 flash = _flash_;
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700283 tss = _tss_;
284 tps = _tps_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800285
Simon Hunt9e2104c2015-02-26 10:48:59 -0800286 function initLink(_api_, _td3_) {
287 api = _api_;
288 td3 = _td3_;
289 svg = api.svg;
290 network = api.network;
Bri Prebilic Coled8745462015-06-01 16:08:57 -0700291 if (showPorts && !fs.isMobile()) {
Simon Hunt9e2104c2015-02-26 10:48:59 -0800292 svg.on('mousemove', mouseMoveHandler);
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800293 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700294 svg.on('click', mouseClickHandler);
Simon Hunt9e2104c2015-02-26 10:48:59 -0800295 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800296
Simon Hunt9e2104c2015-02-26 10:48:59 -0800297 function destroyLink() {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700298 // unconditionally remove any event handlers
Simon Hunt9e2104c2015-02-26 10:48:59 -0800299 svg.on('mousemove', null);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700300 svg.on('click', null);
Simon Hunt9e2104c2015-02-26 10:48:59 -0800301 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800302
Simon Hunt9e2104c2015-02-26 10:48:59 -0800303 return {
304 initLink: initLink,
305 destroyLink: destroyLink,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700306 togglePorts: togglePorts,
307 deselectLink: deselectLink
Simon Hunt9e2104c2015-02-26 10:48:59 -0800308 };
309 }]);
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800310}());