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