Создание отталкивания между массами pymunk

Я пытаюсь сделать анимацию, в которой два магнита (отталкивающие друг друга) падают во вращающуюся трубу. У меня есть падающая (гравитационная) бита и вращение вниз, но у меня проблемы с магнитными силами. Уравнение для силы, которое я использую, это (магнитная сила магнита 1 x магнитная сила магнита 2)/(расстояние между магнитами)^2. В основном сила силы уменьшается пропорционально квадрату расстояния между магнитами. Цель состоит в том, чтобы магниты отталкивались друг от друга, поскольку одни и те же полюса обращены друг к другу. Я считаю, что неправильно использую команду apply_force_at_local_point. Я особенно не уверен в обновлении направлений сил x и y в команде apply_force_at_local_point.

Для этого совсем не обязательно знать физику. Спасибо за помощь в продвижении

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()

person Ken Morison    schedule 20.10.2020    source источник


Ответы (1)


Есть несколько проблем с кодом. Я думаю, что основная проблема в том, что вы используете apply_force_at_local_point вместо apply_force_at_world_point. Применить в локальной точке применяет силу из локальной точки тела. То есть и сила, и положение должны быть заданы в локальных координатах тела. Так, например, если вы поместите положение силы в (0,0), это означает, что сила будет приложена в центре тела. Обратите внимание, что угол также следует учитывать.

Обычно проще думать в мировых координатах и ​​вместо этого использовать apply_force_at_world_point. Из вашего кода кажется, что вы тоже об этом думали, поскольку вы поставили точку, чтобы применить силу в положении тела в мире.

Некоторые другие проблемы:

  1. Вы перезаписываете массу на прямоугольнике Тела. Само по себе это не проблема, но вы будете сбиты с толку :) Если вы зададите плотность фигур, прикрепленных к телу, они перекроют любую указанную вами массу. Так что в вашем коде rect_mass фактически не используется. Вместо этого масса и момент тела рассчитываются с использованием плотности, которую вы установили для формы. Чтобы увидеть результирующие массу и момент, вы можете просто напечатать их после того, как они будут добавлены в пространство: print(body.mass, body.moment)

  2. Во втором примените силу, которую вы умножаете на -1. Однако то, на что вы умножаете, является кортежем, поэтому конечным результатом является пустой кортеж.

-1 * (
    mag_force * (temp[1][0] - temp[0][0]),
    mag_force * (temp[1][1] - temp[0][1]),
) 

Вместо этого вы можете либо обернуть его в pymunk.Vec2d, либо переместить -1 внутрь:

-1 * Vec2d(
    mag_force * (temp[1][0] - temp[0][0]),
    mag_force * (temp[1][1] - temp[0][1]),
)
#or 
(
    -1 * mag_force * (temp[1][0] - temp[0][0]),
    -1 * mag_force * (temp[1][1] - temp[0][1]),
)

Наконец, когда я посмотрел на ваш код, я сделал две вещи, чтобы помочь мне отладить проблему. Первый — распечатать/нарисовать проблемные детали.

  1. Вы можете установить отдельные цвета для фигур. Так легче понять, как они себя ведут. Например, вот так shape.color = (255,0,0,255), где кортеж является кортежем RGBA, чтобы сделать фигуру красной.

  2. Мне было полезно нарисовать приложенную силу. Один из способов - сделать что-то вроде этого:

f1 = (
    mag_force * (temp[1][0] - temp[0][0]),
    mag_force * (temp[1][1] - temp[0][1]),
)
masses[0].body.apply_force_at_world_point(
    f1, (masses[0].body.position.x, masses[0].body.position.y)
)
p2 = (100, 100) + pymunk.Vec2d(f1) / 10000
pygame.draw.lines(
    screen,
    (100, 0, 0),
    False,
    [(100, 100), (round(p2.x), round(p2.y))],
    3,
)

(А затем сделайте второй рисунок, например, на (300 100))

Наконец, я хочу добавить, что pymunk.Vec2d имеет функции для выполнения вычислений, таких как расстояние между двумя точками (например, rel_dist = temp[0].get_distance(temp[1])). Но это скорее личный выбор, и я довольно предвзят, так как я автор Pymunk :)

person viblo    schedule 22.10.2020