{
const prefix = "cons/rect-"
const width = 1600
const height = 1000
const links = data.edges.map(d => ({...d}));
const nodes = data.nodes.map(d => ({...d}));
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(d=>distance))
.force("charge", d3.forceManyBody())
.force("collide", d3.forceCollide(1.2*radius))
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", ticked);
const chart = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto;");
chart.append('defs').append('marker')
.attr("id",'arrowhead')
.attr('viewBox','-0 -5 10 10')
.attr('refX',radius)
.attr('refY',0)
.attr('orient','auto')
.attr('markerWidth',13)
.attr('markerHeight',13)
.attr('xoverflow','visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke','none');
// setup group for all graph objects
const g = chart.append('g'). attr('class', 'graph');
// Add a line for each link, and a circle for each node.
const link = chart.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.9)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", '1px').attr('marker-end','url(#arrowhead)') //The marker-end attribute defines the arrowhead or polymarker that will be drawn at the final vertex of the given shape.
//The <title> element provides an accessible, short-text description of any SVG container element or graphics element.
//Text in a <title> element is not rendered as part of the graphic, but browsers usually display it as a tooltip.
link.append("title")
.text(d => d.label);
const edgepaths = chart.selectAll(".edgepath") //make path go along with the link provide position for link labels
.data(links)
.enter()
.append('path')
.attr('class', 'edgepath')
.attr('fill-opacity', 0)
.attr('stroke-opacity', 0)
.attr('id', function (d, i) {return 'edgepath' + i})
.style("pointer-events", "none");
const edgelabels = chart.selectAll(".edgelabel")
.data(links)
.enter()
.append('text')
.style("pointer-events", "none")
.attr('class', 'edgelabel')
.attr('id', function (d, i) {return 'edgelabel' + i})
.attr('font-size', 18)
.attr('fill', '#aaa');
edgelabels.append('textPath') //To render text along the shape of a <path>, enclose the text in a <textPath> element that has an href attribute with a reference to the <path> element.
.attr('xlink:href', function (d, i) {return '#edgepath' + i})
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("startOffset", "50%")
.text(d => d.label);
let s =0.5;
let node = chart.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(nodes)
.join("g")
.call(drag(simulation));
node.append((d) => svg`${images.get(prefix + d.icon + ".svg")}`)
.attr("r", radius)
.attr("cx", `${radius}`)
.attr("cy", `${radius}`);
node.append("title")
.text(d =>
d.label);
node.append("text")
.text(function(d) {
return d.icon + ": " + d.label;
})
.style('fill', '#000')
.style('font-size', '12px')
.attr('x', 16)
.attr('y', 13);
// Set the position attributes of links and nodes each time the simulation ticks.
function ticked() {
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);
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
edgepaths
.attr('d', d => 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y);
}
// When this cell is re-run, stop the previous simulation. (This doesn’t
// really matter since the target alpha is zero and the simulation will
// stop naturally, but it’s a good practice.)
invalidation.then(() => simulation.stop());
let zoomLvl = 1;
let lastK = 0;
chart.call(d3.zoom()
.extent([[0, 0], [width, height]])
.scaleExtent([1, 80])
.on("zoom", zoomed));
function zoomed() {
let e = d3.event
if(e.transform.k > 2 && lastK != e.transform.k){
lastK = e.transform.k;
console.log("zoomed");
zoomLvl =Math.log2(e.transform.k);
g.attr("stroke-width", 1.5/zoomLvl );
link.attr("stroke-width", d => Math.sqrt(d.value)/(zoomLvl));
}
g.attr("transform", e.transform);
}
return chart.node();
}