Skip to content

Instantly share code, notes, and snippets.

@steveharoz
Forked from mbostock/.block
Last active September 15, 2024 20:03

Revisions

  1. steveharoz revised this gist Oct 11, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .block
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,3 @@
    license: gpl-3.0
    height: 1025
    height: 1030
    scrolling: yes
  2. steveharoz revised this gist Oct 11, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .block
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,3 @@
    license: gpl-3.0
    height: 1000
    height: 1025
    scrolling: yes
  3. steveharoz revised this gist Oct 11, 2016. 2 changed files with 11 additions and 0 deletions.
    1 change: 1 addition & 0 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -164,6 +164,7 @@ function ticked() {
    node
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
    d3.select('#alpha_value').style('flex-basis', (simulation.alpha()*100) + '%');
    }


    10 changes: 10 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -23,6 +23,12 @@
    .controls .force label { display: inline-block; }
    .controls input[type="checkbox"] { transform: scale(1.2, 1.2); }
    .controls input[type="range"] { margin: 0 5% 0.5em 5%; width: 90%; }
    /* alpha viewer */
    .controls .alpha p { margin-bottom: .25em; }
    .controls .alpha .alpha_bar { height: .5em; border: 1px #777 solid; border-radius: 2px; padding: 1px; display: flex; }
    .controls .alpha .alpha_bar #alpha_value { background-color: #555; border-radius: 1px; flex-basis: 100% }
    .controls .alpha .alpha_bar:hover { border-width: 2px; margin:-1px; }
    .controls .alpha .alpha_bar:active #alpha_value { background-color: #222 }

    /* SVG styles */
    svg {
    @@ -40,6 +46,10 @@
    </head>
    <body>
    <div class="controls">
    <div class="force alpha">
    <p><label>alpha</label> Simulation activity</p>
    <div class="alpha_bar" onclick="updateAll();"><div id="alpha_value"></div></div>
    </div>
    <div class="force">
    <p><label>center</label> Shifts the view, so the graph is centered at this location.</p>
    <label>
  4. steveharoz revised this gist Oct 11, 2016. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -136,7 +136,6 @@
    </div>
    <svg></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/d3-force.v1.min.js"></script> <!-- use the latest d3-force -->
    <script src="code.js"></script>
    </body>
    </html>
  5. steveharoz revised this gist Oct 8, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion code.js
    Original file line number Diff line number Diff line change
    @@ -182,7 +182,7 @@ function dragged(d) {
    }

    function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    if (!d3.event.active) simulation.alphaTarget(0.0001);
    d.fx = null;
    d.fy = null;
    }
  6. steveharoz revised this gist Oct 8, 2016. 1 changed file with 52 additions and 30 deletions.
    82 changes: 52 additions & 30 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -5,10 +5,31 @@ var svg = d3.select("svg"),

    // svg objects
    var link, node;
    // the data
    // the data - an object with nodes and links
    var graph;

    // load the data
    d3.json("miserables.json", function(error, _graph) {
    if (error) throw error;
    graph = _graph;
    initializeDisplay();
    initializeSimulation();
    });



    //////////// FORCE SIMULATION ////////////

    // force simulator
    var simulation = d3.forceSimulation();

    // set up the simulation and event to update locations after each tick
    function initializeSimulation() {
    simulation.nodes(graph.nodes);
    initializeForces();
    simulation.on("tick", ticked);
    }

    // values for all forces
    forceProperties = {
    center: {
    @@ -84,32 +105,17 @@ function updateForces() {
    .iterations(forceProperties.link.iterations)
    .links(forceProperties.link.enabled ? graph.links : []);

    // updates ugnored until this is run
    // updates ignored until this is run
    // restarts the simulation (important if simulation has already slowed down)
    simulation.alpha(1).restart();
    }
    // update the display based on the forces
    function updateDisplay() {
    node
    .attr("r", forceProperties.collide.radius)
    .attr("stroke", forceProperties.charge.strength > 0 ? "blue" : "red")
    .attr("stroke-width", forceProperties.charge.enabled==false ? 0 : Math.abs(forceProperties.charge.strength)/15);

    link
    .attr("stroke-width", forceProperties.link.enabled ? 1 : .5)
    .attr("opacity", forceProperties.link.enabled ? 1 : 0);
    }
    // convenience function to update everything
    function updateAll() {
    updateForces();
    updateDisplay();
    }

    // load the data then generate the svg objects and force simulation
    d3.json("miserables.json", function(error, _graph) {
    if (error) throw error;
    graph = _graph;

    //////////// DISPLAY ////////////

    // generate the svg objects and force simulation
    function initializeDisplay() {
    // set the data and properties of link lines
    link = svg.append("g")
    .attr("class", "links")
    @@ -133,13 +139,22 @@ d3.json("miserables.json", function(error, _graph) {
    .text(function(d) { return d.id; });
    // visualize the graph
    updateDisplay();
    }

    // set up the simulation and event to update locations after each tick
    simulation.nodes(graph.nodes);
    initializeForces();
    simulation.on("tick", ticked);
    // update the display based on the forces (but not positions)
    function updateDisplay() {
    node
    .attr("r", forceProperties.collide.radius)
    .attr("stroke", forceProperties.charge.strength > 0 ? "blue" : "red")
    .attr("stroke-width", forceProperties.charge.enabled==false ? 0 : Math.abs(forceProperties.charge.strength)/15);

    function ticked() {
    link
    .attr("stroke-width", forceProperties.link.enabled ? 1 : .5)
    .attr("opacity", forceProperties.link.enabled ? 1 : 0);
    }

    // update the display positions after each simulation tick
    function ticked() {
    link
    .attr("x1", function(d) { return d.source.x; })
    .attr("y1", function(d) { return d.source.y; })
    @@ -149,11 +164,12 @@ d3.json("miserables.json", function(error, _graph) {
    node
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
    }
    });
    }



    // drag-drop events
    //////////// UI EVENTS ////////////

    function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    @@ -176,4 +192,10 @@ d3.select(window).on("resize", function(){
    width = +svg.node().getBoundingClientRect().width;
    height = +svg.node().getBoundingClientRect().height;
    updateForces();
    });
    });

    // convenience function to update everything (run after UI input)
    function updateAll() {
    updateForces();
    updateDisplay();
    }
  7. steveharoz revised this gist Oct 7, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion code.js
    Original file line number Diff line number Diff line change
    @@ -79,7 +79,7 @@ function updateForces() {
    .strength(forceProperties.forceY.strength * forceProperties.forceY.enabled)
    .y(height * forceProperties.forceY.y);
    simulation.force("link")
    .id(d => d.id)
    .id(function(d) {return d.id;})
    .distance(forceProperties.link.distance)
    .iterations(forceProperties.link.iterations)
    .links(forceProperties.link.enabled ? graph.links : []);
  8. steveharoz revised this gist Oct 7, 2016. 2 changed files with 8 additions and 7 deletions.
    6 changes: 3 additions & 3 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -25,7 +25,7 @@ forceProperties = {
    enabled: true,
    strength: .7,
    iterations: 1,
    radius: 10
    radius: 5
    },
    forceX: {
    enabled: false,
    @@ -86,12 +86,12 @@ function updateForces() {

    // updates ugnored until this is run
    // restarts the simulation (important if simulation has already slowed down)
    simulation.alphaTarget(.3).alpha(1).restart();
    simulation.alpha(1).restart();
    }
    // update the display based on the forces
    function updateDisplay() {
    node
    .attr("r", forceProperties.collide.radius/2)
    .attr("r", forceProperties.collide.radius)
    .attr("stroke", forceProperties.charge.strength > 0 ? "blue" : "red")
    .attr("stroke-width", forceProperties.charge.enabled==false ? 0 : Math.abs(forceProperties.charge.strength)/15);

    9 changes: 5 additions & 4 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -78,17 +78,17 @@
    <label>
    strength
    <output id="collide_StrengthSliderOutput">.7</output>
    <input type="range" min="0" max="50" value=".7" step=".1" oninput="d3.select('#collide_StrengthSliderOutput').text(value); forceProperties.collide.strength=value; updateAll();">
    <input type="range" min="0" max="2" value=".7" step=".1" oninput="d3.select('#collide_StrengthSliderOutput').text(value); forceProperties.collide.strength=value; updateAll();">
    </label>
    <label title="Size of nodes">
    radius
    <output id="collide_radiusSliderOutput">10</output>
    <input type="range" min="0" max="100" value="10" step="1" oninput="d3.select('#collide_radiusSliderOutput').text(value); forceProperties.collide.radius=value; updateAll();">
    <output id="collide_radiusSliderOutput">5</output>
    <input type="range" min="0" max="100" value="5" step="1" oninput="d3.select('#collide_radiusSliderOutput').text(value); forceProperties.collide.radius=value; updateAll();">
    </label>
    <label title="Higher values increase rigidity of the nodes (WARNING: high values are computationally expensive)">
    iterations
    <output id="collide_iterationsSliderOutput">1</output>
    <input type="range" min="0" max="10" value="1" step="1" oninput="d3.select('#collide_iterationsSliderOutput').text(value); forceProperties.collide.iterations=value; updateAll();">
    <input type="range" min="1" max="10" value="1" step="1" oninput="d3.select('#collide_iterationsSliderOutput').text(value); forceProperties.collide.iterations=value; updateAll();">
    </label>
    </div>

    @@ -136,6 +136,7 @@
    </div>
    <svg></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/d3-force.v1.min.js"></script> <!-- use the latest d3-force -->
    <script src="code.js"></script>
    </body>
    </html>
  9. steveharoz revised this gist Oct 7, 2016. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@
    width: 100%;
    margin: 0; padding: 0;
    display: flex;
    font-family: sans-serif; font-size: 80%; }
    font-family: sans-serif; font-size: 75%; }
    .controls {
    flex-basis: 200px;
    padding: 0 5px;
    @@ -18,7 +18,7 @@
    padding: 5px;
    margin: 5px 0;
    }
    .controls .force p label { margin-right: .5em; font-size: 125%; font-weight: bold;}
    .controls .force p label { margin-right: .5em; font-size: 120%; font-weight: bold;}
    .controls .force p { margin-top: 0;}
    .controls .force label { display: inline-block; }
    .controls input[type="checkbox"] { transform: scale(1.2, 1.2); }
  10. steveharoz revised this gist Oct 7, 2016. 2 changed files with 11 additions and 4 deletions.
    8 changes: 5 additions & 3 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -39,7 +39,8 @@ forceProperties = {
    },
    link: {
    enabled: true,
    distance: 30
    distance: 30,
    iterations: 1
    }
    }

    @@ -80,11 +81,12 @@ function updateForces() {
    simulation.force("link")
    .id(d => d.id)
    .distance(forceProperties.link.distance)
    .iterations(forceProperties.link.iterations)
    .links(forceProperties.link.enabled ? graph.links : []);

    // updates ugnored until this is run
    // "reheats" the simulation (important if simulation has already slowed down)
    simulation.alphaTarget(0.3).restart();
    // restarts the simulation (important if simulation has already slowed down)
    simulation.alphaTarget(.3).alpha(1).restart();
    }
    // update the display based on the forces
    function updateDisplay() {
    7 changes: 6 additions & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -121,12 +121,17 @@
    </div>

    <div class="force">
    <p><label><input type="checkbox" checked onchange="forceProperties.link.enabled = this.checked; updateAll();"> link</label> Tries to set length of links</p>
    <p><label><input type="checkbox" checked onchange="forceProperties.link.enabled = this.checked; updateAll();"> link</label> Sets link length</p>
    <label title="The force will push/pull nodes to make links this long">
    distance
    <output id="link_DistanceSliderOutput">30</output>
    <input type="range" min="0" max="100" value="30" step="1" oninput="d3.select('#link_DistanceSliderOutput').text(value); forceProperties.link.distance=value; updateAll();">
    </label>
    <label title="Higher values increase rigidity of the links (WARNING: high values are computationally expensive)">
    iterations
    <output id="link_IterationsSliderOutput">1</output>
    <input type="range" min="1" max="10" value="1" step="1" oninput="d3.select('#link_IterationsSliderOutput').text(value); forceProperties.link.iterations=value; updateAll();">
    </label>
    </div>
    </div>
    <svg></svg>
  11. steveharoz revised this gist Oct 7, 2016. No changes.
  12. steveharoz revised this gist Oct 7, 2016. 6 changed files with 27 additions and 31 deletions.
    3 changes: 2 additions & 1 deletion .block
    Original file line number Diff line number Diff line change
    @@ -1,2 +1,3 @@
    license: gpl-3.0
    height: 600
    height: 1000
    scrolling: yes
    6 changes: 2 additions & 4 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    This example demonstrates applying [d3-drag](https://github.com/d3/d3-drag) to a [force-directed graph](/mbostock/f584aa36df54c451c94a9d0798caed35) computed using [d3-force](https://github.com/d3/d3-force). When the drag gesture starts, the targetted node is [fixed](https://github.com/d3/d3-force#simulation_fix) to the pointer; it is released when the gesture ends. In addition, the simulation is temporarily “heated” during interaction by setting the [target *alpha*](https://github.com/d3/d3-force#simulation_alphaTarget) to a non-zero value.
    This demo lets you interactively try various settings and combinations of forces for [d3-force](https://github.com/d3/d3-force).

    If desired, you could refine this technique by also fixing nodes on mouseover, and releasing them on mouseout. (However, if there’s an active drag gesture during mouseout, you wouldn’t want to release the node until the gesture finishes.)

    Compare to the [Canvas version](/mbostock/ad70335eeef6d167bc36fd3c04378048).
    It's based on [Mike Bostock's](https://bost.ocks.org/) example code for a [force directed graph](http://bl.ocks.org/mbostock/2675ff61ea5e063ede2b5d63c08020c7).
    7 changes: 4 additions & 3 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -45,20 +45,21 @@ forceProperties = {

    // add forces to the simulation
    function initializeForces() {
    // add forces and associate each with a name
    simulation
    .force("link", d3.forceLink().id(d => d.id))
    .force("link", d3.forceLink())
    .force("charge", d3.forceManyBody())
    .force("collide", d3.forceCollide())
    .force("center", d3.forceCenter())
    .force("forceX", d3.forceX())
    .force("forceY", d3.forceY());

    // apply properties to each of the forces
    updateForces();
    simulation.force("link").links(graph.links);
    }

    // apply new force properties
    function updateForces() {
    // get each force by name and update the properties
    simulation.force("center")
    .x(width * forceProperties.center.x)
    .y(height * forceProperties.center.y);
    42 changes: 19 additions & 23 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -2,30 +2,32 @@
    <head>
    <style>
    /* HTML styles */
    html{ height: 100%; width: 100%; }
    body{ height: 100%; width: 100%; margin: 0; padding: 0; display: flex; font-family: sans-serif; }
    html{ width: 100%; }
    body{
    width: 100%;
    margin: 0; padding: 0;
    display: flex;
    font-family: sans-serif; font-size: 80%; }
    .controls {
    flex-basis: 20%;
    min-width: 120px;
    height: 100%;
    padding: 5px;
    flex-basis: 200px;
    padding: 0 5px;
    }
    .controls .force {
    background-color:#eee;
    border-radius: 3px;
    padding: 5px;
    margin: 5px 0 5px 0;
    margin: 5px 0;
    }
    .controls .force h3 { margin-top:0; margin-bottom: 0.25em; }
    .controls .force p { margin-top: 0; }
    .controls .force p label { margin-right: .5em; font-size: 125%; font-weight: bold;}
    .controls .force p { margin-top: 0;}
    .controls .force label { display: inline-block; }
    .controls input[type="checkbox"] { transform: scale(1.2, 1.2); }
    .controls input[type="range"] { margin: 0 5% 0.5em 5%; width: 90%; }

    /* SVG styles */
    svg {
    flex-basis: 80%;
    flex-basis: 100%;
    min-width: 200px;
    height: 100%;
    }
    .links line {
    stroke: #aaa;
    @@ -39,8 +41,7 @@
    <body>
    <div class="controls">
    <div class="force">
    <h3>center</h3>
    <p>Shifts the view, so the graph is centered at this location.</p>
    <p><label>center</label> Shifts the view, so the graph is centered at this location.</p>
    <label>
    x
    <output id="center_XSliderOutput">.5</output>
    @@ -54,8 +55,7 @@ <h3>center</h3>
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.charge.enabled = this.checked; updateAll();"> charge</h3>
    <p>Attracts (+) or repels (-) nodes to/from each other.</p>
    <p><label><input type="checkbox" checked onchange="forceProperties.charge.enabled = this.checked; updateAll();"> charge</label> Attracts (+) or repels (-) nodes to/from each other.</p>
    <label title="Negative strength repels nodes. Positive strength attracts nodes.">
    strength
    <output id="charge_StrengthSliderOutput">-30</output>
    @@ -74,8 +74,7 @@ <h3><input type="checkbox" checked onchange="forceProperties.charge.enabled = th
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.collide.enabled = this.checked; updateAll();"> collide</h3>
    <p>Prevents nodes from overlapping</p>
    <p><label><input type="checkbox" checked onchange="forceProperties.collide.enabled = this.checked; updateAll();"> collide</label> Prevents nodes from overlapping</p>
    <label>
    strength
    <output id="collide_StrengthSliderOutput">.7</output>
    @@ -94,8 +93,7 @@ <h3><input type="checkbox" checked onchange="forceProperties.collide.enabled = t
    </div>

    <div class="force">
    <h3><input type="checkbox" onchange="forceProperties.forceX.enabled = this.checked; updateAll();"> forceX</h3>
    <p>Acts like gravity. Pulls all points towards an X location.</p>
    <p><label><input type="checkbox" onchange="forceProperties.forceX.enabled = this.checked; updateAll();"> forceX</label> Acts like gravity. Pulls all points towards an X location.</p>
    <label>
    strength
    <output id="forceX_StrengthSliderOutput">.1</output>
    @@ -109,8 +107,7 @@ <h3><input type="checkbox" onchange="forceProperties.forceX.enabled = this.check
    </div>

    <div class="force">
    <h3><input type="checkbox" onchange="forceProperties.forceY.enabled = this.checked; updateAll();"> forceY</h3>
    <p>Acts like gravity. Pulls all points towards a Y location.</p>
    <p><label><input type="checkbox" onchange="forceProperties.forceY.enabled = this.checked; updateAll();"> forceY</label> Acts like gravity. Pulls all points towards a Y location.</p>
    <label>
    strength
    <output id="forceY_StrengthSliderOutput">.1</output>
    @@ -124,8 +121,7 @@ <h3><input type="checkbox" onchange="forceProperties.forceY.enabled = this.check
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.link.enabled = this.checked; updateAll();"> link</h3>
    <p>Tries to set length of links</p>
    <p><label><input type="checkbox" checked onchange="forceProperties.link.enabled = this.checked; updateAll();"> link</label> Tries to set length of links</p>
    <label title="The force will push/pull nodes to make links this long">
    distance
    <output id="link_DistanceSliderOutput">30</output>
    Binary file modified preview.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    Binary file modified thumbnail.png

    Unable to render rich display

    Invalid image source.

  13. steveharoz revised this gist Oct 6, 2016. 2 changed files with 59 additions and 34 deletions.
    49 changes: 38 additions & 11 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -3,9 +3,11 @@ var svg = d3.select("svg"),
    width = +svg.node().getBoundingClientRect().width,
    height = +svg.node().getBoundingClientRect().height;

    // svg objects
    var link, node;
    // the data
    var graph;
    // force direction simulator
    // force simulator
    var simulation = d3.forceSimulation();
    // values for all forces
    forceProperties = {
    @@ -41,7 +43,7 @@ forceProperties = {
    }
    }


    // add forces to the simulation
    function initializeForces() {
    simulation
    .force("link", d3.forceLink().id(d => d.id))
    @@ -55,6 +57,7 @@ function initializeForces() {
    simulation.force("link").links(graph.links);
    }

    // apply new force properties
    function updateForces() {
    simulation.force("center")
    .x(width * forceProperties.center.x)
    @@ -82,31 +85,53 @@ function updateForces() {
    // "reheats" the simulation (important if simulation has already slowed down)
    simulation.alphaTarget(0.3).restart();
    }
    // update the display based on the forces
    function updateDisplay() {
    node
    .attr("r", forceProperties.collide.radius/2)
    .attr("stroke", forceProperties.charge.strength > 0 ? "blue" : "red")
    .attr("stroke-width", forceProperties.charge.enabled==false ? 0 : Math.abs(forceProperties.charge.strength)/15);

    link
    .attr("stroke-width", forceProperties.link.enabled ? 1 : .5)
    .attr("opacity", forceProperties.link.enabled ? 1 : 0);
    }
    // convenience function to update everything
    function updateAll() {
    updateForces();
    updateDisplay();
    }

    // load the data then generate the svg objects and force simulation
    d3.json("miserables.json", function(error, _graph) {
    if (error) throw error;
    graph = _graph;

    var link = svg.append("g")
    .attr("class", "links")
    // set the data and properties of link lines
    link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line");

    var node = svg.append("g")
    .attr("class", "nodes")
    // set the data and properties of node circles
    node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 2.5)
    .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));
    .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

    // node tooltip
    node.append("title")
    .text(function(d) { return d.id; });
    // visualize the graph
    updateDisplay();

    // set up the simulation and event to update locations after each tick
    simulation.nodes(graph.nodes);
    initializeForces();
    simulation.on("tick", ticked);
    @@ -124,6 +149,8 @@ d3.json("miserables.json", function(error, _graph) {
    }
    });


    // drag-drop events
    function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    44 changes: 21 additions & 23 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    <style>
    /* HTML styles */
    html{ height: 100%; width: 100%; }
    body{ height: 100%; width: 100%; margin: 0; padding: 0; display: flex; }
    body{ height: 100%; width: 100%; margin: 0; padding: 0; display: flex; font-family: sans-serif; }
    .controls {
    flex-basis: 20%;
    min-width: 120px;
    @@ -32,8 +32,6 @@
    }
    .nodes circle {
    pointer-events: all;
    stroke: none;
    stroke-width: 40px;
    }

    </style>
    @@ -46,92 +44,92 @@ <h3>center</h3>
    <label>
    x
    <output id="center_XSliderOutput">.5</output>
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#center_XSliderOutput').text(value); forceProperties.center.x=value; updateForces();">
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#center_XSliderOutput').text(value); forceProperties.center.x=value; updateAll();">
    </label>
    <label>
    y
    <output id="center_YSliderOutput">.5</output>
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#center_YSliderOutput').text(value); forceProperties.center.y=value; updateForces();">
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#center_YSliderOutput').text(value); forceProperties.center.y=value; updateAll();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.charge.enabled = this.checked; updateForces();"> charge</h3>
    <h3><input type="checkbox" checked onchange="forceProperties.charge.enabled = this.checked; updateAll();"> charge</h3>
    <p>Attracts (+) or repels (-) nodes to/from each other.</p>
    <label>
    <label title="Negative strength repels nodes. Positive strength attracts nodes.">
    strength
    <output id="charge_StrengthSliderOutput">-30</output>
    <input type="range" min="-200" max="1" value="-30" step=".1" oninput="d3.select('#charge_StrengthSliderOutput').text(value); forceProperties.charge.strength=value; updateForces();">
    <input type="range" min="-200" max="50" value="-30" step=".1" oninput="d3.select('#charge_StrengthSliderOutput').text(value); forceProperties.charge.strength=value; updateAll();">
    </label>
    <label title="Minimum distance where force is applied">
    distanceMin
    <output id="charge_distanceMinSliderOutput">1</output>
    <input type="range" min="0" max="50" value="1" step=".1" oninput="d3.select('#charge_distanceMinSliderOutput').text(value); forceProperties.charge.distanceMin=value; updateForces();">
    <input type="range" min="0" max="50" value="1" step=".1" oninput="d3.select('#charge_distanceMinSliderOutput').text(value); forceProperties.charge.distanceMin=value; updateAll();">
    </label>
    <label title="Maximum distance where force is applied">
    distanceMax
    <output id="charge_distanceMaxSliderOutput">2000</output>
    <input type="range" min="0" max="2000" value="2000" step=".1" oninput="d3.select('#charge_distanceMaxSliderOutput').text(value); forceProperties.charge.distanceMax=value; updateForces();">
    <input type="range" min="0" max="2000" value="2000" step=".1" oninput="d3.select('#charge_distanceMaxSliderOutput').text(value); forceProperties.charge.distanceMax=value; updateAll();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.collide.enabled = this.checked; updateForces();"> collide</h3>
    <h3><input type="checkbox" checked onchange="forceProperties.collide.enabled = this.checked; updateAll();"> collide</h3>
    <p>Prevents nodes from overlapping</p>
    <label>
    strength
    <output id="collide_StrengthSliderOutput">.7</output>
    <input type="range" min="0" max="50" value=".7" step=".1" oninput="d3.select('#collide_StrengthSliderOutput').text(value); forceProperties.collide.strength=value; updateForces();">
    <input type="range" min="0" max="50" value=".7" step=".1" oninput="d3.select('#collide_StrengthSliderOutput').text(value); forceProperties.collide.strength=value; updateAll();">
    </label>
    <label title="Size of nodes">
    radius
    <output id="collide_radiusSliderOutput">10</output>
    <input type="range" min="0" max="100" value="10" step="1" oninput="d3.select('#collide_radiusSliderOutput').text(value); forceProperties.collide.radius=value; updateForces();">
    <input type="range" min="0" max="100" value="10" step="1" oninput="d3.select('#collide_radiusSliderOutput').text(value); forceProperties.collide.radius=value; updateAll();">
    </label>
    <label title="Higher values increase rigidity of the nodes (WARNING: high values are computationally expensive)">
    iterations
    <output id="collide_iterationsSliderOutput">1</output>
    <input type="range" min="0" max="10" value="1" step="1" oninput="d3.select('#collide_iterationsSliderOutput').text(value); forceProperties.collide.iterations=value; updateForces();">
    <input type="range" min="0" max="10" value="1" step="1" oninput="d3.select('#collide_iterationsSliderOutput').text(value); forceProperties.collide.iterations=value; updateAll();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" onchange="forceProperties.forceX.enabled = this.checked; updateForces();"> forceX</h3>
    <h3><input type="checkbox" onchange="forceProperties.forceX.enabled = this.checked; updateAll();"> forceX</h3>
    <p>Acts like gravity. Pulls all points towards an X location.</p>
    <label>
    strength
    <output id="forceX_StrengthSliderOutput">.1</output>
    <input type="range" min="0" max="1" value=".1" step="0.01" oninput="d3.select('#forceX_StrengthSliderOutput').text(value); forceProperties.forceX.strength=value; updateForces();">
    <input type="range" min="0" max="1" value=".1" step="0.01" oninput="d3.select('#forceX_StrengthSliderOutput').text(value); forceProperties.forceX.strength=value; updateAll();">
    </label>
    <label title="The X location that the force will push the nodes to (NOTE: This demo multiplies by the svg width)">
    x
    <output id="forceX_XSliderOutput">.5</output>
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#forceX_XSliderOutput').text(value); forceProperties.forceX.x=value; updateForces();">
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#forceX_XSliderOutput').text(value); forceProperties.forceX.x=value; updateAll();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" onchange="forceProperties.forceY.enabled = this.checked; updateForces();"> forceY</h3>
    <h3><input type="checkbox" onchange="forceProperties.forceY.enabled = this.checked; updateAll();"> forceY</h3>
    <p>Acts like gravity. Pulls all points towards a Y location.</p>
    <label>
    strength
    <output id="forceY_StrengthSliderOutput">.1</output>
    <input type="range" min="0" max="1" value=".1" step="0.01" oninput="d3.select('#forceY_StrengthSliderOutput').text(value); forceProperties.forceY.strength=value; updateForces();">
    <input type="range" min="0" max="1" value=".1" step="0.01" oninput="d3.select('#forceY_StrengthSliderOutput').text(value); forceProperties.forceY.strength=value; updateAll();">
    </label>
    <label title="The Y location that the force will push the nodes to (NOTE: This demo multiplies by the svg height)">
    y
    <output id="forceY_YSliderOutput">.5</output>
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#forceY_YSliderOutput').text(value); forceProperties.forceY.y=value; updateForces();">
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#forceY_YSliderOutput').text(value); forceProperties.forceY.y=value; updateAll();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.link.enabled = this.checked; updateForces();"> link</h3>
    <h3><input type="checkbox" checked onchange="forceProperties.link.enabled = this.checked; updateAll();"> link</h3>
    <p>Tries to set length of links</p>
    <label title="The link length that the force will push/pull nodes to achieve">
    <label title="The force will push/pull nodes to make links this long">
    distance
    <output id="link_DistanceSliderOutput">30</output>
    <input type="range" min="0" max="100" value="30" step="1" oninput="d3.select('#link_DistanceSliderOutput').text(value); forceProperties.link.distance=value; updateForces();">
    <input type="range" min="0" max="100" value="30" step="1" oninput="d3.select('#link_DistanceSliderOutput').text(value); forceProperties.link.distance=value; updateAll();">
    </label>
    </div>
    </div>
  14. steveharoz revised this gist Oct 6, 2016. 2 changed files with 124 additions and 20 deletions.
    20 changes: 14 additions & 6 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ var svg = d3.select("svg"),
    var graph;
    // force direction simulator
    var simulation = d3.forceSimulation();
    // force properties
    // values for all forces
    forceProperties = {
    center: {
    x: 0.5,
    @@ -22,15 +22,16 @@ forceProperties = {
    collide: {
    enabled: true,
    strength: .7,
    iterations: 1,
    radius: 10
    },
    forceX: {
    enabled: true,
    enabled: false,
    strength: .1,
    x: .5
    },
    forceY: {
    enabled: true,
    enabled: false,
    strength: .1,
    y: .5
    },
    @@ -64,16 +65,22 @@ function updateForces() {
    .distanceMax(forceProperties.charge.distanceMax);
    simulation.force("collide")
    .strength(forceProperties.collide.strength * forceProperties.collide.enabled)
    .radius(forceProperties.collide.radius);
    .radius(forceProperties.collide.radius)
    .iterations(forceProperties.collide.iterations);
    simulation.force("forceX")
    .strength(forceProperties.forceX.strength * forceProperties.forceX.enabled)
    .x(width * forceProperties.forceX.x);
    simulation.force("forceY")
    .strength(forceProperties.forceY.strength * forceProperties.forceY.enabled)
    .y(height * forceProperties.forceY.y);

    simulation.force("link")
    .id(d => d.id)
    .distance(forceProperties.link.distance)
    .links(forceProperties.link.enabled ? graph.links : []);

    // updates ugnored until this is run
    simulation.nodes(graph.nodes);
    // "reheats" the simulation (important if simulation has already slowed down)
    simulation.alphaTarget(0.3).restart();
    }

    d3.json("miserables.json", function(error, _graph) {
    @@ -100,6 +107,7 @@ d3.json("miserables.json", function(error, _graph) {
    node.append("title")
    .text(function(d) { return d.id; });

    simulation.nodes(graph.nodes);
    initializeForces();
    simulation.on("tick", ticked);

    124 changes: 110 additions & 14 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,43 +1,139 @@
    <html>
    <head>
    <style>

    /* HTML styles */
    html{ height: 100%; width: 100%; }
    body{ height: 100%; width: 100%; margin: 0; padding: 0; display: flex; }
    .controls
    {
    .controls {
    flex-basis: 20%;
    min-width: 100px;
    min-width: 120px;
    height: 100%;
    padding: 5px;
    }
    .controls .force {
    background-color:#eee;
    border-radius: 3px;
    padding: 5px;
    margin: 5px 0 5px 0;
    }
    svg
    {
    .controls .force h3 { margin-top:0; margin-bottom: 0.25em; }
    .controls .force p { margin-top: 0; }
    .controls input[type="checkbox"] { transform: scale(1.2, 1.2); }
    .controls input[type="range"] { margin: 0 5% 0.5em 5%; width: 90%; }

    /* SVG styles */
    svg {
    flex-basis: 80%;
    min-width: 200px;
    height: 100%;
    }

    .links line {
    stroke: #aaa;
    }

    .nodes circle {
    pointer-events: all;
    stroke: none;
    stroke-width: 40px;
    }

    .controls input { width: 90%; }


    </style>
    </head>
    <body>
    <div class="controls">
    <label for="slider1">Volume:&nbsp;</label><output id="slider1output">3</output>
    <input type="range" min="0" max="5" value="3" oninput="d3.select('#slider1output').text(value);" id="slider1">
    <button onclick="updateForces();"> update </button>
    <div class="force">
    <h3>center</h3>
    <p>Shifts the view, so the graph is centered at this location.</p>
    <label>
    x
    <output id="center_XSliderOutput">.5</output>
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#center_XSliderOutput').text(value); forceProperties.center.x=value; updateForces();">
    </label>
    <label>
    y
    <output id="center_YSliderOutput">.5</output>
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#center_YSliderOutput').text(value); forceProperties.center.y=value; updateForces();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.charge.enabled = this.checked; updateForces();"> charge</h3>
    <p>Attracts (+) or repels (-) nodes to/from each other.</p>
    <label>
    strength
    <output id="charge_StrengthSliderOutput">-30</output>
    <input type="range" min="-200" max="1" value="-30" step=".1" oninput="d3.select('#charge_StrengthSliderOutput').text(value); forceProperties.charge.strength=value; updateForces();">
    </label>
    <label title="Minimum distance where force is applied">
    distanceMin
    <output id="charge_distanceMinSliderOutput">1</output>
    <input type="range" min="0" max="50" value="1" step=".1" oninput="d3.select('#charge_distanceMinSliderOutput').text(value); forceProperties.charge.distanceMin=value; updateForces();">
    </label>
    <label title="Maximum distance where force is applied">
    distanceMax
    <output id="charge_distanceMaxSliderOutput">2000</output>
    <input type="range" min="0" max="2000" value="2000" step=".1" oninput="d3.select('#charge_distanceMaxSliderOutput').text(value); forceProperties.charge.distanceMax=value; updateForces();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.collide.enabled = this.checked; updateForces();"> collide</h3>
    <p>Prevents nodes from overlapping</p>
    <label>
    strength
    <output id="collide_StrengthSliderOutput">.7</output>
    <input type="range" min="0" max="50" value=".7" step=".1" oninput="d3.select('#collide_StrengthSliderOutput').text(value); forceProperties.collide.strength=value; updateForces();">
    </label>
    <label title="Size of nodes">
    radius
    <output id="collide_radiusSliderOutput">10</output>
    <input type="range" min="0" max="100" value="10" step="1" oninput="d3.select('#collide_radiusSliderOutput').text(value); forceProperties.collide.radius=value; updateForces();">
    </label>
    <label title="Higher values increase rigidity of the nodes (WARNING: high values are computationally expensive)">
    iterations
    <output id="collide_iterationsSliderOutput">1</output>
    <input type="range" min="0" max="10" value="1" step="1" oninput="d3.select('#collide_iterationsSliderOutput').text(value); forceProperties.collide.iterations=value; updateForces();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" onchange="forceProperties.forceX.enabled = this.checked; updateForces();"> forceX</h3>
    <p>Acts like gravity. Pulls all points towards an X location.</p>
    <label>
    strength
    <output id="forceX_StrengthSliderOutput">.1</output>
    <input type="range" min="0" max="1" value=".1" step="0.01" oninput="d3.select('#forceX_StrengthSliderOutput').text(value); forceProperties.forceX.strength=value; updateForces();">
    </label>
    <label title="The X location that the force will push the nodes to (NOTE: This demo multiplies by the svg width)">
    x
    <output id="forceX_XSliderOutput">.5</output>
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#forceX_XSliderOutput').text(value); forceProperties.forceX.x=value; updateForces();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" onchange="forceProperties.forceY.enabled = this.checked; updateForces();"> forceY</h3>
    <p>Acts like gravity. Pulls all points towards a Y location.</p>
    <label>
    strength
    <output id="forceY_StrengthSliderOutput">.1</output>
    <input type="range" min="0" max="1" value=".1" step="0.01" oninput="d3.select('#forceY_StrengthSliderOutput').text(value); forceProperties.forceY.strength=value; updateForces();">
    </label>
    <label title="The Y location that the force will push the nodes to (NOTE: This demo multiplies by the svg height)">
    y
    <output id="forceY_YSliderOutput">.5</output>
    <input type="range" min="0" max="1" value=".5" step="0.01" oninput="d3.select('#forceY_YSliderOutput').text(value); forceProperties.forceY.y=value; updateForces();">
    </label>
    </div>

    <div class="force">
    <h3><input type="checkbox" checked onchange="forceProperties.link.enabled = this.checked; updateForces();"> link</h3>
    <p>Tries to set length of links</p>
    <label title="The link length that the force will push/pull nodes to achieve">
    distance
    <output id="link_DistanceSliderOutput">30</output>
    <input type="range" min="0" max="100" value="30" step="1" oninput="d3.select('#link_DistanceSliderOutput').text(value); forceProperties.link.distance=value; updateForces();">
    </label>
    </div>
    </div>
    <svg></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>
  15. steveharoz revised this gist Oct 5, 2016. 1 changed file with 48 additions and 16 deletions.
    64 changes: 48 additions & 16 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,38 @@ var svg = d3.select("svg"),
    var graph;
    // force direction simulator
    var simulation = d3.forceSimulation();
    // force properties
    forceProperties = {
    center: {
    x: 0.5,
    y: 0.5
    },
    charge: {
    enabled: true,
    strength: -30,
    distanceMin: 1,
    distanceMax: 2000
    },
    collide: {
    enabled: true,
    strength: .7,
    radius: 10
    },
    forceX: {
    enabled: true,
    strength: .1,
    x: .5
    },
    forceY: {
    enabled: true,
    strength: .1,
    y: .5
    },
    link: {
    enabled: true,
    distance: 30
    }
    }


    function initializeForces() {
    @@ -15,30 +47,30 @@ function initializeForces() {
    .force("charge", d3.forceManyBody())
    .force("collide", d3.forceCollide())
    .force("center", d3.forceCenter())
    .force("gravityX", d3.forceX())
    .force("gravityY", d3.forceY());
    .force("forceX", d3.forceX())
    .force("forceY", d3.forceY());

    updateForces();
    simulation.force("link").links(graph.links);
    }

    function updateForces() {
    simulation.force("center")
    .x(width * forceProperties.center.x)
    .y(height * forceProperties.center.y);
    simulation.force("charge")
    .strength(-30)
    .distanceMin(1)
    .distanceMax(Infinity);
    .strength(forceProperties.charge.strength * forceProperties.charge.enabled)
    .distanceMin(forceProperties.charge.distanceMin)
    .distanceMax(forceProperties.charge.distanceMax);
    simulation.force("collide")
    .strength(.7)
    .radius(10);
    simulation.force("center")
    .x(width / 2)
    .y(height / 2);
    simulation.force("gravityX")
    .strength(.1*0)
    .x(width / 2);
    simulation.force("gravityY")
    .strength(.1*0)
    .y(height / 2);
    .strength(forceProperties.collide.strength * forceProperties.collide.enabled)
    .radius(forceProperties.collide.radius);
    simulation.force("forceX")
    .strength(forceProperties.forceX.strength * forceProperties.forceX.enabled)
    .x(width * forceProperties.forceX.x);
    simulation.force("forceY")
    .strength(forceProperties.forceY.strength * forceProperties.forceY.enabled)
    .y(height * forceProperties.forceY.y);

    // updates ugnored until this is run
    simulation.nodes(graph.nodes);
  16. steveharoz revised this gist Oct 5, 2016. 2 changed files with 71 additions and 24 deletions.
    63 changes: 51 additions & 12 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -3,14 +3,50 @@ var svg = d3.select("svg"),
    width = +svg.node().getBoundingClientRect().width,
    height = +svg.node().getBoundingClientRect().height;

    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))
    .force("collide", d3.forceCollide(0));
    // the data
    var graph;
    // force direction simulator
    var simulation = d3.forceSimulation();

    d3.json("miserables.json", function(error, graph) {

    function initializeForces() {
    simulation
    .force("link", d3.forceLink().id(d => d.id))
    .force("charge", d3.forceManyBody())
    .force("collide", d3.forceCollide())
    .force("center", d3.forceCenter())
    .force("gravityX", d3.forceX())
    .force("gravityY", d3.forceY());

    updateForces();
    simulation.force("link").links(graph.links);
    }

    function updateForces() {
    simulation.force("charge")
    .strength(-30)
    .distanceMin(1)
    .distanceMax(Infinity);
    simulation.force("collide")
    .strength(.7)
    .radius(10);
    simulation.force("center")
    .x(width / 2)
    .y(height / 2);
    simulation.force("gravityX")
    .strength(.1*0)
    .x(width / 2);
    simulation.force("gravityY")
    .strength(.1*0)
    .y(height / 2);

    // updates ugnored until this is run
    simulation.nodes(graph.nodes);
    }

    d3.json("miserables.json", function(error, _graph) {
    if (error) throw error;
    graph = _graph;

    var link = svg.append("g")
    .attr("class", "links")
    @@ -32,12 +68,8 @@ d3.json("miserables.json", function(error, graph) {
    node.append("title")
    .text(function(d) { return d.id; });

    simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

    simulation.force("link")
    .links(graph.links);
    initializeForces();
    simulation.on("tick", ticked);

    function ticked() {
    link
    @@ -68,3 +100,10 @@ function dragended(d) {
    d.fx = null;
    d.fy = null;
    }

    // update size-related forces
    d3.select(window).on("resize", function(){
    width = +svg.node().getBoundingClientRect().width;
    height = +svg.node().getBoundingClientRect().height;
    updateForces();
    });
    32 changes: 20 additions & 12 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -2,15 +2,8 @@
    <head>
    <style>

    .links line {
    stroke: #aaa;
    }

    .nodes circle {
    pointer-events: all;
    stroke: none;
    stroke-width: 40px;
    }
    html{ height: 100%; width: 100%; }
    body{ height: 100%; width: 100%; margin: 0; padding: 0; display: flex; }
    .controls
    {
    flex-basis: 20%;
    @@ -24,13 +17,28 @@
    min-width: 200px;
    height: 100%;
    }
    html{ height: 100%; width: 100%; }
    body{ height: 100%; width: 100%; margin: 0; padding: 0; display: flex; }

    .links line {
    stroke: #aaa;
    }

    .nodes circle {
    pointer-events: all;
    stroke: none;
    stroke-width: 40px;
    }

    .controls input { width: 90%; }


    </style>
    </head>
    <body>
    <div class="controls"></div>
    <div class="controls">
    <label for="slider1">Volume:&nbsp;</label><output id="slider1output">3</output>
    <input type="range" min="0" max="5" value="3" oninput="d3.select('#slider1output').text(value);" id="slider1">
    <button onclick="updateForces();"> update </button>
    </div>
    <svg></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="code.js"></script>
  17. steveharoz revised this gist Oct 2, 2016. 2 changed files with 94 additions and 75 deletions.
    70 changes: 70 additions & 0 deletions code.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,70 @@

    var svg = d3.select("svg"),
    width = +svg.node().getBoundingClientRect().width,
    height = +svg.node().getBoundingClientRect().height;

    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))
    .force("collide", d3.forceCollide(0));

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

    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line");

    var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 2.5)
    .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

    node.append("title")
    .text(function(d) { return d.id; });

    simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

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

    function ticked() {
    link
    .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; });

    node
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
    }
    });

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

    function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
    }

    function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
    }
    99 changes: 24 additions & 75 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <!DOCTYPE html>
    <meta charset="utf-8">
    <html>
    <head>
    <style>

    .links line {
    @@ -11,79 +11,28 @@
    stroke: none;
    stroke-width: 40px;
    }

    </style>
    <svg width="960" height="600"></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script>

    var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

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

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

    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line");

    var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 2.5)
    .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

    node.append("title")
    .text(function(d) { return d.id; });

    simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

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

    function ticked() {
    link
    .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; });

    node
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
    }
    });

    function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
    .controls
    {
    flex-basis: 20%;
    min-width: 100px;
    height: 100%;
    background-color:#eee;
    }

    function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
    svg
    {
    flex-basis: 80%;
    min-width: 200px;
    height: 100%;
    }
    html{ height: 100%; width: 100%; }
    body{ height: 100%; width: 100%; margin: 0; padding: 0; display: flex; }

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

    </script>
    </style>
    </head>
    <body>
    <div class="controls"></div>
    <svg></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="code.js"></script>
    </body>
    </html>
  18. @mbostock mbostock revised this gist Jul 4, 2016. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -76,8 +76,8 @@
    }

    function dragged(d) {
    d.fx = d.event.x;
    d.fy = d.event.y;
    d.fx = d3.event.x;
    d.fy = d3.event.y;
    }

    function dragended(d) {
  19. @mbostock mbostock revised this gist Jul 4, 2016. 1 changed file with 8 additions and 5 deletions.
    13 changes: 8 additions & 5 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@

    </style>
    <svg width="960" height="600"></svg>
    <script src="https://d3js.org/d3.v4.0.0-alpha.40.min.js"></script>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script>

    var svg = d3.select("svg"),
    @@ -70,17 +70,20 @@
    });

    function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart()
    simulation.fix(d);
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
    }

    function dragged(d) {
    simulation.fix(d, d3.event.x, d3.event.y);
    d.fx = d.event.x;
    d.fy = d.event.y;
    }

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

    </script>
  20. @mbostock mbostock revised this gist May 15, 2016. 1 changed file with 13 additions and 11 deletions.
    24 changes: 13 additions & 11 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -7,23 +7,23 @@
    }

    .nodes circle {
    stroke: #fff;
    stroke-width: 2px;
    pointer-events: all;
    stroke: none;
    stroke-width: 40px;
    }

    </style>
    <svg width="960" height="600"></svg>
    <script src="https://d3js.org/d3.v4.0.0-alpha.37.min.js"></script>
    <script src="https://d3js.org/d3.v4.0.0-alpha.40.min.js"></script>
    <script>

    var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    radius = 5;
    height = +svg.attr("height");

    var simulation = d3.forceSimulation()
    .force("charge", d3.forceManyBody().strength(-150))
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

    d3.json("miserables.json", function(error, graph) {
    @@ -32,15 +32,15 @@
    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .data(graph.links)
    .enter().append("line");

    var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", radius)
    .attr("r", 2.5)
    .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    @@ -70,15 +70,17 @@
    });

    function dragstarted(d) {
    simulation.fix(d).alphaTarget(0.3).restart();
    if (!d3.event.active) simulation.alphaTarget(0.3).restart()
    simulation.fix(d);
    }

    function dragged(d) {
    simulation.fix(d, d3.event.x, d3.event.y);
    }

    function dragended(d) {
    simulation.unfix(d).alphaTarget(0);
    if (!d3.event.active) simulation.alphaTarget(0);
    simulation.unfix(d);
    }

    </script>
  21. @mbostock mbostock revised this gist May 11, 2016. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    This example demonstrates applying [d3-drag](https://github.com/d3/d3-drag) to a [force-directed graph](/mbostock/f584aa36df54c451c94a9d0798caed35) computed using [d3-force](https://github.com/d3/d3-force). When the drag gesture starts, the targetted node is [fixed](https://github.com/d3/d3-force#simulation_fix) to the pointer; it is released when the gesture ends. In addition, the simulation is temporarily “heated” during interaction by setting the [target *alpha*](https://github.com/d3/d3-force#simulation_alphaTarget) to a non-zero value.

    If desired, you could refine this technique by also fixing nodes on mouseover, and releasing them on mouseout. (However, if there’s an active drag gesture during mouseout, you wouldn’t want to release the node until the gesture finishes.)

    Compare to the [Canvas version](/mbostock/ad70335eeef6d167bc36fd3c04378048).
  22. @mbostock mbostock revised this gist May 11, 2016. 2 changed files with 0 additions and 0 deletions.
    Binary file added preview.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    Binary file added thumbnail.png

    Unable to render rich display

    Invalid image source.

  23. @mbostock mbostock created this gist May 11, 2016.
    2 changes: 2 additions & 0 deletions .block
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    license: gpl-3.0
    height: 600
    3 changes: 3 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    This example demonstrates applying [d3-drag](https://github.com/d3/d3-drag) to a [force-directed graph](/mbostock/f584aa36df54c451c94a9d0798caed35) computed using [d3-force](https://github.com/d3/d3-force). When the drag gesture starts, the targetted node is [fixed](https://github.com/d3/d3-force#simulation_fix) to the pointer; it is released when the gesture ends. In addition, the simulation is temporarily “heated” during interaction by setting the [target *alpha*](https://github.com/d3/d3-force#simulation_alphaTarget) to a non-zero value.

    If desired, you could refine this technique by also fixing nodes on mouseover, and releasing them on mouseout. (However, if there’s an active drag gesture during mouseout, you wouldn’t want to release the node until the gesture finishes.)
    84 changes: 84 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    <!DOCTYPE html>
    <meta charset="utf-8">
    <style>

    .links line {
    stroke: #aaa;
    }

    .nodes circle {
    stroke: #fff;
    stroke-width: 2px;
    }

    </style>
    <svg width="960" height="600"></svg>
    <script src="https://d3js.org/d3.v4.0.0-alpha.37.min.js"></script>
    <script>

    var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    radius = 5;

    var simulation = d3.forceSimulation()
    .force("charge", d3.forceManyBody().strength(-150))
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("center", d3.forceCenter(width / 2, height / 2));

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

    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line");

    var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", radius)
    .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

    node.append("title")
    .text(function(d) { return d.id; });

    simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

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

    function ticked() {
    link
    .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; });

    node
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
    }
    });

    function dragstarted(d) {
    simulation.fix(d).alphaTarget(0.3).restart();
    }

    function dragged(d) {
    simulation.fix(d, d3.event.x, d3.event.y);
    }

    function dragended(d) {
    simulation.unfix(d).alphaTarget(0);
    }

    </script>
    337 changes: 337 additions & 0 deletions miserables.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,337 @@
    {
    "nodes": [
    {"id": "Myriel", "group": 1},
    {"id": "Napoleon", "group": 1},
    {"id": "Mlle.Baptistine", "group": 1},
    {"id": "Mme.Magloire", "group": 1},
    {"id": "CountessdeLo", "group": 1},
    {"id": "Geborand", "group": 1},
    {"id": "Champtercier", "group": 1},
    {"id": "Cravatte", "group": 1},
    {"id": "Count", "group": 1},
    {"id": "OldMan", "group": 1},
    {"id": "Labarre", "group": 2},
    {"id": "Valjean", "group": 2},
    {"id": "Marguerite", "group": 3},
    {"id": "Mme.deR", "group": 2},
    {"id": "Isabeau", "group": 2},
    {"id": "Gervais", "group": 2},
    {"id": "Tholomyes", "group": 3},
    {"id": "Listolier", "group": 3},
    {"id": "Fameuil", "group": 3},
    {"id": "Blacheville", "group": 3},
    {"id": "Favourite", "group": 3},
    {"id": "Dahlia", "group": 3},
    {"id": "Zephine", "group": 3},
    {"id": "Fantine", "group": 3},
    {"id": "Mme.Thenardier", "group": 4},
    {"id": "Thenardier", "group": 4},
    {"id": "Cosette", "group": 5},
    {"id": "Javert", "group": 4},
    {"id": "Fauchelevent", "group": 0},
    {"id": "Bamatabois", "group": 2},
    {"id": "Perpetue", "group": 3},
    {"id": "Simplice", "group": 2},
    {"id": "Scaufflaire", "group": 2},
    {"id": "Woman1", "group": 2},
    {"id": "Judge", "group": 2},
    {"id": "Champmathieu", "group": 2},
    {"id": "Brevet", "group": 2},
    {"id": "Chenildieu", "group": 2},
    {"id": "Cochepaille", "group": 2},
    {"id": "Pontmercy", "group": 4},
    {"id": "Boulatruelle", "group": 6},
    {"id": "Eponine", "group": 4},
    {"id": "Anzelma", "group": 4},
    {"id": "Woman2", "group": 5},
    {"id": "MotherInnocent", "group": 0},
    {"id": "Gribier", "group": 0},
    {"id": "Jondrette", "group": 7},
    {"id": "Mme.Burgon", "group": 7},
    {"id": "Gavroche", "group": 8},
    {"id": "Gillenormand", "group": 5},
    {"id": "Magnon", "group": 5},
    {"id": "Mlle.Gillenormand", "group": 5},
    {"id": "Mme.Pontmercy", "group": 5},
    {"id": "Mlle.Vaubois", "group": 5},
    {"id": "Lt.Gillenormand", "group": 5},
    {"id": "Marius", "group": 8},
    {"id": "BaronessT", "group": 5},
    {"id": "Mabeuf", "group": 8},
    {"id": "Enjolras", "group": 8},
    {"id": "Combeferre", "group": 8},
    {"id": "Prouvaire", "group": 8},
    {"id": "Feuilly", "group": 8},
    {"id": "Courfeyrac", "group": 8},
    {"id": "Bahorel", "group": 8},
    {"id": "Bossuet", "group": 8},
    {"id": "Joly", "group": 8},
    {"id": "Grantaire", "group": 8},
    {"id": "MotherPlutarch", "group": 9},
    {"id": "Gueulemer", "group": 4},
    {"id": "Babet", "group": 4},
    {"id": "Claquesous", "group": 4},
    {"id": "Montparnasse", "group": 4},
    {"id": "Toussaint", "group": 5},
    {"id": "Child1", "group": 10},
    {"id": "Child2", "group": 10},
    {"id": "Brujon", "group": 4},
    {"id": "Mme.Hucheloup", "group": 8}
    ],
    "links": [
    {"source": "Napoleon", "target": "Myriel", "value": 1},
    {"source": "Mlle.Baptistine", "target": "Myriel", "value": 8},
    {"source": "Mme.Magloire", "target": "Myriel", "value": 10},
    {"source": "Mme.Magloire", "target": "Mlle.Baptistine", "value": 6},
    {"source": "CountessdeLo", "target": "Myriel", "value": 1},
    {"source": "Geborand", "target": "Myriel", "value": 1},
    {"source": "Champtercier", "target": "Myriel", "value": 1},
    {"source": "Cravatte", "target": "Myriel", "value": 1},
    {"source": "Count", "target": "Myriel", "value": 2},
    {"source": "OldMan", "target": "Myriel", "value": 1},
    {"source": "Valjean", "target": "Labarre", "value": 1},
    {"source": "Valjean", "target": "Mme.Magloire", "value": 3},
    {"source": "Valjean", "target": "Mlle.Baptistine", "value": 3},
    {"source": "Valjean", "target": "Myriel", "value": 5},
    {"source": "Marguerite", "target": "Valjean", "value": 1},
    {"source": "Mme.deR", "target": "Valjean", "value": 1},
    {"source": "Isabeau", "target": "Valjean", "value": 1},
    {"source": "Gervais", "target": "Valjean", "value": 1},
    {"source": "Listolier", "target": "Tholomyes", "value": 4},
    {"source": "Fameuil", "target": "Tholomyes", "value": 4},
    {"source": "Fameuil", "target": "Listolier", "value": 4},
    {"source": "Blacheville", "target": "Tholomyes", "value": 4},
    {"source": "Blacheville", "target": "Listolier", "value": 4},
    {"source": "Blacheville", "target": "Fameuil", "value": 4},
    {"source": "Favourite", "target": "Tholomyes", "value": 3},
    {"source": "Favourite", "target": "Listolier", "value": 3},
    {"source": "Favourite", "target": "Fameuil", "value": 3},
    {"source": "Favourite", "target": "Blacheville", "value": 4},
    {"source": "Dahlia", "target": "Tholomyes", "value": 3},
    {"source": "Dahlia", "target": "Listolier", "value": 3},
    {"source": "Dahlia", "target": "Fameuil", "value": 3},
    {"source": "Dahlia", "target": "Blacheville", "value": 3},
    {"source": "Dahlia", "target": "Favourite", "value": 5},
    {"source": "Zephine", "target": "Tholomyes", "value": 3},
    {"source": "Zephine", "target": "Listolier", "value": 3},
    {"source": "Zephine", "target": "Fameuil", "value": 3},
    {"source": "Zephine", "target": "Blacheville", "value": 3},
    {"source": "Zephine", "target": "Favourite", "value": 4},
    {"source": "Zephine", "target": "Dahlia", "value": 4},
    {"source": "Fantine", "target": "Tholomyes", "value": 3},
    {"source": "Fantine", "target": "Listolier", "value": 3},
    {"source": "Fantine", "target": "Fameuil", "value": 3},
    {"source": "Fantine", "target": "Blacheville", "value": 3},
    {"source": "Fantine", "target": "Favourite", "value": 4},
    {"source": "Fantine", "target": "Dahlia", "value": 4},
    {"source": "Fantine", "target": "Zephine", "value": 4},
    {"source": "Fantine", "target": "Marguerite", "value": 2},
    {"source": "Fantine", "target": "Valjean", "value": 9},
    {"source": "Mme.Thenardier", "target": "Fantine", "value": 2},
    {"source": "Mme.Thenardier", "target": "Valjean", "value": 7},
    {"source": "Thenardier", "target": "Mme.Thenardier", "value": 13},
    {"source": "Thenardier", "target": "Fantine", "value": 1},
    {"source": "Thenardier", "target": "Valjean", "value": 12},
    {"source": "Cosette", "target": "Mme.Thenardier", "value": 4},
    {"source": "Cosette", "target": "Valjean", "value": 31},
    {"source": "Cosette", "target": "Tholomyes", "value": 1},
    {"source": "Cosette", "target": "Thenardier", "value": 1},
    {"source": "Javert", "target": "Valjean", "value": 17},
    {"source": "Javert", "target": "Fantine", "value": 5},
    {"source": "Javert", "target": "Thenardier", "value": 5},
    {"source": "Javert", "target": "Mme.Thenardier", "value": 1},
    {"source": "Javert", "target": "Cosette", "value": 1},
    {"source": "Fauchelevent", "target": "Valjean", "value": 8},
    {"source": "Fauchelevent", "target": "Javert", "value": 1},
    {"source": "Bamatabois", "target": "Fantine", "value": 1},
    {"source": "Bamatabois", "target": "Javert", "value": 1},
    {"source": "Bamatabois", "target": "Valjean", "value": 2},
    {"source": "Perpetue", "target": "Fantine", "value": 1},
    {"source": "Simplice", "target": "Perpetue", "value": 2},
    {"source": "Simplice", "target": "Valjean", "value": 3},
    {"source": "Simplice", "target": "Fantine", "value": 2},
    {"source": "Simplice", "target": "Javert", "value": 1},
    {"source": "Scaufflaire", "target": "Valjean", "value": 1},
    {"source": "Woman1", "target": "Valjean", "value": 2},
    {"source": "Woman1", "target": "Javert", "value": 1},
    {"source": "Judge", "target": "Valjean", "value": 3},
    {"source": "Judge", "target": "Bamatabois", "value": 2},
    {"source": "Champmathieu", "target": "Valjean", "value": 3},
    {"source": "Champmathieu", "target": "Judge", "value": 3},
    {"source": "Champmathieu", "target": "Bamatabois", "value": 2},
    {"source": "Brevet", "target": "Judge", "value": 2},
    {"source": "Brevet", "target": "Champmathieu", "value": 2},
    {"source": "Brevet", "target": "Valjean", "value": 2},
    {"source": "Brevet", "target": "Bamatabois", "value": 1},
    {"source": "Chenildieu", "target": "Judge", "value": 2},
    {"source": "Chenildieu", "target": "Champmathieu", "value": 2},
    {"source": "Chenildieu", "target": "Brevet", "value": 2},
    {"source": "Chenildieu", "target": "Valjean", "value": 2},
    {"source": "Chenildieu", "target": "Bamatabois", "value": 1},
    {"source": "Cochepaille", "target": "Judge", "value": 2},
    {"source": "Cochepaille", "target": "Champmathieu", "value": 2},
    {"source": "Cochepaille", "target": "Brevet", "value": 2},
    {"source": "Cochepaille", "target": "Chenildieu", "value": 2},
    {"source": "Cochepaille", "target": "Valjean", "value": 2},
    {"source": "Cochepaille", "target": "Bamatabois", "value": 1},
    {"source": "Pontmercy", "target": "Thenardier", "value": 1},
    {"source": "Boulatruelle", "target": "Thenardier", "value": 1},
    {"source": "Eponine", "target": "Mme.Thenardier", "value": 2},
    {"source": "Eponine", "target": "Thenardier", "value": 3},
    {"source": "Anzelma", "target": "Eponine", "value": 2},
    {"source": "Anzelma", "target": "Thenardier", "value": 2},
    {"source": "Anzelma", "target": "Mme.Thenardier", "value": 1},
    {"source": "Woman2", "target": "Valjean", "value": 3},
    {"source": "Woman2", "target": "Cosette", "value": 1},
    {"source": "Woman2", "target": "Javert", "value": 1},
    {"source": "MotherInnocent", "target": "Fauchelevent", "value": 3},
    {"source": "MotherInnocent", "target": "Valjean", "value": 1},
    {"source": "Gribier", "target": "Fauchelevent", "value": 2},
    {"source": "Mme.Burgon", "target": "Jondrette", "value": 1},
    {"source": "Gavroche", "target": "Mme.Burgon", "value": 2},
    {"source": "Gavroche", "target": "Thenardier", "value": 1},
    {"source": "Gavroche", "target": "Javert", "value": 1},
    {"source": "Gavroche", "target": "Valjean", "value": 1},
    {"source": "Gillenormand", "target": "Cosette", "value": 3},
    {"source": "Gillenormand", "target": "Valjean", "value": 2},
    {"source": "Magnon", "target": "Gillenormand", "value": 1},
    {"source": "Magnon", "target": "Mme.Thenardier", "value": 1},
    {"source": "Mlle.Gillenormand", "target": "Gillenormand", "value": 9},
    {"source": "Mlle.Gillenormand", "target": "Cosette", "value": 2},
    {"source": "Mlle.Gillenormand", "target": "Valjean", "value": 2},
    {"source": "Mme.Pontmercy", "target": "Mlle.Gillenormand", "value": 1},
    {"source": "Mme.Pontmercy", "target": "Pontmercy", "value": 1},
    {"source": "Mlle.Vaubois", "target": "Mlle.Gillenormand", "value": 1},
    {"source": "Lt.Gillenormand", "target": "Mlle.Gillenormand", "value": 2},
    {"source": "Lt.Gillenormand", "target": "Gillenormand", "value": 1},
    {"source": "Lt.Gillenormand", "target": "Cosette", "value": 1},
    {"source": "Marius", "target": "Mlle.Gillenormand", "value": 6},
    {"source": "Marius", "target": "Gillenormand", "value": 12},
    {"source": "Marius", "target": "Pontmercy", "value": 1},
    {"source": "Marius", "target": "Lt.Gillenormand", "value": 1},
    {"source": "Marius", "target": "Cosette", "value": 21},
    {"source": "Marius", "target": "Valjean", "value": 19},
    {"source": "Marius", "target": "Tholomyes", "value": 1},
    {"source": "Marius", "target": "Thenardier", "value": 2},
    {"source": "Marius", "target": "Eponine", "value": 5},
    {"source": "Marius", "target": "Gavroche", "value": 4},
    {"source": "BaronessT", "target": "Gillenormand", "value": 1},
    {"source": "BaronessT", "target": "Marius", "value": 1},
    {"source": "Mabeuf", "target": "Marius", "value": 1},
    {"source": "Mabeuf", "target": "Eponine", "value": 1},
    {"source": "Mabeuf", "target": "Gavroche", "value": 1},
    {"source": "Enjolras", "target": "Marius", "value": 7},
    {"source": "Enjolras", "target": "Gavroche", "value": 7},
    {"source": "Enjolras", "target": "Javert", "value": 6},
    {"source": "Enjolras", "target": "Mabeuf", "value": 1},
    {"source": "Enjolras", "target": "Valjean", "value": 4},
    {"source": "Combeferre", "target": "Enjolras", "value": 15},
    {"source": "Combeferre", "target": "Marius", "value": 5},
    {"source": "Combeferre", "target": "Gavroche", "value": 6},
    {"source": "Combeferre", "target": "Mabeuf", "value": 2},
    {"source": "Prouvaire", "target": "Gavroche", "value": 1},
    {"source": "Prouvaire", "target": "Enjolras", "value": 4},
    {"source": "Prouvaire", "target": "Combeferre", "value": 2},
    {"source": "Feuilly", "target": "Gavroche", "value": 2},
    {"source": "Feuilly", "target": "Enjolras", "value": 6},
    {"source": "Feuilly", "target": "Prouvaire", "value": 2},
    {"source": "Feuilly", "target": "Combeferre", "value": 5},
    {"source": "Feuilly", "target": "Mabeuf", "value": 1},
    {"source": "Feuilly", "target": "Marius", "value": 1},
    {"source": "Courfeyrac", "target": "Marius", "value": 9},
    {"source": "Courfeyrac", "target": "Enjolras", "value": 17},
    {"source": "Courfeyrac", "target": "Combeferre", "value": 13},
    {"source": "Courfeyrac", "target": "Gavroche", "value": 7},
    {"source": "Courfeyrac", "target": "Mabeuf", "value": 2},
    {"source": "Courfeyrac", "target": "Eponine", "value": 1},
    {"source": "Courfeyrac", "target": "Feuilly", "value": 6},
    {"source": "Courfeyrac", "target": "Prouvaire", "value": 3},
    {"source": "Bahorel", "target": "Combeferre", "value": 5},
    {"source": "Bahorel", "target": "Gavroche", "value": 5},
    {"source": "Bahorel", "target": "Courfeyrac", "value": 6},
    {"source": "Bahorel", "target": "Mabeuf", "value": 2},
    {"source": "Bahorel", "target": "Enjolras", "value": 4},
    {"source": "Bahorel", "target": "Feuilly", "value": 3},
    {"source": "Bahorel", "target": "Prouvaire", "value": 2},
    {"source": "Bahorel", "target": "Marius", "value": 1},
    {"source": "Bossuet", "target": "Marius", "value": 5},
    {"source": "Bossuet", "target": "Courfeyrac", "value": 12},
    {"source": "Bossuet", "target": "Gavroche", "value": 5},
    {"source": "Bossuet", "target": "Bahorel", "value": 4},
    {"source": "Bossuet", "target": "Enjolras", "value": 10},
    {"source": "Bossuet", "target": "Feuilly", "value": 6},
    {"source": "Bossuet", "target": "Prouvaire", "value": 2},
    {"source": "Bossuet", "target": "Combeferre", "value": 9},
    {"source": "Bossuet", "target": "Mabeuf", "value": 1},
    {"source": "Bossuet", "target": "Valjean", "value": 1},
    {"source": "Joly", "target": "Bahorel", "value": 5},
    {"source": "Joly", "target": "Bossuet", "value": 7},
    {"source": "Joly", "target": "Gavroche", "value": 3},
    {"source": "Joly", "target": "Courfeyrac", "value": 5},
    {"source": "Joly", "target": "Enjolras", "value": 5},
    {"source": "Joly", "target": "Feuilly", "value": 5},
    {"source": "Joly", "target": "Prouvaire", "value": 2},
    {"source": "Joly", "target": "Combeferre", "value": 5},
    {"source": "Joly", "target": "Mabeuf", "value": 1},
    {"source": "Joly", "target": "Marius", "value": 2},
    {"source": "Grantaire", "target": "Bossuet", "value": 3},
    {"source": "Grantaire", "target": "Enjolras", "value": 3},
    {"source": "Grantaire", "target": "Combeferre", "value": 1},
    {"source": "Grantaire", "target": "Courfeyrac", "value": 2},
    {"source": "Grantaire", "target": "Joly", "value": 2},
    {"source": "Grantaire", "target": "Gavroche", "value": 1},
    {"source": "Grantaire", "target": "Bahorel", "value": 1},
    {"source": "Grantaire", "target": "Feuilly", "value": 1},
    {"source": "Grantaire", "target": "Prouvaire", "value": 1},
    {"source": "MotherPlutarch", "target": "Mabeuf", "value": 3},
    {"source": "Gueulemer", "target": "Thenardier", "value": 5},
    {"source": "Gueulemer", "target": "Valjean", "value": 1},
    {"source": "Gueulemer", "target": "Mme.Thenardier", "value": 1},
    {"source": "Gueulemer", "target": "Javert", "value": 1},
    {"source": "Gueulemer", "target": "Gavroche", "value": 1},
    {"source": "Gueulemer", "target": "Eponine", "value": 1},
    {"source": "Babet", "target": "Thenardier", "value": 6},
    {"source": "Babet", "target": "Gueulemer", "value": 6},
    {"source": "Babet", "target": "Valjean", "value": 1},
    {"source": "Babet", "target": "Mme.Thenardier", "value": 1},
    {"source": "Babet", "target": "Javert", "value": 2},
    {"source": "Babet", "target": "Gavroche", "value": 1},
    {"source": "Babet", "target": "Eponine", "value": 1},
    {"source": "Claquesous", "target": "Thenardier", "value": 4},
    {"source": "Claquesous", "target": "Babet", "value": 4},
    {"source": "Claquesous", "target": "Gueulemer", "value": 4},
    {"source": "Claquesous", "target": "Valjean", "value": 1},
    {"source": "Claquesous", "target": "Mme.Thenardier", "value": 1},
    {"source": "Claquesous", "target": "Javert", "value": 1},
    {"source": "Claquesous", "target": "Eponine", "value": 1},
    {"source": "Claquesous", "target": "Enjolras", "value": 1},
    {"source": "Montparnasse", "target": "Javert", "value": 1},
    {"source": "Montparnasse", "target": "Babet", "value": 2},
    {"source": "Montparnasse", "target": "Gueulemer", "value": 2},
    {"source": "Montparnasse", "target": "Claquesous", "value": 2},
    {"source": "Montparnasse", "target": "Valjean", "value": 1},
    {"source": "Montparnasse", "target": "Gavroche", "value": 1},
    {"source": "Montparnasse", "target": "Eponine", "value": 1},
    {"source": "Montparnasse", "target": "Thenardier", "value": 1},
    {"source": "Toussaint", "target": "Cosette", "value": 2},
    {"source": "Toussaint", "target": "Javert", "value": 1},
    {"source": "Toussaint", "target": "Valjean", "value": 1},
    {"source": "Child1", "target": "Gavroche", "value": 2},
    {"source": "Child2", "target": "Gavroche", "value": 2},
    {"source": "Child2", "target": "Child1", "value": 3},
    {"source": "Brujon", "target": "Babet", "value": 3},
    {"source": "Brujon", "target": "Gueulemer", "value": 3},
    {"source": "Brujon", "target": "Thenardier", "value": 3},
    {"source": "Brujon", "target": "Gavroche", "value": 1},
    {"source": "Brujon", "target": "Eponine", "value": 1},
    {"source": "Brujon", "target": "Claquesous", "value": 1},
    {"source": "Brujon", "target": "Montparnasse", "value": 1},
    {"source": "Mme.Hucheloup", "target": "Bossuet", "value": 1},
    {"source": "Mme.Hucheloup", "target": "Joly", "value": 1},
    {"source": "Mme.Hucheloup", "target": "Grantaire", "value": 1},
    {"source": "Mme.Hucheloup", "target": "Bahorel", "value": 1},
    {"source": "Mme.Hucheloup", "target": "Courfeyrac", "value": 1},
    {"source": "Mme.Hucheloup", "target": "Gavroche", "value": 1},
    {"source": "Mme.Hucheloup", "target": "Enjolras", "value": 1}
    ]
    }