#javascript #d3.js
#javascript #d3.js
Вопрос:
Я пытаюсь понять, как я мог бы добавить определение столкновения в мой симулятор частиц (разработанный на основе этой скрипки)
Смотрите мою текущую скрипку здесь
Я хотел бы обнаруживать только столкновение между частицами с классами reactantA и reactantB particles, и когда они сталкиваются, измените class на 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 amp;amp; particle.vy > 0) {
particle.vy = particle.vy * -1;
}
if (particle.y < particle.r amp;amp; particle.vy < 0) {
particle.vy = particle.vy * -1;
}
if (particle.x > gridSize - particle.r amp;amp; particle.vx > 0) {
particle.vx = particle.vx * -1;
}
if (particle.x < particle.r amp;amp; 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. Вы видели этот пример ?
2. Да, я видел это, но изо всех сил пытаюсь понять, как это реализовать. Есть какие-нибудь подсказки?
Ответ №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 amp;amp; particle.vy > 0) {
particle.vy = particle.vy * -1;
}
if (particle.y < particle.r amp;amp; particle.vy < 0) {
particle.vy = particle.vy * -1;
}
if (particle.x > gridSize - particle.r amp;amp; particle.vx > 0) {
particle.vx = particle.vx * -1;
}
if (particle.x < particle.r amp;amp; 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" amp;amp; particle.type === "reactantB") ||
(particle.type === "reactantA" amp;amp; 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";
}
}
}
}
};
Обновлена скрипка.
Теперь я называю это наивным, потому что в нем очень много перебора. Это двойной цикл, и он будет медленным по мере увеличения количества частиц. Это проблема, которую d3.quadtree стремится решить. Он разделяет пространство для оптимизации поиска. Вот отличное объяснение.
Там, где я нахожусь, уже немного поздно, надеюсь, завтра у меня будет немного времени, и я перекодирую это, используя d3.quadtree…
Комментарии:
1. Это потрясающе, это отлично подходит для моих целей. Я обновил свое моделирование, используя предложенный вами алгоритм столкновения: fiddle.jshell.net/mephisto73/Lw6589xf/7