GUI -- [ONOS-309] - Oblique view of packet and optical layers (Experimental).
Change-Id: I5e3ac53192eb6c0d7bfab599fe2254f58f192a50
Showing
14 changed files
with
427 additions
and
23 deletions
| ... | @@ -149,7 +149,7 @@ | ... | @@ -149,7 +149,7 @@ |
| 149 | A: [showAllTrafficAction, 'Show all traffic'], | 149 | A: [showAllTrafficAction, 'Show all traffic'], |
| 150 | F: [showDeviceLinkFlowsAction, 'Show device link flows'], | 150 | F: [showDeviceLinkFlowsAction, 'Show device link flows'], |
| 151 | X: [toggleNodeLock, 'Lock / unlock node positions'], | 151 | X: [toggleNodeLock, 'Lock / unlock node positions'], |
| 152 | - Z: [toggleOblique, 'Toggle oblique view'], | 152 | + Z: [toggleOblique, 'Toggle oblique view (Experimental)'], |
| 153 | esc: handleEscape | 153 | esc: handleEscape |
| 154 | }; | 154 | }; |
| 155 | 155 | ||
| ... | @@ -326,6 +326,12 @@ | ... | @@ -326,6 +326,12 @@ |
| 326 | bgImg.style('visibility', visVal(vis === 'hidden')); | 326 | bgImg.style('visibility', visVal(vis === 'hidden')); |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | + function opacifyBg(b) { | ||
| 330 | + bgImg.transition() | ||
| 331 | + .duration(1000) | ||
| 332 | + .attr('opacity', b ? 1 : 0); | ||
| 333 | + } | ||
| 334 | + | ||
| 329 | function toggleNodeLock() { | 335 | function toggleNodeLock() { |
| 330 | nodeLock = !nodeLock; | 336 | nodeLock = !nodeLock; |
| 331 | flash('Node positions ' + (nodeLock ? 'locked' : 'unlocked')) | 337 | flash('Node positions ' + (nodeLock ? 'locked' : 'unlocked')) |
| ... | @@ -333,7 +339,12 @@ | ... | @@ -333,7 +339,12 @@ |
| 333 | 339 | ||
| 334 | function toggleOblique() { | 340 | function toggleOblique() { |
| 335 | oblique = !oblique; | 341 | oblique = !oblique; |
| 336 | - // TODO: oblique transformation | 342 | + if (oblique) { |
| 343 | + network.force.stop(); | ||
| 344 | + toObliqueView(); | ||
| 345 | + } else { | ||
| 346 | + toNormalView(); | ||
| 347 | + } | ||
| 337 | } | 348 | } |
| 338 | 349 | ||
| 339 | function toggleHosts() { | 350 | function toggleHosts() { |
| ... | @@ -368,7 +379,7 @@ | ... | @@ -368,7 +379,7 @@ |
| 368 | sendUpdateMeta(hovered); | 379 | sendUpdateMeta(hovered); |
| 369 | hovered.fixed = false; | 380 | hovered.fixed = false; |
| 370 | hovered.el.classed('fixed', false); | 381 | hovered.el.classed('fixed', false); |
| 371 | - network.force.resume(); | 382 | + fResume(); |
| 372 | } | 383 | } |
| 373 | } | 384 | } |
| 374 | 385 | ||
| ... | @@ -388,6 +399,179 @@ | ... | @@ -388,6 +399,179 @@ |
| 388 | } | 399 | } |
| 389 | 400 | ||
| 390 | // ============================== | 401 | // ============================== |
| 402 | + // Oblique view ... | ||
| 403 | + | ||
| 404 | + var obview = { | ||
| 405 | + tt: -.7, // x skew y factor | ||
| 406 | + xsk: -35, // x skew angle | ||
| 407 | + ysc: 0.5, // y scale | ||
| 408 | + pad: 50, | ||
| 409 | + time: 1500, | ||
| 410 | + fill: { | ||
| 411 | + pkt: 'rgba(130,130,170,0.3)', | ||
| 412 | + opt: 'rgba(170,130,170,0.3)' | ||
| 413 | + }, | ||
| 414 | + id: function (tag) { | ||
| 415 | + return 'obview-' + tag + 'Plane'; | ||
| 416 | + }, | ||
| 417 | + yt: function (h, dir) { | ||
| 418 | + return h * obview.ysc * dir * 1.1; | ||
| 419 | + }, | ||
| 420 | + obXform: function (h, dir) { | ||
| 421 | + var yt = obview.yt(h, dir); | ||
| 422 | + return scale(1, obview.ysc) + translate(0, yt) + skewX(obview.xsk); | ||
| 423 | + }, | ||
| 424 | + noXform: function () { | ||
| 425 | + return skewX(0) + translate(0,0) + scale(1,1); | ||
| 426 | + }, | ||
| 427 | + xffn: null, | ||
| 428 | + plane: {} | ||
| 429 | + }; | ||
| 430 | + | ||
| 431 | + | ||
| 432 | + function toObliqueView() { | ||
| 433 | + var box = nodeG.node().getBBox(), | ||
| 434 | + ox, oy; | ||
| 435 | + | ||
| 436 | + padBox(box, obview.pad); | ||
| 437 | + | ||
| 438 | + ox = box.x + box.width / 2; | ||
| 439 | + oy = box.y + box.height / 2; | ||
| 440 | + | ||
| 441 | + // remember node lock state, then lock the nodes down | ||
| 442 | + obview.nodeLock = nodeLock; | ||
| 443 | + nodeLock = true; | ||
| 444 | + opacifyBg(false); | ||
| 445 | + | ||
| 446 | + insertPlanes(ox, oy); | ||
| 447 | + | ||
| 448 | + obview.xffn = function (xy, dir) { | ||
| 449 | + var yt = obview.yt(box.height, dir), | ||
| 450 | + ax = xy.x - ox, | ||
| 451 | + ay = xy.y - oy, | ||
| 452 | + x = ax + ay * obview.tt, | ||
| 453 | + y = ay * obview.ysc + obview.ysc * yt; | ||
| 454 | + return {x: ox + x, y: oy + y}; | ||
| 455 | + }; | ||
| 456 | + | ||
| 457 | + showPlane('pkt', box, -1); | ||
| 458 | + showPlane('opt', box, 1); | ||
| 459 | + obTransitionNodes(); | ||
| 460 | + } | ||
| 461 | + | ||
| 462 | + function toNormalView() { | ||
| 463 | + obview.xffn = null; | ||
| 464 | + | ||
| 465 | + hidePlane('pkt'); | ||
| 466 | + hidePlane('opt'); | ||
| 467 | + obTransitionNodes(); | ||
| 468 | + | ||
| 469 | + removePlanes(); | ||
| 470 | + | ||
| 471 | + // restore node lock state | ||
| 472 | + nodeLock = obview.nodeLock; | ||
| 473 | + opacifyBg(true); | ||
| 474 | + } | ||
| 475 | + | ||
| 476 | + function obTransitionNodes() { | ||
| 477 | + var xffn = obview.xffn; | ||
| 478 | + | ||
| 479 | + // return the direction for the node | ||
| 480 | + // -1 for pkt layer, 1 for optical layer | ||
| 481 | + function dir(d) { | ||
| 482 | + return inLayer(d, 'pkt') ? -1 : 1; | ||
| 483 | + } | ||
| 484 | + | ||
| 485 | + if (xffn) { | ||
| 486 | + network.nodes.forEach(function (d) { | ||
| 487 | + var oldxy = {x: d.x, y: d.y}, | ||
| 488 | + coords = xffn(oldxy, dir(d)); | ||
| 489 | + d.oldxy = oldxy; | ||
| 490 | + d.px = d.x = coords.x; | ||
| 491 | + d.py = d.y = coords.y; | ||
| 492 | + }); | ||
| 493 | + } else { | ||
| 494 | + network.nodes.forEach(function (d) { | ||
| 495 | + var old = d.oldxy || {x: d.x, y: d.y}; | ||
| 496 | + d.px = d.x = old.x; | ||
| 497 | + d.py = d.y = old.y; | ||
| 498 | + delete d.oldxy; | ||
| 499 | + }); | ||
| 500 | + } | ||
| 501 | + | ||
| 502 | + node.transition() | ||
| 503 | + .duration(obview.time) | ||
| 504 | + .attr(tickStuff.nodeAttr); | ||
| 505 | + link.transition() | ||
| 506 | + .duration(obview.time) | ||
| 507 | + .attr(tickStuff.linkAttr); | ||
| 508 | + linkLabel.transition() | ||
| 509 | + .duration(obview.time) | ||
| 510 | + .attr(tickStuff.linkLabelAttr); | ||
| 511 | + } | ||
| 512 | + | ||
| 513 | + function showPlane(tag, box, dir) { | ||
| 514 | + var g = obview.plane[tag]; | ||
| 515 | + | ||
| 516 | + // set box origin at center.. | ||
| 517 | + box.x = -box.width/2; | ||
| 518 | + box.y = -box.height/2; | ||
| 519 | + | ||
| 520 | + g.select('rect') | ||
| 521 | + .attr(box) | ||
| 522 | + .attr('opacity', 0) | ||
| 523 | + .transition() | ||
| 524 | + .duration(obview.time) | ||
| 525 | + .attr('opacity', 1) | ||
| 526 | + .attr('transform', obview.obXform(box.height, dir)); | ||
| 527 | + } | ||
| 528 | + | ||
| 529 | + function hidePlane(tag) { | ||
| 530 | + var g = obview.plane[tag]; | ||
| 531 | + | ||
| 532 | + g.select('rect') | ||
| 533 | + .transition() | ||
| 534 | + .duration(obview.time) | ||
| 535 | + .attr('opacity', 0) | ||
| 536 | + .attr('transform', obview.noXform()); | ||
| 537 | + } | ||
| 538 | + | ||
| 539 | + function insertPlanes(ox, oy) { | ||
| 540 | + function ins(tag) { | ||
| 541 | + var id = obview.id(tag), | ||
| 542 | + g = panZoomContainer.insert('g', '#topo-G') | ||
| 543 | + .attr('id', id) | ||
| 544 | + .attr('transform', translate(ox,oy)); | ||
| 545 | + g.append('rect') | ||
| 546 | + .attr('fill', obview.fill[tag]) | ||
| 547 | + .attr('opacity', 0); | ||
| 548 | + obview.plane[tag] = g; | ||
| 549 | + } | ||
| 550 | + ins('opt'); | ||
| 551 | + ins('pkt'); | ||
| 552 | + } | ||
| 553 | + | ||
| 554 | + function removePlanes() { | ||
| 555 | + function rem(tag) { | ||
| 556 | + var id = obview.id(tag); | ||
| 557 | + panZoomContainer.select('#'+id) | ||
| 558 | + .transition() | ||
| 559 | + .duration(obview.time + 50) | ||
| 560 | + .remove(); | ||
| 561 | + delete obview.plane[tag]; | ||
| 562 | + } | ||
| 563 | + rem('opt'); | ||
| 564 | + rem('pkt'); | ||
| 565 | + } | ||
| 566 | + | ||
| 567 | + function padBox(box, p) { | ||
| 568 | + box.x -= p; | ||
| 569 | + box.y -= p; | ||
| 570 | + box.width += p*2; | ||
| 571 | + box.height += p*2; | ||
| 572 | + } | ||
| 573 | + | ||
| 574 | + // ============================== | ||
| 391 | // Radio Button Callbacks | 575 | // Radio Button Callbacks |
| 392 | 576 | ||
| 393 | var layerLookup = { | 577 | var layerLookup = { |
| ... | @@ -651,7 +835,7 @@ | ... | @@ -651,7 +835,7 @@ |
| 651 | network.nodes.push(d); | 835 | network.nodes.push(d); |
| 652 | network.lookup[id] = d; | 836 | network.lookup[id] = d; |
| 653 | updateNodes(); | 837 | updateNodes(); |
| 654 | - network.force.start(); | 838 | + fStart(); |
| 655 | } | 839 | } |
| 656 | 840 | ||
| 657 | function addLink(data) { | 841 | function addLink(data) { |
| ... | @@ -678,7 +862,7 @@ | ... | @@ -678,7 +862,7 @@ |
| 678 | network.links.push(d); | 862 | network.links.push(d); |
| 679 | network.lookup[d.key] = d; | 863 | network.lookup[d.key] = d; |
| 680 | updateLinks(); | 864 | updateLinks(); |
| 681 | - network.force.start(); | 865 | + fStart(); |
| 682 | } | 866 | } |
| 683 | } | 867 | } |
| 684 | 868 | ||
| ... | @@ -707,7 +891,7 @@ | ... | @@ -707,7 +891,7 @@ |
| 707 | network.lookup[d.egress] = lnk; | 891 | network.lookup[d.egress] = lnk; |
| 708 | updateLinks(); | 892 | updateLinks(); |
| 709 | } | 893 | } |
| 710 | - network.force.start(); | 894 | + fStart(); |
| 711 | } | 895 | } |
| 712 | 896 | ||
| 713 | // TODO: fold updateX(...) methods into one base method; remove duplication | 897 | // TODO: fold updateX(...) methods into one base method; remove duplication |
| ... | @@ -1332,7 +1516,12 @@ | ... | @@ -1332,7 +1516,12 @@ |
| 1332 | function translate(x, y) { | 1516 | function translate(x, y) { |
| 1333 | return 'translate(' + x + ',' + y + ')'; | 1517 | return 'translate(' + x + ',' + y + ')'; |
| 1334 | } | 1518 | } |
| 1335 | - | 1519 | + function scale(x,y) { |
| 1520 | + return 'scale(' + x + ',' + y + ')'; | ||
| 1521 | + } | ||
| 1522 | + function skewX(x) { | ||
| 1523 | + return 'skewX(' + x + ')'; | ||
| 1524 | + } | ||
| 1336 | function rotate(deg) { | 1525 | function rotate(deg) { |
| 1337 | return 'rotate(' + deg + ')'; | 1526 | return 'rotate(' + deg + ')'; |
| 1338 | } | 1527 | } |
| ... | @@ -1965,8 +2154,7 @@ | ... | @@ -1965,8 +2154,7 @@ |
| 1965 | .style('fill', '#888') | 2154 | .style('fill', '#888') |
| 1966 | .style('opacity', 0.5); | 2155 | .style('opacity', 0.5); |
| 1967 | }); | 2156 | }); |
| 1968 | - | 2157 | + fResume(); |
| 1969 | - network.force.resume(); | ||
| 1970 | } | 2158 | } |
| 1971 | 2159 | ||
| 1972 | var dCol = { | 2160 | var dCol = { |
| ... | @@ -2091,7 +2279,7 @@ | ... | @@ -2091,7 +2279,7 @@ |
| 2091 | // remove from lookup cache | 2279 | // remove from lookup cache |
| 2092 | delete network.lookup[removed[0].key]; | 2280 | delete network.lookup[removed[0].key]; |
| 2093 | updateLinks(); | 2281 | updateLinks(); |
| 2094 | - network.force.resume(); | 2282 | + fResume(); |
| 2095 | } | 2283 | } |
| 2096 | } | 2284 | } |
| 2097 | 2285 | ||
| ... | @@ -2113,7 +2301,7 @@ | ... | @@ -2113,7 +2301,7 @@ |
| 2113 | // NOTE: upd is false if we were called from removeDeviceElement() | 2301 | // NOTE: upd is false if we were called from removeDeviceElement() |
| 2114 | if (upd) { | 2302 | if (upd) { |
| 2115 | updateNodes(); | 2303 | updateNodes(); |
| 2116 | - network.force.resume(); | 2304 | + fResume(); |
| 2117 | } | 2305 | } |
| 2118 | } | 2306 | } |
| 2119 | 2307 | ||
| ... | @@ -2131,7 +2319,7 @@ | ... | @@ -2131,7 +2319,7 @@ |
| 2131 | network.nodes.splice(idx, 1); | 2319 | network.nodes.splice(idx, 1); |
| 2132 | // remove from SVG | 2320 | // remove from SVG |
| 2133 | updateNodes(); | 2321 | updateNodes(); |
| 2134 | - network.force.resume(); | 2322 | + fResume(); |
| 2135 | } | 2323 | } |
| 2136 | 2324 | ||
| 2137 | function findAttachedHosts(devId) { | 2325 | function findAttachedHosts(devId) { |
| ... | @@ -2154,20 +2342,30 @@ | ... | @@ -2154,20 +2342,30 @@ |
| 2154 | return links; | 2342 | return links; |
| 2155 | } | 2343 | } |
| 2156 | 2344 | ||
| 2157 | - function tick() { | 2345 | + function fResume() { |
| 2158 | - node.attr({ | 2346 | + if (!oblique) { |
| 2159 | - transform: function (d) { return translate(d.x, d.y); } | 2347 | + network.force.resume(); |
| 2160 | - }); | 2348 | + } |
| 2349 | + } | ||
| 2161 | 2350 | ||
| 2162 | - link.attr({ | 2351 | + function fStart() { |
| 2352 | + if (!oblique) { | ||
| 2353 | + network.force.start(); | ||
| 2354 | + } | ||
| 2355 | + } | ||
| 2356 | + | ||
| 2357 | + var tickStuff = { | ||
| 2358 | + nodeAttr: { | ||
| 2359 | + transform: function (d) { return translate(d.x, d.y); } | ||
| 2360 | + }, | ||
| 2361 | + linkAttr: { | ||
| 2163 | x1: function (d) { return d.source.x; }, | 2362 | x1: function (d) { return d.source.x; }, |
| 2164 | y1: function (d) { return d.source.y; }, | 2363 | y1: function (d) { return d.source.y; }, |
| 2165 | x2: function (d) { return d.target.x; }, | 2364 | x2: function (d) { return d.target.x; }, |
| 2166 | y2: function (d) { return d.target.y; } | 2365 | y2: function (d) { return d.target.y; } |
| 2167 | - }); | 2366 | + }, |
| 2168 | - | 2367 | + linkLabelAttr: { |
| 2169 | - linkLabel.each(function (d) { | 2368 | + transform: function (d) { |
| 2170 | - var el = d3.select(this); | ||
| 2171 | var lnk = findLinkById(d.key); | 2369 | var lnk = findLinkById(d.key); |
| 2172 | 2370 | ||
| 2173 | if (lnk) { | 2371 | if (lnk) { |
| ... | @@ -2177,9 +2375,16 @@ | ... | @@ -2177,9 +2375,16 @@ |
| 2177 | x2: lnk.target.x, | 2375 | x2: lnk.target.x, |
| 2178 | y2: lnk.target.y | 2376 | y2: lnk.target.y |
| 2179 | }; | 2377 | }; |
| 2180 | - el.attr('transform', transformLabel(parms)); | 2378 | + return transformLabel(parms); |
| 2181 | } | 2379 | } |
| 2182 | - }); | 2380 | + } |
| 2381 | + } | ||
| 2382 | + }; | ||
| 2383 | + | ||
| 2384 | + function tick() { | ||
| 2385 | + node.attr(tickStuff.nodeAttr); | ||
| 2386 | + link.attr(tickStuff.linkAttr); | ||
| 2387 | + linkLabel.attr(tickStuff.linkLabelAttr); | ||
| 2183 | } | 2388 | } |
| 2184 | 2389 | ||
| 2185 | // ============================== | 2390 | // ============================== | ... | ... |
-
Please register or login to post a comment