blob: 3686bc5ae62b28a9a3d29f439f04707ba4d52618 [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 Hunt9e2104c2015-02-26 10:48:59 -080026 var $log, fs, sus, ts, flash;
Simon Huntfb8ea1f2015-02-24 21:38:09 -080027
28 var api,
Simon Hunt1a5301e2015-02-25 15:31:25 -080029 td3,
Simon Huntfb8ea1f2015-02-24 21:38:09 -080030 network,
31 enhancedLink = null; // the link which the mouse is hovering over
32
33 // SVG elements;
Simon Hunt9e2104c2015-02-26 10:48:59 -080034 var svg;
35
36 // internal state
37 var showPorts = true; // enable port highlighting by default
Simon Huntfb8ea1f2015-02-24 21:38:09 -080038
39
40 // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ========
41
Simon Hunt9e2104c2015-02-26 10:48:59 -080042 function mouseMoveHandler() {
43 var m = d3.mouse(this),
44 sc = api.zoomer.scale(),
45 tr = api.zoomer.translate(),
46 mx = (m[0] - tr[0]) / sc,
47 my = (m[1] - tr[1]) / sc;
48 computeNearestLink({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(),
53 nearest, minDist;
Simon Huntfb8ea1f2015-02-24 21:38:09 -080054
55 function sq(x) { return x * x; }
56
Simon Hunt9e2104c2015-02-26 10:48:59 -080057 function mdist(p, m) {
58 return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
59 }
60
Simon Huntfb8ea1f2015-02-24 21:38:09 -080061 function pdrop(line, mouse) {
62 var x1 = line.x1,
63 y1 = line.y1,
64 x2 = line.x2,
65 y2 = line.y2,
66 x3 = mouse.x,
67 y3 = mouse.y,
68 k = ((y2-y1) * (x3-x1) - (x2-x1) * (y3-y1)) /
69 (sq(y2-y1) + sq(x2-x1)),
70 x4 = x3 - k * (y2-y1),
71 y4 = y3 + k * (x2-x1);
72 return {x:x4, y:y4};
73 }
74
Simon Huntfb8ea1f2015-02-24 21:38:09 -080075 function lineSeg(d) {
76 return {
77 x1: d.source.x,
78 y1: d.source.y,
79 x2: d.target.x,
80 y2: d.target.y
81 };
82 }
83
84 function lineHit(line, p, m) {
85 if (p.x < line.x1 && p.x < line.x2) return false;
86 if (p.x > line.x1 && p.x > line.x2) return false;
87 if (p.y < line.y1 && p.y < line.y2) return false;
88 if (p.y > line.y1 && p.y > line.y2) return false;
Simon Hunt5f361082015-02-25 11:36:38 -080089 // line intersects, but are we close enough?
Simon Huntfb8ea1f2015-02-24 21:38:09 -080090 return mdist(p, m) <= proximity;
91 }
92
93 if (network.links.length) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -080094 nearest = null;
95 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 });
115
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800116 enhanceNearestLink(nearest);
117 }
118 }
119
120
121 function enhanceNearestLink(ldata) {
122 // if the new link is same as old link, do nothing
123 if (enhancedLink && ldata && enhancedLink.key === ldata.key) return;
124
125 // first, unenhance the currently enhanced link
126 if (enhancedLink) {
127 unenhance(enhancedLink);
128 }
129 enhancedLink = ldata;
130 if (enhancedLink) {
131 enhance(enhancedLink);
132 }
133 }
134
135 function unenhance(d) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800136 // guard against link element not set
137 if (d.el) {
138 d.el.classed('enhanced', false);
139 }
Simon Hunt1a5301e2015-02-25 15:31:25 -0800140 api.portLabelG().selectAll('.portLabel').remove();
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800141 }
142
143 function enhance(d) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800144 var data = [],
145 point;
146
147 // guard against link element not set
148 if (!d.el) return;
149
Simon Huntd5264122015-02-25 10:17:43 -0800150 d.el.classed('enhanced', true);
Simon Hunt5f361082015-02-25 11:36:38 -0800151 $log.debug('[' + (d.srcPort || 'H') + '] ---> [' + d.tgtPort + ']', d.key);
Simon Hunt1a5301e2015-02-25 15:31:25 -0800152
Simon Hunt8ae474b2015-02-25 15:39:14 -0800153 // Define port label data objects.
154 // NOTE: src port is absent in the case of host-links.
155
Simon Hunt969b3c92015-02-25 18:11:31 -0800156 point = locatePortLabel(d);
157 angular.extend(point, {
Simon Hunt8ae474b2015-02-25 15:39:14 -0800158 id: 'topo-port-tgt',
Simon Hunt969b3c92015-02-25 18:11:31 -0800159 num: d.tgtPort
160 });
161 data.push(point);
Simon Hunt8ae474b2015-02-25 15:39:14 -0800162
163 if (d.srcPort) {
Simon Hunt969b3c92015-02-25 18:11:31 -0800164 point = locatePortLabel(d, 1);
165 angular.extend(point, {
Simon Hunt1a5301e2015-02-25 15:31:25 -0800166 id: 'topo-port-src',
Simon Hunt969b3c92015-02-25 18:11:31 -0800167 num: d.srcPort
Simon Hunt8ae474b2015-02-25 15:39:14 -0800168 });
Simon Hunt969b3c92015-02-25 18:11:31 -0800169 data.push(point);
Simon Hunt8ae474b2015-02-25 15:39:14 -0800170 }
Simon Hunt1a5301e2015-02-25 15:31:25 -0800171
172 td3.applyPortLabels(data, api.portLabelG());
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800173 }
174
Simon Hunt969b3c92015-02-25 18:11:31 -0800175 function locatePortLabel(link, src) {
176 var near = src ? 'source' : 'target',
177 far = src ? 'target' : 'source',
178 ln = link[near],
179 lf = link[far],
180 offset = 32;
181
182 function dist(x, y) { return Math.sqrt(x*x + y*y); }
183
184 var dx = lf.x - ln.x,
185 dy = lf.y - ln.y,
186 k = offset / dist(dx, dy);
187
188 return {x: k * dx + ln.x, y: k * dy + ln.y};
189 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800190
Simon Hunt9e2104c2015-02-26 10:48:59 -0800191 function togglePorts() {
192 showPorts = !showPorts;
193
194 var what = showPorts ? 'Enable' : 'Disable',
195 handler = showPorts ? mouseMoveHandler : null;
196
197 if (!showPorts) {
198 enhanceNearestLink(null);
199 }
200 svg.on('mousemove', handler);
201 flash.flash(what + ' port highlighting');
202 }
203
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800204 // ==========================
205 // Module definition
206
207 angular.module('ovTopo')
208 .factory('TopoLinkService',
Simon Hunt9e2104c2015-02-26 10:48:59 -0800209 ['$log', 'FnService', 'SvgUtilService', 'ThemeService', 'FlashService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800210
Simon Hunt9e2104c2015-02-26 10:48:59 -0800211 function (_$log_, _fs_, _sus_, _ts_, _flash_) {
212 $log = _$log_;
213 fs = _fs_;
214 sus = _sus_;
215 ts = _ts_;
216 flash = _flash_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800217
Simon Hunt9e2104c2015-02-26 10:48:59 -0800218 function initLink(_api_, _td3_) {
219 api = _api_;
220 td3 = _td3_;
221 svg = api.svg;
222 network = api.network;
223 if (showPorts) {
224 svg.on('mousemove', mouseMoveHandler);
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800225 }
Simon Hunt9e2104c2015-02-26 10:48:59 -0800226 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800227
Simon Hunt9e2104c2015-02-26 10:48:59 -0800228 function destroyLink() {
229 // unconditionally remove any mousemove event handler
230 svg.on('mousemove', null);
231 }
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800232
Simon Hunt9e2104c2015-02-26 10:48:59 -0800233 return {
234 initLink: initLink,
235 destroyLink: destroyLink,
236 togglePorts: togglePorts
237 };
238 }]);
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800239}());