blob: 9aafc98b2e070ae257c8c856e67460f259d8a947 [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',
77 roadm_otn: 'm_roadm_otn',
78 fiber_switch: 'm_fiberSwitch',
79 microwave: 'm_microwave',
80 };
81
82 var remappedHostTypes = {
83 router: 'm_router',
84 endstation: 'm_endstation',
Steven Burrows1c2a9682017-07-14 16:52:46 +010085 bgpSpeaker: 'm_bgpSpeaker',
Simon Hunt1eee51d2016-02-26 19:12:13 -080086 };
87
88 function mapDeviceTypeToGlyph(type) {
89 return remappedDeviceTypes[type] || type || 'unknown';
90 }
91
Simon Hunte9062fc2016-12-22 11:40:06 -080092 function mapHostTypeToGlyph(type) {
93 return remappedHostTypes[type] || type || 'unknown';
94 }
95
Simon Hunt5674db92015-10-22 16:12:48 -070096 function badgeStatus(badge) {
97 return status[badge.status] || status.i;
98 }
99
Simon Hunta4242de2015-02-24 17:11:55 -0800100 // internal state
101 var deviceLabelIndex = 0,
102 hostLabelIndex = 0;
103
Simon Hunta5487ad2016-06-16 13:10:41 -0700104 // note: these are the device icon colors without affinity (no master)
Simon Hunta4242de2015-02-24 17:11:55 -0800105 var dColTheme = {
106 light: {
Simon Huntf44d7262016-06-14 14:46:56 -0700107 online: '#444444',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100108 offline: '#cccccc',
Simon Hunta4242de2015-02-24 17:11:55 -0800109 },
110 dark: {
Simon Huntf44d7262016-06-14 14:46:56 -0700111 // TODO: theme
112 online: '#444444',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100113 offline: '#cccccc',
114 },
Simon Hunta4242de2015-02-24 17:11:55 -0800115 };
116
Simon Huntf44d7262016-06-14 14:46:56 -0700117 function devGlyphColor(d) {
118 var o = d.online,
119 id = d.master,
120 otag = o ? 'online' : 'offline';
121 return o ? sus.cat7().getColor(id, 0, ts.theme())
122 : dColTheme[ts.theme()][otag];
Simon Hunta4242de2015-02-24 17:11:55 -0800123 }
124
125 function setDeviceColor(d) {
Simon Hunte9062fc2016-12-22 11:40:06 -0800126 // want to color the square rectangle (no longer the 'use' glyph)
Steven Burrows1c2a9682017-07-14 16:52:46 +0100127 d.el.selectAll('rect').filter(function (d, i) { return i === 1; })
Simon Huntf44d7262016-06-14 14:46:56 -0700128 .style('fill', devGlyphColor(d));
Simon Hunta4242de2015-02-24 17:11:55 -0800129 }
130
Simon Hunta4242de2015-02-24 17:11:55 -0800131 function incDevLabIndex() {
Steven Burrowsbbe3dda2016-09-26 14:41:59 -0700132 setDevLabIndex(deviceLabelIndex+1);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100133 switch (deviceLabelIndex) {
Simon Hunte2d9dc72017-08-10 15:21:04 -0700134 case 0: return topoLion('fl_device_labels_hide');
135 case 1: return topoLion('fl_device_labels_show_friendly');
136 case 2: return topoLion('fl_device_labels_show_id');
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700137 }
Simon Hunta4242de2015-02-24 17:11:55 -0800138 }
139
Thomas Vachuska0af26912016-03-21 21:37:30 -0700140 function setDevLabIndex(mode) {
141 deviceLabelIndex = mode % 3;
142 var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
143 p.dlbls = deviceLabelIndex;
144 ps.setPrefs('topo_prefs', p);
145 }
146
Simon Hunt10618f62017-06-15 19:30:52 -0700147 function incHostLabIndex() {
148 setHostLabIndex(hostLabelIndex+1);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100149 switch (hostLabelIndex) {
Simon Hunte2d9dc72017-08-10 15:21:04 -0700150 case 0: return topoLion('fl_host_labels_show_friendly');
151 case 1: return topoLion('fl_host_labels_show_ip');
152 case 2: return topoLion('fl_host_labels_show_mac');
Simon Hunt10618f62017-06-15 19:30:52 -0700153 }
154 }
155
156 function setHostLabIndex(mode) {
157 hostLabelIndex = mode % 3;
158 var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
159 p.hlbls = hostLabelIndex;
160 ps.setPrefs('topo_prefs', p);
161 }
162
Simon Hunta4242de2015-02-24 17:11:55 -0800163 function hostLabel(d) {
164 var idx = (hostLabelIndex < d.labels.length) ? hostLabelIndex : 0;
165 return d.labels[idx];
166 }
Simon Huntf44d7262016-06-14 14:46:56 -0700167
Simon Hunta4242de2015-02-24 17:11:55 -0800168 function deviceLabel(d) {
169 var idx = (deviceLabelIndex < d.labels.length) ? deviceLabelIndex : 0;
170 return d.labels[idx];
171 }
Simon Huntf44d7262016-06-14 14:46:56 -0700172
Simon Hunta4242de2015-02-24 17:11:55 -0800173 function trimLabel(label) {
174 return (label && label.trim()) || '';
175 }
176
Simon Huntf44d7262016-06-14 14:46:56 -0700177 function computeLabelWidth(n) {
178 var text = n.select('text'),
179 box = text.node().getBBox();
180 return box.width + labelPad * 2;
181 }
182
183 function iconBox(dim, labelWidth) {
Simon Hunta4242de2015-02-24 17:11:55 -0800184 return {
Simon Huntf44d7262016-06-14 14:46:56 -0700185 x: -dim/2,
186 y: -dim/2,
187 width: dim + labelWidth,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100188 height: dim,
189 };
Simon Hunta4242de2015-02-24 17:11:55 -0800190 }
191
Simon Hunt5674db92015-10-22 16:12:48 -0700192 function updateDeviceRendering(d) {
Simon Huntf44d7262016-06-14 14:46:56 -0700193 var node = d.el,
194 bdg = d.badge,
195 label = trimLabel(deviceLabel(d)),
196 labelWidth;
Simon Hunta4242de2015-02-24 17:11:55 -0800197
Simon Huntf44d7262016-06-14 14:46:56 -0700198 node.select('text').text(label);
199 labelWidth = label ? computeLabelWidth(node) : 0;
Simon Hunta4242de2015-02-24 17:11:55 -0800200
201 node.select('rect')
202 .transition()
Simon Huntf44d7262016-06-14 14:46:56 -0700203 .attr(iconBox(devIconDim, labelWidth));
Simon Hunta4242de2015-02-24 17:11:55 -0800204
Simon Hunt5674db92015-10-22 16:12:48 -0700205 if (bdg) {
Simon Hunta5487ad2016-06-16 13:10:41 -0700206 renderBadge(node, bdg, devBadgeOff);
Simon Hunte9343f32015-10-21 18:07:46 -0700207 }
208 }
209
Andrea Campanella52125412015-12-03 14:50:40 -0800210 function updateHostRendering(d) {
211 var node = d.el,
Simon Huntc2bfe332015-12-04 11:01:24 -0800212 bdg = d.badge;
Andrea Campanella52125412015-12-03 14:50:40 -0800213
214 updateHostLabel(d);
Andrea Campanella52125412015-12-03 14:50:40 -0800215
Andrea Campanella52125412015-12-03 14:50:40 -0800216 if (bdg) {
Simon Hunta5487ad2016-06-16 13:10:41 -0700217 renderBadge(node, bdg, hostBadgeOff);
Simon Huntc2bfe332015-12-04 11:01:24 -0800218 }
219 }
Andrea Campanella52125412015-12-03 14:50:40 -0800220
Simon Huntc2bfe332015-12-04 11:01:24 -0800221 function renderBadge(node, bdg, boff) {
222 var bsel,
223 bcr = badgeConfig.radius,
224 bcgd = badgeConfig.gdelta;
Andrea Campanella52125412015-12-03 14:50:40 -0800225
Simon Huntc2bfe332015-12-04 11:01:24 -0800226 node.select('g.badge').remove();
Andrea Campanella52125412015-12-03 14:50:40 -0800227
Simon Huntc2bfe332015-12-04 11:01:24 -0800228 bsel = node.append('g')
229 .classed('badge', true)
230 .classed(badgeStatus(bdg), true)
231 .attr('transform', sus.translate(boff.dx, boff.dy));
Andrea Campanella52125412015-12-03 14:50:40 -0800232
Simon Huntc2bfe332015-12-04 11:01:24 -0800233 bsel.append('circle')
234 .attr('r', bcr);
235
236 if (bdg.txt) {
237 bsel.append('text')
238 .attr('dy', badgeConfig.yoff)
239 .attr('text-anchor', 'middle')
240 .text(bdg.txt);
241 } else if (bdg.gid) {
242 bsel.append('use')
243 .attr({
244 width: bcgd * 2,
245 height: bcgd * 2,
246 transform: sus.translate(-bcgd, -bcgd),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100247 'xlink:href': '#' + bdg.gid,
Simon Huntc2bfe332015-12-04 11:01:24 -0800248 });
Andrea Campanella52125412015-12-03 14:50:40 -0800249 }
250 }
251
Simon Hunta4242de2015-02-24 17:11:55 -0800252 function updateHostLabel(d) {
253 var label = trimLabel(hostLabel(d));
254 d.el.select('text').text(label);
255 }
256
257 function updateDeviceColors(d) {
258 if (d) {
259 setDeviceColor(d);
260 } else {
261 api.node().filter('.device').each(function (d) {
262 setDeviceColor(d);
263 });
264 }
265 }
266
267
268 // ==========================
269 // updateNodes - subfunctions
270
271 function deviceExisting(d) {
272 var node = d.el;
273 node.classed('online', d.online);
Simon Hunt5674db92015-10-22 16:12:48 -0700274 updateDeviceRendering(d);
Simon Hunta4242de2015-02-24 17:11:55 -0800275 api.posNode(d, true);
276 }
277
278 function hostExisting(d) {
Andrea Campanella52125412015-12-03 14:50:40 -0800279 updateHostRendering(d);
Simon Hunta4242de2015-02-24 17:11:55 -0800280 api.posNode(d, true);
281 }
282
283 function deviceEnter(d) {
284 var node = d3.select(this),
Simon Hunt1eee51d2016-02-26 19:12:13 -0800285 glyphId = mapDeviceTypeToGlyph(d.type),
Simon Hunta4242de2015-02-24 17:11:55 -0800286 label = trimLabel(deviceLabel(d)),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100287 rect, crect, glyph, labelWidth;
Simon Hunta4242de2015-02-24 17:11:55 -0800288
289 d.el = node;
290
Simon Huntf44d7262016-06-14 14:46:56 -0700291 rect = node.append('rect');
Simon Hunte9062fc2016-12-22 11:40:06 -0800292 crect = node.append('rect');
Simon Hunta4242de2015-02-24 17:11:55 -0800293
Steven Burrows1c2a9682017-07-14 16:52:46 +0100294 node.append('text').text(label)
Simon Huntf44d7262016-06-14 14:46:56 -0700295 .attr('text-anchor', 'left')
296 .attr('y', '0.3em')
Simon Hunta5487ad2016-06-16 13:10:41 -0700297 .attr('x', halfDevIcon + labelPad);
Simon Hunta4242de2015-02-24 17:11:55 -0800298
Simon Huntf44d7262016-06-14 14:46:56 -0700299 glyph = is.addDeviceIcon(node, glyphId, devIconDim);
Simon Hunta4242de2015-02-24 17:11:55 -0800300
Simon Huntf44d7262016-06-14 14:46:56 -0700301 labelWidth = label ? computeLabelWidth(node) : 0;
302
303 rect.attr(iconBox(devIconDim, labelWidth));
Simon Hunte9062fc2016-12-22 11:40:06 -0800304 crect.attr(iconBox(devColorDim, 0));
Simon Huntf44d7262016-06-14 14:46:56 -0700305 glyph.attr(iconBox(devIconDim, 0));
306
Simon Hunta5487ad2016-06-16 13:10:41 -0700307 node.attr('transform', sus.translate(-halfDevIcon, -halfDevIcon));
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700308
309 d.el.selectAll('*')
310 .style('transform', 'scale(' + api.deviceScale() + ')');
Simon Hunta4242de2015-02-24 17:11:55 -0800311 }
312
313 function hostEnter(d) {
314 var node = d3.select(this),
Simon Hunte9062fc2016-12-22 11:40:06 -0800315 glyphId = mapHostTypeToGlyph(d.type),
Simon Hunta5487ad2016-06-16 13:10:41 -0700316 textDy = hostRadius + 10;
Simon Hunta4242de2015-02-24 17:11:55 -0800317
318 d.el = node;
319 sus.visible(node, api.showHosts());
320
Simon Hunte9062fc2016-12-22 11:40:06 -0800321 is.addHostIcon(node, hostRadius, glyphId);
Simon Hunta4242de2015-02-24 17:11:55 -0800322
323 node.append('text')
324 .text(hostLabel)
325 .attr('dy', textDy)
326 .attr('text-anchor', 'middle');
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700327
328 d.el.selectAll('g').style('transform', 'scale(' + api.deviceScale() + ')');
329 d.el.selectAll('text').style('transform', 'scale(' + api.deviceScale() + ')');
Simon Hunta4242de2015-02-24 17:11:55 -0800330 }
331
332 function hostExit(d) {
333 var node = d.el;
334 node.select('use')
335 .style('opacity', 0.5)
336 .transition()
337 .duration(800)
338 .style('opacity', 0);
339
340 node.select('text')
341 .style('opacity', 0.5)
342 .transition()
343 .duration(800)
344 .style('opacity', 0);
345
346 node.select('circle')
347 .style('stroke-fill', '#555')
348 .style('fill', '#888')
349 .style('opacity', 0.5)
350 .transition()
351 .duration(1500)
352 .attr('r', 0);
353 }
354
355 function deviceExit(d) {
356 var node = d.el;
357 node.select('use')
358 .style('opacity', 0.5)
359 .transition()
360 .duration(800)
361 .style('opacity', 0);
362
363 node.selectAll('rect')
364 .style('stroke-fill', '#555')
365 .style('fill', '#888')
366 .style('opacity', 0.5);
367 }
368
369
370 // ==========================
371 // updateLinks - subfunctions
372
Simon Hunta4242de2015-02-24 17:11:55 -0800373 function linkEntering(d) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100374
Simon Hunta4242de2015-02-24 17:11:55 -0800375 var link = d3.select(this);
376 d.el = link;
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700377 d.el.style('stroke-width', api.linkWidthScale() + 'px');
Simon Hunta4242de2015-02-24 17:11:55 -0800378 api.restyleLinkElement(d);
379 if (d.type() === 'hostLink') {
380 sus.visible(link, api.showHosts());
381 }
382 }
383
384 var linkLabelOffset = '0.3em';
385
386 function applyLinkLabels() {
387 var entering;
388
389 api.updateLinkLabelModel();
390
391 // for elements already existing, we need to update the text
392 // and adjust the rectangle size to fit
393 api.linkLabel().each(function (d) {
394 var el = d3.select(this),
395 rect = el.select('rect'),
396 text = el.select('text');
397 text.text(d.label);
398 rect.attr(rectAroundText(el));
399 });
400
401 entering = api.linkLabel().enter().append('g')
402 .classed('linkLabel', true)
403 .attr('id', function (d) { return d.id; });
404
405 entering.each(function (d) {
406 var el = d3.select(this),
407 rect,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700408 text;
Simon Hunta4242de2015-02-24 17:11:55 -0800409
410 if (d.ldata.type() === 'hostLink') {
411 el.classed('hostLinkLabel', true);
412 sus.visible(el, api.showHosts());
413 }
414
415 d.el = el;
416 rect = el.append('rect');
417 text = el.append('text').text(d.label);
418 rect.attr(rectAroundText(el));
419 text.attr('dy', linkLabelOffset);
420
Carmelo Casconed01eda62016-08-02 10:19:15 -0700421 el.attr('transform', transformLabel(d.ldata.position, d.key));
Simon Hunta4242de2015-02-24 17:11:55 -0800422 });
423
424 // Remove any labels that are no longer required.
425 api.linkLabel().exit().remove();
426 }
427
428 function rectAroundText(el) {
429 var text = el.select('text'),
430 box = text.node().getBBox();
431
432 // translate the bbox so that it is centered on [x,y]
433 box.x = -box.width / 2;
434 box.y = -box.height / 2;
435
436 // add padding
437 box.x -= 1;
438 box.width += 2;
439 return box;
440 }
441
Carmelo Casconed01eda62016-08-02 10:19:15 -0700442 function generateLabelFunction() {
Simon Hunte093e782017-03-31 10:19:08 -0700443 var labels = [],
444 xGap = 15,
445 yGap = 17;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700446
Simon Hunte093e782017-03-31 10:19:08 -0700447 return function (newId, newX, newY) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700448 var idx = -1;
449
Simon Hunte093e782017-03-31 10:19:08 -0700450 labels.forEach(function (lab, i) {
451 var minX, maxX, minY, maxY;
452
453 if (lab.id === newId) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700454 idx = i;
455 return;
456 }
Simon Hunte093e782017-03-31 10:19:08 -0700457 minX = lab.x - xGap;
458 maxX = lab.x + xGap;
459 minY = lab.y - yGap;
460 maxY = lab.y + yGap;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700461
462 if (newX > minX && newX < maxX && newY > minY && newY < maxY) {
463 // labels are overlapped
464 newX = newX - xGap;
465 newY = newY - yGap;
466 }
467 });
468
469 if (idx === -1) {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100470 labels.push({ id: newId, x: newX, y: newY });
Simon Hunte093e782017-03-31 10:19:08 -0700471 } else {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100472 labels[idx] = { id: newId, x: newX, y: newY };
Carmelo Casconed01eda62016-08-02 10:19:15 -0700473 }
474
Steven Burrows1c2a9682017-07-14 16:52:46 +0100475 return { x: newX, y: newY };
476 };
Carmelo Casconed01eda62016-08-02 10:19:15 -0700477 }
478
Simon Hunte093e782017-03-31 10:19:08 -0700479 var getLabelPos = generateLabelFunction();
Carmelo Casconed01eda62016-08-02 10:19:15 -0700480
481 function transformLabel(p, id) {
Simon Hunta4242de2015-02-24 17:11:55 -0800482 var dx = p.x2 - p.x1,
483 dy = p.y2 - p.y1,
484 xMid = dx/2 + p.x1,
485 yMid = dy/2 + p.y1;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700486
487 if (id) {
Simon Hunte093e782017-03-31 10:19:08 -0700488 var pos = getLabelPos(id, xMid, yMid);
Carmelo Casconed01eda62016-08-02 10:19:15 -0700489 return sus.translate(pos.x, pos.y);
490 }
491
Simon Hunta4242de2015-02-24 17:11:55 -0800492 return sus.translate(xMid, yMid);
493 }
494
Simon Hunt1a5301e2015-02-25 15:31:25 -0800495 function applyPortLabels(data, portLabelG) {
496 var entering = portLabelG.selectAll('.portLabel')
497 .data(data).enter().append('g')
498 .classed('portLabel', true)
499 .attr('id', function (d) { return d.id; });
500
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700501 var labelScale = portLabelDim / (portLabelDim * zoomer.scale());
502
Simon Hunt1a5301e2015-02-25 15:31:25 -0800503 entering.each(function (d) {
504 var el = d3.select(this),
505 rect = el.append('rect'),
506 text = el.append('text').text(d.num);
507
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700508 rect.attr(rectAroundText(el))
509 .style('transform', 'scale(' + labelScale + ')');
510 text.attr('dy', linkLabelOffset)
511 .style('transform', 'scale(' + labelScale + ')');
512
Simon Hunt969b3c92015-02-25 18:11:31 -0800513 el.attr('transform', sus.translate(d.x, d.y));
Simon Hunt1a5301e2015-02-25 15:31:25 -0800514 });
515 }
516
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700517 function labelPoint(linkPos) {
518 var lengthUpLine = 1 / 3,
519 dx = linkPos.x2 - linkPos.x1,
520 dy = linkPos.y2 - linkPos.y1,
521 movedX = dx * lengthUpLine,
522 movedY = dy * lengthUpLine;
523
524 return {
525 x: movedX,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100526 y: movedY,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700527 };
528 }
529
530 function calcGroupPos(linkPos) {
531 var moved = labelPoint(linkPos);
532 return sus.translate(linkPos.x1 + moved.x, linkPos.y1 + moved.y);
533 }
534
535 // calculates where on the link that the hash line for 5+ label appears
536 function hashAttrs(linkPos) {
537 var hashLength = 25,
538 halfLength = hashLength / 2,
539 dx = linkPos.x2 - linkPos.x1,
540 dy = linkPos.y2 - linkPos.y1,
541 length = Math.sqrt((dx * dx) + (dy * dy)),
542 moveAmtX = (dx / length) * halfLength,
543 moveAmtY = (dy / length) * halfLength,
544 mid = labelPoint(linkPos),
545 angle = Math.atan(dy / dx) + 45;
546
547 return {
548 x1: mid.x - moveAmtX,
549 y1: mid.y - moveAmtY,
550 x2: mid.x + moveAmtX,
551 y2: mid.y + moveAmtY,
552 stroke: api.linkConfig()[ts.theme()].baseColor,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100553 transform: 'rotate(' + angle + ',' + mid.x + ',' + mid.y + ')',
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700554 };
555 }
556
557 function textLabelPos(linkPos) {
558 var point = labelPoint(linkPos),
559 dist = 20;
560 return {
561 x: point.x + dist,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100562 y: point.y + dist,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700563 };
564 }
565
566 function applyNumLinkLabels(data, lblsG) {
567 var labels = lblsG.selectAll('g.numLinkLabel')
568 .data(data, function (d) { return 'pair-' + d.id; }),
569 entering;
570
571 // update existing labels
572 labels.each(function (d) {
573 var el = d3.select(this);
574
575 el.attr({
Steven Burrows1c2a9682017-07-14 16:52:46 +0100576 transform: function (d) { return calcGroupPos(d.linkCoords); },
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700577 });
578 el.select('line')
579 .attr(hashAttrs(d.linkCoords));
580 el.select('text')
581 .attr(textLabelPos(d.linkCoords))
582 .text(d.num);
583 });
584
585 // add new labels
586 entering = labels
587 .enter()
588 .append('g')
589 .attr({
590 transform: function (d) { return calcGroupPos(d.linkCoords); },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100591 id: function (d) { return 'pair-' + d.id; },
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700592 })
593 .classed('numLinkLabel', true);
594
595 entering.each(function (d) {
596 var el = d3.select(this);
597
598 el.append('line')
599 .classed('numLinkHash', true)
600 .attr(hashAttrs(d.linkCoords));
601 el.append('text')
602 .classed('numLinkText', true)
603 .attr(textLabelPos(d.linkCoords))
604 .text(d.num);
605 });
606
607 // remove old labels
608 labels.exit().remove();
609 }
610
Simon Hunte2d9dc72017-08-10 15:21:04 -0700611 // invoked after the localization bundle has been received from the server
612 function setLionBundle(bundle) {
613 topoLion = bundle;
614 }
615
Simon Hunta4242de2015-02-24 17:11:55 -0800616 // ==========================
617 // Module definition
618
619 angular.module('ovTopo')
620 .factory('TopoD3Service',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100621 ['SvgUtilService', 'IconService', 'ThemeService',
Thomas Vachuska0af26912016-03-21 21:37:30 -0700622 'PrefsService', 'TopoToolbarService',
Simon Hunta4242de2015-02-24 17:11:55 -0800623
Steven Burrows1c2a9682017-07-14 16:52:46 +0100624 function (_sus_, _is_, _ts_, _ps_, _ttbs_) {
Simon Hunta4242de2015-02-24 17:11:55 -0800625 sus = _sus_;
626 is = _is_;
627 ts = _ts_;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700628 ps = _ps_;
629 ttbs = _ttbs_;
Simon Hunta4242de2015-02-24 17:11:55 -0800630
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700631 function initD3(_api_, _zoomer_) {
Simon Hunta4242de2015-02-24 17:11:55 -0800632 api = _api_;
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700633 zoomer = _zoomer_;
Simon Hunta4242de2015-02-24 17:11:55 -0800634 }
635
636 function destroyD3() { }
637
638 return {
639 initD3: initD3,
640 destroyD3: destroyD3,
641
642 incDevLabIndex: incDevLabIndex,
Thomas Vachuska0af26912016-03-21 21:37:30 -0700643 setDevLabIndex: setDevLabIndex,
Simon Hunt10618f62017-06-15 19:30:52 -0700644 incHostLabIndex: incHostLabIndex,
645 setHostLabIndex: setHostLabIndex,
Simon Hunta4242de2015-02-24 17:11:55 -0800646 hostLabel: hostLabel,
647 deviceLabel: deviceLabel,
648 trimLabel: trimLabel,
649
Simon Hunt5674db92015-10-22 16:12:48 -0700650 updateDeviceLabel: updateDeviceRendering,
Simon Hunta4242de2015-02-24 17:11:55 -0800651 updateHostLabel: updateHostLabel,
652 updateDeviceColors: updateDeviceColors,
653
654 deviceExisting: deviceExisting,
655 hostExisting: hostExisting,
656 deviceEnter: deviceEnter,
657 hostEnter: hostEnter,
658 hostExit: hostExit,
659 deviceExit: deviceExit,
660
Simon Hunta4242de2015-02-24 17:11:55 -0800661 linkEntering: linkEntering,
662 applyLinkLabels: applyLinkLabels,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800663 transformLabel: transformLabel,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700664 applyPortLabels: applyPortLabels,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100665 applyNumLinkLabels: applyNumLinkLabels,
Simon Hunte2d9dc72017-08-10 15:21:04 -0700666
667 setLionBundle: setLionBundle,
Simon Hunta4242de2015-02-24 17:11:55 -0800668 };
669 }]);
670}());