Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The collision between two segments #240

Open
ptwaixingren opened this issue Feb 28, 2024 · 6 comments
Open

The collision between two segments #240

ptwaixingren opened this issue Feb 28, 2024 · 6 comments

Comments

@ptwaixingren
Copy link

Hi,
I'm trying to simulate the collision between two segments, s1 and s2, which have the same shape. The initial positions of s1 and s2 are p1 = Vec2d(100, 100) and p2 = Vec2d(150, 92), respectively. Their initial angles are a1 = np.pi / 2 for s1 and a2 = -np.pi for s2. The elasticity and gravity for both segments are set to 0. The initial scenario is shown below.
simulation_steps_3

s2 approaches s1 using two methods. The first method involves setting the velocity of s2 (v2) to Vec2d(-0.5, 0) at the first step (agents[1].body.velocity = Vec2d(-0.5, 0.)). The second method involves applying an impulse to s2 with the code agents[1].body.apply_impulse_at_world_point(Vec2d(-1.0, 0), agents[1].body.position) at the first step.
After a few steps, the segments collide. Initially, when s1 and s2 collide, I check the total_impulse and total_ke of the system, which are (0, 0) and 0, respectively. These values remain unchanged until the positions of the two segments are as shown in the following figure.
simulation_steps_106
At this step, the two contact points obtained from the collide_handler are [100.0, 100.0] and [101.5, 100.0]. Subsequently, the segments separate from each other, resulting in the final scenario depicted below.
simulation_steps_108

I wonder why this happen? Do you have any pointers for me?
Thank you!

@viblo
Copy link
Owner

viblo commented Feb 28, 2024

Take a look at this example and check what might be different from your setup. If you want you can also set b1.position to something else as I have commented out, its just that then it will keep collide with impulse & ke > 0 for more frames so I left it out to easily see the result.

import math

import pygame

import pymunk
import pymunk.pygame_util

pygame.init()
screen = pygame.display.set_mode((800, 800))
draw_options = pymunk.pygame_util.DrawOptions(screen)

clock = pygame.time.Clock()
space = pymunk.Space()

b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-25, 0), (25, 0), 5)
s1.mass = 1
#b1.position = 300, 125
b1.position = 300, 100
b1.angle = math.pi / 2

b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-25, 0), (25, 0), 5)
s2.mass = 1
b2.position = 400, 100

space.static_body.position = 250, 100
s3 = pymunk.Circle(space.static_body, 25)
space.add(s3)


space.add(b1, s1, b2, s2)

b2.apply_impulse_at_local_point((-10, 0))

def post_solve(arbiter: pymunk.Arbiter, space, data):
    if arbiter.total_ke == 0:
        return
    print("post_solve")
    print(f"total_impulse: {arbiter.total_impulse}, total_ke {arbiter.total_ke}")


handler = space.add_default_collision_handler()
handler.post_solve = post_solve

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()
    screen.fill((0, 0, 0))

    space.debug_draw(draw_options)

    space.step(1 / 50)

    pygame.display.update()
    clock.tick(50)

@ptwaixingren
Copy link
Author

Thanks for your help! I changed the parameters of the shape in your code and found that the problem still persists. The new code is shown below.

import math

import pygame

import pymunk
import pymunk.pygame_util

pygame.init()
screen = pygame.display.set_mode((800, 800))
draw_options = pymunk.pygame_util.DrawOptions(screen)

clock = pygame.time.Clock()
space = pymunk.Space()

b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-8, 0), (8, 0), 8)
s1.mass = 1
#b1.position = 300, 125
b1.position = 300, 100
b1.angle = math.pi / 2

b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-8, 0), (8, 0), 8)
s2.mass = 1
b2.position = 400, 92

# space.static_body.position = 250, 100
# s3 = pymunk.Circle(space.static_body, 25)
# space.add(s3)


space.add(b1, s1, b2, s2)

b2.apply_impulse_at_local_point((-10, 0))

def post_solve(arbiter: pymunk.Arbiter, space, data):
    if arbiter.total_ke == 0:
        return
    print("post_solve")
    print(f"total_impulse: {arbiter.total_impulse}, total_ke {arbiter.total_ke}")


handler = space.add_default_collision_handler()
handler.post_solve = post_solve

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()
    screen.fill((0, 0, 0))

    space.debug_draw(draw_options)

    space.step(1 / 50)

    pygame.display.update()
    clock.tick(50)

I found that the result is the same as my previous question.
test
By the way, I also tried to use the code you commented on, but the collision does not work.
test3
I also tried changing the angle of body1 by setting b1.angle=0. The collision result is incorrect.
test2
However, if I change b1.position to (300, 115), the collision works.
test4

Initially, I guessed the problem might be that the collision detection does not work when the circular parts of the segments collide. Then, I changed the positions and angles of the two objects. The difference between the new value and the old value is very small.

b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-25, 0), (25, 0), 5)
s1.mass = 1
#b1.position = 300, 125
b1.position = 300, 100
# b1.angle = math.pi / 2

b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-25, 0), (25, 0), 5)
s2.mass = 1
#b2.position = 400, 99
b2.position = 400, 101

space.static_body.position = 200, 100
s3 = pymunk.Circle(space.static_body, 25)
space.add(s3)

The collision detection of the two segments works well.
test5
I'm very curious about why this happens. Do you have any pointers for me?
Thank you!

@viblo
Copy link
Owner

viblo commented Mar 2, 2024

Ah, now I understand the issue. Something is broken with the segment/segment collision I think.

When playing around with it I also noticed that there are two different errors possible:

This arrangement will not create a collision at all until they intersect "without" radius:

b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-80, 0), (80, 0), 80)
s1.mass = 1
b1.angle = math.pi / 2
b1.position = 200, 200

b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-80, 0), (80, 0), 80)
s2.mass = 1
b2.position = 500, 280

While this one will create a collision, but no impulse to resolve it etc until the meet at the "no radius" point :

b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-80, 0), (80, 0), 80)
s1.mass = 1
b1.angle = math.pi / 2
# b1.position = 300, 125
b1.position = 200, 200

b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-80, 0), (80, 0), 80)
s2.mass = 1
b2.position = 500, 120

(I increased the size of the segments to better see the collision drawing)

The code for the segment collisions is here in case you are interested: https://github.com/viblo/Chipmunk2D/blob/2e8d4104b7e2380d1a73f5363a931b3eb3de8d07/src/cpCollision.c#L573 I suspect I wont be able to solve this quickly..

Anyway, I saw you sent me an email as well, and I think the idea you had of a workaround with two circles and a box all attached to the same body should work. Something like this, but maybe with some more consideration of the mass in case its important since now its not exactly the same.

b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-80, 0), (80, 0), 80)
s1.mass = 1
b1.angle = math.pi / 2
# b1.position = 300, 125
b1.position = 200, 200

b2 = pymunk.Body()
s2a = pymunk.Circle(b2, 80, (-80, 0))
s2b = pymunk.Circle(b2, 80, (80, 0))
s2c = pymunk.Poly.create_box(b2, (160, 160))
# s2 = pymunk.Segment(b2, (-80, 0), (80, 0), 80)
s2c.mass = 1
b2.position = 500, 120

space.add(b1, s1, b2, s2a, s2b, s2c)

@ptwaixingren
Copy link
Author

Hi, thank you for your reply. I tested the code you provided, and the collision detection works well. I also have two questions about pymunk. The first one is, why should we consider the mass more in the previous example? In the example, the mass center of body 2 is at the center of the box. Could this cause errors when calculating the energy loss during collision? The second question is about the collision_type in the callback function, which seems to change its order when two bodies collide over many steps. I will describe this second question further.
I created two bodies

b1 = pymunk.Body()
s1 = pymunk.Segment(b1, (-80, 0), (80, 0), 80)
s1.mass = 1
b1.angle = math.pi / 2
# b1.position = 300, 125
b1.position = 200, 20
b1.collision_type = 1

b2 = pymunk.Body()
s2 = pymunk.Segment(b2, (-80, 0), (80, 0), 80)
s2.mass = 1
# b1.position = 300, 125
b2.position = 250, 20
b2.collision_type = 2

def post_solve(arb, space, data):
      collide_pair_contact_points = []
      body1_id = arb.shapes[0].collision_type
      body2_id = arb.shapes[1].collision_type

space.add_collision_handler(1, 2).post_solve = post_solve

"When I use body2 to collide with body1 continuously, I check the collision_type in the post_solve function. At first, it will return [1, 2]. Then, after some steps, it will return [2, 1]. Why does this happen?

@viblo
Copy link
Owner

viblo commented Mar 12, 2024

So, to answer your first question about the mass: The moment of the body (body.moment) will be different even when the center of gravity is the same.

For the second case I believe you have a small bug, at least in the code you pasted here. The collision type should be set on the shape and not body. If I change it to do that the ordering is consistent all the time for me.

@ptwaixingren
Copy link
Author

Oh, I see. Thank you for your information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants