blob: d89efd7e0cedf126de153968216232786d5772ac [file] [log] [blame]
Simon Hunta4242de2015-02-24 17:11:55 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Simon Hunta4242de2015-02-24 17:11:55 -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 D3 Module.
19 Functions for manipulating the D3 visualizations of the Topology
20 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Steven Burrows1c2a9682017-07-14 16:52:46 +010026 var sus, is, ts, ps, ttbs;
Simon Hunta4242de2015-02-24 17:11:55 -080027
Simon Hunte2d9dc72017-08-10 15:21:04 -070028 // function to be replaced by the localization bundle function
29 var topoLion = function (x) {
30 return '#tfs#' + x + '#';
31 };
32
Simon Hunta4242de2015-02-24 17:11:55 -080033 // api to topoForce
Steven Burrowsf17f0ab2017-04-11 11:03:58 -070034 var zoomer, api;
Simon Hunta4242de2015-02-24 17:11:55 -080035 /*
36 node() // get ref to D3 selection of nodes
37 link() // get ref to D3 selection of links
38 linkLabel() // get ref to D3 selection of link labels
39 instVisible() // true if instances panel is visible
40 posNode() // position node
41 showHosts() // true if hosts are to be shown
42 restyleLinkElement() // update link styles based on backing data
43 updateLinkLabelModel() // update backing data for link labels
44 */
45
46 // configuration
Simon Hunta5487ad2016-06-16 13:10:41 -070047 var devIconDim = 36,
Simon Hunte9062fc2016-12-22 11:40:06 -080048 devColorDim = 32,
Simon Hunta5487ad2016-06-16 13:10:41 -070049 labelPad = 4,
50 hostRadius = 14,
51 badgeConfig = {
Simon Hunt5674db92015-10-22 16:12:48 -070052 radius: 12,
Simon Hunt004fc2c2015-10-23 11:55:58 -070053 yoff: 5,
Steven Burrows1c2a9682017-07-14 16:52:46 +010054 gdelta: 10,
Simon Hunta5487ad2016-06-16 13:10:41 -070055 },
56 halfDevIcon = devIconDim / 2,
57 devBadgeOff = { dx: -halfDevIcon, dy: -halfDevIcon },
58 hostBadgeOff = { dx: -hostRadius, dy: -hostRadius },
Steven Burrowsf17f0ab2017-04-11 11:03:58 -070059 portLabelDim = 30,
Simon Hunta5487ad2016-06-16 13:10:41 -070060 status = {
61 i: 'badgeInfo',
62 w: 'badgeWarn',
Steven Burrows1c2a9682017-07-14 16:52:46 +010063 e: 'badgeError',
Simon Huntf44d7262016-06-14 14:46:56 -070064 };
65
Simon Hunt1eee51d2016-02-26 19:12:13 -080066 // NOTE: this type of hack should go away once we have implemented
67 // the server-side UiModel code.
68 // {virtual -> cord} is for the E-CORD demo at ONS 2016
69 var remappedDeviceTypes = {
Simon Hunte9062fc2016-12-22 11:40:06 -080070 virtual: 'cord',
71
72 // for now, map to the new glyphs via this lookup.
73 // may have to find a better way to do this...
74 'switch': 'm_switch',
75 roadm: 'm_roadm',
76 otn: 'm_otn',
Andrea Campanella1c24fb92018-12-20 16:43:59 +010077 ols: 'm_roadm',
78 terminal_device: 'm_otn',
Simon Hunte9062fc2016-12-22 11:40:06 -080079 roadm_otn: 'm_roadm_otn',
80 fiber_switch: 'm_fiberSwitch',
81 microwave: 'm_microwave',
82 };
83
84 var remappedHostTypes = {
85 router: 'm_router',
86 endstation: 'm_endstation',
Steven Burrows1c2a9682017-07-14 16:52:46 +010087 bgpSpeaker: 'm_bgpSpeaker',
Simon Hunt1eee51d2016-02-26 19:12:13 -080088 };
89
90 function mapDeviceTypeToGlyph(type) {
91 return remappedDeviceTypes[type] || type || 'unknown';
92 }
93
Simon Hunte9062fc2016-12-22 11:40:06 -080094 function mapHostTypeToGlyph(type) {
95 return remappedHostTypes[type] || type || 'unknown';
96 }
97
Simon Hunt5674db92015-10-22 16:12:48 -070098 function badgeStatus(badge) {
99 return status[badge.status] || status.i;
100 }
101
Simon Hunta4242de2015-02-24 17:11:55 -0800102 // internal state
103 var deviceLabelIndex = 0,
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800104 hostLabelIndex = 0,
105 linkLabelsEnabled = true;
Simon Hunta4242de2015-02-24 17:11:55 -0800106
Simon Hunta5487ad2016-06-16 13:10:41 -0700107 // note: these are the device icon colors without affinity (no master)
Simon Hunta4242de2015-02-24 17:11:55 -0800108 var dColTheme = {
109 light: {
Simon Huntf44d7262016-06-14 14:46:56 -0700110 online: '#444444',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100111 offline: '#cccccc',
Simon Hunta4242de2015-02-24 17:11:55 -0800112 },
113 dark: {
Simon Huntf44d7262016-06-14 14:46:56 -0700114 // TODO: theme
115 online: '#444444',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100116 offline: '#cccccc',
117 },
Simon Hunta4242de2015-02-24 17:11:55 -0800118 };
119
Simon Huntf44d7262016-06-14 14:46:56 -0700120 function devGlyphColor(d) {
121 var o = d.online,
122 id = d.master,
123 otag = o ? 'online' : 'offline';
124 return o ? sus.cat7().getColor(id, 0, ts.theme())
125 : dColTheme[ts.theme()][otag];
Simon Hunta4242de2015-02-24 17:11:55 -0800126 }
127
128 function setDeviceColor(d) {
Simon Hunte9062fc2016-12-22 11:40:06 -0800129 // want to color the square rectangle (no longer the 'use' glyph)
Steven Burrows1c2a9682017-07-14 16:52:46 +0100130 d.el.selectAll('rect').filter(function (d, i) { return i === 1; })
Simon Huntf44d7262016-06-14 14:46:56 -0700131 .style('fill', devGlyphColor(d));
Simon Hunta4242de2015-02-24 17:11:55 -0800132 }
133
Simon Hunta4242de2015-02-24 17:11:55 -0800134 function incDevLabIndex() {
Steven Burrowsbbe3dda2016-09-26 14:41:59 -0700135 setDevLabIndex(deviceLabelIndex+1);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100136 switch (deviceLabelIndex) {
Simon Hunte2d9dc72017-08-10 15:21:04 -0700137 case 0: return topoLion('fl_device_labels_hide');
138 case 1: return topoLion('fl_device_labels_show_friendly');
139 case 2: return topoLion('fl_device_labels_show_id');
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700140 }
Simon Hunta4242de2015-02-24 17:11:55 -0800141 }
142
Thomas Vachuska0af26912016-03-21 21:37:30 -0700143 function setDevLabIndex(mode) {
144 deviceLabelIndex = mode % 3;
145 var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
146 p.dlbls = deviceLabelIndex;
147 ps.setPrefs('topo_prefs', p);
148 }
149
Simon Hunt10618f62017-06-15 19:30:52 -0700150 function incHostLabIndex() {
151 setHostLabIndex(hostLabelIndex+1);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100152 switch (hostLabelIndex) {
Simon Hunte2d9dc72017-08-10 15:21:04 -0700153 case 0: return topoLion('fl_host_labels_show_friendly');
154 case 1: return topoLion('fl_host_labels_show_ip');
155 case 2: return topoLion('fl_host_labels_show_mac');
Thomas Vachuska528ea952017-10-09 18:25:35 +0200156 case 3: return topoLion('fl_host_labels_hide');
Simon Hunt10618f62017-06-15 19:30:52 -0700157 }
158 }
159
160 function setHostLabIndex(mode) {
Thomas Vachuska528ea952017-10-09 18:25:35 +0200161 hostLabelIndex = mode % 4;
Simon Hunt10618f62017-06-15 19:30:52 -0700162 var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
163 p.hlbls = hostLabelIndex;
164 ps.setPrefs('topo_prefs', p);
165 }
166
Simon Hunta4242de2015-02-24 17:11:55 -0800167 function hostLabel(d) {
168 var idx = (hostLabelIndex < d.labels.length) ? hostLabelIndex : 0;
169 return d.labels[idx];
170 }
Simon Huntf44d7262016-06-14 14:46:56 -0700171
Simon Hunta4242de2015-02-24 17:11:55 -0800172 function deviceLabel(d) {
173 var idx = (deviceLabelIndex < d.labels.length) ? deviceLabelIndex : 0;
174 return d.labels[idx];
175 }
Simon Huntf44d7262016-06-14 14:46:56 -0700176
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800177 function toggleLinkLabels() {
178 linkLabelsEnabled = !linkLabelsEnabled;
179 return linkLabelsEnabled;
180 }
181
Simon Hunta4242de2015-02-24 17:11:55 -0800182 function trimLabel(label) {
183 return (label && label.trim()) || '';
184 }
185
Simon Huntf44d7262016-06-14 14:46:56 -0700186 function computeLabelWidth(n) {
187 var text = n.select('text'),
188 box = text.node().getBBox();
189 return box.width + labelPad * 2;
190 }
191
192 function iconBox(dim, labelWidth) {
Simon Hunta4242de2015-02-24 17:11:55 -0800193 return {
Simon Huntf44d7262016-06-14 14:46:56 -0700194 x: -dim/2,
195 y: -dim/2,
196 width: dim + labelWidth,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100197 height: dim,
198 };
Simon Hunta4242de2015-02-24 17:11:55 -0800199 }
200
Simon Hunt5674db92015-10-22 16:12:48 -0700201 function updateDeviceRendering(d) {
Simon Huntf44d7262016-06-14 14:46:56 -0700202 var node = d.el,
203 bdg = d.badge,
204 label = trimLabel(deviceLabel(d)),
205 labelWidth;
Simon Hunta4242de2015-02-24 17:11:55 -0800206
Simon Huntf44d7262016-06-14 14:46:56 -0700207 node.select('text').text(label);
208 labelWidth = label ? computeLabelWidth(node) : 0;
Simon Hunta4242de2015-02-24 17:11:55 -0800209
210 node.select('rect')
211 .transition()
Simon Huntf44d7262016-06-14 14:46:56 -0700212 .attr(iconBox(devIconDim, labelWidth));
Simon Hunta4242de2015-02-24 17:11:55 -0800213
Simon Hunt5674db92015-10-22 16:12:48 -0700214 if (bdg) {
Simon Hunta5487ad2016-06-16 13:10:41 -0700215 renderBadge(node, bdg, devBadgeOff);
Simon Hunte9343f32015-10-21 18:07:46 -0700216 }
217 }
218
Andrea Campanella52125412015-12-03 14:50:40 -0800219 function updateHostRendering(d) {
220 var node = d.el,
Simon Huntc2bfe332015-12-04 11:01:24 -0800221 bdg = d.badge;
Andrea Campanella52125412015-12-03 14:50:40 -0800222
223 updateHostLabel(d);
Andrea Campanella52125412015-12-03 14:50:40 -0800224
Andrea Campanella52125412015-12-03 14:50:40 -0800225 if (bdg) {
Simon Hunta5487ad2016-06-16 13:10:41 -0700226 renderBadge(node, bdg, hostBadgeOff);
Simon Huntc2bfe332015-12-04 11:01:24 -0800227 }
228 }
Andrea Campanella52125412015-12-03 14:50:40 -0800229
Simon Huntc2bfe332015-12-04 11:01:24 -0800230 function renderBadge(node, bdg, boff) {
231 var bsel,
232 bcr = badgeConfig.radius,
233 bcgd = badgeConfig.gdelta;
Andrea Campanella52125412015-12-03 14:50:40 -0800234
Simon Huntc2bfe332015-12-04 11:01:24 -0800235 node.select('g.badge').remove();
Andrea Campanella52125412015-12-03 14:50:40 -0800236
Simon Huntc2bfe332015-12-04 11:01:24 -0800237 bsel = node.append('g')
238 .classed('badge', true)
239 .classed(badgeStatus(bdg), true)
240 .attr('transform', sus.translate(boff.dx, boff.dy));
Andrea Campanella52125412015-12-03 14:50:40 -0800241
Simon Huntc2bfe332015-12-04 11:01:24 -0800242 bsel.append('circle')
243 .attr('r', bcr);
244
245 if (bdg.txt) {
246 bsel.append('text')
247 .attr('dy', badgeConfig.yoff)
248 .attr('text-anchor', 'middle')
249 .text(bdg.txt);
250 } else if (bdg.gid) {
251 bsel.append('use')
252 .attr({
253 width: bcgd * 2,
254 height: bcgd * 2,
255 transform: sus.translate(-bcgd, -bcgd),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100256 'xlink:href': '#' + bdg.gid,
Simon Huntc2bfe332015-12-04 11:01:24 -0800257 });
Andrea Campanella52125412015-12-03 14:50:40 -0800258 }
259 }
260
Simon Hunta4242de2015-02-24 17:11:55 -0800261 function updateHostLabel(d) {
262 var label = trimLabel(hostLabel(d));
263 d.el.select('text').text(label);
264 }
265
266 function updateDeviceColors(d) {
267 if (d) {
268 setDeviceColor(d);
269 } else {
270 api.node().filter('.device').each(function (d) {
271 setDeviceColor(d);
272 });
273 }
274 }
275
276
277 // ==========================
278 // updateNodes - subfunctions
279
280 function deviceExisting(d) {
281 var node = d.el;
282 node.classed('online', d.online);
Simon Hunt5674db92015-10-22 16:12:48 -0700283 updateDeviceRendering(d);
Simon Hunta4242de2015-02-24 17:11:55 -0800284 api.posNode(d, true);
285 }
286
287 function hostExisting(d) {
Andrea Campanella52125412015-12-03 14:50:40 -0800288 updateHostRendering(d);
Simon Hunta4242de2015-02-24 17:11:55 -0800289 api.posNode(d, true);
290 }
291
292 function deviceEnter(d) {
293 var node = d3.select(this),
Simon Hunt1eee51d2016-02-26 19:12:13 -0800294 glyphId = mapDeviceTypeToGlyph(d.type),
Simon Hunta4242de2015-02-24 17:11:55 -0800295 label = trimLabel(deviceLabel(d)),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100296 rect, crect, glyph, labelWidth;
Simon Hunta4242de2015-02-24 17:11:55 -0800297
298 d.el = node;
299
Simon Huntf44d7262016-06-14 14:46:56 -0700300 rect = node.append('rect');
Simon Hunte9062fc2016-12-22 11:40:06 -0800301 crect = node.append('rect');
Simon Hunta4242de2015-02-24 17:11:55 -0800302
Steven Burrows1c2a9682017-07-14 16:52:46 +0100303 node.append('text').text(label)
Simon Huntf44d7262016-06-14 14:46:56 -0700304 .attr('text-anchor', 'left')
305 .attr('y', '0.3em')
Simon Hunta5487ad2016-06-16 13:10:41 -0700306 .attr('x', halfDevIcon + labelPad);
Simon Hunta4242de2015-02-24 17:11:55 -0800307
Simon Huntf44d7262016-06-14 14:46:56 -0700308 glyph = is.addDeviceIcon(node, glyphId, devIconDim);
Simon Hunta4242de2015-02-24 17:11:55 -0800309
Simon Huntf44d7262016-06-14 14:46:56 -0700310 labelWidth = label ? computeLabelWidth(node) : 0;
311
312 rect.attr(iconBox(devIconDim, labelWidth));
Simon Hunte9062fc2016-12-22 11:40:06 -0800313 crect.attr(iconBox(devColorDim, 0));
Simon Huntf44d7262016-06-14 14:46:56 -0700314 glyph.attr(iconBox(devIconDim, 0));
315
Simon Hunta5487ad2016-06-16 13:10:41 -0700316 node.attr('transform', sus.translate(-halfDevIcon, -halfDevIcon));
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700317
318 d.el.selectAll('*')
319 .style('transform', 'scale(' + api.deviceScale() + ')');
Simon Hunta4242de2015-02-24 17:11:55 -0800320 }
321
322 function hostEnter(d) {
323 var node = d3.select(this),
Simon Hunte9062fc2016-12-22 11:40:06 -0800324 glyphId = mapHostTypeToGlyph(d.type),
Simon Hunta5487ad2016-06-16 13:10:41 -0700325 textDy = hostRadius + 10;
Simon Hunta4242de2015-02-24 17:11:55 -0800326
327 d.el = node;
328 sus.visible(node, api.showHosts());
329
Simon Hunte9062fc2016-12-22 11:40:06 -0800330 is.addHostIcon(node, hostRadius, glyphId);
Simon Hunta4242de2015-02-24 17:11:55 -0800331
332 node.append('text')
333 .text(hostLabel)
334 .attr('dy', textDy)
335 .attr('text-anchor', 'middle');
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700336
337 d.el.selectAll('g').style('transform', 'scale(' + api.deviceScale() + ')');
338 d.el.selectAll('text').style('transform', 'scale(' + api.deviceScale() + ')');
Simon Hunta4242de2015-02-24 17:11:55 -0800339 }
340
341 function hostExit(d) {
342 var node = d.el;
343 node.select('use')
344 .style('opacity', 0.5)
345 .transition()
346 .duration(800)
347 .style('opacity', 0);
348
349 node.select('text')
350 .style('opacity', 0.5)
351 .transition()
352 .duration(800)
353 .style('opacity', 0);
354
355 node.select('circle')
356 .style('stroke-fill', '#555')
357 .style('fill', '#888')
358 .style('opacity', 0.5)
359 .transition()
360 .duration(1500)
361 .attr('r', 0);
362 }
363
364 function deviceExit(d) {
365 var node = d.el;
366 node.select('use')
367 .style('opacity', 0.5)
368 .transition()
369 .duration(800)
370 .style('opacity', 0);
371
372 node.selectAll('rect')
373 .style('stroke-fill', '#555')
374 .style('fill', '#888')
375 .style('opacity', 0.5);
376 }
377
378
379 // ==========================
380 // updateLinks - subfunctions
381
Simon Hunta4242de2015-02-24 17:11:55 -0800382 function linkEntering(d) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100383
Simon Hunta4242de2015-02-24 17:11:55 -0800384 var link = d3.select(this);
385 d.el = link;
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700386 d.el.style('stroke-width', api.linkWidthScale() + 'px');
Simon Hunta4242de2015-02-24 17:11:55 -0800387 api.restyleLinkElement(d);
388 if (d.type() === 'hostLink') {
389 sus.visible(link, api.showHosts());
390 }
391 }
392
393 var linkLabelOffset = '0.3em';
394
395 function applyLinkLabels() {
396 var entering;
397
398 api.updateLinkLabelModel();
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800399 if (linkLabelsEnabled) {
Simon Hunta4242de2015-02-24 17:11:55 -0800400
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800401 // for elements already existing, we need to update the text
402 // and adjust the rectangle size to fit
403 api.linkLabel().each(function (d) {
404 var el = d3.select(this),
405 rect = el.select('rect'),
406 text = el.select('text');
407 text.text(d.label);
408 rect.attr(rectAroundText(el));
409 });
Simon Hunta4242de2015-02-24 17:11:55 -0800410
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800411 entering = api.linkLabel().enter().append('g')
412 .classed('linkLabel', true)
413 .attr('id', function (d) { return d.id; });
Simon Hunta4242de2015-02-24 17:11:55 -0800414
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800415 entering.each(function (d) {
416 var el = d3.select(this),
417 rect,
418 text;
Simon Hunta4242de2015-02-24 17:11:55 -0800419
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800420 if (d.ldata.type() === 'hostLink') {
421 el.classed('hostLinkLabel', true);
422 sus.visible(el, api.showHosts());
423 }
Simon Hunta4242de2015-02-24 17:11:55 -0800424
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800425 d.el = el;
426 rect = el.append('rect');
427 text = el.append('text').text(d.label);
428 rect.attr(rectAroundText(el));
429 text.attr('dy', linkLabelOffset);
Simon Hunta4242de2015-02-24 17:11:55 -0800430
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800431 el.attr('transform', transformLabel(d.ldata.position, d.key));
432 });
433 } else {
434 api.linkLabel().each(function (d) {
435 var el = d3.select(this),
436 rect = el.select('rect'),
437 text = el.select('text');
438 text.text('');
439 rect.attr(rectAroundText(el));
440 });
441 }
Simon Hunta4242de2015-02-24 17:11:55 -0800442
443 // Remove any labels that are no longer required.
444 api.linkLabel().exit().remove();
445 }
446
447 function rectAroundText(el) {
448 var text = el.select('text'),
449 box = text.node().getBBox();
450
451 // translate the bbox so that it is centered on [x,y]
452 box.x = -box.width / 2;
453 box.y = -box.height / 2;
454
455 // add padding
456 box.x -= 1;
457 box.width += 2;
458 return box;
459 }
460
Carmelo Casconed01eda62016-08-02 10:19:15 -0700461 function generateLabelFunction() {
Simon Hunte093e782017-03-31 10:19:08 -0700462 var labels = [],
463 xGap = 15,
464 yGap = 17;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700465
Simon Hunte093e782017-03-31 10:19:08 -0700466 return function (newId, newX, newY) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700467 var idx = -1;
468
Simon Hunte093e782017-03-31 10:19:08 -0700469 labels.forEach(function (lab, i) {
470 var minX, maxX, minY, maxY;
471
472 if (lab.id === newId) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700473 idx = i;
474 return;
475 }
Simon Hunte093e782017-03-31 10:19:08 -0700476 minX = lab.x - xGap;
477 maxX = lab.x + xGap;
478 minY = lab.y - yGap;
479 maxY = lab.y + yGap;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700480
481 if (newX > minX && newX < maxX && newY > minY && newY < maxY) {
482 // labels are overlapped
483 newX = newX - xGap;
484 newY = newY - yGap;
485 }
486 });
487
488 if (idx === -1) {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100489 labels.push({ id: newId, x: newX, y: newY });
Simon Hunte093e782017-03-31 10:19:08 -0700490 } else {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100491 labels[idx] = { id: newId, x: newX, y: newY };
Carmelo Casconed01eda62016-08-02 10:19:15 -0700492 }
493
Steven Burrows1c2a9682017-07-14 16:52:46 +0100494 return { x: newX, y: newY };
495 };
Carmelo Casconed01eda62016-08-02 10:19:15 -0700496 }
497
Simon Hunte093e782017-03-31 10:19:08 -0700498 var getLabelPos = generateLabelFunction();
Carmelo Casconed01eda62016-08-02 10:19:15 -0700499
500 function transformLabel(p, id) {
Simon Hunta4242de2015-02-24 17:11:55 -0800501 var dx = p.x2 - p.x1,
502 dy = p.y2 - p.y1,
503 xMid = dx/2 + p.x1,
504 yMid = dy/2 + p.y1;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700505
506 if (id) {
Simon Hunte093e782017-03-31 10:19:08 -0700507 var pos = getLabelPos(id, xMid, yMid);
Carmelo Casconed01eda62016-08-02 10:19:15 -0700508 return sus.translate(pos.x, pos.y);
509 }
510
Simon Hunta4242de2015-02-24 17:11:55 -0800511 return sus.translate(xMid, yMid);
512 }
513
Simon Hunt1a5301e2015-02-25 15:31:25 -0800514 function applyPortLabels(data, portLabelG) {
515 var entering = portLabelG.selectAll('.portLabel')
516 .data(data).enter().append('g')
517 .classed('portLabel', true)
518 .attr('id', function (d) { return d.id; });
519
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700520 var labelScale = portLabelDim / (portLabelDim * zoomer.scale());
521
Simon Hunt1a5301e2015-02-25 15:31:25 -0800522 entering.each(function (d) {
523 var el = d3.select(this),
524 rect = el.append('rect'),
525 text = el.append('text').text(d.num);
526
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700527 rect.attr(rectAroundText(el))
528 .style('transform', 'scale(' + labelScale + ')');
529 text.attr('dy', linkLabelOffset)
530 .style('transform', 'scale(' + labelScale + ')');
531
Simon Hunt969b3c92015-02-25 18:11:31 -0800532 el.attr('transform', sus.translate(d.x, d.y));
Simon Hunt1a5301e2015-02-25 15:31:25 -0800533 });
534 }
535
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700536 function labelPoint(linkPos) {
537 var lengthUpLine = 1 / 3,
538 dx = linkPos.x2 - linkPos.x1,
539 dy = linkPos.y2 - linkPos.y1,
540 movedX = dx * lengthUpLine,
541 movedY = dy * lengthUpLine;
542
543 return {
544 x: movedX,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100545 y: movedY,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700546 };
547 }
548
549 function calcGroupPos(linkPos) {
550 var moved = labelPoint(linkPos);
551 return sus.translate(linkPos.x1 + moved.x, linkPos.y1 + moved.y);
552 }
553
554 // calculates where on the link that the hash line for 5+ label appears
555 function hashAttrs(linkPos) {
556 var hashLength = 25,
557 halfLength = hashLength / 2,
558 dx = linkPos.x2 - linkPos.x1,
559 dy = linkPos.y2 - linkPos.y1,
560 length = Math.sqrt((dx * dx) + (dy * dy)),
561 moveAmtX = (dx / length) * halfLength,
562 moveAmtY = (dy / length) * halfLength,
563 mid = labelPoint(linkPos),
564 angle = Math.atan(dy / dx) + 45;
565
566 return {
567 x1: mid.x - moveAmtX,
568 y1: mid.y - moveAmtY,
569 x2: mid.x + moveAmtX,
570 y2: mid.y + moveAmtY,
571 stroke: api.linkConfig()[ts.theme()].baseColor,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100572 transform: 'rotate(' + angle + ',' + mid.x + ',' + mid.y + ')',
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700573 };
574 }
575
576 function textLabelPos(linkPos) {
577 var point = labelPoint(linkPos),
578 dist = 20;
579 return {
580 x: point.x + dist,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100581 y: point.y + dist,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700582 };
583 }
584
585 function applyNumLinkLabels(data, lblsG) {
586 var labels = lblsG.selectAll('g.numLinkLabel')
587 .data(data, function (d) { return 'pair-' + d.id; }),
588 entering;
589
590 // update existing labels
591 labels.each(function (d) {
592 var el = d3.select(this);
593
594 el.attr({
Steven Burrows1c2a9682017-07-14 16:52:46 +0100595 transform: function (d) { return calcGroupPos(d.linkCoords); },
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700596 });
597 el.select('line')
598 .attr(hashAttrs(d.linkCoords));
599 el.select('text')
600 .attr(textLabelPos(d.linkCoords))
601 .text(d.num);
602 });
603
604 // add new labels
605 entering = labels
606 .enter()
607 .append('g')
608 .attr({
609 transform: function (d) { return calcGroupPos(d.linkCoords); },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100610 id: function (d) { return 'pair-' + d.id; },
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700611 })
612 .classed('numLinkLabel', true);
613
614 entering.each(function (d) {
615 var el = d3.select(this);
616
617 el.append('line')
618 .classed('numLinkHash', true)
619 .attr(hashAttrs(d.linkCoords));
620 el.append('text')
621 .classed('numLinkText', true)
622 .attr(textLabelPos(d.linkCoords))
623 .text(d.num);
624 });
625
626 // remove old labels
627 labels.exit().remove();
628 }
629
Simon Hunte2d9dc72017-08-10 15:21:04 -0700630 // invoked after the localization bundle has been received from the server
631 function setLionBundle(bundle) {
632 topoLion = bundle;
633 }
634
Simon Hunta4242de2015-02-24 17:11:55 -0800635 // ==========================
636 // Module definition
637
638 angular.module('ovTopo')
639 .factory('TopoD3Service',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100640 ['SvgUtilService', 'IconService', 'ThemeService',
Thomas Vachuska0af26912016-03-21 21:37:30 -0700641 'PrefsService', 'TopoToolbarService',
Simon Hunta4242de2015-02-24 17:11:55 -0800642
Steven Burrows1c2a9682017-07-14 16:52:46 +0100643 function (_sus_, _is_, _ts_, _ps_, _ttbs_) {
Simon Hunta4242de2015-02-24 17:11:55 -0800644 sus = _sus_;
645 is = _is_;
646 ts = _ts_;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700647 ps = _ps_;
648 ttbs = _ttbs_;
Simon Hunta4242de2015-02-24 17:11:55 -0800649
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700650 function initD3(_api_, _zoomer_) {
Simon Hunta4242de2015-02-24 17:11:55 -0800651 api = _api_;
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700652 zoomer = _zoomer_;
Simon Hunta4242de2015-02-24 17:11:55 -0800653 }
654
655 function destroyD3() { }
656
657 return {
658 initD3: initD3,
659 destroyD3: destroyD3,
660
661 incDevLabIndex: incDevLabIndex,
Thomas Vachuska0af26912016-03-21 21:37:30 -0700662 setDevLabIndex: setDevLabIndex,
Simon Hunt10618f62017-06-15 19:30:52 -0700663 incHostLabIndex: incHostLabIndex,
664 setHostLabIndex: setHostLabIndex,
Simon Hunta4242de2015-02-24 17:11:55 -0800665 hostLabel: hostLabel,
666 deviceLabel: deviceLabel,
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800667 toggleLinkLabels: toggleLinkLabels,
Simon Hunta4242de2015-02-24 17:11:55 -0800668 trimLabel: trimLabel,
669
Simon Hunt5674db92015-10-22 16:12:48 -0700670 updateDeviceLabel: updateDeviceRendering,
Simon Hunta4242de2015-02-24 17:11:55 -0800671 updateHostLabel: updateHostLabel,
672 updateDeviceColors: updateDeviceColors,
673
674 deviceExisting: deviceExisting,
675 hostExisting: hostExisting,
676 deviceEnter: deviceEnter,
677 hostEnter: hostEnter,
678 hostExit: hostExit,
679 deviceExit: deviceExit,
680
Simon Hunta4242de2015-02-24 17:11:55 -0800681 linkEntering: linkEntering,
682 applyLinkLabels: applyLinkLabels,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800683 transformLabel: transformLabel,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700684 applyPortLabels: applyPortLabels,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100685 applyNumLinkLabels: applyNumLinkLabels,
Simon Hunte2d9dc72017-08-10 15:21:04 -0700686
687 setLionBundle: setLionBundle,
Simon Hunta4242de2015-02-24 17:11:55 -0800688 };
689 }]);
690}());