block by fil 18ef9696f217cd242f6fb8ec776dc3e3

Circular Vortex Emergence

Full Screen

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. We add another force that speeds up slow particles and slows down the fastest…

Click to add a central barrier.

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

forked from Fil‘s block: Boltzmann not ideal gas

forked from Fil‘s block: Boltzmann not ideal gas [UNLISTED]

forked from Fil‘s block: Vortex

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.js"></script>
<script>

var width = 400,
    height = 400,
    τ = 2 * Math.PI;

var nodes = d3.range(1000).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(3).strength(0.7))
    .force("bounce-on-container", function () {
        for (var i = 0, n = nodes.length, node; i < n; ++i) {
            node = nodes[i];
            var dx = node.x / width - 1 / 2,
                dy = node.y / height - 1 / 2,
                d2 = dx * dx + dy * dy;
            if (d2 > 0.16) {
                var angle = Math.atan2(dy, dx),
                    angle1 = Math.atan2(node.vy, node.vx),
                    angle2 = Math.PI - angle1 + 2 * angle,
                    norm = Math.sqrt(node.vx*node.vx + node.vy*node.vy);
                node.vx = norm * Math.cos(angle2);
                node.vy = norm * Math.sin(angle2);
            }

        }
    })

    .force("internal-barrier", function (alpha) {
      if (!force.barrier) return;
        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;

            if (dx0 * dx1 < 0 && Math.abs(dy0) > aperture(alpha) ) {
              node.vx *= -1;
            }
        }
    })
    .force("limit-speed", function (alpha) {
      
      for (var i = 0, n = nodes.length, node; i < n; ++i) {
        node = nodes[i];
        var E = 1 + node.vx*node.vx + node.vy*node.vy,
            mult = 1 + 0.03 * Math.log(6/E);
          node.vx *= mult;
          node.vy *= mult;
      }
    })
    .on("tick", ticked)
    .alphaDecay(0.01)
    .alphaMin(0)
    .velocityDecay(0);

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.barrier = !force.barrier;
    force.alpha(1).restart();
}


var speeds1 = [], speeds2 = [], ct = 0;
  
var y = d3.scaleLinear()
   .range([100,0]);
  
var line = d3.line(),
    speedline1 = svg.append('path')
.attr('fill','none')
.attr('stroke', 'red'),
    speedline2 = svg.append('path')
.attr('fill','none')
.attr('stroke', 'green');
 
  
function ticked() {

    var speed1 = d3.mean(nodes
  .filter(function(n) {
    return n.x> width/2;
  })
  .map(function(n){
    return n.vx*n.vx + n.vy*n.vy;
  })),
      speed2 = d3.mean(nodes
  .filter(function(n) {
    return n.x < width/2;
  })
  .map(function(n){
    return n.vx*n.vx + n.vy*n.vy;
  }));
  if (ct++ > width) {speeds1.shift();speeds2.shift();}
  speeds1.push(speed1 || 0);
  speeds2.push(speed2 || 0)
  y.domain([0,d3.max(d3.merge([speeds1,speeds2]))]);
  speedline1.attr('d', line(speeds1.map(function(d,i){
    return [i,y(d)];
  })));
  speedline2.attr('d', line(speeds2.map(function(d,i){
    return [i,y(d)];
  })));


  context.clearRect(0, 0, width, height);

    context.strokeStyle = "#444";
    context.beginPath();
    // rectangle
    context.strokeStyle = "#fcc";
    context.beginPath();
    context.arc(width/2, height/2, width * 0.4 + 2, 0, τ);
    context.lineWidth = 6;
    context.stroke()
    // internal barrier
if (force.barrier){
    context.moveTo(width * 0.5, height * 0.1);
    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 = 3;
    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.5, 0, τ);
    }
    context.fillStyle = "#644";
    context.fill();

}

</script>