{
let margin = { top: 30, right: 40, bottom: 40, left: 50 };
const tree ={id:"0",children:[{id:"1",children:[{id:"3",children:[{id:"5",impurity:"0.0",samples:"11",value:"[0.20.]",class:"1",},{id:"6",children:[{id:"7",impurity:"0.0",samples:"5",value:"[6.0.]",class:"0",},{id:"8",impurity:"0.0",samples:"2",value:"[0.2.]",class:"1",},],value:4.5000,color:"#e99f46",impurity:"0.8112781244591328",samples:"7",},],value:97.0509,color:"#feeca9",impurity:"0.74959525725948",samples:"18",},{id:"4",children:[{id:"9",impurity:"0.0",samples:"3",value:"[5.0.]",class:"0",},{id:"10",impurity:"0.9418285354475157",samples:"25",value:"[25.14.]",class:"0",},],value:1.5000,color:"#ce3874",impurity:"0.9023932827949789",samples:"28",},],value:0.5000,color:"#ce3874",impurity:"1.0",samples:"46",split:"2"},{id:"2",impurity:"0.0",samples:"7",value:"[9.0.]",class:"0"},],name:"NumberofColors",value:3.5000,color:"#005da3",impurity:"0.9910760598382222",samples:"53",split:"1",};
let width = 600;
let height = 500;
const squareSize = 100;
const squareCol = "#de3d83";
const squareBgd = "red";
const strokeCol = "#fff";
const strokeWidth = 1;
const treeSize = 300;
const root = d3.hierarchy(tree, (d) => d.children);
const d3Tree = d3.tree().size([treeSize * 1.2, treeSize * 1]);
const treeData = d3Tree(root);
const svg = DOM.svg(width + margin.left + margin.right,
height + margin.bottom + margin.top)
const svgSel = d3.select(svg)
const sel = svgSel.append('g').attr('transform', `translate(${margin.left},${margin.top}))`);
let path0;
let path3;
let path4 ;
let path6;
let path;
let l;
let initialTime;
const yScale = d3.scaleLinear().domain([0,height]).range([margin.top*2,height-(margin.top)]);
drawTree(sel);
function drawTree(svgLocation) {
let rScale = d3.scaleSequential().domain([0, 1]).range([0, 4]);
// draw links
const cell = svgLocation;
const treePaths = cell
.append("g")
.attr("class", "tree-link-g")
.selectAll("path")
.data(treeData.links())
.join("path")
.attr(
"d",
d3
.linkVertical()
.x((d) => d.x)
.y((d) => yScale(d.y))
)
.attr("class", (d,i) => `link${i}`)
.attr("stroke", "#354848")
.attr("stroke-width", 0.75 * strokeWidth);
// draw nodes
const treeNodes = cell
.append("g")
.attr("class", "tree-node-g")
.selectAll("rect")
.data(treeData.descendants());
const rectSize = rScale(treeSize / 50);
const treeCircles = treeNodes
.join("rect")
.attr("fill", "rgb(206, 56, 116)")
.attr("height", rectSize)
.attr("width", rectSize)
.attr("rx", 1)
.attr("ry", 1)
.attr("x", (d) => d.x - rectSize / 2)
.attr("y", (d) => yScale(d.y) - rectSize / 2)
.attr("stroke", "#354848")
.attr("stroke-width", strokeWidth / 2)
.transition() // This is more like a "HACK" to make the stuff below runs only after the tree is drawn. It's observable specific.
.on("end", (d,i)=>{
if(i===0) {
// Finally, let's pick a path that an object must follow!
//link0->link2->link5->link8 Change this sequence and see what happens!!
path0 = d3.select(".link0").node();
path3 = d3.select(".link2").node();
path4 = d3.select(".link5").node();
path6 = d3.select(".link8").node();
path = d3.select(".tree-link-g").append("path").attr("class","route").attr("opacity",0);
path.attr("d",
`${path0.getAttribute("d")} ${path3.getAttribute("d")} ${path4.getAttribute("d")} ${path6.getAttribute("d")}`)
// Get the length of the path
l = path.node().getTotalLength();
// Below are the same code as the simple path example.
// Add a point at the beginning of the path
initialTime = 0 ; //this will keep track of the time when the person clicks the red dot
sel
.append("circle")
.attr("cx", path.node().getPointAtLength(0).x)
.attr("cy", path.node().getPointAtLength(0).y)
.attr("class", "move")
.attr("r",20)
.style("fill", "red")
.style("stroke", "white")
.on("click", ()=> {
initialTime=performance.now(); // record the time when the user clicked the circle.
start()});
// Tell the viewer to click
sel
.append("text")
.attr("x", path.node().getPointAtLength(0).x + 20)
.attr("y", path.node().getPointAtLength(0).y + 20)
.text("Click Me!!")
.style("fill", "blue");
}});;;
}
// Animation Function. Use (l- time) as a parameter to reverse the animation that goes from getPointAtLength(l) to getPointAtLength(0)!! (And don't forget to start from the end, getPointAtLength(l)!)
function start() {
let time = (performance.now() - initialTime) / 20; // scaled time lapse, change 10 to something else to adjust speed!
if (time < l) { // Move the circle until it reaches almost the end!
sel
.select(".move")
.attr("cx", path.node().getPointAtLength(time).x)
.attr("cy", path.node().getPointAtLength(time).y)
.attr("r", 10)
.style("fill", "black")
.style("stroke", "black");
window.requestAnimationFrame(start); // don't forget this!
}
else{ //When time>l, just move to the end point of the path, or do other cool stuff.
sel
.select(".move")
.attr("cx", path.node().getPointAtLength(l).x)
.attr("cy", path.node().getPointAtLength(l).y)
.attr("r", 5)
.style("fill", "black")
.style("stroke", "black")
.transition()
.attr("r", 20)
.style("fill", "red")
.style("stroke", "red")
;
}
}
return svg}