aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/tensorboard/components/tf-graph-common/lib/scene
diff options
context:
space:
mode:
authorGravatar Dan Smilkov <dsmilkov@gmail.com>2016-03-29 17:48:02 -0800
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2016-03-29 19:04:05 -0700
commite3a0d6fb61cbb1dd9864684c20e49ef3fa385bb6 (patch)
tree2bec64d3292dc779e0084e8d1aeb2bb45811e33d /tensorflow/tensorboard/components/tf-graph-common/lib/scene
parent972d7327896a193b32c1a08eeb6c328e9ec95ea4 (diff)
Fix arrow marker for thick reference edges.
- Added several fixed-sized arrow markers for reference edges (small, medium, large, xlarge) - Added a method to shorten the path enough such that the tip of the start/end marker will point to the start/end of the path. It needs an arbitrary marker. - Updated the legend to reflect that only ref edges have an arrow. - Removed unused css related to this. Change: 118530630
Diffstat (limited to 'tensorflow/tensorboard/components/tf-graph-common/lib/scene')
-rw-r--r--tensorflow/tensorboard/components/tf-graph-common/lib/scene/edge.ts101
1 files changed, 93 insertions, 8 deletions
diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/scene/edge.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/scene/edge.ts
index 2938aa3f1d..4fa4606100 100644
--- a/tensorflow/tensorboard/components/tf-graph-common/lib/scene/edge.ts
+++ b/tensorflow/tensorboard/components/tf-graph-common/lib/scene/edge.ts
@@ -17,6 +17,28 @@ module tf.graph.scene.edge {
/** Delimiter between dimensions when showing sizes of tensors. */
const TENSOR_SHAPE_DELIM = "×";
+/** The minimum stroke width of an edge. */
+const MIN_EDGE_WIDTH = 0.75;
+
+/** The maximum stroke width of an edge. */
+const MAX_EDGE_WIDTH = 12;
+
+/** The exponent used in the power scale for edge thickness. */
+const EDGE_WIDTH_SCALE_EXPONENT = 0.3;
+
+/** The domain (min and max value) for the edge width. */
+const DOMAIN_EDGE_WIDTH_SCALE = [1, 5E6];
+
+let edgeWidthScale = d3.scale.pow()
+ .exponent(EDGE_WIDTH_SCALE_EXPONENT)
+ .domain(DOMAIN_EDGE_WIDTH_SCALE)
+ .range([MIN_EDGE_WIDTH, MAX_EDGE_WIDTH])
+ .clamp(true);
+
+let arrowheadMap = d3.scale.quantize()
+ .domain([MIN_EDGE_WIDTH, MAX_EDGE_WIDTH])
+ .range(["small", "medium", "large", "xlarge"]);
+
export type EdgeData = {v: string, w: string, label: render.RenderMetaedgeInfo};
export function getEdgeKey(edgeObj: EdgeData) {
@@ -79,13 +101,9 @@ export function buildGroup(sceneGroup,
// index node group for quick highlighting
sceneElement._edgeGroupIndex[getEdgeKey(d)] = edgeGroup;
- // If any edges are reference edges, add the reference edge class.
- let extraEdgeClass = d.label.metaedge && d.label.metaedge.numRefEdges
- ? Class.Edge.REF_LINE + " " + Class.Edge.LINE
- : undefined;
// Add line during enter because we're assuming that type of line
// normally does not change.
- appendEdge(edgeGroup, d, sceneElement, extraEdgeClass);
+ appendEdge(edgeGroup, d, sceneElement);
});
edgeGroups.each(position);
@@ -137,6 +155,54 @@ export function getLabelForEdge(metaedge: Metaedge,
}
/**
+ * Shortens the path enought such that the tip of the start/end marker will
+ * point to the start/end of the path. The marker can be of arbitrary size.
+ *
+ * @param points Array of path control points.
+ * @param marker D3 selection of the <marker> svg element.
+ * @param isStart Is the marker a `start-marker`. If false, the marker is
+ * an `end-marker`.
+ * @return The new array of control points.
+ */
+function adjustPathPointsForMarker(points: render.Point[],
+ marker: d3.Selection<any>, isStart: boolean): render.Point[] {
+ let lineFunc = d3.svg.line<render.Point>()
+ .x(d => d.x)
+ .y(d => d.y);
+ let path = d3.select(
+ document.createElementNS("http://www.w3.org/2000/svg", "path")
+ ).attr("d", lineFunc(points));
+ let markerWidth = +marker.attr("markerWidth");
+ let viewBox = marker.attr("viewBox").split(" ").map(Number);
+ let viewBoxWidth = viewBox[2] - viewBox[0];
+ let refX = +marker.attr("refX");
+ let pathNode = <SVGPathElement> path.node();
+ if (isStart) {
+ let fractionStickingOut = refX / viewBoxWidth;
+ let length = markerWidth * fractionStickingOut;
+ let point = pathNode.getPointAtLength(length);
+ // Figure out how many segments of the path we need to remove in order
+ // to shorten the path.
+ let segIndex = pathNode.getPathSegAtLength(length);
+ // Update the very first segment.
+ points[segIndex - 1] = {x: point.x, y: point.y};
+ // Ignore every point before segIndex - 1.
+ return points.slice(segIndex - 1);
+ } else {
+ let fractionStickingOut = 1 - refX / viewBoxWidth;
+ let length = pathNode.getTotalLength() - markerWidth * fractionStickingOut;
+ let point = pathNode.getPointAtLength(length);
+ // Figure out how many segments of the path we need to remove in order
+ // to shorten the path.
+ let segIndex = pathNode.getPathSegAtLength(length);
+ // Update the very last segment.
+ points[segIndex] = {x: point.x, y: point.y};
+ // Ignore every point after segIndex.
+ return points.slice(0, segIndex + 1);
+ }
+}
+
+/**
* For a given d3 selection and data object, create a path to represent the
* edge described in d.label.
*
@@ -146,7 +212,7 @@ export function getLabelForEdge(metaedge: Metaedge,
*/
export function appendEdge(edgeGroup, d: EdgeData,
sceneElement: {renderHierarchy: render.RenderGraphInfo},
- edgeClass: string) {
+ edgeClass?: string) {
let size = 1;
if (d.label != null && d.label.metaedge != null) {
// There is an underlying Metaedge.
@@ -160,9 +226,9 @@ export function appendEdge(edgeGroup, d: EdgeData,
// Give the path a unique id, which will be used to link
// the textPath (edge label) to this path.
let pathId = "path_" + getEdgeKey(d);
- let strokeWidth = sceneElement.renderHierarchy.edgeWidthScale(size);
+ let strokeWidth = edgeWidthScale(size);
- edgeGroup.append("path")
+ let path = edgeGroup.append("path")
.attr({
"id": pathId,
"class": edgeClass,
@@ -170,6 +236,13 @@ export function appendEdge(edgeGroup, d: EdgeData,
"stroke-width": strokeWidth + "px"
});
+ // Check if there is a reference edge and add an arrowhead of the right size.
+ if (d.label && d.label.metaedge && d.label.metaedge.numRefEdges) {
+ let markerId = `ref-arrowhead-${arrowheadMap(strokeWidth)}`;
+ path.style("marker-start", `url(#${markerId})`);
+ d.label.startMarkerId = markerId;
+ }
+
if (d.label == null || d.label.metaedge == null) {
// There is no associated metaedge, thus no text.
// This happens for annotation edges.
@@ -201,6 +274,18 @@ function getEdgePathInterpolator(d: EdgeData, i: number, a: string) {
let renderMetaedgeInfo = <render.RenderMetaedgeInfo> d.label;
let adjoiningMetaedge = renderMetaedgeInfo.adjoiningMetaedge;
let points = renderMetaedgeInfo.points;
+
+ // Adjust the path so that start/end markers point to the end
+ // of the path.
+ if (d.label.startMarkerId) {
+ points = adjustPathPointsForMarker(points,
+ d3.select("#" + d.label.startMarkerId), true);
+ }
+ if (d.label.endMarkerId) {
+ points = adjustPathPointsForMarker(points,
+ d3.select("#" + d.label.endMarkerId), false);
+ }
+
if (!adjoiningMetaedge) {
return d3.interpolate(a, interpolate(points));
}