If you're seeing this message, it means we're having trouble loading external resources on our website.

Se você está atrás de um filtro da Web, certifique-se que os domínios *.kastatic.org e *.kasandbox.org estão desbloqueados.

Conteúdo principal

Sistemas de partículas com forças

Até agora, nesta seção, estivemos focando na estruturação de nosso código de forma orientada a objetos para gerenciar uma coleção de partículas. Talvez tenha reparado, ou talvez não, mas durante este processo nós involuntariamente voltamos alguns passos para trás, retornando para onde estávamos nas seções anteriores. Vamos examinar o construtor de nosso simples objeto Particle.
var Particle = function(position) {
  this.acceleration = new PVector(0, 0.05);
  this.velocity = new PVector(random(-1, 1), random(-1, 0));
  this.position = new PVector(position.x, position.y);
  this.timeToLive = 255.0;
};
E agora vamos olhar para o método update():
Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
  this.timeToLive -= 2;
};
Observe que nossa aceleração é constante, ela nunca situa-se além do construtor. Uma estrutura bem melhor seria seguir a segunda lei de Newton (F=MA) e incorporar o algoritmo de acumulação de força que tanto estudamos na seção sobre Forças.
O primeiro passo é acrescentar um método applyForce(). (Lembre-se, nós precisamos fazer uma cópia do PVector antes de dividi-lo pela massa.)
Particle.prototype.applyForce = function(force) {
  var f = force.get();
  f.div(this.mass);
  this.acceleration.add(f);
};
Neste ponto, podemos acrescentar mais uma linha de código para limpar a aceleração ao final de update().
Particle.prototype.update = function() {
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
  this.acceleration.mult(0);
  this.timeToLive -= 2.0;
};
Dessa forma, temos um objeto Particle que pode ter uma força aplicada sobre ele. Agora, onde devemos chamar a função applyForce()? Onde no código é apropriado aplicar força em uma partícula? A verdade é que não existe resposta certa ou errada; depende da funcionalidade exata e dos objetivos específicos de um programa. Ainda sim, podemos criar uma situação genérica onde a maioria dos casos se encaixem e gerar um modelo para aplicação de forças em partículas individuais dentro de um sistema.

Adicionando vento

Vamos considerar o seguinte objetivo: aplicar uma força globalmente cada vez através de draw() para todas as partículas. Iremos começar com a aplicação de uma força de vento simples que empurra as partículas para a direita:
var wind = new PVector(0.4, 0);
Dissemos que ela deve ser aplicada sempre, ou seja, em draw(), então vamos analisar nossa função draw().
draw = function() {
  background(168, 255, 156);
  particleSystem.addParticle();
  particleSystem.run();
};
Bem, parece que temos um pequeno problema. applyForce()é um método escrito dentro do objeto Particle, mas não temos qualquer referência às próprias partículas individuais, apenas o objeto ParticleSystem : a variável particleSystem.
Entretanto, como queremos que todas as partículas recebam a força, é possível optar por aplicá-la ao sistema de partículas e deixá-lo gerenciar a aplicação da força a todas as partículas individuais.
draw = function() {
  background(168, 255, 156);
  particleSystem.applyForce(wind);
  particleSystem.addParticle();
  particleSystem.run();
};
Obviamente, se designarmos uma nova função no objeto ParticleSystem em draw(), bem, nós temos que escrever essa função no objeto ParticleSystem. Vamos descrever o trabalho que essa função deve fazer: receber uma força como um PVector e aplicar tal força em todas as partículas.
Em código:
ParticleSystem.prototype.applyForce = function(f){
  for(var i = 0; i < this.particles.length; i++){
    this.particles[i].applyForce(f);
  }
};
Parece bobo escrever esta função. O que estamos dizendo é "aplique uma força em um sistema de partículas para que o sistema possa aplicar essa força para todas as partículas individuais." No entanto, é bastante razoável. Afinal de contas, o objeto ParticleSystem é responsável pelo gerenciamento das partículas então, se quisermos falar com as partículas, tem que ser através do seu gerente.
E aqui juntamos tudo. Brinque um pouco com a força do vento e veja como isso afeta o movimento das partículas. Observe como as partículas de diferentes massas também respondem de maneiras diferentes. Pense no motivo disso acontecer.

Adicionando gravidade

Agora vamos aplicar uma força mais complexa, a gravidade, que é diferente do vento pois ela varia de acordo com a massa dos objetos aos quais ela é aplicada.
Vamos lembrar a equação para calcular a força da gravidade entre duas massas: Fg=Gm1m2||r||2r^
Lembre-se de que, quando representamos a força da gravidade na Terra, a força exercida pela Terra é superior a todas as outras forças gravitacionais, então, a única equação com a qual estamos lidando trata da força da gravidade entre a Terra e o objeto. G and m1 são iguais para todas as partículas, e r (o raio da Terra) é basicamente o mesmo (já que o raio da Terra é significativamente grande em relação à curta distância que as partículas conseguem se afastar da Terra), então, geralmente simplificamos essas informações usando apenas g, isto é, a constante para a gravidade na Terra:
g=Gm1||r||2
Agora, a força da gravidade é simplesmente uma constante g, vezes a massa das partículas, multiplicada por um vetor unitário na direção da força (que será sempre para baixo):
Fg=gm2r^
No código, significa que teremos que aplicar uma força gravitacional diferente para cada partícula dependendo da sua massa. Como fazemos isso? Não podemos reutilizar a função applyForce existente, pois ela espera a mesma força para cada partícula. Podemos considerar passar um parâmetro para ela instruindo applyForce para multiplicar pela massa, mas vamos deixar essa função em paz e criar uma nova, applyGravity , que calcula a força baseada em um vetor global constante:
// Um vetor constante para baixo, declarado no topo
var gravity = new PVector(0, 0.2);
ParticleSystem.prototype.applyGravity = function() {
    for(var i = 0; i < this.particles.length; i++) {
        var particleG = gravity.get();
        particleG.mult(this.particles[i].mass);
        this.particles[i].applyForce(particleG);
    }
};
Agora, se fizemos isso corretamente, todas as nossas partículas devem cair no mesmo ritmo na simulação abaixo. Isso ocorre pois a força da gravidade baseia-se na multiplicação da massa, mas a aceleração é baseada na divisão pela massa, então no final, a massa não tem efeito. Pode parecer bobagem ter esse esforço todo para não obter um efeito, mas é importante uma vez que começamos a combinar múltiplas forças diferentes.

Adicionando repulsores

E se quisermos dar um passo a mais nesse exemplo e adicionar um objeto repulsor que empurra as partículas para longe quando elas chegam perto? Seria semelhante ao objeto atrator que criamos anteriormente, só que empurrando na direção oposta. Mais uma vez, como a gravidade, devemos calcular uma força diferente para cada partícula, mas no caso do repulsor, a diferença é que o cálculo não é baseado na massa, mas sim na distância. Para a gravidade, todos os nossos vetores força tinham a mesma direção, mas para o repulsor, todos os vetores força terão direções diferentes:
Força da gravidade: todos os vetores têm a mesma direção
Força repulsora: todos os vetores de direção são diferentes
Como o cálculo de uma força repulsora é um pouco mais complexo que o cálculo da gravidade (e, no final das contas, podemos querer vários repulsores!), resolveremos este problema incorporando um novo objeto Repulsor em nosso sistema simples de partículas, além do exemplo da gravidade. Iremos precisar de duas adições importantes ao nosso código:
  1. um objeto Repeller (declarado, inicializado e exibido).
  2. Uma função que passa o objeto Repeller para o ParticleSystem para que ele possa aplicar uma força para cada objeto de partícula.
var particleSystem = new ParticleSystem(new PVector(width/2, 50));
var repeller = new Repeller(width/2-20, height/2);
var gravity = new PVector(0, 0.1);

draw = function() {
  background(214, 255, 171);

  // Aplica forca da gravidade a todas as partículas
  particleSystem.applyForce(gravity);
  particleSystem.applyRepeller(repeller);
  repeller.display();
  particleSystem.addParticle();
  particleSystem.run();
};
Fazer um objeto Repeller que pode ser exibido é fácil. Trata-se de uma cópia do objeto Attractor que criamos anteriormente:
var Repeller = function(x, y) {
  this.position = new PVector(x, y);
};

Repeller.prototype.display = function() {
  stroke(255);
  strokeWeight(2);
  fill(127);
  ellipse(this.position.x, this.position.y, 32, 32);
};
A pergunta mais difícil é: como escrevemos o método applyRepeller()? Ao invés de passar um PVector para uma função como fazemos com applyForce(), vamos passar um objeto Repeller para applyRepeller() e pedir que essa função faça o trabalho de calcular a força entre o repulsor e todas as partículas:
ParticleSystem.prototype.applyRepeller = function(r) {
  for(var i = 0; i < this.particles.length; i++){
    var p = this.particles[i];
    var force = r.calculateRepelForce(p);
    p.applyForce(force);
  }
};
A grande diferença aqui é que uma nova força é calculada para cada partícula, porque, como vimos antes, a força é diferente dependendo das propriedades de cada partícula em relação ao repulsor. Calculamos essa força usando a função calculateRepelForce, que é o inverso da função calculateAttractionForce do nosso Attractor.
Repeller.prototype.calculateRepelForce = function(p) {
  // Calcula os vetores força entre objetos
  var dir = PVector.sub(this.position, p.position); 
  // Calcula a distância entre objetos
  var dist = dir.mag();
  // Mantém a distância a um alcance razoável
  dist = constrain(dist, 1, 100);    
  // Calcula forças de repulsão,
  // inversamente proporcionais ao quadrado da distância
  var force = -1 * this.power/ (dist * dist);     
  // Normaliza o vetor de direção
  // (descartando informações sobre distância)
  dir.normalize();
  // Calcula o vetor força: direção * magnitude
  dir.mult(force);                                  
  return dir;
};
Observe como ao longo de todo o processo de adicionar um repulsor para o ambiente, nunca pensamos em editar o objeto Particle. Uma partícula não precisa saber nada sobre os detalhes do seu ambiente; Ela simplesmente precisa gerenciar sua localização, velocidade e aceleração, bem como ter a capacidade de receber uma força externa e agir de acordo.
Agora podemos analisar este exemplo como um todo. Tente mudar a potência das forças que atuam nas partículas - a gravidade e o repulsor - e veja como isso as afeta:

Quer participar da conversa?

Nenhuma postagem por enquanto.
Você entende inglês? Clique aqui para ver mais debates na versão em inglês do site da Khan Academy.