I am trying to make an animation where two magnets (that are repelling each other) are falling in a rotating tube. I have the falling (gravity) bit and rotation down but I am having trouble with magnetic forces. The equation for the force that I am using is (magnetic strength of magnet 1 x magnetic strength of magnet 2)/(distance between the magnets)^2. Basically, the strength of the force decreases with the square of the distance between the magnets. The goal is to have magnets repel each other as the same poles are facing each other. I believe I am not using the "apply_force_at_local_point" command properly. I am especially unsure about updating the x and y directions of the forces in the "apply_force_at_local_point" command
You really don't need to know much physics for this. Thanks for the help in advance
import pymunk
import pymunk.pygame_util
import pygame
import numpy as np
GRAY = (220, 220, 220)
width_mass=48
height_mass=48
charges=[10000,-10000] #magnet strengths
pygame.init()
size = 800,600
screen = pygame.display.set_mode(size,pygame.FULLSCREEN)
draw_options = pymunk.pygame_util.DrawOptions(screen)
space = pymunk.Space()
space.gravity = (0,-900)
pts = [(-27, -238.5), (27,-238.5), (27,238.5), (-27,238.5)] #enpoints of the endlessly rotating rectangle
body_type=pymunk.Body(body_type=pymunk.Body.KINEMATIC)
body_type.position = (400, 263.5)
space.add(body_type)
for i in range(4):
segment = pymunk.Segment(body_type, pts[i], pts[(i+1)%4], 2)
space.add(segment)
body_type.angular_velocity=1 #rotation speed
class Rectangle:
def __init__(self, rect_mass, pos,size=(80, 50)):
self.body = pymunk.Body(mass=rect_mass)
self.body.position = pos
shape = pymunk.Poly.create_box(self.body, size)
shape.density = 0.1
space.add(self.body, shape)
mass_1 = Rectangle(rect_mass=1,pos=(400,473),size=(50,50))
mass_2 = Rectangle(rect_mass=1,pos=(400,420),size=(50,50))
masses=[mass_1,mass_2]
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(GRAY)
space.debug_draw(draw_options)
pygame.display.update()
space.step(0.01)
temp=[] #collecting the positions of all masses in one place
for mass in masses:
temp.append(mass.body.position)
if len(masses)==2:
rel_dist=np.sqrt((temp[1][1]-temp[0][1])**2+(temp[1][0]-temp[0][0])**2) #euclidean distance between magnets
mag_force=charges[0]*charges[1]/(rel_dist**2 + 0.00001) #force = magnet1*magnet2/dist of the magnets^2
masses[0].body.apply_force_at_local_point(
(mag_force*(temp[1][0]-temp[0][0]),
mag_force*(temp[1][1]-temp[0][1])),
(masses[0].body.position.x,masses[0].body.position.y)) #this needs to be fixed
masses[1].body.apply_force_at_local_point(
-1*(mag_force*(temp[1][0]-temp[0][0]),
mag_force*(temp[1][1]-temp[0][1])),
(masses[1].body.position.x,masses[1].body.position.y)) #this needs to be fixed
pygame.quit()
There are several issues with the code. I think the main issue is that you use
apply_force_at_local_point
instead ofapply_force_at_world_point
. Apply at local point applies the force from the local point of the body. That is, both force and position should be given in body local coordinates. So for example, if you put the force position at (0,0), it means the force will be applied at the center of the body. Note that the angle should be considered as well.Usually its easier to think in world coordinates and use the
apply_force_at_world_point
instead. From your code it seems like this is how you thought about it as well, as you put the point to apply the force at the body world position.Some other issues:
You overwrite the mass on the rectangle Bodies. Not a problem in itself, but you will be confused :) If you set density of the shapes attached to a body they will override whatever mass you have specified. So in your code
rect_mass
is actually not used. Instead mass and moment of the body is calculated by using the density you set on the shape. To see the resulting mass and moment you can just print it after they are added to space:print(body.mass, body.moment)
In the second apply force you multiply with
-1
. However, the thing you multiply with is a tuple, so the end result is a empty tuple.Instead you either can wrap it in a pymunk.Vec2d, or move the -1 inside:
Finally, when I looked at your code I did two things to help me debug the issue. The first is to print/draw the problematic parts.
You can set individual colors to the shapes. This way its easier to understand how they behave. For example like this
shape.color = (255,0,0,255)
where the tuple is a RGBA tuple, to make the shape red.It was useful for me to draw the force applied. One way is to do something like this:
(And then make a second drawing at for example (300,100))
Finally I want to add that the
pymunk.Vec2d
has functions to do calculations like distance between two points (e.g.rel_dist = temp[0].get_distance(temp[1])
). But that is more of a personal choice, and I am quite biased since Im the Pymunk author :)