如何添加碰撞检测我的d3js粒子模拟器

时间:2016-10-21 13:58:58

标签: javascript d3.js

我试图理解如何将碰撞反应添加到我的粒子模拟器(从this fiddle开发)

See my current fiddle here

我只想检测具有reactantA和reactantB粒子类的粒子之间的碰撞,当它们碰撞时,将类更改为productC。但我不知道从哪里开始以有效的方式做到这一点。

// Basic control variables
var gridSize = 600; // The square size in pixels of the 2-d world
var numParticles = 150;
var epochTarget = 10;
var epochActual = 0;
var counter = 0;
var pType = 'reactantA';
var rx = 0;
var pRadius = 0;

var getXSpeed = function() {
  // Returns a number from -25 to -1 or 1 to 25
  return ((Math.random() > 0.5) ? -1 : 1) * ((Math.random() * 24) + 1);
};

var getYSpeed = function() {
  // Returns a number from 25-100
  return ((Math.random() * 75) - 50);
};

/*
 */
var particles = [];
for (var i = 0; i < numParticles; i++) {

  pType = "reactantA";
  pRadius = 8;
  rx = Math.floor((Math.random() * 10) + 1);
  if (rx > 5) {
    pType = "reactantB";
    pRadius = 5;
  }
  console.log(i, pType);
  particles.push({
    x: Math.floor(Math.random() * gridSize),
    y: Math.floor(Math.random() * gridSize),
    r: pRadius,
    key: counter++,
    type: pType,
    vx: getXSpeed(),
    vy: getYSpeed()
  });
}

// Create the initial structure of the game board (using SVG rectangles)
var svg = d3.select("#container").append("svg")
  .attr("height", gridSize)
  .attr("width", gridSize)
  .append("g");

// Redraw function is responsible for updating the state of the dom
var redraw = function(elapsed) {
  // Bind the data to the particles
  var particle = svg.selectAll("circle").data(particles, function(d) {
    return d.key;
  });

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

  // Enter

  particle.enter().append("circle")
    .attr("class", function(d) {
      return d.type;
    })
    .attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    })
    .attr("r", function(d) {
      return d.r;
    });

  particle.exit().remove();
};

/*
 */
var update = function(elapsed) {
  for (var j = 0; j < particles.length; j++) {
    var particle = particles[j];

    particle.x = particle.x + (elapsed / 1000) * particle.vx;
    particle.y = particle.y + (elapsed / 1000) * particle.vy;

    if (particle.y > gridSize - particle.r && particle.vy > 0) {
      particle.vy = particle.vy * -1;
    }
    if (particle.y < particle.r && particle.vy < 0) {
      particle.vy = particle.vy * -1;
    }
    if (particle.x > gridSize - particle.r && particle.vx > 0) {
      particle.vx = particle.vx * -1;
    }
    if (particle.x < particle.r && particle.vx < 0) {
      particle.vx = particle.vx * -1;
    }
    /* Particle is done, so recreate it
    if((particle.y > gridSize - 1) || (particle.x > gridSize - 1) || (particle.y < 1) || (particle.x < 1)  ) { 
        particle.x = Math.floor(Math.random() * gridSize);
        particle.y = Math.floor(Math.random() * ridSize);
        particle.key = counter++;
        particle.vx = getXSpeed();
        particle.vy = getYSpeed();
    }*/
  }
};

/*
/ This function will orchestrate the main game loop, incrementing the
/ current epoch, calling update and then calling redraw for each epoch.
*/
var doEpoch = function() {
  var dtg = new Date();
  var elapsed = dtg.getTime() - epochActual;

  update(elapsed);
  redraw(elapsed);

  epochActual = dtg.getTime();
  window.setTimeout(doEpoch, epochTarget);
};

// Add the click handler to the start button
d3.select("#start").on('click', function(d) {
  d3.select("#start").text("Running...");

  var dtg = new Date();
  epochActual = dtg.getTime();
  doEpoch();
});

1 个答案:

答案 0 :(得分:1)

所以这是解决问题的天真方法:

var update = function(elapsed) {
  for (var j = 0; j < particles.length; j++) {
    var particle = particles[j];

    particle.x = particle.x + (elapsed / 1000) * particle.vx;
    particle.y = particle.y + (elapsed / 1000) * particle.vy;

    if (particle.y > gridSize - particle.r && particle.vy > 0) {
      particle.vy = particle.vy * -1;
    }
    if (particle.y < particle.r && particle.vy < 0) {
      particle.vy = particle.vy * -1;
    }
    if (particle.x > gridSize - particle.r && particle.vx > 0) {
      particle.vx = particle.vx * -1;
    }
    if (particle.x < particle.r && particle.vx < 0) {
      particle.vx = particle.vx * -1;
    }

    // loop other particles
    for (var k = 0; k < particles.length; k++){
        var detP = particles[k];
      // if the other is different, check collision
      if (
        (detP.type === "reactantA" && particle.type === "reactantB") ||
        (particle.type === "reactantA" && detP.type === "reactantB")
      ){          
        // l is the distance between two particles
        var x = particle.x - detP.x,
            y = particle.y - detP.y,
            l = Math.sqrt(x * x + y * y); 
        // if distance is less then radius, we have collision
        if (l < particle.r) {   
          particle.type = "reactantC";
          detP.type = "reactantC";
        }
      }
    }
  }
};

更新了fiddle

现在,我称之为天真,因为它非常强力循环。这是一个双循环,随着粒子数量的增加,它会变慢。这是d3.quadtree旨在解决的问题。它划分空间以优化搜索。这是一个很棒的explanation

我现在有点晚了,希望我明天会有一些时间,我会用d3.quadtree重新编码...