Simon Hunt

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).
...@@ -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
......