Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active September 13, 2019 01:32
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbostock/d6da1bb68556875396fd61b13ca9173c to your computer and use it in GitHub Desktop.
Save mbostock/d6da1bb68556875396fd61b13ca9173c to your computer and use it in GitHub Desktop.
Force GIF
{
"license": "GPL-3.0",
"author": {
"name": "Mike Bostock",
"url": "http://bost.ocks.org/mike"
},
"dependencies": {
"canvas": "^1.3.12",
"d3": "^3.5.16",
"d3-array": "^0.7.1",
"d3-force": "^0.3.0",
"d3-format": "^0.5.1"
}
}
#!/bin/bash
./render-v4
convert frame-0030.png -colors 16 palette.gif
convert -dither none -remap palette.gif frame-*.png v4-uncompressed.gif
gifsicle -O3 -d4 < v4-uncompressed.gif > v4.gif
./render-v3
convert -dither none -remap palette.gif frame-*.png v3-uncompressed.gif
gifsicle -O3 -d4 < v3-uncompressed.gif > v3.gif
rm -- palette.gif *.png *-uncompressed.gif
#!/usr/bin/env node
var Canvas = require("canvas"),
fs = require("fs"),
d3 = require("d3");
var width = 960,
height = 960,
canvas = new Canvas(width, height),
context = canvas.getContext("2d");
var nodes = d3.range(1000).map(function(i) {
return {
index: i
};
});
var links = d3.range(nodes.length - 1).map(function(i) {
return {
source: (Math.sqrt(i) | 0),
target: i + 1
};
});
var force = d3.layout.force()
.friction(0.7) // friction = 1 - drag
.nodes(nodes)
.links(links)
.charge(-20)
.linkDistance(0)
.on("tick", ticked)
.start();
var frame = 0,
formatFrame = d3.format("04d");
while (++frame <= 60) {
force.tick();
var name = "frame-" + formatFrame(frame) + ".png";
fs.writeFileSync(name, canvas.toBuffer());
console.log(name);
}
force.stop();
function ticked() {
context.fillStyle = "#fff";
context.fillRect(0, 0, width, height);
context.save();
context.translate(width / 2, height / 2);
context.beginPath();
links.forEach(drawLink);
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath();
nodes.forEach(drawNode);
context.fillStyle = "#000";
context.fill();
context.strokeStyle = "#fff";
context.stroke();
context.restore();
}
function drawLink(d) {
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
}
function drawNode(d) {
context.moveTo(d.x + 3, d.y);
context.arc(d.x, d.y, 3, 0, 2 * Math.PI);
}
#!/usr/bin/env node
var Canvas = require("canvas"),
fs = require("fs"),
d3_array = require("d3-array"),
d3_format = require("d3-format"),
d3_force = require("d3-force");
var width = 960,
height = 960,
canvas = new Canvas(width, height),
context = canvas.getContext("2d");
var nodes = d3_array.range(1000).map(function(i) {
return {
index: i
};
});
var links = d3_array.range(nodes.length - 1).map(function(i) {
return {
source: (Math.sqrt(i) | 0),
target: i + 1
};
});
var simulation = d3_force.forceSimulation(nodes)
.drag(0.2)
.force("charge", d3_force.forceManyBody().strength(-20))
.force("link", d3_force.forceLink(links).distance(0).strength(0.5))
.force("position", d3_force.forcePosition())
.stop();
var frame = 0,
formatFrame = d3_format.format("04d");
while (++frame <= 60) {
simulation.tick();
render(frame);
}
function render(frame) {
var name = "frame-" + formatFrame(frame) + ".png";
console.log(name);
context.fillStyle = "#fff";
context.fillRect(0, 0, width, height);
context.save();
context.translate(width / 2, height / 2);
context.beginPath();
links.forEach(drawLink);
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath();
nodes.forEach(drawNode);
context.fillStyle = "#000";
context.fill();
context.strokeStyle = "#fff";
context.stroke();
context.restore();
fs.writeFileSync(name, canvas.toBuffer());
}
function drawLink(d) {
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
}
function drawNode(d) {
context.moveTo(d.x + 3, d.y);
context.arc(d.x, d.y, 3, 0, 2 * Math.PI);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment