chart = {
let marginTop =30
let marginLeft = 10;
const score_names = ['initial',
'count'
]
const score_names_true = ['Initial',
'Number of \ncomparisons'
]
const newColumnsCount =2
const columnSpacing = 100;
let columns = []
columns = score_names.map((name, index) => {
return {
label: score_names_true[index],
index:index,
key: name,
format: (value, d) => value ? format(value) : "",
x: 950 + (index - 2) * columnSpacing,
fill: (value) => null,
};
});
const container = d3.create("div")
.style("width", "100%")
.style("height", "1000px")
.style("overflow-x", "scroll");
const totalWidth = columns.reduce((acc, col) => acc + col.x, 0) + 150;
const svg = d3.create("svg")
.classed("CollapibleIndentedTree",true)
.attr("width", totalWidth) // Set the width of the SVG element
.style("overflow", "visible");
container.append(() => svg.node());
const gLink = svg.append("g")
.attr("name", "link")
.attr("fill", "none")
.attr("stroke", "#999")
.attr("transform", "translate(" + marginLeft + "," +marginTop + ")");
const gIcon = svg.append("g")
.attr("name", "icon")
.attr("fill", "grey")
.attr("stroke", "none")
.attr("transform", "translate(" + marginLeft + "," +marginTop + ")");
const gNode = svg.append("g")
.attr("class", "gNode")
// .attr("transform", `translate(0, ${marginTop})`)
.attr("transform", "translate(" + marginLeft + "," +marginTop + ")");
// .attr("transform", (d) => `translate(0, ${marginTop}, 0, ${marginLeft})`);
;
//put index to each tree node using eachBefore
function indexEachBefore(r){
let i=0;
r.eachBefore(n => {
n.index = i++;
})
return r
}
function sortTree(source) {
if (source.children) {
source.children.sort((a, b) => {
// Sort in decreasing order
return data_attr[b.data.efo_true]["count"] - data_attr[a.data.efo_true]["count"]
});
source.children.forEach(child => sortTree(child));
}
}
function update(source){
const duration = d3.event && d3.event.altKey ? 2500 : 250;
const nodes = source.descendants()
const links = source.links();
indexEachBefore(source);
const height = (nodes.length + 1) * nodeSize
const transition = svg.transition()
.duration(duration)
.attr("viewBox", [0, -nodeSize * 3 / 2, totalWidth, height])
.attr("height", height + marginTop)
.tween("resize", window.ResizeObserver ? null : () => () => svg.dispatch("toggle"));
const node = gNode.selectAll("g")
.data(nodes, d => d.id);
const nodeEnter = node.enter().append("g")
.attr("transform", d => `translate(0,${d.index * nodeSize})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0)
// .attr("cursor", d => _.has(d,'collapsed') ?"pointer":null)
.attr("pointer-events", "all")
// .on("dblclick", d => {
// uncollapseNodesByEfoTrue(d.data.efo_true, root);
// update(root);
// })
.on("click", d => {
const nodesWithSameName = findNodesByName(d.data.efo_true, data);
if (!_.has(d, 'collapsed')) return;
d.collapsed = !d.collapsed;
d.children = d.children ? null : d._children;
if (d.children) {
sortTree(d);
}
update(source);
})
.on('mouseover', function(d) {
d3.select(this)
.attr("fill", "darkblue")
.attr("font-weight", 700)
// .attr("fill-opacity", 0.5)
// .classed('hover', true).style('opacity', 1);
})
.on('mouseout', function(d) {
d3.select(this)
.attr("fill", "")
.attr("font-weight", 300)
// .attr("fill-opacity",1)
// .classed('hover', false).style('opacity', 1)
});
nodeEnter.append("circle")
.attr("cx", d => d.depth * nodeSize)
.attr("r", d => _.has(d,'collapsed')?0:4)
.attr("fill", "grey")
nodeEnter.append("rect")
.attr("dy", "0.32em")
.attr("x", d => d.depth * nodeSize + 6)
.attr("y", "-0.62em")
.attr("width", "100%").attr("height", nodeSize)
.attr("fill", "none")
// .attr("transform", d => `translate(${d.x},${d.y})`); // add this line to update the position of the rectangle
function assignFontWeight(node) {
if (node.depth ==1) {
return 700;
} else {
return 300;
}
}
nodeEnter.append("text")
.attr("dy", "0.32em")
.attr("x", d => d.depth * nodeSize + 8)
.attr("font-weight", d=> assignFontWeight(d))
.text(d => d.data.name)
nodeEnter.append("title")
.text(d => d.data.name);
for (const {label,
key,
x,
fill=()=>null} of columns) {
// console.log(value, fillBy)
nodeEnter.append("rect")
.attr("x", x - columnSpacing-10)
// .attr("x", x - (backgroundWidth / 2))
.attr("y", "-0.62em")
.attr("width", columnSpacing)
.attr("height", nodeSize)
.attr("fill", "lightgrey")
.attr("fill", d => getTextColor(d, key))
// .attr("fill", d => colorScale((data_attr[d.data.efo_true][key])));
// .attr("fill", d => fill(d.data[`${label}`]));
// .attr("opacity", fill_zeros(key))
}
for (const {label,
key,
index,
x,
fill=()=>null } of columns) {
const columnLabels = svg.append("g")
.attr("class", "columnLabels");
const label = columnLabels.selectAll(".label")
.data(columns)
.enter().append("g")
.attr("text-anchor", "start")
.attr("class", "label")
.attr("transform", (d, i, index) => "translate(" + (d.x-30 + 30**d.index) + "," + (-nodeSize - 7) + ") rotate(0)");
label.each(function (d) {
const lines = d.label.split(/\n/);
if (Array.isArray(lines)) {
lines.forEach((line, index) => {
d3.select(this)
.append("text")
.text(line)
.attr("text-anchor", "end")
.attr("fill", "#333")
.attr("dy", index === 0 ? 0 : "1.2em");
});
}
});
let columnText = nodeEnter.append("text");
columnText.attr("dy", "0.32em")
.attr("x", x)
.attr("text-anchor", "end")
.text(d => mapper(d, key))
// .attr("fill", fill_zeros_text(key))
const backgroundWidth = 90;
const middlePoint = x - (backgroundWidth / 2);
columnText
.attr("x", middlePoint) // Set the x position to the middle point
.attr("text-anchor", "middle") // Set the text-anchor to "middle"
}
const nodeUpdate = node.merge(nodeEnter).transition(transition)
.attr("transform", d => `translate(0,${d.index * nodeSize})`)
.attr("cursor", d => _.has(d,'collapsed') ?"pointer":null)
.attr("pointer-events", d => "all")
.attr("fill-opacity", 1)
.attr("stroke-opacity", 1);
const nodeExit = node.exit().transition(transition).remove()
.attr("transform", d => `translate(0,${d.index * nodeSize})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0);
const link = gLink.selectAll("path")
.data(links, d => d.target.id);
// Enter any new links at the parent's previous position.
const xOffset = -2, yOffset=3;
const linkEnter = link.enter().append("path")//.transition(transition)
.attr("d", d => `
M${d.source.depth * nodeSize},${d.source.index * nodeSize+yOffset}
V${d.target.index * nodeSize}
h${nodeSize+xOffset}
`);
link.merge(linkEnter)//.transition(transition)
.attr("d", d => `
M${d.source.depth * nodeSize},${d.source.index * nodeSize+yOffset}
V${d.target.index * nodeSize}
h${nodeSize+xOffset}
`);
link.exit().remove()
.attr("d", d => `
M${d.source.depth * nodeSize},${d.source.index * nodeSize+yOffset}
V${d.target.index * nodeSize}
h${nodeSize+xOffset}
`);
const icon = gIcon.selectAll("path")
.data(source.descendants().filter(d=>_.has(d,'collapsed')), d => d.id);
const iconPathData = d=> _.has(d,'collapsed')?(d.collapsed?"M10 4L1 12V-4z":"M4 7L0 1h8z"):''
const iconEnter = icon.enter().append("path")
// .attr("transform", d => `translate(${d.depth * nodeSize-4},${d.index * nodeSize-3})`)
// .attr("fill", d => d.collapsed?"#999":null)
.attr("d", iconPathData);
// Transition links to their new position.
icon.merge(iconEnter)
.attr("transform", d => `translate(${d.depth * nodeSize-4},${d.index * nodeSize-4})`)
.attr("fill", "grey")
// .attr("fill", d => d.collapsed?("rgb(255" + ("," + Math.floor(Math.random() * 255)).repeat(2) + ")")
// :null)
.attr("cursor", "pointer")
.attr("pointer-events", "all")
.on("click", d => {
if(!_.has(d,'collapsed')) return;
d.collapsed=!d.collapsed;
d.children = d.children ? null : d._children;
update(source);
})
.attr("d", iconPathData);
// Transition exiting nodes to the parent's new position.
icon.exit().remove();
}
let copyOfRoot = root.copy();
const allNodes = root.descendants();
const minScore = d3.min(allNodes, d => d.data.Score);
const maxScore = d3.max(allNodes, d => d.data.Score);
copyOfRoot=identifyDescendants.call(copyOfRoot,copyOfRoot,...postIdentifyFns)
sortTree(copyOfRoot);
update(copyOfRoot);
return container.node();
// return svg.node();
}