chart = {
replay;
const nodes = pack2.leaves().map(d => { d.id=String("node" + d.data.parent.replace(/\s+/g, '') + d.data.child.replace(/\s+/g,'')).trim(); return Object.create(d); });
const parents = pack2.descendants().filter(d => d.height == 1);
const labelNodes = pack3.leaves().map(d => {
d.id=String("label" + d.data.parent.replace(/\s+/g, '') + d.data.child.replace(/\s+/g, '')).trim();
d.type = "label";
return Object.create(d);
});
const links = nodes.map((n,i) => { return Object.create({"target": "node" + n.data.parent.replace(/\s+/g, '') + n.data.child.replace(/\s+/g, ''), "source": "label" + n.data.parent.replace(/\s+/g, '') + n.data.child.replace(/\s+/g, ''), "group": n.data.parent, "value": 5}); });
const simulation = d3.forceSimulation((v == "Centered" || v == "Spread") ? nodes.concat(parents) : nodes.concat(labelNodes).filter(x => x.data.parent == v))
.force("x", v == "Spread" ? forceXPos() : forceX(width / 2).strength(.06))
.force("y", d3.forceY(height / 2).strength(0.06))
.force("cluster", (v == "Centered" || v == "Spread") ? forceCluster(.05) : null)
.force("link",d3.forceLink((v == "Centered" || v == "Spread") ? [] : links.filter(x => x.group == v)).id(d => d.id).distance(0).strength(.01))
.force("radial",(function(){
if(v == "Centered")
return forceRadial(Math.sqrt(width*width + height*height)/2,function(_) {return _.data.parent == "root" ? .1 : 0; },width / 2,height / 2);
else if( v == "Spread" )
return null;
else
return forceRadial(Math.sqrt(width*width + height*height)/3,function(_) {return _.type == "label" ? .05 : 0; },width / 2,height / 2);
})())
.force("collide", d3.forceCollide((node, i, nodes) => node.r * .95).strength(.12) );
const svg = d3.select(DOM.svg(width, height));
const node = svg.append("g").classed("nodes",true).selectAll("circle.node")
.data((v == "Centered" || v == "Spread") ? nodes.concat(parents) : nodes.filter(x => x.data.parent == v), d => d.id)
.join(
enter => enter.append("circle")
.classed("node",true)
.classed("label", d => d.data.parent == "root")
.attr("id", function(d){return d.id;})
.attr("fill", d => color(d.data.parent))
.attr("stroke","black")
.attr("r", d => d.data.parent == "root" ? 0 : d.r)
.attr("cx", width / 2)//d => d.x)
.attr("cy", height / 2)//d => d.y)
.on("mouseover", () => {
const c = d3.select(d3.event.target);
c.attr("stroke-width",3);
txt
.attr("x", c.attr("cx"))
.attr("y", c.attr("cy"))
.text(c.datum().data.child);
})
.on("mouseout", () => {
const c = d3.select(d3.event.target);
c.attr("stroke-width",1);
txt
.attr("x", 0)
.attr("y", 0)
.text("");
})
.call(drag(simulation)),
update => update,
exit => exit.remove()
);
const labelGroup = svg.select("g.nodes").selectAll("text.label")
.data(v == "Centered" ? parents : [], d => d.id)
.join(
enter => enter.append("text")
.classed("label",true)
.text(d => d.id),
update => update,
exit => exit.remove()
);
const labelNode = svg.append("g").selectAll("g.label")
.data((v == "Centered" || v == "Spread") ? [] : labelNodes.filter(x => x.data.parent == v), d => d.id)
.join("g")
.classed("label",true)
.attr("id", d => d.id)
.on("click", function(d){
const target = d3.select(d3.event.target);
const id = d3.select(d3.event.target.parentNode.parentNode).attr("id");
d3.select(id.replace(/^/,"#")).attr("opacity",0);
});
const label = labelNode.append("circle")
.classed("label",true)
.attr("id", d => d.id)
.attr("cx", d => width / 2)
.attr("cy", d => height / 2)
.attr("r", 0)
.attr("fill", d => color(d.data.parent))
.attr("stroke","black")
.on("click", () => {
//d3.select(d3.event.target).attr("fill","black");
//txt.text(d3.select(d3.event.target).datum().id);
});
const labelTxt = labelNode.append("text")
.attr("x",function(d){ return d.x;} )
.attr("y",function(d){ return d.y;} );
labelTxt.selectAll("tspan")
.data(d => d.data.child.split(" "))
.join("tspan")
.attr("x",function(d,i){ return d3.select(this.parentNode).attr("x"); })
.attr("dx",0)
.attr("dy","1em")
.attr("opacity", 0)
.text(d => d);
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.0) //0.6)
.selectAll("line")
.data((v == "Centered" || v == "Spread") ? [] : links)
.join("line")
.classed("link",true)
.attr("stroke-width", d => Math.sqrt(d.value))
.on("click", function(d){
d3.select(d3.event.target).attr("opacity",0);
});
//node
// .transition()
// .delay((d, i) => 300)//Math.random() * 500)
// .duration(750)
// .attrTween("r", d => {
// const i = d3.interpolate(0, d.r);
// return d.data.parent == "root" ? t => d.r = 0 : t => d.r = i(t);
// });
labelTxt.selectAll("tspan")
.transition()
.delay(300)
.duration(750)
.attr("opacity", 1);
link
.transition()
.delay(300)
.duration(750)
.attr("stroke-opacity", 0.6);
simulation.on("tick", () => {
label
.attr("cx", d => d.x)
.attr("cy", d => d.y);
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
labelGroup
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("text-anchor", function(d,i) {
return "middle";
});
labelTxt
.attr("x", d => d.x)
.attr("y", function(d) { return d.y - this.children.length * 10;} )
.attr("text-anchor", function(d,i) {
const parent = this.parentNode;
const diff = d3.select(parent).select("circle.label").attr("cx") > d3.select(parent.id.replace(/^label/,"#node")).attr("cx");
return Math.abs(diff) < 20 ? "middle" : diff >= 0 ? "start" : "end";
});
labelTxt.selectAll("tspan")
.attr("x", function(d,i){ return d3.select(this.parentNode).attr("x"); });
});
const txt = svg.append("text")
.attr("x",5)
.attr("y",14)
.text("");
invalidation.then(() => simulation.stop());
return svg.node();
}