Fixed node geometry so that [x,y] is now at the center of the rectangle. (Added debug circles).
Added radio buttons in mast head.
Showing
3 changed files
with
143 additions
and
64 deletions
| ... | @@ -25,9 +25,11 @@ | ... | @@ -25,9 +25,11 @@ |
| 25 | --> | 25 | --> |
| 26 | <html> | 26 | <html> |
| 27 | <head> | 27 | <head> |
| 28 | + <meta charset="utf-8"> | ||
| 28 | <title>ONOS GUI</title> | 29 | <title>ONOS GUI</title> |
| 29 | 30 | ||
| 30 | - <script src="libs/d3.min.js"></script> | 31 | + <!--TODO: use the minified version of d3, once debugging is complete --> |
| 32 | + <script src="libs/d3.js"></script> | ||
| 31 | <script src="libs/jquery-2.1.1.min.js"></script> | 33 | <script src="libs/jquery-2.1.1.min.js"></script> |
| 32 | 34 | ||
| 33 | <link rel="stylesheet" href="base.css"> | 35 | <link rel="stylesheet" href="base.css"> |
| ... | @@ -42,10 +44,10 @@ | ... | @@ -42,10 +44,10 @@ |
| 42 | <div id="mast"> | 44 | <div id="mast"> |
| 43 | <img id="logo" src="img/onos-logo.png" width="60" height="38"> | 45 | <img id="logo" src="img/onos-logo.png" width="60" height="38"> |
| 44 | <span class="title">Open Network Operating System</span> | 46 | <span class="title">Open Network Operating System</span> |
| 45 | - <span class="right"> | 47 | + <span id="displayModes" class="right"> |
| 46 | - <span class="radio">[All Layers]</span> | 48 | + <span id="showAll" class="radio active">All Layers</span> |
| 47 | - <span class="radio">[Packet Only]</span> | 49 | + <span id="showPkt" class="radio">Packet Only</span> |
| 48 | - <span class="radio">[Optical Only]</span> | 50 | + <span id="showOpt" class="radio">Optical Only</span> |
| 49 | </span> | 51 | </span> |
| 50 | </div> | 52 | </div> |
| 51 | <div id="view"></div> | 53 | <div id="view"></div> | ... | ... |
| ... | @@ -31,7 +31,8 @@ | ... | @@ -31,7 +31,8 @@ |
| 31 | var config = { | 31 | var config = { |
| 32 | options: { | 32 | options: { |
| 33 | layering: true, | 33 | layering: true, |
| 34 | - collisionPrevention: true | 34 | + collisionPrevention: true, |
| 35 | + showNodeXY: true | ||
| 35 | }, | 36 | }, |
| 36 | XjsonUrl: 'rs/topology/graph', | 37 | XjsonUrl: 'rs/topology/graph', |
| 37 | jsonUrl: 'network.json', | 38 | jsonUrl: 'network.json', |
| ... | @@ -91,7 +92,8 @@ | ... | @@ -91,7 +92,8 @@ |
| 91 | view = {}, | 92 | view = {}, |
| 92 | network = {}, | 93 | network = {}, |
| 93 | selected = {}, | 94 | selected = {}, |
| 94 | - highlighted = null; | 95 | + highlighted = null, |
| 96 | + viewMode = 'showAll'; | ||
| 95 | 97 | ||
| 96 | 98 | ||
| 97 | function loadNetworkView() { | 99 | function loadNetworkView() { |
| ... | @@ -126,6 +128,21 @@ | ... | @@ -126,6 +128,21 @@ |
| 126 | }); | 128 | }); |
| 127 | 129 | ||
| 128 | $(window).on('resize', resize); | 130 | $(window).on('resize', resize); |
| 131 | + | ||
| 132 | + // set up radio button behavior | ||
| 133 | + d3.selectAll("#displayModes .radio").on('click', function() { | ||
| 134 | + var id = d3.select(this).attr("id"); | ||
| 135 | + if (id !== viewMode) { | ||
| 136 | + radioButton('displayModes', id); | ||
| 137 | + viewMode = id; | ||
| 138 | + alert("action: " + id); | ||
| 139 | + } | ||
| 140 | + }); | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + function radioButton(group, id) { | ||
| 144 | + d3.selectAll("#" + group + " .radio").classed("active", false); | ||
| 145 | + d3.select("#" + group + " #" + id).classed("active", true); | ||
| 129 | } | 146 | } |
| 130 | 147 | ||
| 131 | 148 | ||
| ... | @@ -263,8 +280,6 @@ | ... | @@ -263,8 +280,6 @@ |
| 263 | // + " scale(" + d3.event.scale + ")"); | 280 | // + " scale(" + d3.event.scale + ")"); |
| 264 | // } | 281 | // } |
| 265 | 282 | ||
| 266 | - // TODO: svg.append('defs') for markers? | ||
| 267 | - | ||
| 268 | // TODO: move glow/blur stuff to util script | 283 | // TODO: move glow/blur stuff to util script |
| 269 | var glow = network.svg.append('filter') | 284 | var glow = network.svg.append('filter') |
| 270 | .attr('x', '-50%') | 285 | .attr('x', '-50%') |
| ... | @@ -295,8 +310,7 @@ | ... | @@ -295,8 +310,7 @@ |
| 295 | // }); | 310 | // }); |
| 296 | 311 | ||
| 297 | 312 | ||
| 298 | - | 313 | + // add links to the display |
| 299 | - | ||
| 300 | network.link = network.svg.append('g').selectAll('.link') | 314 | network.link = network.svg.append('g').selectAll('.link') |
| 301 | .data(network.force.links(), function(d) {return d.id}) | 315 | .data(network.force.links(), function(d) {return d.id}) |
| 302 | .enter().append('line') | 316 | .enter().append('line') |
| ... | @@ -350,6 +364,7 @@ | ... | @@ -350,6 +364,7 @@ |
| 350 | }); | 364 | }); |
| 351 | 365 | ||
| 352 | 366 | ||
| 367 | + // add nodes to the display | ||
| 353 | network.node = network.svg.selectAll('.node') | 368 | network.node = network.svg.selectAll('.node') |
| 354 | .data(network.force.nodes(), function(d) {return d.id}) | 369 | .data(network.force.nodes(), function(d) {return d.id}) |
| 355 | .enter().append('g') | 370 | .enter().append('g') |
| ... | @@ -386,15 +401,19 @@ | ... | @@ -386,15 +401,19 @@ |
| 386 | }); | 401 | }); |
| 387 | 402 | ||
| 388 | network.nodeRect = network.node.append('rect') | 403 | network.nodeRect = network.node.append('rect') |
| 389 | - .attr('rx', 5) | 404 | + .attr({ |
| 390 | - .attr('ry', 5); | 405 | + rx: 5, |
| 406 | + ry: 5, | ||
| 407 | + width: 100, | ||
| 408 | + height: 12 | ||
| 409 | + }); | ||
| 391 | // note that width/height are adjusted to fit the label text | 410 | // note that width/height are adjusted to fit the label text |
| 392 | 411 | ||
| 393 | network.node.each(function(d) { | 412 | network.node.each(function(d) { |
| 394 | var node = d3.select(this), | 413 | var node = d3.select(this), |
| 395 | - rect = node.select('rect'), | 414 | + icon = iconUrl(d); |
| 396 | - icon = iconUrl(d), | 415 | + |
| 397 | - text = node.append('text') | 416 | + node.append('text') |
| 398 | // TODO: add label cycle behavior | 417 | // TODO: add label cycle behavior |
| 399 | .text(d.id) | 418 | .text(d.id) |
| 400 | .attr('dy', '1.1em'); | 419 | .attr('dy', '1.1em'); |
| ... | @@ -402,63 +421,85 @@ | ... | @@ -402,63 +421,85 @@ |
| 402 | if (icon) { | 421 | if (icon) { |
| 403 | var cfg = config.icons; | 422 | var cfg = config.icons; |
| 404 | node.append('svg:image') | 423 | node.append('svg:image') |
| 405 | - .attr('width', cfg.w) | 424 | + .attr({ |
| 406 | - .attr('height', cfg.h) | 425 | + width: cfg.w, |
| 407 | - .attr('xlink:href', icon); | 426 | + height: cfg.h, |
| 427 | + 'xlink:href': icon | ||
| 428 | + }); | ||
| 408 | // note, icon relative positioning (x,y) is done after we have | 429 | // note, icon relative positioning (x,y) is done after we have |
| 409 | // adjusted the bounds of the rectangle... | 430 | // adjusted the bounds of the rectangle... |
| 410 | } | 431 | } |
| 411 | 432 | ||
| 433 | + // for debugging... | ||
| 434 | + if (config.options.showNodeXY) { | ||
| 435 | + node.append('circle') | ||
| 436 | + .attr({ | ||
| 437 | + class: 'debug', | ||
| 438 | + cx: 0, | ||
| 439 | + cy: 0, | ||
| 440 | + r: '3px' | ||
| 441 | + }); | ||
| 442 | + } | ||
| 412 | }); | 443 | }); |
| 413 | 444 | ||
| 445 | + | ||
| 446 | + // returns the newly computed bounding box | ||
| 447 | + function adjustRectToFitText(n) { | ||
| 448 | + var text = n.select('text'), | ||
| 449 | + box = text.node().getBBox(), | ||
| 450 | + lab = config.labels; | ||
| 451 | + | ||
| 452 | + text.attr('text-anchor', 'middle') | ||
| 453 | + .attr('y', '-0.8em') | ||
| 454 | + .attr('x', lab.imgPad/2) | ||
| 455 | + ; | ||
| 456 | + | ||
| 457 | + // TODO: figure out how to access the data on selection | ||
| 458 | + console.log("\nadjust rect for " + n.data().id); | ||
| 459 | + console.log(box); | ||
| 460 | + | ||
| 461 | + // translate the bbox so that it is centered on [x,y] | ||
| 462 | + box.x = -box.width / 2; | ||
| 463 | + box.y = -box.height / 2; | ||
| 464 | + | ||
| 465 | + // add padding | ||
| 466 | + box.x -= (lab.padLR + lab.imgPad/2); | ||
| 467 | + box.width += lab.padLR * 2 + lab.imgPad; | ||
| 468 | + box.y -= lab.padTB; | ||
| 469 | + box.height += lab.padTB * 2; | ||
| 470 | + | ||
| 471 | + return box; | ||
| 472 | + } | ||
| 473 | + | ||
| 474 | + function boundsFromBox(box) { | ||
| 475 | + return { | ||
| 476 | + x1: box.x, | ||
| 477 | + y1: box.y, | ||
| 478 | + x2: box.x + box.width, | ||
| 479 | + y2: box.y + box.height | ||
| 480 | + }; | ||
| 481 | + } | ||
| 482 | + | ||
| 414 | // this function is scheduled to happen soon after the given thread ends | 483 | // this function is scheduled to happen soon after the given thread ends |
| 415 | setTimeout(function() { | 484 | setTimeout(function() { |
| 416 | network.node.each(function(d) { | 485 | network.node.each(function(d) { |
| 417 | // for every node, recompute size, padding, etc. so text fits | 486 | // for every node, recompute size, padding, etc. so text fits |
| 418 | var node = d3.select(this), | 487 | var node = d3.select(this), |
| 419 | - text = node.selectAll('text'), | 488 | + text = node.select('text'), |
| 420 | - bounds = {}, | 489 | + box = adjustRectToFitText(node), |
| 421 | - first = true; | 490 | + lab = config.labels; |
| 422 | - | ||
| 423 | - // NOTE: probably unnecessary code if we only have one line. | ||
| 424 | - text.each(function() { | ||
| 425 | - var box = this.getBBox(); | ||
| 426 | - if (first || box.x < bounds.x1) { | ||
| 427 | - bounds.x1 = box.x; | ||
| 428 | - } | ||
| 429 | - if (first || box.y < bounds.y1) { | ||
| 430 | - bounds.y1 = box.y; | ||
| 431 | - } | ||
| 432 | - if (first || box.x + box.width < bounds.x2) { | ||
| 433 | - bounds.x2 = box.x + box.width; | ||
| 434 | - } | ||
| 435 | - if (first || box.y + box.height < bounds.y2) { | ||
| 436 | - bounds.y2 = box.y + box.height; | ||
| 437 | - } | ||
| 438 | - first = false; | ||
| 439 | - }).attr('text-anchor', 'middle'); | ||
| 440 | - | ||
| 441 | - var lab = config.labels, | ||
| 442 | - oldWidth = bounds.x2 - bounds.x1; | ||
| 443 | - | ||
| 444 | - bounds.x1 -= oldWidth / 2; | ||
| 445 | - bounds.x2 -= oldWidth / 2; | ||
| 446 | - | ||
| 447 | - bounds.x1 -= (lab.padLR + lab.imgPad); | ||
| 448 | - bounds.y1 -= lab.padTB; | ||
| 449 | - bounds.x2 += lab.padLR; | ||
| 450 | - bounds.y2 += lab.padTB; | ||
| 451 | 491 | ||
| 492 | + // now make the computed adjustment | ||
| 452 | node.select('rect') | 493 | node.select('rect') |
| 453 | - .attr('x', bounds.x1) | 494 | + .attr(box); |
| 454 | - .attr('y', bounds.y1) | ||
| 455 | - .attr('width', bounds.x2 - bounds.x1) | ||
| 456 | - .attr('height', bounds.y2 - bounds.y1); | ||
| 457 | 495 | ||
| 458 | node.select('image') | 496 | node.select('image') |
| 459 | - .attr('x', bounds.x1 + config.icons.xoff) | 497 | + .attr('x', box.x + config.icons.xoff) |
| 460 | - .attr('y', bounds.y1 + config.icons.yoff); | 498 | + .attr('y', box.y + config.icons.yoff); |
| 499 | + | ||
| 500 | + var bounds = boundsFromBox(box); | ||
| 461 | 501 | ||
| 502 | + // todo: clean up extent and edge work.. | ||
| 462 | d.extent = { | 503 | d.extent = { |
| 463 | left: bounds.x1 - lab.marginLR, | 504 | left: bounds.x1 - lab.marginLR, |
| 464 | right: bounds.x2 + lab.marginLR, | 505 | right: bounds.x2 + lab.marginLR, |
| ... | @@ -473,7 +514,6 @@ | ... | @@ -473,7 +514,6 @@ |
| 473 | bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2) | 514 | bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2) |
| 474 | }; | 515 | }; |
| 475 | 516 | ||
| 476 | - // ==== | ||
| 477 | }); | 517 | }); |
| 478 | 518 | ||
| 479 | network.numTicks = 0; | 519 | network.numTicks = 0; | ... | ... |
| ... | @@ -48,6 +48,26 @@ span.right { | ... | @@ -48,6 +48,26 @@ span.right { |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | /* | 50 | /* |
| 51 | + * Radio Buttons | ||
| 52 | + */ | ||
| 53 | + | ||
| 54 | +span.radio { | ||
| 55 | + margin: 4px 0; | ||
| 56 | + border: 1px dotted #222; | ||
| 57 | + padding: 1px 6px; | ||
| 58 | + color: #eee; | ||
| 59 | + cursor: pointer; | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +span.radio.active { | ||
| 63 | + background-color: #bbb; | ||
| 64 | + border: 1px solid #eee; | ||
| 65 | + padding: 1px 6px; | ||
| 66 | + color: #666; | ||
| 67 | + font-weight: bold; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +/* | ||
| 51 | * === DEBUGGING ====== | 71 | * === DEBUGGING ====== |
| 52 | */ | 72 | */ |
| 53 | svg { | 73 | svg { |
| ... | @@ -71,12 +91,6 @@ svg .link { | ... | @@ -71,12 +91,6 @@ svg .link { |
| 71 | -moz-transition: opacity 250ms; | 91 | -moz-transition: opacity 250ms; |
| 72 | } | 92 | } |
| 73 | 93 | ||
| 74 | -marker#end { | ||
| 75 | - fill: #666; | ||
| 76 | - stroke: #666; | ||
| 77 | - stroke-width: 1.5px; | ||
| 78 | -} | ||
| 79 | - | ||
| 80 | svg .node rect { | 94 | svg .node rect { |
| 81 | stroke-width: 1.5px; | 95 | stroke-width: 1.5px; |
| 82 | 96 | ||
| ... | @@ -88,6 +102,7 @@ svg .node rect { | ... | @@ -88,6 +102,7 @@ svg .node rect { |
| 88 | svg .node.device.roadm rect { | 102 | svg .node.device.roadm rect { |
| 89 | fill: #229; | 103 | fill: #229; |
| 90 | } | 104 | } |
| 105 | + | ||
| 91 | svg .node.device.switch rect { | 106 | svg .node.device.switch rect { |
| 92 | fill: #55f; | 107 | fill: #55f; |
| 93 | } | 108 | } |
| ... | @@ -102,6 +117,18 @@ svg .node text { | ... | @@ -102,6 +117,18 @@ svg .node text { |
| 102 | pointer-events: none; | 117 | pointer-events: none; |
| 103 | } | 118 | } |
| 104 | 119 | ||
| 120 | +/* for debugging */ | ||
| 121 | +svg .node circle.debug { | ||
| 122 | + fill: white; | ||
| 123 | + stroke: red; | ||
| 124 | +} | ||
| 125 | +svg .node rect.debug { | ||
| 126 | + fill: yellow; | ||
| 127 | + stroke: red; | ||
| 128 | + opacity: 0.35; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | + | ||
| 105 | svg .node.selected rect { | 132 | svg .node.selected rect { |
| 106 | filter: url(#blue-glow); | 133 | filter: url(#blue-glow); |
| 107 | } | 134 | } |
| ... | @@ -119,6 +146,16 @@ svg .node.inactive.selected image { | ... | @@ -119,6 +146,16 @@ svg .node.inactive.selected image { |
| 119 | opacity: .6; | 146 | opacity: .6; |
| 120 | } | 147 | } |
| 121 | 148 | ||
| 149 | +/* | ||
| 150 | + * === currently unused =============================================== | ||
| 151 | + */ | ||
| 152 | + | ||
| 153 | +svg marker#end { | ||
| 154 | + fill: #666; | ||
| 155 | + stroke: #666; | ||
| 156 | + stroke-width: 1.5px; | ||
| 157 | +} | ||
| 158 | + | ||
| 122 | svg .legend { | 159 | svg .legend { |
| 123 | position: fixed; | 160 | position: fixed; |
| 124 | } | 161 | } | ... | ... |
-
Please register or login to post a comment