chart = {
const width = 928;
const height = 800;
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
const finalProcessedNodes = [];
const finalProcessedLinks = [];
const nodeIdsPresent = new Set();
const NodeType = { FILM: "filme", DIRECTOR: "diretor", ACTOR: "ator" };
const NodeGroup = {
[NodeType.FILM]: 1,
[NodeType.DIRECTOR]: 2,
[NodeType.ACTOR]: 3
};
data.nodes.forEach(movie => {
const movieId = movie.id;
if (!nodeIdsPresent.has(movieId)) {
finalProcessedNodes.push({
id: movieId, originalData: movie, nodeType: NodeType.FILM,
group: NodeGroup[NodeType.FILM], movieCount: 0, filmList: []
});
nodeIdsPresent.add(movieId);
}
if (movie.director) {
const directorName = movie.director;
if (!nodeIdsPresent.has(directorName)) {
finalProcessedNodes.push({
id: directorName, nodeType: NodeType.DIRECTOR,
group: NodeGroup[NodeType.DIRECTOR], movieCount: 0, filmList: []
});
nodeIdsPresent.add(directorName);
}
finalProcessedLinks.push({ source: movieId, target: directorName, value: 1.5 });
}
const actorFields = ["actor1", "actor2"];
actorFields.forEach(actorField => {
if (movie[actorField]) {
const actorName = movie[actorField];
if (!nodeIdsPresent.has(actorName)) {
finalProcessedNodes.push({
id: actorName, nodeType: NodeType.ACTOR,
group: NodeGroup[NodeType.ACTOR], movieCount: 0, filmList: []
});
nodeIdsPresent.add(actorName);
}
finalProcessedLinks.push({ source: movieId, target: actorName, value: 1 });
}
});
});
const nodeMapForCounting = new Map(finalProcessedNodes.map(n => [n.id, n]));
finalProcessedLinks.forEach(link => {
const sourceNode = nodeMapForCounting.get(link.source);
const targetNode = nodeMapForCounting.get(link.target);
if (sourceNode && targetNode && sourceNode.nodeType === NodeType.FILM) {
if (targetNode.nodeType === NodeType.DIRECTOR || targetNode.nodeType === NodeType.ACTOR) {
targetNode.movieCount++;
targetNode.filmList.push(sourceNode.id);
}
}
});
const nodesForSimulation = finalProcessedNodes;
const linksForSimulation = finalProcessedLinks.map(d => ({...d}));
const topDirector = nodesForSimulation
.filter(n => n.nodeType === NodeType.DIRECTOR && n.movieCount > 0)
.sort((a, b) => b.movieCount - a.movieCount)[0];
const topDirectorId = topDirector ? topDirector.id : null;
const topActor = nodesForSimulation
.filter(n => n.nodeType === NodeType.ACTOR && n.movieCount > 0)
.sort((a, b) => b.movieCount - a.movieCount)[0];
const topActorId = topActor ? topActor.id : null;
// Imprimir no console (mantido para depuração)
if (topDirector) console.log(`Top Diretor: ${topDirector.id} (${topDirector.movieCount} filmes)`);
if (topActor) console.log(`Top Ator: ${topActor.id} (${topActor.movieCount} filmes)`);
// FIM: PRÉ-PROCESSAMENTO
// AJUSTES NA SIMULAÇÃO DE FORÇA PARA LAYOUT MAIS COMPACTO
const simulation = d3.forceSimulation(nodesForSimulation)
.force("link", d3.forceLink(linksForSimulation)
.id(d => d.id)
.distance(10) // Reduzido: links mais curtos para compactar
.strength(0.15) // Aumentado ligeiramente: links um pouco mais "firmes"
)
.force("charge", d3.forceManyBody().strength(-60)) // Repulsão branda (pode testar -30 ou -35)
.force("x", d3.forceX().strength(0.15)) // Aumentado: puxa mais forte para o centro X
.force("y", d3.forceY().strength(0.15)) // Aumentado: puxa mais forte para o centro Y
.tick(300);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto; font-family: sans-serif; font-size: 10px;");
const linkElements = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(linksForSimulation)
.join("line")
.attr("stroke-width", d => Math.sqrt(d.value || 1));
// AJUSTES NOS RAIOS E STROKES DOS NÓS PARA APARÊNCIA MAIS COMPACTA
const nodeElements = svg.append("g")
.selectAll("circle")
.data(nodesForSimulation)
.join("circle")
.attr("r", d => {
if (d.id === topDirectorId || d.id === topActorId) return 16; // Reduzido
if (d.nodeType === NodeType.DIRECTOR || d.nodeType === NodeType.ACTOR) return 6; // Reduzido
return 6;
})
.attr("fill", d => colorScale(d.group))
.attr("stroke", d => (d.id === topDirectorId || d.id === topActorId) ? "#000000" : "#FFFFFF")
.attr("stroke-width", d => (d.id === topDirectorId || d.id === topActorId) ? 2 : 1); // Reduzido
nodeElements.append("title")
.text(d => {
if (d.nodeType === NodeType.DIRECTOR) return `${d.id}\nTipo: Diretor\nDirigiu: ${d.movieCount} filme(s)\nFilmes: ${d.filmList.length > 0 ? d.filmList.join("; ") : "N/A"}`;
if (d.nodeType === NodeType.ACTOR) return `${d.id}\nTipo: Ator\nAtuou em: ${d.movieCount} filme(s)\nFilmes: ${d.filmList.length > 0 ? d.filmList.join("; ") : "N/A"}`;
if (d.nodeType === NodeType.FILM) {
const movieData = d.originalData;
return `Filme: ${d.id}\nAno: ${movieData.year}\nScore: ${movieData.score}\nDiretor: ${movieData.director}\nAtores: ${movieData.actor1}${movieData.actor2 ? ', ' + movieData.actor2 : ''}`;
}
return d.id;
});
nodeElements.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
simulation.on("tick", () => {
linkElements
.attr("x1", d => d.source.x).attr("y1", d => d.source.y)
.attr("x2", d => d.target.x).attr("y2", d => d.target.y);
nodeElements
.attr("cx", d => d.x).attr("cy", d => d.y);
});
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x; d.fy = d.y;
}
function dragged(event, d) { d.fx = event.x; d.fy = event.y; }
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null; d.fy = null;
}
if (typeof invalidation !== 'undefined') {
invalidation.then(() => simulation.stop());
}
return Object.assign(svg.node(), {
scales: {color: colorScale},
processedNodes: nodesForSimulation,
processedLinks: linksForSimulation
});
}