blob: 05041c4c7adc9a044dadb39c2edb811340f8144d [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
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;
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) {
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) {
Simon Huntd5264122015-02-25 10:17:43 -0800192 d.el.classed('enhanced', false);
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800193 }
194
195 function enhance(d) {
Simon Huntd5264122015-02-25 10:17:43 -0800196 d.el.classed('enhanced', true);
Simon Hunt5f361082015-02-25 11:36:38 -0800197 $log.debug('[' + (d.srcPort || 'H') + '] ---> [' + d.tgtPort + ']', d.key);
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800198 }
199
200
201
202
203 // ==========================
204 // Module definition
205
206 angular.module('ovTopo')
207 .factory('TopoLinkService',
208 ['$log', 'FnService', 'SvgUtilService', 'ThemeService',
209
210 function (_$log_, _fs_, _sus_, _ts_) {
211 $log = _$log_;
212 fs = _fs_;
213 sus = _sus_;
214 ts = _ts_;
215
216 function initLink(_api_) {
217 api = _api_;
218 svg = api.svg;
219 network = api.network;
220 setupMouse(api.forceG, api.zoomer);
221 }
222
223 function destroyLink() {
224 svg.on('mouseenter', null)
225 .on('mouseleave', null)
226 .on('mousemove', null);
227 }
228
229 return {
230 initLink: initLink,
231 destroyLink: destroyLink
232 };
233 }]);
234}());