{
let borders = false;
const WIDTH = width / 2 - 40;
const HEIGHT = 600;
const MARGIN = { TOP: 10, BOTTOM: 10, LEFT: 10, RIGHT: 10 };
const MAX_RADIUS = 20;
const widthSVG = WIDTH - MARGIN.RIGHT - MARGIN.LEFT;
const height = HEIGHT - MARGIN.TOP - MARGIN.BOTTOM;
const FILEPATH =
"https://raw.githubusercontent.com/PUC-Infovis/syllabus-2018/master/ayudantia07/force-graph/dataset.json";
d3.select("#graph").selectAll("svg").remove();
const SVG = d3
.select("#graph")
.append("svg")
.attr("width", WIDTH)
.attr("height", HEIGHT);
const container = SVG.append("g").attr(
"transform",
`translate(${MARGIN.LEFT}, ${MARGIN.TOP})`
);
const div = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
const simulation = d3
.forceSimulation()
.force("center", d3.forceCenter(widthSVG / 2, height / 2))
.force("collision", d3.forceCollide(20).radius(20))
.force(
"charge",
d3.forceManyBody().strength(-500).distanceMin(30).distanceMax(80)
)
.force(
"link",
d3.forceLink().id((node) => node.id)
);
const ticked = () => {
d3.select("#alphaValue").style(
"width",
`${Math.round((simulation.alpha() * width) / 5)}px`
);
d3.select("#alphaValueOutput").text(String(simulation.alpha()).slice(0, 6));
if (borders) {
container
.selectAll(".node")
.attr("cx", function (d) {
return (d.x = Math.max(
MAX_RADIUS,
Math.min(widthSVG - MAX_RADIUS, d.x)
));
})
.attr("cy", function (d) {
return (d.y = Math.max(
MAX_RADIUS,
Math.min(height - MAX_RADIUS, d.y)
));
});
}
container
.selectAll(".node")
.attr("transform", (node) => `translate(${node.x}, ${node.y})`);
container
.selectAll("line")
.attr("x1", (link) => link.source.x)
.attr("y1", (link) => link.source.y)
.attr("x2", (link) => link.target.x)
.attr("y2", (link) => link.target.y);
};
d3.json(FILEPATH).then((dataset) => {
simulation
.nodes(dataset.nodes)
.on("tick", ticked)
.force("link")
.links(dataset.links)
.distance(80);
container
.selectAll("line")
.data(dataset.links)
.enter()
.append("line")
.attr("x1", (link) => link.source.x)
.attr("y1", (link) => link.source.y)
.attr("x2", (link) => link.target.x)
.attr("y2", (link) => link.target.y);
const nodes = container
.selectAll(".node")
.data(dataset.nodes)
.enter()
.append("g")
.attr("class", "node");
const mouseover = (node) => {
let targetLinks = dataset.links.filter(
(link) => link.target.id == node.id
).length;
let sourceLinks = dataset.links.filter(
(link) => link.source.id == node.id
).length;
let content =
"<span style='margin-left: 2.5px;'><b>" + node.id + "</b></span><br>";
content +=
`<table style="margin-top: 2.5px;">
<tr><td>Links que apuntan a mi: </td><td style="text-align: right">` +
targetLinks +
`</td></tr>
<tr><td>Links que salen de mi: </td><td style="text-align: right">` +
sourceLinks +
`</td></tr>
</table>`;
div.transition().duration(200).style("opacity", 0.9);
div
.html(content)
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY - 28 + "px");
};
const mouseout = (_) => {
div.transition().duration(200).style("opacity", 0);
};
nodes
.append("circle")
.attr("r", MAX_RADIUS)
.on("mouseover", mouseover)
.on("mouseout", mouseout);
nodes
.append("text")
.text((node) => node.id)
.attr("dy", 5)
.on("mouseover", mouseover)
.on("mouseout", mouseout);
d3.select("#forceInput").on("input", (d) => {
let value = d3.select("#forceInput").property("value");
d3.select("#forceOutput").text(value);
simulation.alpha(1);
simulation.force("charge", d3.forceManyBody().strength(value)).restart();
});
d3.select("#forceInputMin").on("input", (d) => {
let value = d3.select("#forceInputMin").property("value");
d3.select("#forceOutputMin").text(value);
simulation.alpha(1);
simulation
.force("charge", d3.forceManyBody().distanceMin(value))
.restart();
});
d3.select("#forceInputMax").on("input", (d) => {
let value = d3.select("#forceInputMax").property("value");
d3.select("#forceOutputMax").text(value);
simulation.alpha(1);
simulation
.force("charge", d3.forceManyBody().distanceMax(value))
.restart();
});
d3.select("#collisionInput").on("input", (d) => {
let value = d3.select("#collisionInput").property("value");
d3.select("#collisionOutput").text(value);
simulation.alpha(1);
simulation.force("collision", d3.forceCollide(value)).restart();
});
d3.select("#distanceInput").on("input", (d) => {
let value = d3.select("#distanceInput").property("value");
d3.select("#distanceOutput").text(value);
simulation.alpha(1);
simulation
.force("link")
.distance(+value)
.restart();
});
d3.select("#centerXInput").on("input", (d) => {
let value = d3.select("#centerXInput").property("value");
d3.select("#centerXOutput").text(value);
let value2 = d3.select("#centerYInput").property("value");
d3.select("#centerYOutput").text(value2);
simulation.alpha(1);
simulation.force("center", d3.forceCenter(value, value2)).restart();
});
d3.select("#centerYInput").on("input", (d) => {
let value = d3.select("#centerXInput").property("value");
d3.select("#centerXOutput").text(value);
let value2 = d3.select("#centerYInput").property("value");
d3.select("#centerYOutput").text(value2);
simulation.alpha(1);
simulation.force("center", d3.forceCenter(value, value2)).restart();
});
d3.select("#alphaBar").on("click", (d) => {
simulation.alpha(1);
simulation.restart();
});
d3.selectAll("#borders").on("change", (_, i, all) => {
borders = all[i].checked;
simulation.alpha(1);
simulation.restart();
});
});
return "Código JS para visualizar";
}