Changed hosts to small circles (rather than labeled rectangles), and updated col…
…lision prevention code appropriately. Removed hover-selection mode (will replace with tooltips later). Added flyout div (hidden for now).
Showing
3 changed files
with
157 additions
and
63 deletions
... | @@ -47,7 +47,10 @@ | ... | @@ -47,7 +47,10 @@ |
47 | <span id="showOpt" class="radio">Optical Only</span> | 47 | <span id="showOpt" class="radio">Optical Only</span> |
48 | </span> | 48 | </span> |
49 | </div> | 49 | </div> |
50 | - <div id="view"></div> | 50 | + <div id="view"> |
51 | + <!-- NOTE: svg layer injected here --> | ||
52 | + </div> | ||
53 | + <div id="flyout"></div> | ||
51 | </div> | 54 | </div> |
52 | 55 | ||
53 | <!-- Initialize the UI...--> | 56 | <!-- Initialize the UI...--> | ... | ... |
... | @@ -49,12 +49,12 @@ | ... | @@ -49,12 +49,12 @@ |
49 | force: { | 49 | force: { |
50 | note: 'node.class or link.class is used to differentiate', | 50 | note: 'node.class or link.class is used to differentiate', |
51 | linkDistance: { | 51 | linkDistance: { |
52 | - infra: 240, | 52 | + infra: 200, |
53 | - host: 100 | 53 | + host: 20 |
54 | }, | 54 | }, |
55 | linkStrength: { | 55 | linkStrength: { |
56 | infra: 1.0, | 56 | infra: 1.0, |
57 | - host: 0.4 | 57 | + host: 1.0 |
58 | }, | 58 | }, |
59 | charge: { | 59 | charge: { |
60 | device: -800, | 60 | device: -800, |
... | @@ -90,6 +90,7 @@ | ... | @@ -90,6 +90,7 @@ |
90 | } | 90 | } |
91 | }, | 91 | }, |
92 | hostLinkWidth: 1.0, | 92 | hostLinkWidth: 1.0, |
93 | + hostRadius: 7, | ||
93 | mouseOutTimerDelayMs: 120 | 94 | mouseOutTimerDelayMs: 120 |
94 | }; | 95 | }; |
95 | 96 | ||
... | @@ -279,16 +280,16 @@ | ... | @@ -279,16 +280,16 @@ |
279 | 280 | ||
280 | 281 | ||
281 | // now, process the explicit links... | 282 | // now, process the explicit links... |
282 | - network.data.links.forEach(function(n) { | 283 | + network.data.links.forEach(function(lnk) { |
283 | - var src = network.lookup[n.src], | 284 | + var src = network.lookup[lnk.src], |
284 | - dst = network.lookup[n.dst], | 285 | + dst = network.lookup[lnk.dst], |
285 | id = src.id + "~" + dst.id; | 286 | id = src.id + "~" + dst.id; |
286 | 287 | ||
287 | var link = { | 288 | var link = { |
288 | class: 'infra', | 289 | class: 'infra', |
289 | id: id, | 290 | id: id, |
290 | - type: n.type, | 291 | + type: lnk.type, |
291 | - width: n.linkWidth, | 292 | + width: lnk.linkWidth, |
292 | source: src, | 293 | source: src, |
293 | target: dst, | 294 | target: dst, |
294 | strength: config.force.linkStrength.infra | 295 | strength: config.force.linkStrength.infra |
... | @@ -379,6 +380,7 @@ | ... | @@ -379,6 +380,7 @@ |
379 | .attr('class', function(d) {return 'link ' + d.class}); | 380 | .attr('class', function(d) {return 'link ' + d.class}); |
380 | 381 | ||
381 | 382 | ||
383 | + // TODO: move drag behavior into separate method. | ||
382 | // == define node drag behavior... | 384 | // == define node drag behavior... |
383 | network.draggedThreshold = d3.scale.linear() | 385 | network.draggedThreshold = d3.scale.linear() |
384 | .domain([0, 0.1]) | 386 | .domain([0, 0.1]) |
... | @@ -442,6 +444,8 @@ | ... | @@ -442,6 +444,8 @@ |
442 | }) | 444 | }) |
443 | .call(network.drag) | 445 | .call(network.drag) |
444 | .on('mouseover', function(d) { | 446 | .on('mouseover', function(d) { |
447 | + // TODO: show tooltip | ||
448 | +/* | ||
445 | if (!selected.obj) { | 449 | if (!selected.obj) { |
446 | if (network.mouseoutTimeout) { | 450 | if (network.mouseoutTimeout) { |
447 | clearTimeout(network.mouseoutTimeout); | 451 | clearTimeout(network.mouseoutTimeout); |
... | @@ -449,8 +453,11 @@ | ... | @@ -449,8 +453,11 @@ |
449 | } | 453 | } |
450 | highlightObject(d); | 454 | highlightObject(d); |
451 | } | 455 | } |
456 | +*/ | ||
452 | }) | 457 | }) |
453 | .on('mouseout', function(d) { | 458 | .on('mouseout', function(d) { |
459 | + // TODO: hide tooltip | ||
460 | +/* | ||
454 | if (!selected.obj) { | 461 | if (!selected.obj) { |
455 | if (network.mouseoutTimeout) { | 462 | if (network.mouseoutTimeout) { |
456 | clearTimeout(network.mouseoutTimeout); | 463 | clearTimeout(network.mouseoutTimeout); |
... | @@ -460,9 +467,13 @@ | ... | @@ -460,9 +467,13 @@ |
460 | highlightObject(null); | 467 | highlightObject(null); |
461 | }, config.mouseOutTimerDelayMs); | 468 | }, config.mouseOutTimerDelayMs); |
462 | } | 469 | } |
470 | +*/ | ||
463 | }); | 471 | }); |
464 | 472 | ||
465 | - network.nodeRect = network.node.append('rect') | 473 | + |
474 | + // deal with device nodes first | ||
475 | + network.nodeRect = network.node.filter('.device') | ||
476 | + .append('rect') | ||
466 | .attr({ | 477 | .attr({ |
467 | rx: 5, | 478 | rx: 5, |
468 | ry: 5, | 479 | ry: 5, |
... | @@ -470,8 +481,9 @@ | ... | @@ -470,8 +481,9 @@ |
470 | height: 12 | 481 | height: 12 |
471 | }); | 482 | }); |
472 | // note that width/height are adjusted to fit the label text | 483 | // note that width/height are adjusted to fit the label text |
484 | + // then padded, and space made for the icon. | ||
473 | 485 | ||
474 | - network.node.each(function(d) { | 486 | + network.node.filter('.device').each(function(d) { |
475 | var node = d3.select(this), | 487 | var node = d3.select(this), |
476 | icon = iconUrl(d); | 488 | icon = iconUrl(d); |
477 | 489 | ||
... | @@ -505,47 +517,35 @@ | ... | @@ -505,47 +517,35 @@ |
505 | } | 517 | } |
506 | }); | 518 | }); |
507 | 519 | ||
520 | + // now process host nodes | ||
521 | + network.nodeCircle = network.node.filter('.host') | ||
522 | + .append('circle') | ||
523 | + .attr({ | ||
524 | + r: config.hostRadius | ||
525 | + }); | ||
508 | 526 | ||
509 | - // returns the newly computed bounding box | 527 | + network.node.filter('.host').each(function(d) { |
510 | - function adjustRectToFitText(n) { | 528 | + var node = d3.select(this), |
511 | - var text = n.select('text'), | 529 | + icon = iconUrl(d); |
512 | - box = text.node().getBBox(), | ||
513 | - lab = config.labels; | ||
514 | - | ||
515 | - text.attr('text-anchor', 'middle') | ||
516 | - .attr('y', '-0.8em') | ||
517 | - .attr('x', lab.imgPad/2) | ||
518 | - ; | ||
519 | - | ||
520 | - // TODO: figure out how to access the data on selection | ||
521 | - console.log("\nadjust rect for " + n.data().id); | ||
522 | - console.log(box); | ||
523 | - | ||
524 | - // translate the bbox so that it is centered on [x,y] | ||
525 | - box.x = -box.width / 2; | ||
526 | - box.y = -box.height / 2; | ||
527 | - | ||
528 | - // add padding | ||
529 | - box.x -= (lab.padLR + lab.imgPad/2); | ||
530 | - box.width += lab.padLR * 2 + lab.imgPad; | ||
531 | - box.y -= lab.padTB; | ||
532 | - box.height += lab.padTB * 2; | ||
533 | - | ||
534 | - return box; | ||
535 | - } | ||
536 | 530 | ||
537 | - function boundsFromBox(box) { | 531 | + // debug function to show the modelled x,y coordinates of nodes... |
538 | - return { | 532 | + if (debug('showNodeXY')) { |
539 | - x1: box.x, | 533 | + node.select('circle').attr('fill-opacity', 0.5); |
540 | - y1: box.y, | 534 | + node.append('circle') |
541 | - x2: box.x + box.width, | 535 | + .attr({ |
542 | - y2: box.y + box.height | 536 | + class: 'debug', |
543 | - }; | 537 | + cx: 0, |
538 | + cy: 0, | ||
539 | + r: '3px' | ||
540 | + }); | ||
544 | } | 541 | } |
542 | + }); | ||
545 | 543 | ||
546 | // this function is scheduled to happen soon after the given thread ends | 544 | // this function is scheduled to happen soon after the given thread ends |
547 | setTimeout(function() { | 545 | setTimeout(function() { |
548 | - network.node.each(function(d) { | 546 | + // post process the device nodes, to pad their size to fit the |
547 | + // label text and attach the icon to the right location. | ||
548 | + network.node.filter('.device').each(function(d) { | ||
549 | // for every node, recompute size, padding, etc. so text fits | 549 | // for every node, recompute size, padding, etc. so text fits |
550 | var node = d3.select(this), | 550 | var node = d3.select(this), |
551 | text = node.select('text'), | 551 | text = node.select('text'), |
... | @@ -589,6 +589,44 @@ | ... | @@ -589,6 +589,44 @@ |
589 | $('#view').css('visibility', 'visible'); | 589 | $('#view').css('visibility', 'visible'); |
590 | }); | 590 | }); |
591 | 591 | ||
592 | + | ||
593 | + // returns the newly computed bounding box of the rectangle | ||
594 | + function adjustRectToFitText(n) { | ||
595 | + var text = n.select('text'), | ||
596 | + box = text.node().getBBox(), | ||
597 | + lab = config.labels; | ||
598 | + | ||
599 | + text.attr('text-anchor', 'middle') | ||
600 | + .attr('y', '-0.8em') | ||
601 | + .attr('x', lab.imgPad/2) | ||
602 | + ; | ||
603 | + | ||
604 | + // TODO: figure out how to access the data on selection | ||
605 | + console.log("\nadjust rect for " + n.data().id); | ||
606 | + console.log(box); | ||
607 | + | ||
608 | + // translate the bbox so that it is centered on [x,y] | ||
609 | + box.x = -box.width / 2; | ||
610 | + box.y = -box.height / 2; | ||
611 | + | ||
612 | + // add padding | ||
613 | + box.x -= (lab.padLR + lab.imgPad/2); | ||
614 | + box.width += lab.padLR * 2 + lab.imgPad; | ||
615 | + box.y -= lab.padTB; | ||
616 | + box.height += lab.padTB * 2; | ||
617 | + | ||
618 | + return box; | ||
619 | + } | ||
620 | + | ||
621 | + function boundsFromBox(box) { | ||
622 | + return { | ||
623 | + x1: box.x, | ||
624 | + y1: box.y, | ||
625 | + x2: box.x + box.width, | ||
626 | + y2: box.y + box.height | ||
627 | + }; | ||
628 | + } | ||
629 | + | ||
592 | } | 630 | } |
593 | 631 | ||
594 | function iconUrl(d) { | 632 | function iconUrl(d) { |
... | @@ -599,24 +637,48 @@ | ... | @@ -599,24 +637,48 @@ |
599 | return 'translate(' + x + ',' + y + ')'; | 637 | return 'translate(' + x + ',' + y + ')'; |
600 | } | 638 | } |
601 | 639 | ||
640 | + // prevents collisions amongst device nodes | ||
602 | function preventCollisions() { | 641 | function preventCollisions() { |
603 | - var quadtree = d3.geom.quadtree(network.nodes); | 642 | + var quadtree = d3.geom.quadtree(network.nodes), |
643 | + hrad = config.hostRadius; | ||
604 | 644 | ||
605 | network.nodes.forEach(function(n) { | 645 | network.nodes.forEach(function(n) { |
606 | - var nx1 = n.x + n.extent.left, | 646 | + var nx1, nx2, ny1, ny2; |
607 | - nx2 = n.x + n.extent.right, | 647 | + |
608 | - ny1 = n.y + n.extent.top, | 648 | + if (n.class === 'device') { |
649 | + nx1 = n.x + n.extent.left; | ||
650 | + nx2 = n.x + n.extent.right; | ||
651 | + ny1 = n.y + n.extent.top; | ||
609 | ny2 = n.y + n.extent.bottom; | 652 | ny2 = n.y + n.extent.bottom; |
610 | 653 | ||
654 | + } else { | ||
655 | + nx1 = n.x - hrad; | ||
656 | + nx2 = n.x + hrad; | ||
657 | + ny1 = n.y - hrad; | ||
658 | + ny2 = n.y + hrad; | ||
659 | + } | ||
660 | + | ||
611 | quadtree.visit(function(quad, x1, y1, x2, y2) { | 661 | quadtree.visit(function(quad, x1, y1, x2, y2) { |
612 | if (quad.point && quad.point !== n) { | 662 | if (quad.point && quad.point !== n) { |
613 | - // check if the rectangles intersect | 663 | + // check if the rectangles/circles intersect |
614 | var p = quad.point, | 664 | var p = quad.point, |
615 | - px1 = p.x + p.extent.left, | 665 | + px1, px2, py1, py2, ix; |
616 | - px2 = p.x + p.extent.right, | 666 | + |
617 | - py1 = p.y + p.extent.top, | 667 | + if (p.class === 'device') { |
618 | - py2 = p.y + p.extent.bottom, | 668 | + px1 = p.x + p.extent.left; |
669 | + px2 = p.x + p.extent.right; | ||
670 | + py1 = p.y + p.extent.top; | ||
671 | + py2 = p.y + p.extent.bottom; | ||
672 | + | ||
673 | + } else { | ||
674 | + px1 = p.x - hrad; | ||
675 | + px2 = p.x + hrad; | ||
676 | + py1 = p.y - hrad; | ||
677 | + py2 = p.y + hrad; | ||
678 | + } | ||
679 | + | ||
619 | ix = (px1 <= nx2 && nx1 <= px2 && py1 <= ny2 && ny1 <= py2); | 680 | ix = (px1 <= nx2 && nx1 <= px2 && py1 <= ny2 && ny1 <= py2); |
681 | + | ||
620 | if (ix) { | 682 | if (ix) { |
621 | var xa1 = nx2 - px1, // shift n left , p right | 683 | var xa1 = nx2 - px1, // shift n left , p right |
622 | xa2 = px2 - nx1, // shift n right, p left | 684 | xa2 = px2 - nx1, // shift n right, p left |
... | @@ -717,6 +779,16 @@ | ... | @@ -717,6 +779,16 @@ |
717 | // return false; | 779 | // return false; |
718 | // }); | 780 | // }); |
719 | 781 | ||
782 | + function findNodeFromData(d) { | ||
783 | + var el = null; | ||
784 | + network.node.filter('.' + d.class).each(function(n) { | ||
785 | + if (n.id === d.id) { | ||
786 | + el = d3.select(this); | ||
787 | + } | ||
788 | + }); | ||
789 | + return el; | ||
790 | + } | ||
791 | + | ||
720 | function selectObject(obj, el) { | 792 | function selectObject(obj, el) { |
721 | var node; | 793 | var node; |
722 | if (el) { | 794 | if (el) { | ... | ... |
... | @@ -88,7 +88,12 @@ svg .link { | ... | @@ -88,7 +88,12 @@ svg .link { |
88 | -moz-transition: opacity 250ms; | 88 | -moz-transition: opacity 250ms; |
89 | } | 89 | } |
90 | 90 | ||
91 | -svg .node rect { | 91 | +svg .link.host { |
92 | + stroke: #6a6; | ||
93 | + stroke-dasharray: 3,3; | ||
94 | +} | ||
95 | + | ||
96 | +svg .node.device rect { | ||
92 | stroke-width: 1.5px; | 97 | stroke-width: 1.5px; |
93 | 98 | ||
94 | transition: opacity 250ms; | 99 | transition: opacity 250ms; |
... | @@ -104,8 +109,9 @@ svg .node.device.switch rect { | ... | @@ -104,8 +109,9 @@ svg .node.device.switch rect { |
104 | fill: #55f; | 109 | fill: #55f; |
105 | } | 110 | } |
106 | 111 | ||
107 | -svg .node.host rect { | 112 | +svg .node.host circle { |
108 | - fill: #787; | 113 | + fill: #898; |
114 | + stroke: #000; | ||
109 | } | 115 | } |
110 | 116 | ||
111 | svg .node text { | 117 | svg .node text { |
... | @@ -132,6 +138,7 @@ svg .node.selected rect { | ... | @@ -132,6 +138,7 @@ svg .node.selected rect { |
132 | 138 | ||
133 | svg .link.inactive, | 139 | svg .link.inactive, |
134 | svg .node.inactive rect, | 140 | svg .node.inactive rect, |
141 | +svg .node.inactive circle, | ||
135 | svg .node.inactive text, | 142 | svg .node.inactive text, |
136 | svg .node.inactive image { | 143 | svg .node.inactive image { |
137 | opacity: .2; | 144 | opacity: .2; |
... | @@ -175,16 +182,28 @@ svg .legend .category text { | ... | @@ -175,16 +182,28 @@ svg .legend .category text { |
175 | * Specific structural elements | 182 | * Specific structural elements |
176 | */ | 183 | */ |
177 | 184 | ||
185 | +#mast { | ||
186 | + height: 36px; | ||
187 | + padding: 4px; | ||
188 | + background-color: #ccc; | ||
189 | + vertical-align: baseline; | ||
190 | +} | ||
191 | + | ||
178 | #frame { | 192 | #frame { |
179 | width: 100%; | 193 | width: 100%; |
180 | height: 100%; | 194 | height: 100%; |
181 | background-color: #fff; | 195 | background-color: #fff; |
182 | } | 196 | } |
183 | 197 | ||
184 | -#mast { | 198 | +#flyout { |
185 | - height: 36px; | 199 | + width: 300px; |
186 | - padding: 4px; | 200 | + height: 80%; |
187 | - background-color: #ccc; | 201 | + top: 10%; |
188 | - vertical-align: baseline; | 202 | + right: 2%; |
203 | + background-color: rgba(0,0,0,0.5); | ||
204 | + | ||
205 | + position: absolute; | ||
206 | + z-index: 100; | ||
207 | + display: none; | ||
189 | } | 208 | } |
190 | 209 | ... | ... |
-
Please register or login to post a comment