Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active April 25, 2017 00:01
Show Gist options
  • Save Fil/6ee077159ca7dc26b2b697064a541dee to your computer and use it in GitHub Desktop.
Save Fil/6ee077159ca7dc26b2b697064a541dee to your computer and use it in GitHub Desktop.
Not an Ideal Gas
license: gpl-3.0
border: no
scrolling: no
height: 500

Trying to make sense of the energy parameters of a simple simulation. forceCollide tends to inject energy into the system, while velocityDecay() removes some.

So, when the density of dots is low, energy tends to decrease; if it's high, energy diverges.

forked from Fil's block: Brownian Motion Constrained in Rectangle

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.js"></script>
<script>
var width = 960,
height = 300,
τ = 2 * Math.PI;
var nodes = d3.range(800).map(function () {
var angle = Math.random() * 2 * Math.PI, radius = 60 * Math.sqrt(Math.random());
return {
x: width/4 + radius * Math.sin(angle),
y: height/2 + radius * Math.cos(angle),
vx: Math.sin(angle) * Math.random(),
vy: Math.cos(angle) * Math.random(),
};
});
var force = d3.forceSimulation()
.nodes(nodes)
.force("collide", d3.forceCollide(2))
.force("bounce-on-container", function () {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i];
var dx = (node.x + node.vx) / width - 1 / 2,
dy = (node.y + node.vy) / height - 1 / 2;
if (dx*dx > 0.16) {
node.vx *= -1;
}
if (dy*dy > 0.16) {
node.vy *= -1;
}
}
})
.force("internal-barrier", function (alpha) {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i];
var dx0 = (node.x) / width - 1 / 2,
dy0 = (node.y) / height - 1 / 2;
var dx1 = (node.x + node.vx) / width - 1 / 2,
dy1 = (node.y + node.vy) / height - 1 / 2;
if (dx0 * dx1 < 0 && Math.abs(dy0) > aperture(alpha) ) {
node.vx *= -1;
}
}
})
.on("tick", ticked)
.alphaDecay(0.01)
.alphaMin(0)
.velocityDecay(0.00001);
function aperture(alpha) {
return 0.2 / (1-Math.log(alpha));
}
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height)
.on('click', click);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", 100);
var context = canvas.node().getContext("2d");
function click() {
force.alpha(1).restart();
}
var speeds = [], ct = 0;
var y = d3.scaleLinear()
.range([100,0]);
var line = d3.area().y0(100),
speedline = svg.append('path')
.attr('fill','none')
.attr('stroke', 'black');
function ticked() {
var speed = d3.mean(nodes.map(function(n){
return n.vx*n.vx + n.vy*n.vy;
}));
if (ct++ > width) speeds.shift();
speeds.push(speed);
y.domain([0,d3.max(speeds)]);
speedline.attr('d', line(speeds.map(function(d,i){
return [i,y(d)];
})));
context.clearRect(0, 0, width, height);
context.strokeStyle = "#444";
context.beginPath();
// rectangle
context.moveTo(width * 0.1, height * 0.1);
context.lineTo(width * 0.1, height * 0.9);
context.lineTo(width * 0.9, height * 0.9);
context.lineTo(width * 0.9, height * 0.1);
context.lineTo(width * 0.1, height * 0.1);
context.moveTo(width * 0.5, height * 0.1);
// internal barrier
context.lineTo(width * 0.5, height * (0.5 - aperture(force.alpha())));
context.moveTo(width * 0.5, height * 0.9);
context.lineTo(width * 0.5, height * (0.5 + aperture(force.alpha())));
context.lineWidth = 1;
context.stroke()
context.beginPath();
for (var i = 0, n = nodes.length; i < n; ++i) {
var node = nodes[i];
context.moveTo(node.x, node.y);
context.arc(node.x, node.y, 1, 0, τ);
}
context.fillStyle = "#644";
context.fill();
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment