如何在tkinter-python3中使两个粒子发生碰撞



我目前正在尝试使两个气态粒子碰撞,但我不太确定如何编写代码来实现这一点,我有一个我能做什么的粗略草图,但我真的不知道如何正确地实现它(第43-54行,即注释#I与另一个粒子j碰撞后的代码(。碰撞发生后,我希望它们以不同的速度朝相反的方向前进。我将根据粒子的质量计算动能。该项目的目标是基本上显示不同参数(速度、质量、方向(的多个粒子在气体中运动的动能守恒。这是我目前的代码,如果有任何帮助,我们将不胜感激!!:

from tkinter import *
from random import *
myHeight=400
myWidth=600
mySpeed=10
x1=5
y=5
radius1=20
x2=7
y2=4
radius1=20
x=60
width=40
length=10
global particules 
particules = []
def initialiseBall(dx,dy,radius,color):
b = [myWidth/2,myHeight/2, dx, dy, radius]
particules.append(b)
k = myCanvas.create_oval(myWidth/2-radius, myHeight/2,
myWidth/2+radius,myHeight/2+radius,
width=2,fill=color)
print(k)
def updateBalls():
N = len(particules)
for i in range(N):
particules[i][0] += particules [i][2]
particules[i][1] += particules [i][3]
# collision of i with the walls
if particules[i][0]<0 or particules[i][0]>=myWidth:
particules[i][2] *= -1
if particules[i][1]<0 or particules[i][1]>=myHeight:
particules[i][3] *= -1
#collision of i with another particle j
# for j in range(N):
#   if j != i:
# compare the position of i and j
# dij = ...
# if dij ... :
#if collision, compute the normal vector
#change velocities
#  if particules[i][1]<=particules[i][1]:
#    particules[i][2] *= -1
#  r = particules[i][4]
myCanvas.coords(i+1, particules[i][0]-particules[i][4],
particules[i][1]-particules[i][4], 
particules[i][0]+particules[i][4], 
particules[i][1]+particules[i][4])
def animation ():
updateBalls()
myCanvas.after(mySpeed, animation)
def kineticenergy(mass, velocity):
Ec = 1/2 * mass * velocity ** 2
return Ec
# def averagetrip(number, radius):
#   # 
#   #
#   # 
#   #
mainWindow=Tk()
mainWindow.title('particles reservoir')
myCanvas = Canvas(mainWindow, bg = 'grey', height = myHeight, width = myWidth)
myCanvas.pack(side=TOP)
# create 2 particules 
initialiseBall(-1,0, 50, 'red')
initialiseBall(1,0, 50, 'blue')
print(particules)
'''
N = 100
for n in range(N):
initialiseBalle(-1 ,0, randint(5,10), 'red')
'''
animation()
mainWindow.mainloop()

试试这个:

from math import sqrt, sin, cos
import tkinter as tk
import time
# For more info about this read: https://stackoverflow.com/a/17985217/11106801
def _create_circle(self, x, y, r, **kwargs):
return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle

WINDOW_WIDTH = 500
WINDOW_HEIGHT = 500
# The coefficient of restitution
# Set to 1 for perfectly elastic collitions
e = 1

class Ball:
def __init__(self, mass:float, r:float, x:float, y:float,
vx:float=0, vy:float=0, **kwargs):
"""
This is a class that defines what a ball is and how it interacts
with other balls.
Arguments:
mass:float    # The mass of the ball
------------------------------------------------------
r:float       # The radius of the ball, must be >0
------------------------------------------------------
x:float       # The x position of the ball
#   must be >0 and <WINDOW_WIDTH
y:float       # The y position of the ball
#   must be >0 and <WINDOW_HEIGHT
------------------------------------------------------
vx:float      # The x velocity of the ball
vy:float      # The y velocity of the ball
------------------------------------------------------
**kwargs      # All of the args to be passed in to `create_circle`
"""
self.m = mass
self.r = r
self.x = x
self.y = y
self.vx = vx
self.vy = vy
self.kwargs = kwargs
def display(self, canvas:tk.Canvas) -> int:
"""
Displays the ball on the screen and returns the canvas_id (which
is a normal python int).
"""
canvas_id = canvas.create_circle(self.x, self.y, self.r, **self.kwargs)
return canvas_id
def move(self, all_balls:list, dt:float) -> (float, float):
"""
This moves the ball according to `self.vx` and `self.vy`.
It also checks for collisions with other balls.
Arguments:
all_balls:list   # A list of all balls that are in the
# simulation. It can include this ball.
---------------------------------------------------
dt:float         # delta time - used to move the balls
Returns:
dx:float         # How much the ball has moved in the x direction
dy:float         # How much the ball has moved in the y direction
Note: This function isn't optimised in any way. If you optimise
it, it should run faster.
"""
# Check if there are any collitions:
for other, _ in all_balls:
# Skip is `ball` is the same as this ball
if id(other) == id(self):
continue
# Check if there is a collision:
distance_squared = (other.x - self.x)**2 + (other.y - self.y)**2
if distance_squared <= (other.r + self.r)**2:
# Now the fun part - calulating the resultant velocity.
# I am assuming you already know all of the reasoning
# behind the math (if you don't ask physics.stackexchange.com)
# First I will find the normal vector of the balls' radii.
# That is just the unit vector in the direction of the
#  balls' radii
ball_radii_vector_x = other.x - self.x
ball_radii_vector_y = other.y - self.y
abs_ball_radii_vector = sqrt(ball_radii_vector_x**2 +
ball_radii_vector_y**2)
nx = ball_radii_vector_x / abs_ball_radii_vector        **2*2
ny = ball_radii_vector_y / abs_ball_radii_vector        **2*2
# Now I will calculate the tangent
tx = -ny
ty = nx
""" Only for debug
print("n =", (nx, ny), "t t =", (tx, ty))
print("u1 =", (self.vx, self.vy),
"t u2 =", (other.vx, other.vy))
#"""
# Now I will split the balls' velocity vectors to the sum
#  of 2 vectors parallel to n and t
# self_velocity  = λ*n + μ*t
# other_velocity = a*n + b*t
λ = (self.vx*ty - self.vy*tx) / (nx*ty - ny*tx)
# Sometimes `tx` can be 0 if so we are going to use `ty` instead
try:
μ = (self.vx - λ*nx) / tx
except ZeroDivisionError:
μ = (self.vy - λ*ny) / ty
""" Only for debug
print("λ =", λ, "t μ =", μ)
#"""
a = (other.vx*ty - other.vy*tx) / (nx*ty - ny*tx)
# Sometimes `tx` can be 0 if so we are going to use `ty` instead
try:
b = (other.vx - a*nx) / tx
except ZeroDivisionError:
b = (other.vy - a*ny) / ty
""" Only for debug
print("a =", a, "t b =", b)
#"""
self_u = λ
other_u = a
sum_mass_u = self.m*self_u + other.m*other_u
sum_masses = self.m + other.m
# Taken from en.wikipedia.org/wiki/Inelastic_collision
self_v = (e*other.m*(other_u-self_u) + sum_mass_u)/sum_masses
other_v = (e*self.m*(self_u-other_u) + sum_mass_u)/sum_masses
self.vx = self_v*nx + μ*tx
self.vy = self_v*ny + μ*ty
other.vx = other_v*nx + b*tx
other.vy = other_v*ny + b*ty
print("v1 =", (self.vx, self.vy),
"t v2 =", (other.vx, other.vy))
# Move the ball
dx = self.vx * dt
dy = self.vy * dt
self.x += dx
self.y += dy
return dx, dy

class Simulator:
def __init__(self):
self.balls = [] # Contains tuples of (<Ball>, <canvas_id>)
self.root = tk.Tk()
self.root.resizable(False, False)
self.canvas = tk.Canvas(self.root, width=WINDOW_WIDTH,
height=WINDOW_HEIGHT)
self.canvas.pack()
def step(self, dt:float) -> None:
"""
Steps the simulation as id `dt` seconds have passed
"""
for ball, canvas_id in self.balls:
dx, dy = ball.move(self.balls, dt)
self.canvas.move(canvas_id, dx, dy)
def run(self, dt:float, total_time:float) -> None:
"""
This function keeps steping `dt` seconds until `total_time` has
elapsed.
Arguments:
dt:float          # The time resolution in seconds
total_time:float  # The number of seconds to simulate
Note: This function is just for proof of concept. It is badly written.
"""
for i in range(int(total_time//dt)):
self.step(dt)
self.root.update()
time.sleep(dt)
def add_ball(self, *args, **kwargs) -> None:
"""
Adds a ball by passing all of the args and keyword args to `Ball`.
It also displays the ball and appends it to the list of balls.
"""
ball = Ball(*args, **kwargs)
canvas_id = ball.display(self.canvas)
self.balls.append((ball, canvas_id))

app = Simulator()
app.add_ball(mass=1, r=10, x=20, y=250, vx=100, vy=0, fill="red")
app.add_ball(mass=1, r=10, x=240, y=250, vx=0, vy=0, fill="blue")
app.add_ball(mass=1, r=10, x=480, y=250, vx=0, vy=0, fill="yellow")
app.run(0.01, 10)

该代码有很多注释描述了它的工作原理。如果你还有任何问题,请问我。此外,move方法没有优化。run方法不太好,如果它仍在运行并且用户关闭了窗口,则可能会引发错误。我会设法为那个方法找到一个更好的方法。如果你发现任何错误,请告诉我。我会设法修复它们。

最新更新