Newer
Older
indexation / public / js / force-directed-graph.js
"use strict";

function initGraph(url, corpus) {
  // VAR
  var loc = window.location.pathname,
    dir = loc.substring(0, loc.lastIndexOf('/')),
    corpus = JSON.parse(corpus);

  d3.json(url, function(error, graph) {
    if (error) throw error;

    var canvas = document.querySelector("#container"),
      d3Canvas = d3.select("#container"),
      color = d3.scaleOrdinal(d3.schemeCategory20),
      width = +d3Canvas.attr("width"),
      height = +d3Canvas.attr("height"),
      radius = 2.5,
      context = canvas.getContext("2d");

    var simulation = d3.forceSimulation()
      .force("link", d3.forceLink().id(function(d) {
        return d.id;
      }))
      .force("charge", d3.forceManyBody())
      .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);
    }

    // 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;
      });

    // FILL CANVAS
    simulation
      .nodes(selectedNodes)
      .on("tick", draw);

    simulation.force("link")
      .links(selectedLinks);

    function draw() {
      context.save();
      context.clearRect(0, 0, width, height);
      context.translate(transform.x, transform.y);
      context.scale(transform.k, transform.k);

      // draw links
      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.stroke();

      // draw nodes
      graph.nodes.forEach(function(d) {
        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.fill();
        context.stroke();
      });
      context.restore();
    }

    // ZOOM
    var transform = d3.zoomIdentity;

    d3Canvas
      .call(d3.drag().subject(dragsubject)
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended))
      .call(d3.zoom().scaleExtent([-100, 100]).on("zoom", zoomed))
      .call(draw);

    function zoomed() {
      transform = d3.event.transform;
      draw();
    }

    function dragsubject() {
      var selectedNode,
        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;
        }
      }
    }

    // function dragged() {
    //   d3.event.subject.x = transform.invertX(d3.event.x);
    //   d3.event.subject.y = transform.invertY(d3.event.y);
    //   draw();
    // }

    function dragstarted() {
      if (!d3.event.active) simulation.alphaTarget(0.3).restart();
      d3.event.subject.fx = transform.invertX(d3.event.subject.x);
      d3.event.subject.fy = transform.invertY(d3.event.subject.y);
    }

    function dragged() {
      d3.event.subject.fx = transform.invertX(d3.event.x);
      d3.event.subject.fy = transform.invertY(d3.event.y);
    }

    function dragended() {
      if (!d3.event.active) simulation.alphaTarget(0);
      d3.event.subject.fx = null;
      d3.event.subject.fy = null;
    }

  });
}