class Tree {
constructor(json) {
this.nodes = []
for (const [index, item] of json.entries()) {
var new_node = new Node(item.name, item.parent);
if (index !== 0) {
new_node.parentNode = this.nodes[index - 1]
}
this.nodes.push(new_node);
}
for (var node of this.nodes) {
var node_name = node.name;
node.children = this.nodes.filter(node => node.parentName === node_name);
}
}
buildLayout() {
this.assignLevelAndPosition(this.nodes[0], 0, 0);
return this;
}
* Assign levels and positions to nodes. The root should be level 0, its children level 1, etc.
*/
assignLevelAndPosition(node, level, position) {
node.level = level
level += 1
node.position = position
if (node.children.length === 0) {
position += 1
} else {
for (var child of node.children) {
child.parentNode = node
position = this.assignLevelAndPosition(child, level, position)
}
}
return position
}
render() {
// Create an SVG and render the tree into it.
var svg = d3.create('svg')
.attr('width', 700)
.attr('height', 700);
// Create axis scales.
const posScale = d3.scaleLinear()
.domain([0, 4])
.range([0, 300])
const levelScale = d3.scaleLinear()
.domain([0, 8])
.range([0, 800])
// Draw edges.
svg.selectAll('line') // This is to make sure that the circle is over the lines, could not find a better way to do it.
.data(this.nodes)
.join("line")
.attr('transform', row => `translate(200,100)`)
.attr("x1", d => levelScale(d.parentNode === null ? 0 : d.parentNode.level))
.attr("y1", d => posScale(d.parentNode === null ? 0 : d.parentNode.position))
.attr("x2", d => levelScale(d.level))
.attr("y2", d => posScale(d.position));
// For nodes to be drawn on.
const g = svg.selectAll('g')
.data(this.nodes)
.join('g')
.attr('transform', row => `translate(200, 100)`);
// Draw nodes.
const node_radius = 35
g.classed("nodeGroup", true).append('circle')
.attr('cx', node => levelScale(node.level))
.attr('cy', node => posScale(node.position))
.attr('r', node_radius)
.attr("text", node => node.name);
// Add text.
g.classed("label", true).append('text')
.attr('x', node => levelScale(node.level))
.attr('y', node => posScale(node.position))
.text(node => node.name)
return svg.node();
}
}