blob: 9ffcbe01f623e40ef7d3dbb9259f703b17c6f3c7 [file] [log] [blame]
Steven Burrowsec1f45c2016-08-08 16:14:41 +01001/*
2 * Copyright 2016-present 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 Layout Module.
19 Module that contains the d3.force.layout logic
20 */
21
22(function () {
23 'use strict';
24
Steven Burrowse7cc3082016-09-27 11:24:58 -070025 var $log, wss, sus, t2rs, t2d3, t2vs, t2ss;
Steven Burrowsec1f45c2016-08-08 16:14:41 +010026
Steven Burrowsdfa52b02016-09-02 13:50:43 +010027 var uplink, linkG, linkLabelG, nodeG;
28 var link, node;
Steven Burrowsec1f45c2016-08-08 16:14:41 +010029
Steven Burrowsdfa52b02016-09-02 13:50:43 +010030 var highlightedLink;
Steven Burrowsec1f45c2016-08-08 16:14:41 +010031
32 // default settings for force layout
33 var defaultSettings = {
34 gravity: 0.4,
35 friction: 0.7,
36 charge: {
37 // note: key is node.class
38 device: -8000,
39 host: -5000,
40 _def_: -12000
41 },
42 linkDistance: {
43 // note: key is link.type
44 direct: 100,
45 optical: 120,
46 hostLink: 3,
47 _def_: 50
48 },
49 linkStrength: {
50 // note: key is link.type
51 // range: {0.0 ... 1.0}
Steven Burrowsdfa52b02016-09-02 13:50:43 +010052 direct: 1.0,
53 optical: 1.0,
54 hostLink: 1.0,
Steven Burrowsec1f45c2016-08-08 16:14:41 +010055 _def_: 1.0
56 }
57 };
58
59 // configuration
60 var linkConfig = {
61 light: {
62 baseColor: '#939598',
63 inColor: '#66f',
64 outColor: '#f00'
65 },
66 dark: {
67 // TODO : theme
68 baseColor: '#939598',
69 inColor: '#66f',
70 outColor: '#f00'
71 },
72 inWidth: 12,
73 outWidth: 10
74 };
75
76 // internal state
77 var settings, // merged default settings and options
78 force, // force layout object
79 drag, // drag behavior handler
Steven Burrowsdfa52b02016-09-02 13:50:43 +010080 nodeLock = false; // whether nodes can be dragged or not (locked)
Steven Burrowsec1f45c2016-08-08 16:14:41 +010081
82 var tickStuff = {
83 nodeAttr: {
84 transform: function (d) {
85 var dx = isNaN(d.x) ? 0 : d.x,
86 dy = isNaN(d.y) ? 0 : d.y;
87 return sus.translate(dx, dy);
88 }
89 },
90 linkAttr: {
91 x1: function (d) { return d.get('position').x1; },
92 y1: function (d) { return d.get('position').y1; },
93 x2: function (d) { return d.get('position').x2; },
94 y2: function (d) { return d.get('position').y2; }
Steven Burrowsec1f45c2016-08-08 16:14:41 +010095 }
96 };
97
98 function init(_svg_, forceG, _uplink_, _dim_, opts) {
99
100 $log.debug("Initialising Topology Layout");
Steven Burrows9edc7e02016-08-29 11:52:07 +0100101 uplink = _uplink_;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100102 settings = angular.extend({}, defaultSettings, opts);
103
104 linkG = forceG.append('g').attr('id', 'topo-links');
105 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100106 forceG.append('g').attr('id', 'topo-numLinkLabels');
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100107 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100108 forceG.append('g').attr('id', 'topo-portLabels');
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100109
110 link = linkG.selectAll('.link');
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100111 linkLabelG.selectAll('.linkLabel');
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100112 node = nodeG.selectAll('.node');
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100113 }
114
115 function createForceLayout() {
116
Steven Burrowsa3fca812016-10-14 15:11:04 -0500117 var regionLinks = t2rs.regionLinks(),
118 regionNodes = t2rs.regionNodes();
119
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100120 force = d3.layout.force()
121 .size(t2vs.getDimensions())
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100122 .charge(settings.charge._def_)
123 .linkDistance(settings.linkDistance._def_)
Steven Burrowsa3fca812016-10-14 15:11:04 -0500124 .on("tick", tick);
125
126 force
127 .nodes(t2rs.regionNodes())
128 .links(regionLinks)
129 .start();
130
131 link = linkG.selectAll('.link')
132 .data(regionLinks, function (d) { return d.get('key'); });
133
134 node = nodeG.selectAll('.node')
135 .data(regionNodes, function (d) { return d.get('id'); });
Steven Burrows9edc7e02016-08-29 11:52:07 +0100136
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100137 drag = sus.createDragBehavior(force,
Steven Burrowsa3fca812016-10-14 15:11:04 -0500138 t2ss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100139
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100140 update();
Steven Burrows9edc7e02016-08-29 11:52:07 +0100141 }
142
Steven Burrowsa3fca812016-10-14 15:11:04 -0500143 // predicate that indicates when clicking is active
144 function clickEnabled() {
145 return true;
146 }
147
Steven Burrows9edc7e02016-08-29 11:52:07 +0100148 function zoomingOrPanning(ev) {
149 return ev.metaKey || ev.altKey;
150 }
151
152 function atDragEnd(d) {
153 // once we've finished moving, pin the node in position
154 d.fixed = true;
155 d3.select(this).classed('fixed', true);
Steven Burrowse7cc3082016-09-27 11:24:58 -0700156 sendUpdateMeta(d);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100157 t2ss.clickConsumed(true);
158 }
159
160 // predicate that indicates when dragging is active
161 function dragEnabled() {
162 var ev = d3.event.sourceEvent;
163 // nodeLock means we aren't allowing nodes to be dragged...
164 return !nodeLock && !zoomingOrPanning(ev);
165 }
166
Steven Burrowse7cc3082016-09-27 11:24:58 -0700167 function sendUpdateMeta(d, clearPos) {
168 var metaUi = {},
169 ll;
170
171 // if we are not clearing the position data (unpinning),
172 // attach the x, y, (and equivalent longitude, latitude)...
173 if (!clearPos) {
174 ll = d.lngLatFromCoord([d.x, d.y]);
175 metaUi = {
176 x: d.x,
177 y: d.y,
178 equivLoc: {
179 lng: ll[0],
180 lat: ll[1]
181 }
182 };
183 }
184 d.metaUi = metaUi;
185 wss.sendEvent('updateMeta2', {
186 id: d.get('id'),
187 class: d.get('class'),
188 memento: metaUi
189 });
190 }
191
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100192 function tick() {
Steven Burrowsa3fca812016-10-14 15:11:04 -0500193 link
194 .attr("x1", function (d) { return d.source.x; })
195 .attr("y1", function (d) { return d.source.y; })
196 .attr("x2", function (d) { return d.target.x; })
197 .attr("y2", function (d) { return d.target.y; });
198
199 node
200 .attr({
201 transform: function (d) {
202 var dx = isNaN(d.x) ? 0 : d.x,
203 dy = isNaN(d.y) ? 0 : d.y;
204 return sus.translate(dx, dy);
205 }
206 });
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100207 }
208
209 function update() {
210 _updateNodes();
211 _updateLinks();
212 }
213
214 function _updateNodes() {
215
216 var regionNodes = t2rs.regionNodes();
217
218 // select all the nodes in the layout:
219 node = nodeG.selectAll('.node')
220 .data(regionNodes, function (d) { return d.get('id'); });
221
222 var entering = node.enter()
223 .append('g')
224 .attr({
225 id: function (d) { return sus.safeId(d.get('id')); },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100226 class: function (d) { return d.svgClassName(); },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100227 transform: function (d) {
228 // Need to guard against NaN here ??
229 return sus.translate(d.node.x, d.node.y);
230 },
231 opacity: 0
232 })
Steven Burrows9edc7e02016-08-29 11:52:07 +0100233 .call(drag)
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100234 // .on('mouseover', tss.nodeMouseOver)
235 // .on('mouseout', tss.nodeMouseOut)
236 .transition()
237 .attr('opacity', 1);
238
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100239 entering.filter('.device').each(t2d3.nodeEnter);
240 entering.filter('.sub-region').each(t2d3.nodeEnter);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100241 entering.filter('.host').each(t2d3.hostEnter);
242
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100243 // operate on exiting nodes:
244 // Note that the node is removed after 2 seconds.
245 // Sub element animations should be shorter than 2 seconds.
246 var exiting = node.exit()
247 .transition()
248 .duration(2000)
249 .style('opacity', 0)
250 .remove();
251
252 // exiting node specifics:
253 // exiting.filter('.host').each(t2d3.hostExit);
254 exiting.filter('.device').each(t2d3.nodeExit);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100255 }
256
257 function _updateLinks() {
258
259 // var th = ts.theme();
260 var regionLinks = t2rs.regionLinks();
261
262 link = linkG.selectAll('.link')
263 .data(regionLinks, function (d) { return d.get('key'); });
264
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100265 // operate on entering links:
266 var entering = link.enter()
267 .append('line')
268 .call(calcPosition)
269 .attr({
270 x1: function (d) { return d.get('position').x1; },
271 y1: function (d) { return d.get('position').y1; },
272 x2: function (d) { return d.get('position').x2; },
273 y2: function (d) { return d.get('position').y2; },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100274 stroke: linkConfig.light.inColor,
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100275 'stroke-width': linkConfig.inWidth
276 });
277
278 entering.each(t2d3.linkEntering);
279
280 // operate on both existing and new links:
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100281 // link.each(...)
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100282
283 // add labels for how many links are in a thick line
284 // t2d3.applyNumLinkLabels(linkNums, numLinkLabelsG);
285
286 // apply or remove labels
287 // t2d3.applyLinkLabels();
288
289 // operate on exiting links:
290 link.exit()
291 .attr('stroke-dasharray', '3 3')
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100292 .attr('stroke', linkConfig.light.outColor)
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100293 .style('opacity', 0.5)
294 .transition()
295 .duration(1500)
296 .attr({
297 'stroke-dasharray': '3 12',
298 'stroke-width': linkConfig.outWidth
299 })
300 .style('opacity', 0.0)
301 .remove();
302 }
303
304 function calcPosition() {
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100305 var lines = this;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100306
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100307 lines.each(function (d) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100308 if (d.get('type') === 'hostLink') {
309 d.set('position', getDefaultPos(d));
310 }
311 });
312
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100313 lines.each(function (d) {
314 d.set('position', getDefaultPos(d));
315 });
316 }
317
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100318 function getDefaultPos(link) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100319 return {
320 x1: link.get('source').x,
321 y1: link.get('source').y,
322 x2: link.get('target').x,
323 y2: link.get('target').y
324 };
325 }
326
327 function setDimensions() {
328 if (force) {
329 force.size(t2vs.getDimensions());
330 }
331 }
332
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100333 function start() {
334 force.start();
335 }
336
337 angular.module('ovTopo2')
338 .factory('Topo2LayoutService',
339 [
Steven Burrowse7cc3082016-09-27 11:24:58 -0700340 '$log', 'WebSocketService', 'SvgUtilService', 'Topo2RegionService',
Steven Burrows9edc7e02016-08-29 11:52:07 +0100341 'Topo2D3Service', 'Topo2ViewService', 'Topo2SelectService',
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100342
Steven Burrowse7cc3082016-09-27 11:24:58 -0700343 function (_$log_, _wss_, _sus_, _t2rs_, _t2d3_, _t2vs_, _t2ss_) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100344
345 $log = _$log_;
Steven Burrowse7cc3082016-09-27 11:24:58 -0700346 wss = _wss_;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100347 t2rs = _t2rs_;
348 t2d3 = _t2d3_;
349 t2vs = _t2vs_;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100350 t2ss = _t2ss_;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100351 sus = _sus_;
352
353 return {
354 init: init,
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100355 createForceLayout: createForceLayout,
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100356 update: update,
Steven Burrows1c5c8612016-10-05 13:45:13 -0500357 tick: tick,
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100358 start: start,
359
360 setDimensions: setDimensions
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100361 };
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100362 }
363 ]
364 );
365})();