/* * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* ONOS GUI -- Topology Oblique View Module. Provides functionality to view the topology as two planes (packet & optical) from an oblique (side-on) perspective. */ (function () { 'use strict'; // injected refs var $log, fs, sus, flash; // api to topoForce var api; /* force() // get ref to force layout object zoomLayer() // get ref to zoom layer nodeGBBox() // get bounding box of node group layer node() // get ref to D3 selection of nodes link() // get ref to D3 selection of links nodes() // get ref to network nodes array tickStuff // ref to tick functions nodeLock(b) // test-and-set nodeLock state opacifyMap(b) // show or hide map layer inLayer(d, layer) // return true if d in layer {'pkt'|'opt'} calcLinkPos() // recomputes link pos based on node data */ // configuration var xsky = -.7, // x skew y factor xsk = -35, // x skew angle ysc = .5, // y scale pad = 50, time = 1500, fill = { pkt: 'rgba(130,130,170,0.3)', // blue-ish opt: 'rgba(170,130,170,0.3)' // magenta-ish }; // internal state var oblique = false, xffn = null, plane = {}, oldNodeLock; function planeId(tag) { return 'topo-obview-' + tag + 'Plane'; } function ytfn(h, dir) { return h * ysc * dir * 1.1; } function obXform(h, dir) { var yt = ytfn(h, dir); return sus.scale(1, ysc) + sus.translate(0, yt) + sus.skewX(xsk); } function noXform() { return sus.skewX(0) + sus.translate(0,0) + sus.scale(1,1); } function padBox(box, p) { box.x -= p; box.y -= p; box.width += p*2; box.height += p*2; } function toObliqueView() { var box = api.nodeGBBox(), ox, oy; padBox(box, pad); ox = box.x + box.width / 2; oy = box.y + box.height / 2; // remember node lock state, then lock the nodes down oldNodeLock = api.nodeLock(true); api.opacifyMap(false); insertPlanes(ox, oy); xffn = function (xy, dir) { var yt = ytfn(box.height, dir), ax = xy.x - ox, ay = xy.y - oy, x = ax + ay * xsky, y = (ay + yt) * ysc; return {x: ox + x, y: oy + y}; }; showPlane('pkt', box, -1); showPlane('opt', box, 1); obTransitionNodes(); } function toNormalView() { xffn = null; hidePlane('pkt'); hidePlane('opt'); obTransitionNodes(); removePlanes(); // restore node lock state api.nodeLock(oldNodeLock); api.opacifyMap(true); } function obTransitionNodes() { // return the direction for the node // -1 for pkt layer, 1 for optical layer function dir(d) { return api.inLayer(d, 'pkt') ? -1 : 1; } if (xffn) { api.nodes().forEach(function (d) { var oldxy = {x: d.x, y: d.y}, coords = xffn(oldxy, dir(d)); d.oldxy = oldxy; d.px = d.x = coords.x; d.py = d.y = coords.y; }); } else { api.nodes().forEach(function (d) { var old = d.oldxy || {x: d.x, y: d.y}; d.px = d.x = old.x; d.py = d.y = old.y; delete d.oldxy; }); } api.node().transition() .duration(time) .attr(api.tickStuff.nodeAttr); api.link().transition() .duration(time) .call(api.calcLinkPos) .attr(api.tickStuff.linkAttr) .call(api.applyNumLinkLabels); api.linkLabel().transition() .duration(time) .attr(api.tickStuff.linkLabelAttr); } function showPlane(tag, box, dir) { // set box origin at center.. box.x = -box.width/2; box.y = -box.height/2; plane[tag].select('rect') .attr(box) .attr('opacity', 0) .transition() .duration(time) .attr('opacity', 1) .attr('transform', obXform(box.height, dir)); } function hidePlane(tag) { plane[tag].select('rect') .transition() .duration(time) .attr('opacity', 0) .attr('transform', noXform()); } function insertPlanes(ox, oy) { function ins(tag) { var id = planeId(tag), g = api.zoomLayer().insert('g', '#topo-G') .attr('id', id) .attr('transform', sus.translate(ox,oy)); g.append('rect') .attr('fill', fill[tag]) .attr('opacity', 0); plane[tag] = g; } ins('opt'); ins('pkt'); } function removePlanes() { function rem(tag) { var id = planeId(tag); api.zoomLayer().select('#'+id) .transition() .duration(time + 50) .remove(); delete plane[tag]; } rem('opt'); rem('pkt'); } // === ----------------------------------------------------- // === MODULE DEFINITION === angular.module('ovTopo') .factory('TopoObliqueService', ['$log', 'FnService', 'SvgUtilService', 'FlashService', function (_$log_, _fs_, _sus_, _flash_) { $log = _$log_; fs = _fs_; sus = _sus_; flash = _flash_; function initOblique(_api_) { api = _api_; } function destroyOblique() { } function toggleOblique() { oblique = !oblique; if (oblique) { api.force().stop(); flash.flash('Oblique view'); toObliqueView(); } else { flash.flash('Normal view'); toNormalView(); } } return { initOblique: initOblique, destroyOblique: destroyOblique, isOblique: function () { return oblique; }, toggleOblique: toggleOblique }; }]); }());