block by enjalot 6f9656f1ac033a1f4277

basic particle interaction

Full Screen

Built with blockbuilder.org

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
  <style>
    body { 
      margin:0;position:fixed;top:0;right:0;bottom:0;left:0; 
      font-family: Helvetica;
    }
    svg { width:100%; height: 100% }
    
    .controls {
      position: absolute;
      top: 30px;
      left: 20px;
    }

    input.steps {
    }
    
    .num {
      margin: 5px;
    }
    
    .current {
			width: 50px;
    }
  </style>
</head>

<body>
  <script>
    // Feel free to change or delete any of the code you see!
    var svg = d3.select("body").append("svg");
    
    var dt = 0.05;
    var numSteps = 60;
    
    var a = {
      index: 0,
      radius: 40,
      // initial position
      x: 152, y: 206,
      // initial velocity
      vx: 61, vy: 0,
      // initial acceleration
      ax: 8, ay: 0
      
    }
    var b =  {
      index: 1,
      radius: 40,
      // initial position
      x: 457, y: 251,
      // initial velocity
      vx: -69, vy: 0,
      // initial acceleration
      ax: 32, ay: -6
    }
    var nodes = [a,b];
    var steps = [nodes]; // the collection of states
    
    function step(nodes) {
      var newNodes = [];
      
      nodes.forEach(function(node) {
        // copy the node
        var newNode = {
          index: node.index,
          radius: node.radius
        }        
        var ax = node.ax;
        var ay = node.ay;
        
        // go through all the neighbors and update vx and other properties
        

        // update the velocity using latest accelerations
        var vx = node.vx + ax * dt;
        var vy = node.vy + ay * dt;
        
        var x = node.x + vx * dt;
        var y = node.y + vy * dt;
        // copy the updated state into the new node
        newNode.x = x;
        newNode.y = y;
        newNode.vx = vx;
        newNode.vy = vy;
        newNode.ax = ax;
        newNode.ay = ay;
        
        // we do collision detection on the positions directly
        nodes.forEach(function(neighbor){
          var xx = newNode.x - neighbor.x;
          var yy = newNode.y - neighbor.y;
          var dist = Math.sqrt(xx * xx + yy * yy);
          var r = node.radius + neighbor.radius;
          //console.log(dist, r)
          if (dist < r) {
            dist = (dist - r) / dist * dt; //don't quite understand this
            newNode.x -= xx *= dist;
            newNode.y -= yy *= dist;
            neighbor.x += xx;
            neighbor.y += yy;
          }
        })
        newNodes.push(newNode)
      })
      return newNodes;
    }
    
    d3.range(numSteps).forEach(function(i) {
      var lastState = steps[steps.length-1];
      console.log("lastState", i, lastState)
      var nextState = step(lastState);
      steps.push(nextState)
    })
    
    console.log("steps", steps)
    
    
    var gstep = svg.selectAll("g.step").data(steps)
    gstep.enter().append("g").classed("step", true)
    .style({
      "stroke-opacity": function(d,i) { return 1 - i/numSteps }
    })
    .selectAll("circle.node").data(function(d) { return d })
    .enter().append("circle").classed("node", true)
    .attr({
      r: function(d) { return d.radius },
      cx: function(d) { return d.x },
      cy: function(d) { return d.y },
      fill: "none",
      stroke: "#111",
      //"stroke-dasharray": "4 4"
    })
    
    var current = svg.append("g").classed("current", true);
    
    var controls = d3.select("body").append("div")
      .classed("controls", true)
    
    var curText = controls.append("div").text("step: ")
     .append("span").classed("current", true).text(0)
    
    controls
      .append("input")
      .classed("steps", true)
      .attr({
        type: "range",
        value: 0,
        min: 0,
        max: numSteps
      })
      .on("input", slide)
      .on("change", slide)
    var numText = controls.append("span").classed("num", true).text(numSteps)
    
    function slide() {
      var i = +this.value || 0;
      console.log(i);
      var state = steps[i];
      
      var circles = current.selectAll("circle.node").data(state)
      circles.enter().append("circle").classed("node", true)
      circles.attr({
        r: function(d) { return d.radius },
        cx: function(d) { return d.x },
        cy: function(d) { return d.y },
        fill: "#fff",
        "fill-opacity": 0.5,
        stroke: "#b41717",
      })
      
      curText.text(i)
    }
    
    slide();
    
  </script>
</body>