Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2015 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 Oblique View Module. |
| 19 | Provides functionality to view the topology as two planes (packet & optical) |
| 20 | from an oblique (side-on) perspective. |
| 21 | */ |
| 22 | |
| 23 | (function () { |
| 24 | 'use strict'; |
| 25 | |
| 26 | // injected refs |
Bri Prebilic Cole | 9cf1a8d | 2015-04-21 13:15:29 -0700 | [diff] [blame] | 27 | var $log, fs, sus, flash; |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 28 | |
| 29 | // api to topoForce |
| 30 | var api; |
| 31 | /* |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 32 | force() // get ref to force layout object |
| 33 | zoomLayer() // get ref to zoom layer |
| 34 | nodeGBBox() // get bounding box of node group layer |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 35 | node() // get ref to D3 selection of nodes |
| 36 | link() // get ref to D3 selection of links |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 37 | nodes() // get ref to network nodes array |
| 38 | tickStuff // ref to tick functions |
| 39 | nodeLock(b) // test-and-set nodeLock state |
| 40 | opacifyMap(b) // show or hide map layer |
| 41 | inLayer(d, layer) // return true if d in layer {'pkt'|'opt'} |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 42 | */ |
| 43 | |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 44 | // configuration |
| 45 | var xsky = -.7, // x skew y factor |
| 46 | xsk = -35, // x skew angle |
| 47 | ysc = .5, // y scale |
| 48 | pad = 50, |
| 49 | time = 1500, |
| 50 | fill = { |
| 51 | pkt: 'rgba(130,130,170,0.3)', // blue-ish |
| 52 | opt: 'rgba(170,130,170,0.3)' // magenta-ish |
| 53 | }; |
| 54 | |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 55 | // internal state |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 56 | var oblique = false, |
| 57 | xffn = null, |
| 58 | plane = {}, |
| 59 | oldNodeLock; |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 60 | |
Simon Hunt | 96f88c6 | 2015-02-19 17:57:25 -0800 | [diff] [blame] | 61 | |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 62 | function planeId(tag) { |
| 63 | return 'topo-obview-' + tag + 'Plane'; |
Simon Hunt | 96f88c6 | 2015-02-19 17:57:25 -0800 | [diff] [blame] | 64 | } |
| 65 | |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 66 | function ytfn(h, dir) { |
| 67 | return h * ysc * dir * 1.1; |
| 68 | } |
| 69 | |
| 70 | function obXform(h, dir) { |
| 71 | var yt = ytfn(h, dir); |
| 72 | return sus.scale(1, ysc) + sus.translate(0, yt) + sus.skewX(xsk); |
| 73 | } |
| 74 | |
| 75 | function noXform() { |
| 76 | return sus.skewX(0) + sus.translate(0,0) + sus.scale(1,1); |
| 77 | } |
| 78 | |
| 79 | function padBox(box, p) { |
| 80 | box.x -= p; |
| 81 | box.y -= p; |
| 82 | box.width += p*2; |
| 83 | box.height += p*2; |
| 84 | } |
| 85 | |
| 86 | function toObliqueView() { |
| 87 | var box = api.nodeGBBox(), |
| 88 | ox, oy; |
| 89 | |
| 90 | padBox(box, pad); |
| 91 | |
| 92 | ox = box.x + box.width / 2; |
| 93 | oy = box.y + box.height / 2; |
| 94 | |
| 95 | // remember node lock state, then lock the nodes down |
| 96 | oldNodeLock = api.nodeLock(true); |
| 97 | api.opacifyMap(false); |
| 98 | |
| 99 | insertPlanes(ox, oy); |
| 100 | |
| 101 | xffn = function (xy, dir) { |
| 102 | var yt = ytfn(box.height, dir), |
| 103 | ax = xy.x - ox, |
| 104 | ay = xy.y - oy, |
| 105 | x = ax + ay * xsky, |
| 106 | y = (ay + yt) * ysc; |
| 107 | return {x: ox + x, y: oy + y}; |
| 108 | }; |
| 109 | |
| 110 | showPlane('pkt', box, -1); |
| 111 | showPlane('opt', box, 1); |
| 112 | obTransitionNodes(); |
| 113 | } |
| 114 | |
| 115 | function toNormalView() { |
| 116 | xffn = null; |
| 117 | |
| 118 | hidePlane('pkt'); |
| 119 | hidePlane('opt'); |
| 120 | obTransitionNodes(); |
| 121 | |
| 122 | removePlanes(); |
| 123 | |
| 124 | // restore node lock state |
| 125 | api.nodeLock(oldNodeLock); |
| 126 | api.opacifyMap(true); |
| 127 | } |
| 128 | |
| 129 | function obTransitionNodes() { |
| 130 | // return the direction for the node |
| 131 | // -1 for pkt layer, 1 for optical layer |
| 132 | function dir(d) { |
| 133 | return api.inLayer(d, 'pkt') ? -1 : 1; |
| 134 | } |
| 135 | |
| 136 | if (xffn) { |
| 137 | api.nodes().forEach(function (d) { |
| 138 | var oldxy = {x: d.x, y: d.y}, |
| 139 | coords = xffn(oldxy, dir(d)); |
| 140 | d.oldxy = oldxy; |
| 141 | d.px = d.x = coords.x; |
| 142 | d.py = d.y = coords.y; |
| 143 | }); |
| 144 | } else { |
| 145 | api.nodes().forEach(function (d) { |
| 146 | var old = d.oldxy || {x: d.x, y: d.y}; |
| 147 | d.px = d.x = old.x; |
| 148 | d.py = d.y = old.y; |
| 149 | delete d.oldxy; |
| 150 | }); |
| 151 | } |
| 152 | |
| 153 | api.node().transition() |
| 154 | .duration(time) |
| 155 | .attr(api.tickStuff.nodeAttr); |
| 156 | api.link().transition() |
| 157 | .duration(time) |
| 158 | .attr(api.tickStuff.linkAttr); |
| 159 | api.linkLabel().transition() |
| 160 | .duration(time) |
| 161 | .attr(api.tickStuff.linkLabelAttr); |
| 162 | } |
| 163 | |
| 164 | function showPlane(tag, box, dir) { |
| 165 | // set box origin at center.. |
| 166 | box.x = -box.width/2; |
| 167 | box.y = -box.height/2; |
| 168 | |
| 169 | plane[tag].select('rect') |
| 170 | .attr(box) |
| 171 | .attr('opacity', 0) |
| 172 | .transition() |
| 173 | .duration(time) |
| 174 | .attr('opacity', 1) |
| 175 | .attr('transform', obXform(box.height, dir)); |
| 176 | } |
| 177 | |
| 178 | function hidePlane(tag) { |
| 179 | plane[tag].select('rect') |
| 180 | .transition() |
| 181 | .duration(time) |
| 182 | .attr('opacity', 0) |
| 183 | .attr('transform', noXform()); |
| 184 | } |
| 185 | |
| 186 | function insertPlanes(ox, oy) { |
| 187 | function ins(tag) { |
| 188 | var id = planeId(tag), |
| 189 | g = api.zoomLayer().insert('g', '#topo-G') |
| 190 | .attr('id', id) |
| 191 | .attr('transform', sus.translate(ox,oy)); |
| 192 | g.append('rect') |
| 193 | .attr('fill', fill[tag]) |
| 194 | .attr('opacity', 0); |
| 195 | plane[tag] = g; |
| 196 | } |
| 197 | ins('opt'); |
| 198 | ins('pkt'); |
| 199 | } |
| 200 | |
| 201 | function removePlanes() { |
| 202 | function rem(tag) { |
| 203 | var id = planeId(tag); |
| 204 | api.zoomLayer().select('#'+id) |
| 205 | .transition() |
| 206 | .duration(time + 50) |
| 207 | .remove(); |
| 208 | delete plane[tag]; |
| 209 | } |
| 210 | rem('opt'); |
| 211 | rem('pkt'); |
| 212 | } |
| 213 | |
| 214 | |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 215 | // === ----------------------------------------------------- |
| 216 | // === MODULE DEFINITION === |
| 217 | |
| 218 | angular.module('ovTopo') |
| 219 | .factory('TopoObliqueService', |
Bri Prebilic Cole | 9cf1a8d | 2015-04-21 13:15:29 -0700 | [diff] [blame] | 220 | ['$log', 'FnService', 'SvgUtilService', 'FlashService', |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 221 | |
Bri Prebilic Cole | 9cf1a8d | 2015-04-21 13:15:29 -0700 | [diff] [blame] | 222 | function (_$log_, _fs_, _sus_, _flash_) { |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 223 | $log = _$log_; |
| 224 | fs = _fs_; |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 225 | sus = _sus_; |
Bri Prebilic Cole | 9cf1a8d | 2015-04-21 13:15:29 -0700 | [diff] [blame] | 226 | flash = _flash_; |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 227 | |
| 228 | function initOblique(_api_) { |
| 229 | api = _api_; |
| 230 | } |
| 231 | |
| 232 | function destroyOblique() { } |
| 233 | |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 234 | function toggleOblique() { |
| 235 | oblique = !oblique; |
| 236 | if (oblique) { |
| 237 | api.force().stop(); |
Bri Prebilic Cole | 9cf1a8d | 2015-04-21 13:15:29 -0700 | [diff] [blame] | 238 | flash.flash('Oblique view'); |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 239 | toObliqueView(); |
| 240 | } else { |
Bri Prebilic Cole | 9cf1a8d | 2015-04-21 13:15:29 -0700 | [diff] [blame] | 241 | flash.flash('Normal view'); |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 242 | toNormalView(); |
| 243 | } |
| 244 | } |
| 245 | |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 246 | return { |
| 247 | initOblique: initOblique, |
Simon Hunt | 96f88c6 | 2015-02-19 17:57:25 -0800 | [diff] [blame] | 248 | destroyOblique: destroyOblique, |
| 249 | |
Simon Hunt | c3c5b67 | 2015-02-20 11:32:13 -0800 | [diff] [blame] | 250 | isOblique: function () { return oblique; }, |
Simon Hunt | 96f88c6 | 2015-02-19 17:57:25 -0800 | [diff] [blame] | 251 | toggleOblique: toggleOblique |
Simon Hunt | ef1138d | 2015-02-19 17:14:03 -0800 | [diff] [blame] | 252 | }; |
| 253 | }]); |
| 254 | }()); |