{
const nodes = await FileAttachment("nodes.json").json();
const links = await FileAttachment("links.json").json();
debugger;
const width = 800;
const height = 800;
let selectedId;
const quadtree = d3
.quadtree()
.x((d) => d.x)
.y((d) => d.y);
const drag = d3
.drag()
.on("start", handleDragStart)
.on("drag", handleDrag)
.on("end", handleDragEnd);
const sqrtScale = d3
.scaleSqrt()
.domain(d3.extent(nodes, (d) => d.count))
.range([5, 25]);
const ordinalScale = d3
.scaleOrdinal()
.domain(["herbivour", "carnivour"])
.range(["#008000", "#FF0000"])
.unknown("#ccc");
const svg = d3
.create('svg')
.attr("width", width)
.attr("height", height);
svg.append("g").classed("nodes", true);
svg.append("g").classed("links", true);
svg
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "white")
.attr("stroke", "black")
.attr("stroke-width", 3);
console.log(links)
const simulation = d3
.forceSimulation(nodes)
.force("center", d3.forceCenter(width / 2, height / 2))
// .force("charge", d3.forceManyBody().strength(-30))
.force("link", d3.forceLink().links(links).id(d => d.id))
.force('collide', d3.forceCollide(d => sqrtScale(d.count)))
.on("tick", ticked);
function updateNodes() {
svg
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", (d) => sqrtScale(d.count))
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
.attr("fill", (d) => (d.id === selectedId ? "#FF0000" : "black"))
.raise()
.call(drag);
}
function updateLinks() {
svg
.selectAll("line")
.data(links)
.join("line")
.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
})
.attr("stroke", (d) => ordinalScale(d.type))
.attr("stroke-width", "3");
}
function ticked() {
updateLinks();
updateNodes();
updateQuadTree();
}
function initEvents() {
svg.on("mousemove", handleMousemove);
}
function handleMousemove(e) {
let pos = d3.pointer(e, this);
let d = quadtree.find(pos[0], pos[1], 20);
selectedId = d ? d.id : undefined;
updateNodes();
}
function updateQuadTree() {
quadtree.addAll(nodes);
}
function random(l, h) {
return l + Math.floor(Math.random() * h - l);
}
function handleDragStart(e) {
if (!e.active) simulation.alphaTarget(0.3).restart();
e.subject.fx = e.subject.x;
e.subject.fy = e.subject.y;
}
function handleDragEnd(e) {
if (!e.active) simulation.alphaTarget(0);
e.subject.fx = null;
e.subject.fy = null;
}
function handleDrag(e) {
e.subject.fx = e.x;
e.subject.fy = e.y;
ticked();
}
initEvents();
invalidation.then(() => simulation.stop());
return svg.node();
}