Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save shricodev/6631aa77a360a43452528d9778d636da to your computer and use it in GitHub Desktop.

Select an option

Save shricodev/6631aa77a360a43452528d9778d636da to your computer and use it in GitHub Desktop.
import math
import sys
import numpy as np
import pygame
from pygame.locals import *
# Initialize pygame
pygame.init()
# Constants
WIDTH, HEIGHT = 800, 600
FPS = 60
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
GRAVITY = 0.5
FRICTION = 0.99
RESTITUTION = 0.8 # Coefficient of restitution (bounciness)
HEX_RADIUS = 200 # Radius of the hexagon
# Set up the display
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Bouncing Ball in Spinning Hexagon")
clock = pygame.time.Clock()
class Ball:
def __init__(self, x, y, radius=15):
self.x = x
self.y = y
self.radius = radius
self.vx = 0
self.vy = 0
def update(self):
# Apply gravity
self.vy += GRAVITY
# Apply friction
self.vx *= FRICTION
self.vy *= FRICTION
# Update position
self.x += self.vx
self.y += self.vy
def draw(self):
pygame.draw.circle(screen, RED, (int(self.x), int(self.y)), self.radius)
def get_hexagon_points(center_x, center_y, radius, angle=0):
points = []
for i in range(6):
# Calculate the angle for each vertex (60 degrees apart)
theta = (
angle + math.pi / 3 * i + math.pi / 6
) # Adding π/6 to rotate the hexagon to make it rest on a side
x = center_x + radius * math.cos(theta)
y = center_y + radius * math.sin(theta)
points.append((x, y))
return points
def line_intersection(line1, line2):
"""Find the intersection point of two lines"""
(x1, y1), (x2, y2) = line1
(x3, y3), (x4, y4) = line2
denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
if denom == 0: # lines are parallel
return None
ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom
ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom
# If ua and ub are between 0-1, lines intersect
if 0 <= ua <= 1 and 0 <= ub <= 1:
x = x1 + ua * (x2 - x1)
y = y1 + ua * (y2 - y1)
return (x, y)
return None
def point_segment_distance(point, segment):
"""Calculate distance from point to line segment"""
(px, py), ((x1, y1), (x2, y2)) = point, segment
# Vector from p to line endpoints
p1_p = px - x1
p1_p2 = x2 - x1
p2_p = px - x2
p1_p_y = py - y1
p1_p2_y = y2 - y1
p2_p_y = py - y2
# Dot product to determine projection
dot = p1_p * p1_p2 + p1_p_y * p1_p2_y
len_sq = p1_p2 * p1_p2 + p1_p2_y * p1_p2_y
# Parametric position of projection
param = dot / len_sq if len_sq != 0 else -1
# Determine nearest point on segment
if param < 0:
xx, yy = x1, y1
elif param > 1:
xx, yy = x2, y2
else:
xx = x1 + param * p1_p2
yy = y1 + param * p1_p2_y
dx = px - xx
dy = py - yy
distance = math.sqrt(dx * dx + dy * dy)
return distance, (xx, yy)
def reflect_velocity(normal, velocity, restitution):
"""Calculate reflected velocity after bouncing off a surface"""
normal = np.array(normal)
normal = normal / np.linalg.norm(normal) # Normalize
velocity = np.array(velocity)
dot_product = np.dot(velocity, normal)
if dot_product < 0: # Only reflect if ball is moving toward the wall
reflected_velocity = velocity - (1 + restitution) * dot_product * normal
return reflected_velocity.tolist()
return velocity.tolist()
# Create ball
ball = Ball(WIDTH // 2, HEIGHT // 2 - 100)
rotation_angle = 0
rotation_speed = 0.03 # radians per frame
def main():
global rotation_angle, rotation_speed
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
rotation_speed = min(0.1, rotation_speed + 0.01)
elif event.key == K_RIGHT:
rotation_speed = max(-0.1, rotation_speed - 0.01)
elif event.key == K_r: # Reset ball position
ball.x, ball.y = WIDTH // 2, HEIGHT // 2 - 100
ball.vx, ball.vy = 0, 0
# Update rotation
rotation_angle += rotation_speed
# Get current hexagon points
hex_points = get_hexagon_points(
WIDTH // 2, HEIGHT // 2, HEX_RADIUS, int(rotation_angle)
)
# Check for collisions with hexagon walls
for i in range(len(hex_points)):
wall_start = hex_points[i]
wall_end = hex_points[(i + 1) % len(hex_points)]
# Check distance from ball to wall
distance, closest_point = point_segment_distance(
(ball.x, ball.y), (wall_start, wall_end)
)
if distance < ball.radius:
# Collision detected - calculate normal vector
wall_vec = (wall_end[0] - wall_start[0], wall_end[1] - wall_start[1])
normal = (-wall_vec[1], wall_vec[0]) # Perpendicular vector
# Reflect velocity
reflected_v = reflect_velocity(normal, (ball.vx, ball.vy), RESTITUTION)
ball.vx, ball.vy = reflected_v
# Move ball outside the wall to prevent sticking
penetration = ball.radius - distance
normal_x = normal[0] / math.sqrt(normal[0] ** 2 + normal[1] ** 2)
normal_y = normal[1] / math.sqrt(normal[0] ** 2 + normal[1] ** 2)
ball.x += normal_x * penetration
ball.y += normal_y * penetration
# Update ball physics
ball.update()
# Draw everything
screen.fill(BLACK)
# Draw hexagon
pygame.draw.polygon(screen, WHITE, hex_points, 2)
# Draw ball
ball.draw()
# Display controls
font = pygame.font.SysFont(None, 24)
text = font.render("LEFT/RIGHT: Rotate Hexagon | R: Reset Ball", True, WHITE)
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(FPS)
if __name__ == "__main__":
main()
@shricodev
Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment