Skip to content
Permalink
master
Switch branches/tags
Go to file
# Conflicts:
#	TODO.txt
#	docs/src/news.rst
#	docs/src/showcase.rst
#	examples/arrows.py
#	examples/balls_and_lines.py
#	examples/constraints.py
#	examples/copy_and_pickle.py
#	examples/damped_rotary_spring_pointer.py
#	examples/deformable.py
#	examples/flipper.py
#	examples/index_video.py
#	examples/newtons_cradle.py
#	examples/playground.py
#	examples/pygame_util_demo.py
#	examples/spiderweb.py
#	examples/tank.py
#	pymunk/__init__.py
#	pymunk/_pickle.py
#	pymunk/body.py
#	pymunk/collision_handler.py
#	pymunk/constraint.py
#	pymunk/contact_point_set.py
#	pymunk/space.py
#	pymunk/tests/__main__.py
#	pymunk/tests/doctests.py
#	pymunk/tests/test_space.py
#	pymunk/vec2d.py
2 contributors

Users who have contributed to this file

@viblo @oprypin
254 lines (215 sloc) 7.68 KB
"""A screensaver version of Newton's Cradle with an interactive mode.
"""
__docformat__ = "reStructuredText"
import os
import random
import sys
description = """
---- Newton's Cradle ----
A screensaver version of Newton's Cradle with an interactive mode
/s - Run in fullscreen screensaver mode
/p #### - Display a preview of the screensaver using a window handler
/i - Interactive mode
"""
if len(sys.argv) < 2:
print(description)
sys.exit()
is_interactive = False
display_flags = 0
if sys.argv[1] == "/p": # preview mode
os.environ["SDL_VIDEODRIVER"] = "windib"
os.environ["SDL_WINDOWID"] = sys.argv[2]
display_size = (100, 100)
is_interactive = False
### We must set OS env before the pygame imports..
import pygame
if sys.argv[1] == "/s": # fullscreen screensaver mode
display_size = (0, 0)
is_interactive = False
display_flags = (
display_flags | pygame.FULLSCREEN
) # FULLSCREEN) # | DOUBLEBUF | HWSURFACE )
elif sys.argv[1] == "/i": # interactive
display_size = (600, 600)
is_interactive = True
import pymunk as pm
from pymunk import Vec2d
def drawcircle(image, colour, origin, radius, width=0):
if width == 0:
pygame.draw.circle(image, colour, origin, int(radius))
else:
if radius > 65534 / 5:
radius = 65534 / 5
circle = pygame.Surface(
[radius * 2 + width, radius * 2 + width]
).convert_alpha()
circle.fill([0, 0, 0, 0])
pygame.draw.circle(
circle,
colour,
[circle.get_width() / 2, circle.get_height() / 2],
radius + (width / 2),
)
if int(radius - (width / 2)) > 0:
pygame.draw.circle(
circle,
[0, 0, 0, 0],
[circle.get_width() / 2, circle.get_height() / 2],
abs(int(radius - (width / 2))),
)
image.blit(
circle,
[
origin[0] - (circle.get_width() / 2),
origin[1] - (circle.get_height() / 2),
],
)
def reset_bodies(space):
for body in space.bodies:
body.position = Vec2d(*body.start_position)
body.force = 0, 0
body.torque = 0
body.velocity = 0, 0
body.angular_velocity = 0
color = pygame.Color(
random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)
)
for shape in space.shapes:
shape.color = color
def main():
pygame.init()
screen = pygame.display.set_mode(display_size, display_flags)
width, height = screen.get_size()
def to_pygame(p):
"""Small hack to convert pymunk to pygame coordinates"""
return int(p.x), int(-p.y + height)
def from_pygame(p):
return to_pygame(p)
clock = pygame.time.Clock()
running = True
font = pygame.font.Font(None, 16)
### Physics stuff
space = pm.Space()
space.gravity = (0.0, -1900.0)
space.damping = 0.999 # to prevent it from blowing up.
mouse_body = pm.Body(body_type=pm.Body.KINEMATIC)
bodies = []
for x in range(-100, 150, 50):
x += width / 2
offset_y = height / 2
mass = 10
radius = 25
moment = pm.moment_for_circle(mass, 0, radius, (0, 0))
body = pm.Body(mass, moment)
body.position = (x, -125 + offset_y)
body.start_position = Vec2d(*body.position)
shape = pm.Circle(body, radius)
shape.elasticity = 0.9999999
space.add(body, shape)
bodies.append(body)
pj = pm.PinJoint(space.static_body, body, (x, 125 + offset_y), (0, 0))
space.add(pj)
reset_bodies(space)
selected = None
if not is_interactive:
pygame.time.set_timer(pygame.USEREVENT + 1, 70000) # apply force
pygame.time.set_timer(pygame.USEREVENT + 2, 120000) # reset
pygame.event.post(pygame.event.Event(pygame.USEREVENT + 1))
pygame.mouse.set_visible(False)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_p:
pygame.image.save(screen, "newtons_cradle.png")
if event.type == pygame.USEREVENT + 1:
r = random.randint(1, 4)
for body in bodies[0:r]:
body.apply_impulse_at_local_point((-6000, 0))
if event.type == pygame.USEREVENT + 2:
reset_bodies(space)
elif (
event.type == pygame.KEYDOWN
and event.key == pygame.K_r
and is_interactive
):
reset_bodies(space)
elif (
event.type == pygame.KEYDOWN
and event.key == pygame.K_f
and is_interactive
):
r = random.randint(1, 4)
for body in bodies[0:r]:
body.apply_impulse_at_local_point((-6000, 0))
elif event.type == pygame.MOUSEBUTTONDOWN and is_interactive:
if selected != None:
space.remove(selected)
p = from_pygame(Vec2d(*event.pos))
hit = space.point_query_nearest(p, 0, pm.ShapeFilter())
if hit != None:
shape = hit.shape
rest_length = mouse_body.position.get_distance(shape.body.position)
ds = pm.DampedSpring(
mouse_body, shape.body, (0, 0), (0, 0), rest_length, 1000, 10
)
space.add(ds)
selected = ds
elif event.type == pygame.MOUSEBUTTONUP and is_interactive:
if selected != None:
space.remove(selected)
selected = None
elif event.type == pygame.KEYDOWN:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
running = False
mpos = pygame.mouse.get_pos()
p = from_pygame(Vec2d(*mpos))
mouse_body.position = p
### Clear screen
screen.fill(pygame.Color("black"))
### Draw stuff
for c in space.constraints:
pv1 = c.a.position + c.anchor_a
pv2 = c.b.position + c.anchor_b
p1 = to_pygame(pv1)
p2 = to_pygame(pv2)
pygame.draw.aalines(screen, pygame.Color("lightgray"), False, [p1, p2])
for ball in space.shapes:
p = to_pygame(ball.body.position)
drawcircle(screen, ball.color, p, int(ball.radius), 0)
# pygame.draw.circle(screen, ball.color, p, int(ball.radius), 0)
### Update physics
fps = 50
iterations = 25
dt = 1.0 / float(fps) / float(iterations)
for x in range(iterations): # 10 iterations to get a more stable simulation
space.step(dt)
### Flip screen
if is_interactive:
screen.blit(
font.render(
"fps: " + str(clock.get_fps()), True, pygame.Color("white")
),
(0, 0),
)
screen.blit(
font.render(
"Press left mouse button and drag to interact",
True,
pygame.Color("darkgrey"),
),
(5, height - 35),
)
screen.blit(
font.render(
"Press R to reset, any other key to quit",
True,
pygame.Color("darkgrey"),
),
(5, height - 20),
)
pygame.display.flip()
clock.tick(fps)
if __name__ == "__main__":
sys.exit(main())