Simon Hunt

Adding port labels to links. WIP.

...@@ -87,7 +87,12 @@ ...@@ -87,7 +87,12 @@
87 padLR: 8, 87 padLR: 8,
88 padTB: 6, 88 padTB: 6,
89 marginLR: 3, 89 marginLR: 3,
90 - marginTB: 2 90 + marginTB: 2,
91 + port: {
92 + gap: 2,
93 + width: 12,
94 + height: 12
95 + }
91 }, 96 },
92 icons: { 97 icons: {
93 w: 32, 98 w: 32,
...@@ -113,7 +118,8 @@ ...@@ -113,7 +118,8 @@
113 selected = {}, 118 selected = {},
114 highlighted = null, 119 highlighted = null,
115 hovered = null, 120 hovered = null,
116 - viewMode = 'showAll'; 121 + viewMode = 'showAll',
122 + portLabelsOn = false;
117 123
118 124
119 function debug(what) { 125 function debug(what) {
...@@ -128,10 +134,13 @@ ...@@ -128,10 +134,13 @@
128 return urlData().jsonUrl; 134 return urlData().jsonUrl;
129 } 135 }
130 136
137 + function safeId(id) {
138 + return id.replace(/[^a-z0-9]/gi, '_');
139 + }
140 +
131 function detailJsonUrl(id) { 141 function detailJsonUrl(id) {
132 var u = urlData(), 142 var u = urlData(),
133 - encId = config.useLiveData ? encodeURIComponent(id) 143 + encId = config.useLiveData ? encodeURIComponent(id) : safeId(id);
134 - : id.replace(/[^a-z0-9]/gi, '_');
135 return u.detailPrefix + encId + u.detailSuffix; 144 return u.detailPrefix + encId + u.detailSuffix;
136 } 145 }
137 146
...@@ -303,7 +312,9 @@ ...@@ -303,7 +312,9 @@
303 } 312 }
304 313
305 function togglePorts() { 314 function togglePorts() {
306 - console.log('Toggle Ports - context = ' + contextLabel()); 315 + portLabelsOn = !portLabelsOn;
316 + var portVis = portLabelsOn ? 'visible' : 'hidden';
317 + d3.selectAll('.port').style('visibility', portVis);
307 } 318 }
308 319
309 function unpin() { 320 function unpin() {
...@@ -347,7 +358,7 @@ ...@@ -347,7 +358,7 @@
347 ix = Math.random() * 0.6 * nw + 0.2 * nw, 358 ix = Math.random() * 0.6 * nw + 0.2 * nw,
348 iy = ypc * nh, 359 iy = ypc * nh,
349 node = { 360 node = {
350 - id: n.id, 361 + id: safeId(n.id),
351 labels: n.labels, 362 labels: n.labels,
352 class: 'device', 363 class: 'device',
353 icon: 'device', 364 icon: 'device',
...@@ -369,7 +380,7 @@ ...@@ -369,7 +380,7 @@
369 ix = Math.random() * 0.6 * nw + 0.2 * nw, 380 ix = Math.random() * 0.6 * nw + 0.2 * nw,
370 iy = ypc * nh, 381 iy = ypc * nh,
371 node = { 382 node = {
372 - id: n.id, 383 + id: safeId(n.id),
373 labels: n.labels, 384 labels: n.labels,
374 class: 'host', 385 class: 'host',
375 icon: 'host', 386 icon: 'host',
...@@ -390,7 +401,7 @@ ...@@ -390,7 +401,7 @@
390 network.data.links.forEach(function(lnk) { 401 network.data.links.forEach(function(lnk) {
391 var src = network.lookup[lnk.src], 402 var src = network.lookup[lnk.src],
392 dst = network.lookup[lnk.dst], 403 dst = network.lookup[lnk.dst],
393 - id = src.id + "~" + dst.id; 404 + id = src.id + "-" + dst.id;
394 405
395 var link = { 406 var link = {
396 class: 'infra', 407 class: 'infra',
...@@ -398,7 +409,9 @@ ...@@ -398,7 +409,9 @@
398 type: lnk.type, 409 type: lnk.type,
399 width: lnk.linkWidth, 410 width: lnk.linkWidth,
400 source: src, 411 source: src,
412 + srcPort: lnk.srcPort,
401 target: dst, 413 target: dst,
414 + tgtPort: lnk.dstPort,
402 strength: config.force.linkStrength.infra 415 strength: config.force.linkStrength.infra
403 }; 416 };
404 network.links.push(link); 417 network.links.push(link);
...@@ -408,7 +421,7 @@ ...@@ -408,7 +421,7 @@
408 network.data.hosts.forEach(function(n) { 421 network.data.hosts.forEach(function(n) {
409 var src = network.lookup[n.id], 422 var src = network.lookup[n.id],
410 dst = network.lookup[n.cp.device], 423 dst = network.lookup[n.cp.device],
411 - id = src.id + "~" + dst.id; 424 + id = src.id + "-" + dst.id;
412 425
413 var link = { 426 var link = {
414 class: 'host', 427 class: 'host',
...@@ -490,13 +503,6 @@ ...@@ -490,13 +503,6 @@
490 // }); 503 // });
491 504
492 505
493 - // add links to the display
494 - network.link = network.svg.append('g').selectAll('.link')
495 - .data(network.force.links(), function(d) {return d.id})
496 - .enter().append('line')
497 - .attr('class', function(d) {return 'link ' + d.class});
498 -
499 -
500 // TODO: move drag behavior into separate method. 506 // TODO: move drag behavior into separate method.
501 // == define node drag behavior... 507 // == define node drag behavior...
502 network.draggedThreshold = d3.scale.linear() 508 network.draggedThreshold = d3.scale.linear()
...@@ -551,6 +557,53 @@ ...@@ -551,6 +557,53 @@
551 } 557 }
552 }); 558 });
553 559
560 + // ...............................................................
561 +
562 + // add links to the display
563 + network.link = network.svg.append('g').attr('id', 'links')
564 + .selectAll('.link')
565 + .data(network.force.links(), function(d) {return d.id})
566 + .enter().append('line')
567 + .attr('class', function(d) {return 'link ' + d.class});
568 +
569 + network.linkSrcPort = network.svg.append('g')
570 + .attr({
571 + id: 'srcPorts',
572 + class: 'portLayer'
573 + });
574 + network.linkTgtPort = network.svg.append('g')
575 + .attr({
576 + id: 'tgtPorts',
577 + class: 'portLayer'
578 + });
579 +
580 + var portVis = portLabelsOn ? 'visible' : 'hidden';
581 +
582 + network.link.filter('.infra').each(function(d, i) {
583 + network.linkSrcPort.append('rect').attr({
584 + id: 'srcPort-' + d.id,
585 + class: 'port',
586 + width: 12,
587 + height: 12,
588 + x: i * 20,
589 + y: 0
590 + })
591 + .style('visibility', portVis)
592 + .append('text').text(d.srcPort);
593 +
594 + network.linkTgtPort.append('rect').attr({
595 + id: 'tgtPort-' + d.id,
596 + class: 'port',
597 + width: 12,
598 + height: 12,
599 + x: i * 20,
600 + y: 20
601 + })
602 + .style('visibility', portVis);
603 +
604 + });
605 +
606 + // ...............................................................
554 607
555 // add nodes to the display 608 // add nodes to the display
556 network.node = network.svg.selectAll('.node') 609 network.node = network.svg.selectAll('.node')
...@@ -659,14 +712,18 @@ ...@@ -659,14 +712,18 @@
659 712
660 // this function is scheduled to happen soon after the given thread ends 713 // this function is scheduled to happen soon after the given thread ends
661 setTimeout(function() { 714 setTimeout(function() {
715 + var lab = config.labels,
716 + portGap = lab.port.gap,
717 + midW = portGap + lab.port.width/ 2,
718 + midH = portGap + lab.port.height / 2;
719 +
662 // post process the device nodes, to pad their size to fit the 720 // post process the device nodes, to pad their size to fit the
663 // label text and attach the icon to the right location. 721 // label text and attach the icon to the right location.
664 network.node.filter('.device').each(function(d) { 722 network.node.filter('.device').each(function(d) {
665 // for every node, recompute size, padding, etc. so text fits 723 // for every node, recompute size, padding, etc. so text fits
666 var node = d3.select(this), 724 var node = d3.select(this),
667 text = node.select('text'), 725 text = node.select('text'),
668 - box = adjustRectToFitText(node), 726 + box = adjustRectToFitText(node);
669 - lab = config.labels;
670 727
671 // now make the computed adjustment 728 // now make the computed adjustment
672 node.select('rect') 729 node.select('rect')
...@@ -676,7 +733,13 @@ ...@@ -676,7 +733,13 @@
676 .attr('x', box.x + config.icons.xoff) 733 .attr('x', box.x + config.icons.xoff)
677 .attr('y', box.y + config.icons.yoff); 734 .attr('y', box.y + config.icons.yoff);
678 735
679 - var bounds = boundsFromBox(box); 736 + var bounds = boundsFromBox(box),
737 + portBounds = {
738 + x1: bounds.x1 - midW,
739 + x2: bounds.x2 + midW,
740 + y1: bounds.y1 - midH,
741 + y2: bounds.y2 + midH
742 + };
680 743
681 // todo: clean up extent and edge work.. 744 // todo: clean up extent and edge work..
682 d.extent = { 745 d.extent = {
...@@ -693,6 +756,21 @@ ...@@ -693,6 +756,21 @@
693 bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2) 756 bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2)
694 }; 757 };
695 758
759 + d.portEdge = {
760 + left : new geo.LineSegment(
761 + portBounds.x1, portBounds.y1, portBounds.x1, portBounds.y2
762 + ),
763 + right : new geo.LineSegment(
764 + portBounds.x2, portBounds.y1, portBounds.x2, portBounds.y2
765 + ),
766 + top : new geo.LineSegment(
767 + portBounds.x1, portBounds.y1, portBounds.x2, portBounds.y1
768 + ),
769 + bottom : new geo.LineSegment(
770 + portBounds.x1, portBounds.y2, portBounds.x2, portBounds.y2
771 + )
772 + };
773 +
696 }); 774 });
697 775
698 network.numTicks = 0; 776 network.numTicks = 0;
...@@ -840,39 +918,71 @@ ...@@ -840,39 +918,71 @@
840 preventCollisions(); 918 preventCollisions();
841 } 919 }
842 920
921 + var portHalfW = config.labels.port.width / 2,
922 + portHalfH = config.labels.port.height / 2;
923 +
843 // clip visualization of links at bounds of nodes... 924 // clip visualization of links at bounds of nodes...
844 network.link.each(function(d) { 925 network.link.each(function(d) {
845 - var xs = d.source.x, 926 + var xs = d.source.x,
846 - ys = d.source.y, 927 + ys = d.source.y,
847 - xt = d.target.x, 928 + xt = d.target.x,
848 - yt = d.target.y, 929 + yt = d.target.y,
849 - line = new geo.LineSegment(xs, ys, xt, yt), 930 + line = new geo.LineSegment(xs, ys, xt, yt),
850 - e, ix; 931 + e, ix,
932 + exs, eys, ext, eyt,
933 + pxs, pys, pxt, pyt;
934 +
935 + if (d.class === 'host') {
936 + // no adjustment for source end of link, since hosts are dots
937 + exs = xs;
938 + eys = ys;
851 939
940 + } else {
852 for (e in d.source.edge) { 941 for (e in d.source.edge) {
853 ix = line.intersect(d.source.edge[e].offset(xs, ys)); 942 ix = line.intersect(d.source.edge[e].offset(xs, ys));
854 if (ix.in1 && ix.in2) { 943 if (ix.in1 && ix.in2) {
855 - xs = ix.x; 944 + exs = ix.x;
856 - ys = ix.y; 945 + eys = ix.y;
946 +
947 + // also pick off the port label intersection
948 + ix = line.intersect(d.source.portEdge[e].offset(xs, ys));
949 + pxs = ix.x;
950 + pys = ix.y;
857 break; 951 break;
858 } 952 }
859 } 953 }
954 + }
860 955
861 - for (e in d.target.edge) { 956 + for (e in d.target.edge) {
862 - ix = line.intersect(d.target.edge[e].offset(xt, yt)); 957 + ix = line.intersect(d.target.edge[e].offset(xt, yt));
863 - if (ix.in1 && ix.in2) { 958 + if (ix.in1 && ix.in2) {
864 - xt = ix.x; 959 + ext = ix.x;
865 - yt = ix.y; 960 + eyt = ix.y;
866 - break; 961 +
867 - } 962 + // also pick off the port label intersection
963 + ix = line.intersect(d.target.portEdge[e].offset(xt, yt));
964 + pxt = ix.x;
965 + pyt = ix.y;
966 + break;
868 } 967 }
968 + }
869 969
870 - d3.select(this) 970 + // adjust the endpoints of the link's line to match rectangles
871 - .attr('x1', xs) 971 + d3.select(this)
872 - .attr('y1', ys) 972 + .attr('x1', exs)
873 - .attr('x2', xt) 973 + .attr('y1', eys)
874 - .attr('y2', yt); 974 + .attr('x2', ext)
875 - }); 975 + .attr('y2', eyt);
976 +
977 + d3.select('#srcPort-' + d.id)
978 + .attr('x', pxs - portHalfW)
979 + .attr('y', pys - portHalfH);
980 +
981 + d3.select('#tgtPort-' + d.id)
982 + .attr('x', pxt - portHalfW)
983 + .attr('y', pyt - portHalfH);
984 +
985 + });
876 986
877 // position each node by translating the node (group) by x,y 987 // position each node by translating the node (group) by x,y
878 network.node 988 network.node
......
...@@ -106,6 +106,12 @@ svg .link.host { ...@@ -106,6 +106,12 @@ svg .link.host {
106 Xstroke-dasharray: 3,3; 106 Xstroke-dasharray: 3,3;
107 } 107 }
108 108
109 +svg g.portLayer rect.port {
110 + stroke-width: 1;
111 + stroke: black;
112 + fill: white;
113 +}
114 +
109 svg .node.device rect { 115 svg .node.device rect {
110 stroke-width: 1.5px; 116 stroke-width: 1.5px;
111 117
......