Perfeita, precisão infinita, física de jogos em Python (parte 1)
Com precisão absoluta, programe “Berço de Newton”, “bola de tênis e queda de basquete” e muito mais
Este é o primeiro de quatro artigos que mostram como programar um mecanismo de física perfeito em Python. É um pequeno passo em minha grande ambição de transformar toda a física, matemática e até filosofia em programação. Através deste projeto, descobriremos surpresas, aumentaremos nossa compreensão e (espero) nos divertiremos. Todo o código está disponível no GitHub .
Limitaremos a máquina de várias maneiras — por exemplo, a colisões newtonianas entre círculos e linhas. No entanto, não limitaremos a precisão do motor. Ele representará todos os tempos, posições e velocidades com expressões exatas como 8*sqrt(3)/3
. Em outras palavras, evita todas as aproximações numéricas.
Os resultados serão simulações perfeitas, por exemplo, do brinquedo Berço de Newton. (Para reproduzir/pausar o vídeo, pressione o botão no canto inferior esquerdo. O som é útil.)
De que adianta um mecanismo de física com precisão absoluta? Entre outras coisas, pode melhorar nossa compreensão do mundo físico. Neste artigo (Parte 1), veremos essas descobertas novas para mim:
- Limite de velocidade da bola de tênis — Em uma demonstração popular de física, você deixa cair uma bola de tênis com uma bola de basquete. A bola de tênis salta muito mais rápido do que cai. Quão rápido? Veremos que a velocidade “para cima” nunca é mais do que três vezes a velocidade “para baixo”. Isso vale mesmo se tornarmos a bola de basquete infinitamente massiva.
- Boas vibrações — Imagine uma bola de tênis em movimento presa entre uma bola de basquete e uma parede. No curto espaço de tempo entre dois quadros de vídeo, a bola de tênis pode quicar 80 vezes. Um mecanismo que usa aproximações pode perder alguns desses saltos. Nosso motor conta cada um.
- Quebra de bilhar — Mesmo com precisão infinita, uma quebra de bilhar não pode ser praticamente revertida. Veremos que mesmo quando removemos o “efeito borboleta” e a incerteza quântica, o mundo ainda é aleatório e não determinístico.
Construindo o Motor
Suponha que já tenhamos funções Python para:
- Dados quaisquer dois objetos (possivelmente em movimento) no mundo, retorne o intervalo de tempo exato até que eles apenas se toquem. A resposta pode ser “nunca”.
- Dados quaisquer dois objetos em colisão, como, exatamente, a colisão mudará sua velocidade e direção?
Vamos trabalhar com uma abordagem observando três mundos da física, começando com o berço de Newton.
berço de newton
A ideia geral para o nosso motor é encontrar o tempo exato até a próxima colisão no mundo. Avance para exatamente esse tempo. Ajuste as velocidades dos objetos em colisão. Repetir.
Para o berço de Newton, primeiro montamos círculos e paredes.
from perfect_physics import World, Circle, Wall
circle_list = [Circle(x=1, y=0, r=1, vx=1, vy=0, m=1)]
for i in range(1, 6):
circle_list.append(Circle(x=i * 2 + 4, y=0, r=1, vx=0, vy=0, m=1))
wall_list = [Wall(x0=0, y0=0, x1=0, y1=1), Wall(x0=20, y0=0, x1=20, y1=1)]
world = World(circle_list, wall_list, xlim=(-1, 21), ylim=(-2, 2))
world.show()
- Entre cada par de objetos, encontre o intervalo de tempo exato em que eles colidem (se houver). Ignore outros pares e colisões. — Nesse caso, o primeiro círculo (em movimento) colidirá com o segundo círculo no intervalo de tempo 3. Ignorando essa colisão, ele também colidirá com o terceiro círculo no intervalo de tempo 5, etc. Ele colidirá com a parede oposta no tempo extensão 18.
- Encontre o intervalo de tempo da(s) primeira(s) colisão(ões) e avance o relógio mundial exatamente o mesmo. Neste caso, 3.
- Ajuste as velocidades de todos os objetos envolvidos nesta(s) colisão(ões). Como esperado com o Berço de Newton, o primeiro círculo fica estacionário e o segundo círculo começa a se mover.
- Repita o tempo que desejar. No exemplo, a segunda e a terceira bola irão colidir no intervalo de tempo 0. Este vídeo mostra a ação, evento por evento. (Um “evento” é o avanço do tempo para uma colisão ou o ajuste de velocidades com base na colisão)
Em geral, para cada quadro, nós:
- Encontre o valor do relógio de simulação para esse quadro.
- Encontre o evento de colisão que precede esse valor de relógio.
- Começando com o mundo na colisão anterior, mova os círculos para o valor do relógio do quadro. (Por padrão, os círculos não irão colidir com nada durante este movimento.)
Círculo e Triângulo
Eu afirmo que os tempos e as posições são todos exatos, mas você acredita em mim? Aqui estão algumas evidências através de um círculo inscrito por um triângulo. Primeiro, aqui está o vídeo mostrando apenas os eventos. Você pode ver o relógio definido para expressões como 8*sqrt(3)/3
.
E aqui está o vídeo regular para o círculo em um triângulo:
Nesse caso, a simulação segue um padrão. Sem precisão infinita, o padrão poderia ter sido perdido.
Queda de bola de tênis e basquete
Vamos examinar mais um mundo da física para ver outra vantagem da precisão infinita. Este mundo inclui uma bola de tênis e uma bola de basquete movendo-se em direção a uma parede com velocidade 1. A massa da bola de basquete é 1.000 vezes maior que a da bola de tênis. (Nossos mundos não têm gravidade.)
from perfect_physics import World, Circle, Wall
big_radius = 10
world_width = 40
folder = root / f"part1/two_size{big_radius}"
big = Circle(x=world_width // 2, y=0, r=big_radius, vx=1, vy=0, m=big_radius**3)
little = Circle(x=big.x - big_radius - 1, y=0, r=1, vx=1, vy=0, m=1)
circle_list = [big, little]
wall_list = [Wall(x0=0, y0=0, x1=0, y1=1), Wall(x0=world_width, y0=0, x1=world_width, y1=1)]
world = World(circle_list, wall_list, xlim=(-1, world_width + 1), ylim=(-big_radius - 1, big_radius + 1))
world.run_in_place(2, show=True)
print([circle.vx for circle in world.circle_list])
Como esperado (se você já tentou isso no mundo físico), a bola de tênis acelera. Surpreendeu-me, no entanto, indo apenas três vezes mais rápido.
Vamos pular um pouco e usar as ferramentas que desenvolveremos na Parte 3. Este código Python mostra o efeito nas bolas de tênis de uma bola de basquete com massa infinita. A bola de tênis entra com uma velocidade de 1 e sai com uma velocidade de exatamente 3.
from sympy import limit, simplify, oo
from perfect_physics import load
cc_velocity_solution = load("data/cc_velocity_solution.sympy")
a_vx, a_vy, b_vx, b_vy = cc_velocity_solution.subs([("a_x", 10), ("a_y", 0), ("a_r", 10), ("a_vx", -1), ("a_vy", 0),
("b_x", -1), ("b_y", 0), ("b_r", 1), ("b_vx", 1), ("b_vy", 0), ("b_m", 1)])
print(simplify(b_vx))
limit(b_vx, "a_m", oo)
# prints (1 - 3*a_m)/(a_m + 1)
# returns -3
O motor me surpreendeu ainda mais quando o deixei funcionando por mais tempo. Veja o que acontece no tempo de vídeo 20 segundos (tempo de simulação 200):
A bola de basquete esmaga a bola de tênis. Isso faz com que a bola de tênis salte para frente e para trás mais de 80 vezes em um único quadro de vídeo. A bola de tênis atinge uma velocidade superior a 31. Na faixa de áudio do vídeo, a vibração da bola de tênis produz um tom de mais de 2000 Hz (batidas/segundo).
Se, em vez do cálculo exato, apenas amostrarmos uma vez por quadro, perderíamos (ou calcularíamos mal) essa ação rápida.
Resumindo Parte 1
Funciona! Temos um mecanismo de física perfeito. Transformamos um pouco da física em programação.
Presumimos que começamos com duas funções Python que nos diziam — para qualquer par de objetos — duas coisas: 1) O tempo até a próxima colisão (se houver) e 2) O efeito dessa colisão em suas velocidades. Vimos como transformar essas duas funções em um mecanismo de física perfeito repetidamente: avançando no tempo até a(s) próxima(s) colisão(ões) e ajustando as velocidades de todos os objetos envolvidos na(s) colisão(ões).
Criamos vídeos procurando a colisão que precedeu um quadro de vídeo e, em seguida, avançando o tempo (sem nos preocupar com colisões) para esse quadro.
O Berço de Newton, o primeiro mundo, se comportou como esperado. O segundo mundo, um círculo inscrito por um triângulo, usando expressões como 8*sqrt(3)/3
para tempo, etc., encontrou um padrão. A queda da bola de tênis e do basquete revelou um limite surpreendente para mim na velocidade da bola de tênis. Além disso, produziu saltos mais rápidos do que métodos menos perfeitos poderiam calcular.
O próximo artigo, Parte 2 , simula uma partida de bilhar com resultados filosóficos surpreendentes para mim. A seguir, na Parte 3, veremos como fazer o computador criar essas duas funções iniciais. Por fim, na Parte 4, aceleraremos um pouco o mecanismo (mas não o suficiente) e discutiremos as limitações.
Se você tiver ideias para simulações que gostaria que eu fizesse, envie-as para mim. Eles podem se tornar a base de uma parte 5.
Você pode baixar este código em CarlKCarlK/perfect-physics (github.com) . Avise-me se houver interesse e criarei um instalador melhor.
Siga Carl M. Kadie - Medium para notificações das próximas partes. Todas as partes serão livres para ler. Por fim, no YouTube, tenho simulações de física mais antigas (aproximadas) e alguns vídeos da natureza que tentam ser bem-humorados .