Se você está vendo esta mensagem, significa que estamos tendo problemas para carregar recursos externos em nosso website.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

Conteúdo principal

Atração gravitacional

Provavelmente a força mais famosa de todas é a gravidade. Nós, humanos na Terra, pensamos na gravidade como a maçã acertando a cabeça de Isaac Newton. Gravidade significa que as coisas caem. Mas essa é apenas a nossa experiência da gravidade. Na realidade, da mesma forma que a Terra atrai a maçã para si devido a uma força gravitacional, a maçã também atrai a Terra. A questão é que a Terra é tão massiva que ela suprime todas as interações gravitacionais de todos os outros corpos no planeta. Cada corpo com massa exerce uma força gravitacional em todos os outros corpos. E existe uma fórmula para calcular a intensidade dessas forças, conforme ilustrado no diagrama abaixo:
Vamos examinar esta fórmula um pouco mais de perto.
  • F refere-se à força gravitacional, o vetor que no final queremos calcular e passar para a função applyForce().
  • G é a constante gravitacional universal, que no nosso mundo é igual a 6,67428 x 10^-11 metros ao cubo por quilograma por segundo quadrado. Este é um número bastante importante, se o seu nome é Isaac Newton ou Albert Einstein. Ele não é um número importante se você é um programador ProcessingJS. De novo, ele é uma constante que nós podemos usar para fazer as forças no nosso mundo serem mais fracas ou mais fortes. Apenas assumindo o valor um e a ignorando não é uma má escolha.
  • m1 e m2 são as massas dos objetos 1 e2. Como nós vimos com a segunda lei de Newton (F=MA), massa também é algo que podemos escolher ignorar. Afinal, formas desenhadas na tela na verdade não têm massa. Porém, se nós mantivermos esses valores, podemos criar simulações mais interessantes nas quais objetos “maiores” exercem uma força gravitacional maior do que os menores.
  • r^ refere-se ao vetor unitário apontando do objeto 1 para o objeto 2. Veremos em um momento que nós podemos computar esse vetor subtraindo a posição de um objeto da posição do outro.
  • r2 refere-se à distância entre os dois objetos ao quadrado. Vamos parar um momento para pensar um pouco mais sobre isso. Com tudo da parte de cima da fórmula—G, m_1, m2—quanto maior o seu valor, mais forte é a força. Grande massa, grande força. Grande G, grande força. Agora, quando dividimos por algo, temos o contrário. A intensidade da força é inversamente proporcional ao quadrado da distância. Quanto mais longe um objeto está, mais fraca é a força; quanto mais perto, mais forte é a força.
Esperamos que agora a fórmula faça algum sentido para nós. Nós vimos um diagrama e dissecamos individualmente os componentes da fórmula. Agora é hora de descobrir como traduzir a matemática para código ProcessingJ. Vamos fazer as seguintes suposições.
Temos dois objetos, e:
  1. Cada objeto tem uma localização PVector: location1 e location2.
  2. Cada objeto tem uma massa numérica: mass1 e mass2.
  3. Existe uma variável G para a constante gravitacional universal.
Dadas essas hipóteses, nós queremos calcular uma PVector force, a força da gravidade. Faremos isso em duas etapas. Primeiro, vamos calcular a direção da força r^ na equação acima. Depois, vamos calcular a intensidade da força de acordo com as massas e distâncias.
Lembra quando nós descobrimos como fazer um objeto acelerar em direção ao mouse? Vamos usar a mesma lógica aqui.
Um vetor é a diferença entre dois pontos. Para fazer um vetor que apontava do círculo para o mouse, simplesmente subtraímos um ponto do outro:
var dir = PVector.sub(mouse, location);
No nosso caso, a direção da força de atração que o objeto 1 exerce sobre o objeto 2 é igual a:
var dir = PVector.sub(location1, location2);
Não se esqueça que já que queremos um vetor unitário, um vetor que nos indique apenas a direção, nós precisaremos normalizá-lo depois de subtrair as posições:
dir.normalize();
OK, nós temos a direção da força. Agora nós apenas precisamos calcular a magnitude e dimensionar o vetor adequadamente.
var m = (G * mass1 * mass2) / (distance * distance);
dir.mult(m);
O único problema é que nós não sabemos a distância. G, mass1, e mass2 são todos dados, mas nós precisaremos na verdade calcular a distância antes que o código acima funcione. Nós não acabamos de criar um vetor que aponta de uma posição para outra? Não seria o comprimento desse vetor igual à distância entre dois objetos?
Bem, se adicionarmos apenas mais uma linha de código e pegarmos a magnitude do vetor antes de normalizá-lo, então nós vamos ter a distância.
// Vetor que aponta de um objeto para outro
var force = PVector.sub(location1, location2);

// Comprimento (magnitude) desse vetor é a distância entre os dois objetos.
var distance = force.mag();

// Use a fórmula da gravidade para calcular a intensidade da força.
var strength = (G * mass1 * mass2) / (distance * distance);

// Normalize e ajuste a escala do vetor força para a magnitude apropriada.
force.normalize();
force.mult(strength);
Note que também renomeei PVector “dir” como “force.” Afinal, quando terminarmos os cálculos, o PVector com que começamos vai acabar sendo o vetor força que queríamos desde o início.
Agora que nós trabalhamos a matemática e o código para calcular uma força de atração (emulando a gravidade), nós precisamos voltar nossa atenção para aplicar esta técnica no contexto um programa ProcessingJS real. Mais cedo nessa seção, nós criamos um objeto simples Mover —um objeto com uma posição PVector, velocidade, e aceleração assim como um método applyForce(). Vamos pegar essa mesma classe e colocá-la em um programa com:
  • Um único objeto Mover.
  • Um único objeto Attractor (um novo tipo de objeto que vai ter uma posição fixa).
O objeto Mover irá experimentar uma força gravitacional que puxa em direção ao objeto Attractor, como ilustrado no diagrama.
Nós podemos começar fazendo um objeto Attractor bastante simples—dando-lhe uma posição e uma massa, juntamente com um método para exibir-se (atrelando a massa ao tamanho).
var Attractor = function() {
    this.position = new PVector(width/2, height/2);
    this.mass = 20;
    this.G = 1;
    this.dragOffset = new PVector(0, 0);
    this.dragging = false;
    this.rollover = false;
};

// Método para exibir
Attractor.prototype.display = function() {
    ellipseMode(CENTER);
    strokeWeight(4);
    stroke(0);
    fill(175, 175, 175, 200);
    ellipse(this.position.x, this.position.y, this.mass*2, this.mass*2);
};
Depois de definir isso, podemos criar uma instância do objeto do tipo Attractor.
var mover = new Mover();
var attractor = new Attractor();

draw = function() {
    background(50, 50, 50);

    attractor.display();
    mover.update();
    mover.display();
};
Essa é uma boa estrutura: um programa principal com um objeto Mover e um objeto Attractor. O último pedaço do quebra-cabeça é como fazer um objeto atrair o outro. Como vamos fazer esses dois objetos falarem um com o outro?
Há várias maneiras de se fazer isso. Aqui estão apenas algumas possibilidades.
TarefaFunção
1. Uma função que recebe um Attractor e um Mover:attraction(a, m);
2. Um método no objeto Attractor que recebe um Mover:a.attract(m);
3. Um método no objeto Mover que recebe um Attractor:mover.attractedTo(a);
4. Um método no objeto Attractor que recebe um Mover e retorna um PVector, que é a força de atração. Essa força de atração é então passada para o método applyForce() do objeto Mover.`var f = a.calculateAttraction(m);
mover.applyForce(f);` |
É bom olhar para um conjunto de opções para fazer os objetos conversarem uns com os outros e você pode provavelmente achar argumentos para cada uma das possibilidades acima. Vamos começar descartando o primeiro, uma vez que uma abordagem orientada a objetos é realmente uma escolha bem melhor do que uma função arbitrária não ligada aos objetos Mover ou Attractor. Escolher a opção 2 ou 3 é a diferença entre dizer "O attractor atrai o Mover" ou "O Mover é atraído pelo attractor". O número 4 parece mais apropriado, pelo menos em termos de onde nós estamos neste curso. Afinal, nós gastamos bastante tempo trabalhando no método applyForce() e eu acho que nossos exemplos serão mais claros se nós continuarmos com a mesma metodologia.
Em outras palavras, onde tínhamos:
var f = new PVector(0.1, 0); // Força inventada
mover.applyForce(f);
Agora teremos:
var f = a.calculateAttraction(m); // Força de atração entre dois objetos
mover.applyForce(f);
Assim, nossa função draw() pode ser escrita como:
draw = function() {
    background(50, 50, 50);

    // Calcula a força de atração e a aplica
    var f = a.calculateAttraction(m);
    mover.applyForce(f);

    attractor.display();
    mover.update();
    mover.display();
};
Estamos quase lá. Uma vez que decidimos colocar o método calculateAttraction() dentro do objeto do tipo Attractor, nós precisaremos escrever essa função. A função precisa receber um objeto Mover e retornar um PVector. E o que há dentro dessa função? Toda aquela agradável matemática que nós resolvemos para a atração gravitacional!
Attractor.prototype.calculateAttraction = function(mover) {

    // Qual é a direção da força?
    var force = PVector.sub(this.position, mover.position);    
    var distance = force.mag();
    force.normalize();

    // Qual é a magnitude da força?
    var strength = (this.G * this.mass * mover.mass) / (distance * distance);
    force.mult(strength);

    // Retornar a força para que ela possa ser aplicada!
    return force;
};
E nós acabamos. De certa forma. Quase. Há uma pequena questão que precisamos resolver. Vamos olhar novamente o código acima. Percebe aquele símbolo de divisão, a barra? Sempre que temos um desses, precisamos nos perguntar: O que aconteceria se a distância fosse um número realmente, realmente pequeno ou (pior ainda!) zero??! Bem, nós sabemos que não podemos dividir um número por 0, e se nós dividíssemos um número por alguma coisa como 0,0001, isso seria o equivalente a multiplicar esse número 10.000! Sim, esta é a equação do mundo real para a força da gravidade, mas nós não vivemos em um mundo real. Nós vivemos no mundo ProcessingJS. E no mundo ProcessingJS, o Mover poderia terminar ficando muito, muito perto do Attractor e a força poderia se tornar tão forte que o Mover iria simplesmente voar para fora da tela. Então, com esta fórmula, é bom sermos práticos e restringir o intervalo de valores que a distância pode ter. Talvez, não importa onde o Mover realmente está, nós nunca devemos considerá-lo a menos de 5 pixels ou mais que 25 pixels de distância do Attractor.
distance = constrain(distance, 5, 25);
Pela mesma razão que nós precisamos restringir a distância mínima, é útil para nós fazer a mesma coisa com a máxima. Afinal, se o mover estivesse, digamos, a 500 pixels do attractor (o que não é absurdo), estaríamos dividindo a força por 250.000. Essa força poderia acabar sendo tão fraca que é quase como se ela não estivesse sendo aplicada.
Agora, é você quem realmente decide qual modo de funcionamento você quer. Mas no caso de, “Eu quero uma atração que pareça razoável, que nunca seja absurdamente fraca ou forte,” então restringir a distância é uma boa técnica.
Vamos agora reunir tudo em um programa. O objeto do tipo Mover não mudou nada, mas agora nosso programa inclui um objeto Attractor e o código que os une. Também adicionamos código no programa para controlar o attractor com o mouse, de modo que seja mais fácil observar os efeitos.
E poderíamos, é claro, expandir este exemplo usando um array para incluir vários objetos Mover, assim como fizemos com o arrasto e o atrito. A principal mudança que fizemos no programa é para ajustar nosso objeto Mover a aceitar massa, x, e y (como fizemos anteriormente), inicializar um array de Movers aleatoriamente posicionados, e percorrer o array para calcular a força de atração em cada um deles, um por vez:
var movers = [];
var attractor = new Attractor();

for (var i = 0; i < 10; i++) {
    movers[i] = new Mover(random(0.1, 2), random(width), random(height));
}

draw = function() {
    background(50, 50, 50);

    attractor.display();
    for (var i = 0; i < movers.length; i++) {
        var force = attractor.calculateAttraction(movers[i]);
        movers[i].applyForce(force);

        movers[i].update();
        movers[i].display();
    }
};
Este curso "Natural Simulations" é um derivado do "The Nature of Code" por Daniel Shiffman, usado sob a Creative Commons Attribution-NonCommercial 3.0 Unported License.

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.