diff --git a/public/js/force-directed-graph.js b/public/js/force-directed-graph.js index d127190..215d91e 100644 --- a/public/js/force-directed-graph.js +++ b/public/js/force-directed-graph.js @@ -14,7 +14,7 @@ color = d3.scaleOrdinal(d3.schemeCategory20), width = +d3Canvas.attr("width"), height = +d3Canvas.attr("height"), - radius = 2.5, + radius = 4, context = canvas.getContext("2d"); var simulation = d3.forceSimulation() @@ -25,48 +25,23 @@ .force("center", d3.forceCenter(width / 2, height / 2)); // FILTER & COMMUNITY - var edges = [], - nodes = []; - for (var i = 0; i < graph.nodes.length; i++) { - nodes.push(i); - } - for (var i = 0; i < graph.links.length; i++) { - var edge = graph.links[i]; - edge.weight = edge.value; - edges.push(edge); - } + var selectedData = getSelectedData(setGroup(graph), 4), + nodes = selectedData.nodes, + links = selectedData.links; - // Create the "community" - var community = jLouvain().nodes(nodes).edges(edges), - res = community(); - - // Affect community for each node - for (var key in res) { - graph.nodes[nodes[key]].group = res[key]; - } - - var deleted_links = {}, - selectedLinks = graph.links.filter(function(d) { - var res = (d.value >= 10); - if (!res) { - deleted_links[d.source] = deleted_links[d.source] + 1 || 1; - deleted_links[d.target] = deleted_links[d.target] + 1 || 1; - } - return res; - }), - selectedNodes = graph.nodes.filter(function(d) { - d.value = d.value - (deleted_links[d.id] || 0); - var res = (d.value > 0); - return res; - }); + // Set radius + nodes.forEach(function(e) { + e.radius = radius; + return e; + }); // FILL CANVAS simulation - .nodes(selectedNodes) + .nodes(nodes) .on("tick", draw); simulation.force("link") - .links(selectedLinks); + .links(links); function draw() { context.save(); @@ -78,8 +53,9 @@ context.strokeStyle = "#546e7a"; context.beginPath(); graph.links.forEach(function(d) { - context.moveTo(d.source.x, d.source.y); - context.lineTo(d.target.x, d.target.y); + context.moveTo(Math.floor(d.source.x), Math.floor(d.source.y)); + context.lineTo(Math.floor(d.target.x), Math.floor(d.target.y)); + context.lineWidth = 1; }); context.stroke(); @@ -88,11 +64,12 @@ context.beginPath(); //for each node do begin path as context fill style and stroke are different. context.strokeStyle = "#fff"; context.fillStyle = color(d.group); - context.moveTo(d.x + radius, d.y); - context.arc(d.x, d.y, radius, 0, 2 * Math.PI); + context.rect(Math.floor(d.x - (d.radius / 2)), Math.floor(d.y - (d.radius / 2)), d.radius, d.radius); + // context.arc(d.x, d.y, d.radius, 0, 2 * Math.PI); context.fill(); context.stroke(); }); + context.restore(); } @@ -107,37 +84,43 @@ .call(d3.zoom().scaleExtent([-100, 100]).on("zoom", zoomed)) .call(draw); - function zoomed() { - transform = d3.event.transform; - draw(); - } - - function dragsubject() { - var selectedNode, + function detectCollision(source, targets) { + var result = null, + target, i, - n, - x = transform.invertX(d3.event.x), - y = transform.invertY(d3.event.y), dx, dy; - for (i = selectedNodes.length - 1; i >= 0; --i) { - selectedNode = selectedNodes[i]; - dx = x - selectedNode.x; - dy = y - selectedNode.y; - if (dx * dx + dy * dy < radius * radius) { - selectedNode.x = transform.applyX(selectedNode.x); - selectedNode.y = transform.applyY(selectedNode.y); - return selectedNode; + for (i = targets.length - 1; i >= 0; --i) { + target = targets[i]; + dx = Math.floor(source.x - target.x); + dy = Math.floor(source.y - target.y); + if ((dx * dx + dy * dy) < (target.radius * target.radius)) { + result = target; } } + return result; } - // function dragged() { - // d3.event.subject.x = transform.invertX(d3.event.x); - // d3.event.subject.y = transform.invertY(d3.event.y); - // draw(); - // } + function mousemove() { + d3.select(this).style('cursor', 'default'); + var node = detectCollision({ + 'x': transform.invertX(d3.event.layerX), + 'y': transform.invertY(d3.event.layerY) + }, nodes); + if (node) d3.select(this).style('cursor', 'move'); + } + + function dragsubject() { + var node = detectCollision({ + 'x': transform.invertX(d3.event.x), + 'y': transform.invertY(d3.event.y) + }, nodes); + node.x = transform.applyX(node.x); + node.y = transform.applyY(node.y); + + return node; + } function dragstarted() { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); @@ -156,5 +139,56 @@ d3.event.subject.fy = null; } + function zoomed() { + transform = d3.event.transform; + draw(); + } }); +} + +// Set a group for each nodes in graph +function setGroup(graph) { + var edges = [], + nodes = []; + for (var i = 0; i < graph.nodes.length; i++) { + nodes.push(i); + } + for (var i = 0; i < graph.links.length; i++) { + var edge = graph.links[i]; + edge.weight = edge.value; + edges.push(edge); + } + + // Create the "community" + var community = jLouvain().nodes(nodes).edges(edges), + res = community(); + + // Affect community for each node + for (var key in res) { + graph.nodes[nodes[key]].group = res[key]; + } + + return graph; +} + +function getSelectedData(graph, lim) { + var deleted_links = {}, + selectedLinks = graph.links.filter(function(d) { + var res = (d.value >= lim); + if (!res) { + deleted_links[d.source] = deleted_links[d.source] + 1 || 1; + deleted_links[d.target] = deleted_links[d.target] + 1 || 1; + } + return res; + }), + selectedNodes = graph.nodes.filter(function(d) { + d.value = d.value - (deleted_links[d.id] || 0); + var res = (d.value > 0); + return res; + }); + + return { + 'links': selectedLinks, + 'nodes': selectedNodes + }; } \ No newline at end of file