dagre-d3v4
Version:
A D3-based renderer for Dagre
136 lines (111 loc) • 3.76 kB
JavaScript
;
var _ = require("./lodash"),
intersectNode = require("./intersect/intersect-node"),
util = require("./util"),
d3 = require("./d3");
module.exports = createEdgePaths;
function createEdgePaths(selection, g, arrows) {
var svgPathsUpdate = selection.selectAll("g.edgePath")
.data(g.edges(), function(e) { return util.edgeToId(e); })
.classed("update", true);
var svgPathsEnter = enter(svgPathsUpdate, g);
exit(svgPathsUpdate, g);
var svgPathsMerge = svgPathsEnter.merge(svgPathsUpdate);
util.applyTransition(svgPathsMerge, g)
.style("opacity", 1);
// Save DOM element in the path group, and set ID and class
svgPathsMerge.each(function(e) {
var domEdge = d3.select(this);
var edge = g.edge(e);
edge.elem = this;
if (edge.id) {
domEdge.attr("id", edge.id);
}
util.applyClass(domEdge, edge["class"],
(domEdge.classed("update") ? "update " : "") + "edgePath");
});
svgPathsMerge.selectAll("path.path")
.each(function(e) {
var edge = g.edge(e);
edge.arrowheadId = _.uniqueId("arrowhead");
var domEdge = d3.select(this)
.attr("marker-end", function() {
return "url(" + makeFragmentRef(location.href, edge.arrowheadId) + ")";
})
.style("fill", "none");
util.applyTransition(domEdge, g)
.attr("d", function(e) { return calcPoints(g, e); });
util.applyStyle(domEdge, edge.style);
});
/* -FIX- d3v4: during render updates, can we be smarter about this remove-and-recreate? */
svgPathsMerge.selectAll("defs *").remove();
svgPathsMerge.selectAll("defs")
.each(function(e) {
var edge = g.edge(e),
arrowhead = arrows[edge.arrowhead];
arrowhead(d3.select(this), edge.arrowheadId, edge, "arrowhead");
});
return svgPathsMerge;
}
function makeFragmentRef(url, fragmentId) {
var baseUrl = url.split("#")[0];
return baseUrl + "#" + fragmentId;
}
function calcPoints(g, e) {
var edge = g.edge(e),
tail = g.node(e.v),
head = g.node(e.w),
points = edge.points.slice(1, edge.points.length - 1);
points.unshift(intersectNode(tail, points[0]));
points.push(intersectNode(head, points[points.length - 1]));
return createLine(edge, points);
}
function createLine(edge, points) {
var line = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
if (edge.hasOwnProperty("curve")) {
line.curve(edge.curve);
}
return line(points);
}
function getCoords(elem) {
var bbox = elem.getBBox(),
matrix = elem.ownerSVGElement.getScreenCTM()
.inverse()
.multiply(elem.getScreenCTM())
.translate(bbox.width / 2, bbox.height / 2);
return { x: matrix.e, y: matrix.f };
}
function enter(svgPaths, g) {
var svgPathsEnter = svgPaths.enter()
.append("g")
.attr("class", "edgePath")
.style("opacity", 0);
svgPathsEnter.append("path")
.attr("class", "path")
.attr("d", function(e) {
var edge = g.edge(e),
sourceElem = g.node(e.v).elem,
points = _.range(edge.points.length).map(function() { return getCoords(sourceElem); });
return createLine(edge, points);
});
svgPathsEnter.append("defs");
return svgPathsEnter;
}
function exit(svgPaths, g) {
var svgPathExit = svgPaths.exit();
util.applyTransition(svgPathExit, g)
.style("opacity", 0)
.remove();
util.applyTransition(svgPathExit.select("path.path"), g)
.attr("d", function(e) {
var source = g.node(e.v);
if (source) {
var points = _.range(this.getTotalLength()).map(function() { return source; });
return createLine({}, points);
} else {
return d3.select(this).attr("d");
}
});
}