blob: 7274761f1917ae1c3e2a22e6987dc16fae99f20f [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 Burrows583f4be2016-11-04 14:06:50 +010027 var linkG, linkLabelG, nodeG;
Steven Burrowsdfa52b02016-09-02 13:50:43 +010028 var link, node;
Steven Burrowsec1f45c2016-08-08 16:14:41 +010029
Steven Burrowsec1f45c2016-08-08 16:14:41 +010030 // default settings for force layout
31 var defaultSettings = {
32 gravity: 0.4,
33 friction: 0.7,
34 charge: {
35 // note: key is node.class
36 device: -8000,
Steven Burrows583f4be2016-11-04 14:06:50 +010037 host: -20000,
38 region: -5000,
Steven Burrowsec1f45c2016-08-08 16:14:41 +010039 _def_: -12000
40 },
41 linkDistance: {
42 // note: key is link.type
43 direct: 100,
44 optical: 120,
Steven Burrows583f4be2016-11-04 14:06:50 +010045 UiEdgeLink: 30,
Steven Burrowsec1f45c2016-08-08 16:14:41 +010046 _def_: 50
47 },
48 linkStrength: {
49 // note: key is link.type
50 // range: {0.0 ... 1.0}
Steven Burrowsdfa52b02016-09-02 13:50:43 +010051 direct: 1.0,
52 optical: 1.0,
Steven Burrows583f4be2016-11-04 14:06:50 +010053 UiEdgeLink: 15.0,
Steven Burrowsec1f45c2016-08-08 16:14:41 +010054 _def_: 1.0
55 }
56 };
57
58 // configuration
59 var linkConfig = {
60 light: {
61 baseColor: '#939598',
62 inColor: '#66f',
63 outColor: '#f00'
64 },
65 dark: {
66 // TODO : theme
67 baseColor: '#939598',
68 inColor: '#66f',
69 outColor: '#f00'
70 },
71 inWidth: 12,
72 outWidth: 10
73 };
74
75 // internal state
76 var settings, // merged default settings and options
77 force, // force layout object
78 drag, // drag behavior handler
Steven Burrowsdfa52b02016-09-02 13:50:43 +010079 nodeLock = false; // whether nodes can be dragged or not (locked)
Steven Burrowsec1f45c2016-08-08 16:14:41 +010080
Steven Burrowsec1f45c2016-08-08 16:14:41 +010081 function init(_svg_, forceG, _uplink_, _dim_, opts) {
82
83 $log.debug("Initialising Topology Layout");
Steven Burrowsec1f45c2016-08-08 16:14:41 +010084 settings = angular.extend({}, defaultSettings, opts);
85
86 linkG = forceG.append('g').attr('id', 'topo-links');
87 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Steven Burrowsdfa52b02016-09-02 13:50:43 +010088 forceG.append('g').attr('id', 'topo-numLinkLabels');
Steven Burrowsec1f45c2016-08-08 16:14:41 +010089 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Steven Burrowsdfa52b02016-09-02 13:50:43 +010090 forceG.append('g').attr('id', 'topo-portLabels');
Steven Burrowsec1f45c2016-08-08 16:14:41 +010091
92 link = linkG.selectAll('.link');
Steven Burrowsdfa52b02016-09-02 13:50:43 +010093 linkLabelG.selectAll('.linkLabel');
Steven Burrowsec1f45c2016-08-08 16:14:41 +010094 node = nodeG.selectAll('.node');
Steven Burrowsdfa52b02016-09-02 13:50:43 +010095 }
96
Steven Burrows583f4be2016-11-04 14:06:50 +010097 function getDeviceChargeForType(node) {
98
99 var nodeType = node.get('nodeType');
100
101 return settings.charge[nodeType] ||
102 settings.charge._def_;
103 }
104
105 function getLinkDistanceForLinkType(node) {
106 var nodeType = node.get('type');
107
108 return settings.linkDistance[nodeType] ||
109 settings.linkDistance._def_;
110 }
111
112 function getLinkStrenghForLinkType(node) {
113 var nodeType = node.get('type');
114
115 return settings.linkStrength[nodeType] ||
116 settings.linkStrength._def_;
117 }
118
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100119 function createForceLayout() {
120
Steven Burrowsa3fca812016-10-14 15:11:04 -0500121 var regionLinks = t2rs.regionLinks(),
122 regionNodes = t2rs.regionNodes();
123
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100124 force = d3.layout.force()
125 .size(t2vs.getDimensions())
Steven Burrows583f4be2016-11-04 14:06:50 +0100126 .gravity(settings.gravity)
127 .friction(settings.friction)
128 .charge(getDeviceChargeForType)
129 .linkDistance(getLinkDistanceForLinkType)
130 .linkStrength(getLinkStrenghForLinkType)
Steven Burrowsa3fca812016-10-14 15:11:04 -0500131 .on("tick", tick);
132
133 force
134 .nodes(t2rs.regionNodes())
135 .links(regionLinks)
136 .start();
137
138 link = linkG.selectAll('.link')
139 .data(regionLinks, function (d) { return d.get('key'); });
140
141 node = nodeG.selectAll('.node')
142 .data(regionNodes, function (d) { return d.get('id'); });
Steven Burrows9edc7e02016-08-29 11:52:07 +0100143
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100144 drag = sus.createDragBehavior(force,
Steven Burrowsa3fca812016-10-14 15:11:04 -0500145 t2ss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100146
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100147 update();
Steven Burrows9edc7e02016-08-29 11:52:07 +0100148 }
149
Steven Burrowsa3fca812016-10-14 15:11:04 -0500150 // predicate that indicates when clicking is active
151 function clickEnabled() {
152 return true;
153 }
154
Steven Burrows9edc7e02016-08-29 11:52:07 +0100155 function zoomingOrPanning(ev) {
156 return ev.metaKey || ev.altKey;
157 }
158
159 function atDragEnd(d) {
160 // once we've finished moving, pin the node in position
161 d.fixed = true;
162 d3.select(this).classed('fixed', true);
Steven Burrowse7cc3082016-09-27 11:24:58 -0700163 sendUpdateMeta(d);
Steven Burrows583f4be2016-11-04 14:06:50 +0100164 $log.debug(d);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100165 t2ss.clickConsumed(true);
166 }
167
168 // predicate that indicates when dragging is active
169 function dragEnabled() {
170 var ev = d3.event.sourceEvent;
171 // nodeLock means we aren't allowing nodes to be dragged...
172 return !nodeLock && !zoomingOrPanning(ev);
173 }
174
Steven Burrowse7cc3082016-09-27 11:24:58 -0700175 function sendUpdateMeta(d, clearPos) {
176 var metaUi = {},
177 ll;
178
179 // if we are not clearing the position data (unpinning),
180 // attach the x, y, (and equivalent longitude, latitude)...
181 if (!clearPos) {
182 ll = d.lngLatFromCoord([d.x, d.y]);
183 metaUi = {
184 x: d.x,
185 y: d.y,
186 equivLoc: {
187 lng: ll[0],
188 lat: ll[1]
189 }
190 };
191 }
192 d.metaUi = metaUi;
193 wss.sendEvent('updateMeta2', {
194 id: d.get('id'),
195 class: d.get('class'),
196 memento: metaUi
197 });
198 }
199
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100200 function tick() {
Steven Burrowsa3fca812016-10-14 15:11:04 -0500201 link
202 .attr("x1", function (d) { return d.source.x; })
203 .attr("y1", function (d) { return d.source.y; })
204 .attr("x2", function (d) { return d.target.x; })
205 .attr("y2", function (d) { return d.target.y; });
206
207 node
208 .attr({
209 transform: function (d) {
210 var dx = isNaN(d.x) ? 0 : d.x,
211 dy = isNaN(d.y) ? 0 : d.y;
212 return sus.translate(dx, dy);
213 }
214 });
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100215 }
216
217 function update() {
218 _updateNodes();
219 _updateLinks();
220 }
221
222 function _updateNodes() {
223
224 var regionNodes = t2rs.regionNodes();
225
226 // select all the nodes in the layout:
227 node = nodeG.selectAll('.node')
228 .data(regionNodes, function (d) { return d.get('id'); });
229
230 var entering = node.enter()
231 .append('g')
232 .attr({
233 id: function (d) { return sus.safeId(d.get('id')); },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100234 class: function (d) { return d.svgClassName(); },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100235 transform: function (d) {
236 // Need to guard against NaN here ??
237 return sus.translate(d.node.x, d.node.y);
238 },
239 opacity: 0
240 })
Steven Burrows9edc7e02016-08-29 11:52:07 +0100241 .call(drag)
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100242 .transition()
243 .attr('opacity', 1);
244
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100245 entering.filter('.device').each(t2d3.nodeEnter);
246 entering.filter('.sub-region').each(t2d3.nodeEnter);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100247 entering.filter('.host').each(t2d3.hostEnter);
248
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100249 // operate on exiting nodes:
250 // Note that the node is removed after 2 seconds.
251 // Sub element animations should be shorter than 2 seconds.
252 var exiting = node.exit()
253 .transition()
Steven Burrows583f4be2016-11-04 14:06:50 +0100254 .duration(300)
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100255 .style('opacity', 0)
256 .remove();
257
258 // exiting node specifics:
259 // exiting.filter('.host').each(t2d3.hostExit);
260 exiting.filter('.device').each(t2d3.nodeExit);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100261 }
262
263 function _updateLinks() {
264
265 // var th = ts.theme();
266 var regionLinks = t2rs.regionLinks();
267
268 link = linkG.selectAll('.link')
269 .data(regionLinks, function (d) { return d.get('key'); });
270
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100271 // operate on entering links:
272 var entering = link.enter()
273 .append('line')
274 .call(calcPosition)
275 .attr({
276 x1: function (d) { return d.get('position').x1; },
277 y1: function (d) { return d.get('position').y1; },
278 x2: function (d) { return d.get('position').x2; },
279 y2: function (d) { return d.get('position').y2; },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100280 stroke: linkConfig.light.inColor,
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100281 'stroke-width': linkConfig.inWidth
282 });
283
284 entering.each(t2d3.linkEntering);
285
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100286 // operate on exiting links:
287 link.exit()
Steven Burrows583f4be2016-11-04 14:06:50 +0100288 .style('opacity', 1)
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100289 .transition()
Steven Burrows583f4be2016-11-04 14:06:50 +0100290 .duration(300)
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100291 .style('opacity', 0.0)
292 .remove();
293 }
294
295 function calcPosition() {
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100296 var lines = this;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100297
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100298 lines.each(function (d) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100299 if (d.get('type') === 'hostLink') {
300 d.set('position', getDefaultPos(d));
301 }
302 });
303
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100304 lines.each(function (d) {
305 d.set('position', getDefaultPos(d));
306 });
307 }
308
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100309 function getDefaultPos(link) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100310 return {
311 x1: link.get('source').x,
312 y1: link.get('source').y,
313 x2: link.get('target').x,
314 y2: link.get('target').y
315 };
316 }
317
318 function setDimensions() {
319 if (force) {
320 force.size(t2vs.getDimensions());
321 }
322 }
323
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100324 function start() {
325 force.start();
326 }
327
328 angular.module('ovTopo2')
329 .factory('Topo2LayoutService',
330 [
Steven Burrowse7cc3082016-09-27 11:24:58 -0700331 '$log', 'WebSocketService', 'SvgUtilService', 'Topo2RegionService',
Steven Burrows9edc7e02016-08-29 11:52:07 +0100332 'Topo2D3Service', 'Topo2ViewService', 'Topo2SelectService',
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100333
Steven Burrowse7cc3082016-09-27 11:24:58 -0700334 function (_$log_, _wss_, _sus_, _t2rs_, _t2d3_, _t2vs_, _t2ss_) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100335
336 $log = _$log_;
Steven Burrowse7cc3082016-09-27 11:24:58 -0700337 wss = _wss_;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100338 t2rs = _t2rs_;
339 t2d3 = _t2d3_;
340 t2vs = _t2vs_;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100341 t2ss = _t2ss_;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100342 sus = _sus_;
343
344 return {
345 init: init,
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100346 createForceLayout: createForceLayout,
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100347 update: update,
Steven Burrows1c5c8612016-10-05 13:45:13 -0500348 tick: tick,
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100349 start: start,
350
351 setDimensions: setDimensions
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100352 };
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100353 }
354 ]
355 );
356})();