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
This commit is contained in:
parent
972d732789
commit
e3a0d6fb61
tensorflow/tensorboard/components
@ -74,18 +74,6 @@ export let SeriesNodeColors = {
|
||||
DEFAULT_STROKE: "#b2b2b2"
|
||||
};
|
||||
|
||||
/** 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];
|
||||
|
||||
/**
|
||||
* Parameters that affect how the graph is rendered on the screen.
|
||||
*/
|
||||
@ -169,7 +157,6 @@ export class RenderGraphInfo {
|
||||
private deviceColorMap: d3.scale.Ordinal<string, string>;
|
||||
private memoryUsageScale: d3.scale.Linear<string, string>;
|
||||
private computeTimeScale: d3.scale.Linear<string, string>;
|
||||
edgeWidthScale: d3.scale.Pow<number, number>;
|
||||
// Since the rendering information for each node is constructed lazily,
|
||||
// upon node's expansion by the user, we keep a map between the node's name
|
||||
// and whether the rendering information was already constructed for that
|
||||
@ -185,12 +172,6 @@ export class RenderGraphInfo {
|
||||
.range(_.map(d3.range(hierarchy.devices.length),
|
||||
MetanodeColors.DEVICE_PALETTE));
|
||||
|
||||
this.edgeWidthScale = d3.scale.pow()
|
||||
.exponent(EDGE_WIDTH_SCALE_EXPONENT)
|
||||
.domain(DOMAIN_EDGE_WIDTH_SCALE)
|
||||
.range([MIN_EDGE_WIDTH, MAX_EDGE_WIDTH])
|
||||
.clamp(true);
|
||||
|
||||
let topLevelGraph = hierarchy.root.metagraph;
|
||||
// Find the maximum and minimum memory usage.
|
||||
let memoryExtent = d3.extent(topLevelGraph.nodes(),
|
||||
@ -1094,6 +1075,12 @@ export class RenderMetaedgeInfo {
|
||||
*/
|
||||
edgeGroup: d3.Selection<RenderMetaedgeInfo>;
|
||||
|
||||
/** Id of the <marker> used as a start-marker for the edge path. */
|
||||
startMarkerId: string;
|
||||
|
||||
/** Id of the <marker> used as an end-marker for the edge path. */
|
||||
endMarkerId: string;
|
||||
|
||||
constructor(metaedge: Metaedge) {
|
||||
this.metaedge = metaedge;
|
||||
this.adjoiningMetaedge = null;
|
||||
|
@ -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);
|
||||
@ -136,6 +154,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));
|
||||
}
|
||||
|
@ -317,16 +317,12 @@ svg.icon {
|
||||
<svg class="icon" height="15px"
|
||||
preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15">
|
||||
<defs>
|
||||
<marker id="arrowhead-legend" fill="#bbb" markerWidth="10"
|
||||
markerHeight="10" refX="9" refY="5" orient="auto">
|
||||
<path d="M 0,0 L 10,5 L 0,10 C 3,7 3,3 0,0"/>
|
||||
</marker>
|
||||
<marker id="ref-arrowhead-legend" fill="#bbb" markerWidth="10"
|
||||
markerHeight="10" refX="1" refY="5" orient="auto">
|
||||
<path d="M 10,0 L 0,5 L 10,10 C 7,7 7,3 10,0"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<path marker-end="url(#arrowhead-legend)" stroke="#bbb"
|
||||
<path stroke="#bbb"
|
||||
d="M2 9 l 23 0" stroke-linecap="round" />
|
||||
</svg>
|
||||
</td>
|
||||
@ -336,7 +332,7 @@ svg.icon {
|
||||
<td>
|
||||
<svg class="icon" height="15px"
|
||||
preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15">
|
||||
<path marker-end="url(#arrowhead-legend)" stroke="#bbb"
|
||||
<path stroke="#bbb"
|
||||
d="M2 9 l 23 0" stroke-linecap="round" stroke-dasharray="2, 2" />
|
||||
</svg>
|
||||
</td>
|
||||
@ -347,7 +343,7 @@ svg.icon {
|
||||
<svg class="icon" height="15px"
|
||||
preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15">
|
||||
<path marker-start="url(#ref-arrowhead-legend)"
|
||||
marker-end="url(#arrowhead-legend)" stroke="#bbb" d="M2 9 l 23 0"
|
||||
stroke="#bbb" d="M2 9 l 23 0"
|
||||
stroke-linecap="round" />
|
||||
</svg>
|
||||
</td>
|
||||
|
@ -289,21 +289,13 @@
|
||||
stroke-width: 0.75;
|
||||
}
|
||||
|
||||
::content .edge > path.edgeline.refline {
|
||||
marker-start: url(#ref-arrowhead);
|
||||
}
|
||||
|
||||
/* Labels showing tensor shapes on edges */
|
||||
::content .edge > text {
|
||||
font-size: 3.5px;
|
||||
fill: #666;
|
||||
}
|
||||
|
||||
::content #arrowhead {
|
||||
fill: #bbb;
|
||||
}
|
||||
|
||||
::content #ref-arrowhead {
|
||||
::content .ref-arrowhead {
|
||||
fill: #bbb;
|
||||
}
|
||||
|
||||
@ -370,15 +362,26 @@
|
||||
</div>
|
||||
<svg id="svg">
|
||||
<defs>
|
||||
<!-- Arrow head for edge paths. -->
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="10"
|
||||
refX="9" refY="5" orient="auto">
|
||||
<path d="M 0,0 L 10,5 L 0,10 C 3,7 3,3 0,0"/>
|
||||
|
||||
<!-- Arrow heads for edge paths of different predefined sizes. -->
|
||||
<path id="ref-arrowhead-path" d="M 10,0 L 0,5 L 10,10 C 7,7 7,3 10,0"/>
|
||||
<marker class="ref-arrowhead" id="ref-arrowhead-small" viewBox="0 0 10 10" markerWidth="10" markerHeight="10"
|
||||
refX="8" refY="5" orient="auto" markerUnits="userSpaceOnUse">
|
||||
<use xlink:href="#ref-arrowhead-path" />
|
||||
</marker>
|
||||
<marker id="ref-arrowhead" markerWidth="10" markerHeight="10"
|
||||
refX="1" refY="5" orient="auto">
|
||||
<path d="M 10,0 L 0,5 L 10,10 C 7,7 7,3 10,0"/>
|
||||
<marker class="ref-arrowhead" id="ref-arrowhead-medium" viewBox="0 0 10 10" markerWidth="13" markerHeight="13"
|
||||
refX="8" refY="5" orient="auto" markerUnits="userSpaceOnUse">
|
||||
<use xlink:href="#ref-arrowhead-path" />
|
||||
</marker>
|
||||
<marker class="ref-arrowhead" id="ref-arrowhead-large" viewBox="0 0 10 10" markerWidth="16" markerHeight="16"
|
||||
refX="8" refY="5" orient="auto" markerUnits="userSpaceOnUse">
|
||||
<use xlink:href="#ref-arrowhead-path" />
|
||||
</marker>
|
||||
<marker class="ref-arrowhead" id="ref-arrowhead-xlarge" viewBox="0 0 10 10" markerWidth="20" markerHeight="20"
|
||||
refX="8" refY="5" orient="auto" markerUnits="userSpaceOnUse">
|
||||
<use xlink:href="#ref-arrowhead-path" />
|
||||
</marker>
|
||||
|
||||
<!-- Arrow head for annotation edge paths. -->
|
||||
<marker id="annotation-arrowhead" markerWidth="5" markerHeight="5"
|
||||
refX="5" refY="2.5" orient="auto">
|
||||
|
Loading…
Reference in New Issue
Block a user