-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample-clear.py
More file actions
155 lines (124 loc) · 5.05 KB
/
Copy pathexample-clear.py
File metadata and controls
155 lines (124 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import pygame
import math
import sys
import numpy as np
# Initialize pygame
pygame.init()
# Screen dimensions
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Ball Bouncing in Spinning Hexagon")
# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
# Physics parameters
FPS = 60
GRAVITY = 0.5
FRICTION = 0.98
BALL_RESTITUTION = 0.8 # Bounce energy retention
# Ball properties
ball_radius = 15
ball_pos = np.array([WIDTH/2, HEIGHT/2], dtype=float)
ball_vel = np.array([0.0, 0.0], dtype=float)
# Hexagon properties
hex_radius = 200
hex_center = np.array([WIDTH/2, HEIGHT/2])
hex_rotation = 0
hex_rotation_speed = 0.01 # radians per frame
def get_hexagon_vertices(center, radius, rotation):
vertices = []
for i in range(6):
angle = rotation + i * (2 * math.pi / 6)
x = center[0] + radius * math.cos(angle)
y = center[1] + radius * math.sin(angle)
vertices.append((x, y))
return vertices
def get_hexagon_edges(vertices):
edges = []
for i in range(6):
edges.append((vertices[i], vertices[(i+1) % 6]))
return edges
def distance_point_to_line(point, line_start, line_end):
"""Calculate the shortest distance from point to line segment."""
line_vec = np.array(line_end) - np.array(line_start)
point_vec = np.array(point) - np.array(line_start)
line_len = np.linalg.norm(line_vec)
line_unitvec = line_vec / line_len if line_len > 0 else np.array([0, 0])
# Project point onto line vector
projection_length = np.dot(point_vec, line_unitvec)
# If projection is outside the line segment, use distance to nearest endpoint
if projection_length < 0:
return np.linalg.norm(point_vec), line_start, -line_unitvec
elif projection_length > line_len:
return np.linalg.norm(np.array(point) - np.array(line_end)), line_end, line_unitvec
else:
# Find closest point on line
closest_point = np.array(line_start) + projection_length * line_unitvec
# Calculate normalized distance vector (perpendicular to line)
dist_vec = np.array(point) - closest_point
dist = np.linalg.norm(dist_vec)
normal = dist_vec / dist if dist > 0 else np.array([0, 0])
return dist, closest_point, normal
def reflect_velocity(velocity, normal):
"""Reflect velocity vector across normal vector."""
normal = np.array(normal)
velocity = np.array(velocity)
# Calculate the reflection: v - 2(v·n)n
return velocity - 2 * np.dot(velocity, normal) * normal
# Main game loop
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_SPACE:
# Give the ball a random initial push when space is pressed
ball_vel = np.array([np.random.uniform(-10, 10), np.random.uniform(-10, 10)])
# Update hexagon rotation
hex_rotation += hex_rotation_speed
# Apply gravity to ball velocity
ball_vel[1] += GRAVITY
# Update ball position based on velocity
ball_pos += ball_vel
# Get hexagon vertices and edges
hex_vertices = get_hexagon_vertices(hex_center, hex_radius, hex_rotation)
hex_edges = get_hexagon_edges(hex_vertices)
# Check for collisions with hexagon edges
for edge in hex_edges:
distance, closest_point, normal = distance_point_to_line(ball_pos, edge[0], edge[1])
if distance <= ball_radius:
# Move ball outside the edge
penetration = ball_radius - distance
ball_pos += penetration * normal
# Reflect velocity with energy loss
ball_vel = reflect_velocity(ball_vel, normal) * BALL_RESTITUTION
# Apply friction to the component parallel to the edge
edge_vector = np.array(edge[1]) - np.array(edge[0])
edge_unit = edge_vector / np.linalg.norm(edge_vector)
parallel_component = np.dot(ball_vel, edge_unit) * edge_unit
perpendicular_component = ball_vel - parallel_component
# Apply friction only to parallel component
parallel_component *= FRICTION
# Recombine components
ball_vel = parallel_component + perpendicular_component
# Clear the screen
screen.fill(BLACK)
# Draw hexagon
pygame.draw.polygon(screen, WHITE, hex_vertices, 2)
# Draw ball
pygame.draw.circle(screen, RED, (int(ball_pos[0]), int(ball_pos[1])), ball_radius)
# Draw velocity vector (for visualization)
line_end = (int(ball_pos[0] + ball_vel[0] * 3), int(ball_pos[1] + ball_vel[1] * 3))
pygame.draw.line(screen, BLUE, (int(ball_pos[0]), int(ball_pos[1])), line_end, 2)
# Update the display
pygame.display.flip()
# Cap the frame rate
clock.tick(FPS)
pygame.quit()
sys.exit()