Skip to content

Commit

Permalink
[tree] modernize tree JavaScript
Browse files Browse the repository at this point in the history
  • Loading branch information
cjyetman committed Dec 27, 2023
1 parent 32a8d1f commit 363fc24
Showing 1 changed file with 87 additions and 106 deletions.
193 changes: 87 additions & 106 deletions inst/tree_network.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
// !preview r2d3 data = jsonlite::toJSON(data.frame(nodeId = LETTERS[1:7], parentId = c("", "A", "A", "B","B", "C", "C"), name = LETTERS[1:7], nodeSize = 8, nodeStroke = "steelblue", nodeColor = "green", nodeSymbol = "circle", nodeFont = "sans-serif", nodeFontSize = 18, textColor = "grey", textOpacity = 1, linkColor = "grey", linkWidth = "1.5px", stringsAsFactors = FALSE), auto_unbox = TRUE), d3_version = 4, options = list(treeType = 'tidy', direction = 'right', linkType = 'diagonal')



r2d3.onRender(function(data, svg, width, height, options) {

// TESTING //
//window.data = data;
//window.options = options;
/////////////

var duration = 800;

var root = d3.stratify()
.id(function(d){ return d.nodeId; })
.parentId(function(d){ return d.parentId; })
(data);
svg.selectAll("g").remove();

const root = d3.stratify()
.id(d => d.nodeId)
.parentId(d => d.parentId)
(data);

function mouseover(d) {
return eval(options.mouseover)
Expand All @@ -35,56 +31,46 @@ r2d3.onRender(function(data, svg, width, height, options) {
"down": {
tree: (options.treeType === "tidy" ? d3.tree() : d3.cluster()).size([width, height]),
linkgen: (options.linkType === "diagonal" ?
d3.linkVertical().x(function(d) { return d.x; }).y(function(d) { return d.y; })
d3.linkVertical().x(d => d.x).y(d => d.y)
:
function unobj(d) {
return d3.line().curve(d3.curveStepAfter)(Object.keys(d).map(function(key) { return [d[key].x, d[key].y]; }));
}
(d) => d3.line().curve(d3.curveStepAfter)(Object.keys(d).map((key) => [d[key].x, d[key].y]))
),
nodegen: function(d) { return "translate(" + d.x + "," + d.y + ")"; }
nodegen: d => "translate(" + d.x + "," + d.y + ")"
},
"left": {
tree: (options.treeType === "tidy" ? d3.tree() : d3.cluster())
.size([height, width]),
linkgen: (options.linkType === "diagonal" ?
d3.linkHorizontal()
.x(function(d) { return width - d.y; })
.y(function(d) { return d.x; })
d3.linkHorizontal().x(d => width - d.y).y(d => d.x)
:
function unobj(d) {
return d3.line().curve(d3.curveStepBefore)(Object.keys(d).map(function(key) { return [width - d[key].y, d[key].x]; }));
}),
nodegen: function(d) { return "translate(" + (width - d.y) + "," + d.x + ")"; }
(d) => d3.line().curve(d3.curveStepBefore)(Object.keys(d).map((key) => [width - d[key].y, d[key].x]))
),
nodegen: d => "translate(" + (width - d.y) + "," + d.x + ")"
},
"up": {
tree: (options.treeType === "tidy" ? d3.tree() : d3.cluster()).size([width, height]),
linkgen: (options.linkType === "diagonal" ?
d3.linkVertical().x(function(d) { return d.x; }).y(function(d) { return height - d.y; })
d3.linkVertical().x(d => d.x).y(d => height - d.y)
:
function unobj(d) {
return d3.line().curve(d3.curveStepAfter)(Object.keys(d).map(function(key) { return [d[key].x, height - d[key].y]; }));
}
(d) => d3.line().curve(d3.curveStepAfter)(Object.keys(d).map((key) => [d[key].x, height - d[key].y]))
),
nodegen: function(d) { return "translate(" + d.x + "," + (height - d.y) + ")"; }
nodegen: d => "translate(" + d.x + "," + (height - d.y) + ")"
},
"right": {
tree: (options.treeType === "tidy" ? d3.tree() : d3.cluster()).size([height, width - 100]),
linkgen: (options.linkType === "diagonal" ?
d3.linkHorizontal()
.x(function(d) { return d.y; })
.y(function(d) { return d.x; })
d3.linkHorizontal().x(d => d.y).y(d => d.x)
:
function unobj(d) {
return d3.line().curve(d3.curveStepBefore)(Object.keys(d).map(function(key) { return [d[key].y, d[key].x]; }));
}),
nodegen: function(d) { return "translate(" + d.y + "," + d.x + ")"; }
(d) => d3.line().curve(d3.curveStepBefore)(Object.keys(d).map((key) => [d[key].y, d[key].x]))
),
nodegen: d => "translate(" + d.y + "," + d.x + ")"
},
"radial": {
tree: (options.treeType === "tidy" ? d3.tree() : d3.cluster())
.size([2 * Math.PI, Math.min(width,height) / 2])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }),
linkgen: d3.linkRadial().angle(function(d) { return d.x; }).radius(function(d) { return d.y; }),
nodegen: function(d) { return "translate(" + [(d.y = +d.y) * Math.cos(d.x -= Math.PI / 2), d.y * Math.sin(d.x)] + ")"; }
.size([2 * Math.PI, Math.min(width, height) / 2])
.separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth),
linkgen: d3.linkRadial().angle(d => d.x).radius(d => d.y),
nodegen: d => "translate(" + [(d.y = +d.y) * Math.cos(d.x -= Math.PI / 2), d.y * Math.sin(d.x)] + ")"
}
};

Expand All @@ -100,8 +86,8 @@ r2d3.onRender(function(data, svg, width, height, options) {
}

var tree = directions[options.direction].tree,
linkgen = directions[options.direction].linkgen,
nodegen = directions[options.direction].nodegen;
linkgen = directions[options.direction].linkgen,
nodegen = directions[options.direction].nodegen;

var linkgrp = g.append("g").attr("class", "linkgrp");
var nodegrp = g.append("g").attr("class", "nodegrp");
Expand All @@ -111,115 +97,110 @@ r2d3.onRender(function(data, svg, width, height, options) {
function update(source) {
var treeData = tree(root);

if (treeData.descendants().reduce(function(a, b) { return typeof b.data.height !== 'undefined' && a; }, true)) {
var ymax = d3.max(treeData.descendants(), function(d) { return d.data.height || d.height; });
var ymin = d3.min(treeData.descendants(), function(d) { return d.data.height || d.height; });
if (treeData.descendants().reduce((a, b) => typeof b.data.height !== 'undefined' && a, true)) {
var ymax = d3.max(treeData.descendants(), d => d.data.height || d.height);
var ymin = d3.min(treeData.descendants(), d => d.data.height || d.height);
heightToY = d3.scaleLinear().domain([ymax, ymin]).range([0, width]);
treeData.eachAfter(function(d) { d.y = heightToY(d.data.height || d.height); });
treeData.eachAfter(d => d.y = heightToY(d.data.height || d.height));
}

var nodes = treeData.descendants(),
links = treeData.links(nodes);

// TESTING //
//window.nodes = nodes;
//window.links = links;
/////////////
links = treeData.links(nodes);

// update the links
var link = linkgrp.selectAll(".link")
.data(links, function(d) { return d.target.id; });
.data(links, d => d.target.id);

// enter any new links at the parent's previous position
var linkEnter = link.enter().insert("path", "g")
.attr("class", "link")
.style("fill", "none")
.style("stroke", function(d){ return d.target.data.linkColor; })
.style("stroke-opacity", 0.4)
.style("stroke-width", function(d){ return d.target.data.linkWidth; })
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return linkgen({source: o, target: o});
})
.attr("class", "link")
.style("fill", "none")
.style("stroke", d => d.target.data.linkColor)
.style("stroke-opacity", 0.4)
.style("stroke-width", d => d.target.data.linkWidth)
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return linkgen({source: o, target: o});
})

// transition links to their new position
var linkUpdate = linkEnter.merge(link);
linkUpdate.transition()
.duration(duration)
.attr("d", linkgen)
.duration(duration)
.attr("d", linkgen)

// Transition exiting nodes to the parent's new position
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return linkgen({source: o, target: o});
})
.remove()
.remove()

var node = nodegrp.selectAll(".node").data(nodes, function(d,i) { return d.id || (d.id = ++i); });
var node = nodegrp.selectAll(".node").data(nodes, (d,i) => d.id || (d.id = ++i));

nodeEnter = node.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d){ return typeof source !== 'undefined' ? nodegen({x: source.x0, y: source.y0}) : nodegen(d); })
.on("click", click)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.attr('cursor', function(d){ return d.children || d._children ? "pointer" : "default"; })
.append("g")
.attr("class", "node")
.attr("transform", d => typeof source !== 'undefined' ? nodegen({x: source.x0, y: source.y0}) : nodegen(d))
.on("click", click)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.attr('cursor', d => d.children || d._children ? "pointer" : "default")

nodeEnter.append("path")
.style("fill", function(d){ return d._children ? d.data.nodeStroke: 'white';})
.style("opacity", 1e-6)
.style("stroke", function(d){ return d.data.nodeStroke; })
.style("stroke-width", "1.5px")
.attr("d", function(d){ return symbolgen(d.data.nodeSymbol).size(Math.pow(d.data.nodeSize, 2))(); })
.style("fill", d => d._children ? d.data.nodeColor : "white")
.style("opacity", 1e-6)
.style("stroke", d => d.data.nodeStroke)
.style("stroke-width", "1.5px")
.attr("d", d => symbolgen(d.data.nodeSymbol).size(Math.pow(d.data.nodeSize, 2))())

nodeEnter.append("text")
.attr("transform", "rotate(" + options.textRotate + ")")
.attr("x", d => d.children ? -6 : 6)
.attr("text-anchor", d => d.children ? "end" : "start")
.style("font-family", function(d){ return d.data.nodeFont; })
.style("font-size", "1px")
.style("opacity", function(d){ return d.data.textOpacity; })
.style("fill", function(d){ return d.data.textColor; })
.text(function(d){ return d.data.name; })
.attr("transform", "rotate(" + options.textRotate + ")")
.attr("x", d => d.children ? -6 : 6)
.attr("text-anchor", d => d.children ? "end" : "start")
.style("font-family", d => d.data.nodeFont)
.style("font-size", "1px")
.style("opacity", d => d.data.textOpacity)
.style("fill", d => d.data.textColor)
.text(d => d.data.name)

var nodeUpdate = nodeEnter.merge(node);

nodeUpdate.transition()
.duration(duration)
.attr("transform", nodegen)
.duration(duration)
.attr("transform", nodegen)

nodeUpdate.select("path")
.transition()
.duration(duration)
.style("opacity", 1)
.style("fill", function(d){ return d._children ? d.data.nodeStroke : 'white'; })
.transition()
.duration(duration)
.style("opacity", 1)
.style("fill", d => d._children ? d.data.nodeColor : "white")

nodeUpdate.select("text")
.transition()
.duration(duration)
.style("font-size", function(d){ return d.data.nodeFontSize + "px"; })
.transition()
.duration(duration)
.style("font-size", d => d.data.nodeFontSize + "px")

var nodeExit = node.exit();

nodeExit.transition("exittransition")
.duration(duration)
.attr("transform", function(d){ return nodegen(source); })
.remove()
.duration(duration)
.attr("transform", d => nodegen(source))
.remove();

nodeExit.select('path')
.transition("exittransition")
.duration(duration)
.style('fill', 1e-6)
.style('fill-opacity', 1e-6)
.attr("d", d3.symbol().type(d3.symbolCircle).size(1e-6))
.transition("exittransition")
.duration(duration)
.style('fill', 1e-6)
.style('fill-opacity', 1e-6)
.attr("d", d3.symbol().type(d3.symbolCircle).size(1e-6));

nodeExit.select('text')
.transition("exittransition")
.duration(duration)
.style('opacity', 1e-6)
.transition("exittransition")
.duration(duration)
.style('opacity', 1e-6)

nodes.forEach(function(d){
d.x0 = d.x;
Expand Down

0 comments on commit 363fc24

Please sign in to comment.