Created
April 15, 2025 15:51
-
-
Save anadim/668040e2c7fbac513bd48fac4923bd21 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import math | |
import pygame | |
# ----------------------------- | |
# Configuration | |
# ----------------------------- | |
WIDTH, HEIGHT = 800, 600 | |
SQUARE_CENTER = (WIDTH // 2, HEIGHT // 2) | |
# The square’s half–side (so the square’s full size is 2*half_side) | |
HALF_SIDE = 150 | |
# The ball’s radius (make sure it is small enough) | |
BALL_RADIUS = 10 | |
# Rotation speed (radians per second) | |
ROT_SPEED = 0.01 | |
# Ball initial position (in global coordinates) and velocity | |
INITIAL_BALL_POS = (SQUARE_CENTER[0] + 50, SQUARE_CENTER[1] + 50) | |
INITIAL_BALL_VEL = (4, 2) | |
# Boundary “margin” in the square’s local coordinates: | |
# We subtract the ball radius so that the ball (a circle) stays entirely inside. | |
BOUNDARY = HALF_SIDE - BALL_RADIUS | |
# ----------------------------- | |
# Pygame initialization | |
# ----------------------------- | |
pygame.init() | |
screen = pygame.display.set_mode((WIDTH, HEIGHT)) | |
pygame.display.set_caption("Bouncing Ball inside a Rotating Square") | |
clock = pygame.time.Clock() | |
# ----------------------------- | |
# Global state variables | |
# ----------------------------- | |
# Represent the ball’s position and velocity in global coordinates. | |
ball_x, ball_y = list(INITIAL_BALL_POS) | |
vx, vy = list(INITIAL_BALL_VEL) | |
# The square’s rotation angle in radians (about its center) | |
angle = 0 | |
# ----------------------------- | |
# Helper functions | |
# ----------------------------- | |
def global_to_local(px, py): | |
"""Transform a point (px,py) in global coordinates to the square’s local frame. | |
(The square’s center is at SQUARE_CENTER and is rotated by 'angle' in global coords.)""" | |
# First, translate so that SQUARE_CENTER is the origin. | |
dx = px - SQUARE_CENTER[0] | |
dy = py - SQUARE_CENTER[1] | |
# Then rotate by -angle so that the square’s axes become horizontal/vertical. | |
local_x = dx * math.cos(angle) + dy * math.sin(angle) | |
local_y = -dx * math.sin(angle) + dy * math.cos(angle) | |
return local_x, local_y | |
def local_to_global(lx, ly): | |
"""Transform a point (lx,ly) in the square’s local frame to global coordinates.""" | |
# Rotate back by angle | |
global_x = lx * math.cos(angle) - ly * math.sin(angle) | |
global_y = lx * math.sin(angle) + ly * math.cos(angle) | |
# Translate back to SQUARE_CENTER | |
return SQUARE_CENTER[0] + global_x, SQUARE_CENTER[1] + global_y | |
def rotate_velocity(vx, vy): | |
"""Rotate a velocity (vx, vy) from the local frame to the global frame.""" | |
# Same rotation as for a point. | |
new_vx = vx * math.cos(angle) - vy * math.sin(angle) | |
new_vy = vx * math.sin(angle) + vy * math.cos(angle) | |
return new_vx, new_vy | |
def get_square_vertices(): | |
"""Return the square’s vertices in global coordinates. | |
The square is defined (in its local frame) by: | |
(HALF_SIDE, HALF_SIDE), (-HALF_SIDE, HALF_SIDE), | |
(-HALF_SIDE, -HALF_SIDE), (HALF_SIDE, -HALF_SIDE) | |
""" | |
local_vertices = [(HALF_SIDE, HALF_SIDE), | |
(-HALF_SIDE, HALF_SIDE), | |
(-HALF_SIDE, -HALF_SIDE), | |
(HALF_SIDE, -HALF_SIDE)] | |
global_vertices = [] | |
for lx, ly in local_vertices: | |
gx, gy = local_to_global(lx, ly) | |
global_vertices.append((gx, gy)) | |
return global_vertices | |
# ----------------------------- | |
# Main loop | |
# ----------------------------- | |
while True: | |
# --- Event handling --- | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
pygame.quit() | |
sys.exit() | |
# --- Time Step --- | |
dt = clock.tick(100) / 10.0 # delta time in seconds | |
# --- Update Square’s rotation --- | |
angle += ROT_SPEED * dt | |
# --- Update ball’s global position (simple Euler integration) --- | |
ball_x += vx * dt | |
ball_y += vy * dt | |
# --- Transform the ball’s position to the square’s local frame --- | |
local_x, local_y = global_to_local(ball_x, ball_y) | |
# Also transform the ball’s velocity to local coordinates: | |
local_vx = vx * math.cos(angle) + vy * math.sin(angle) | |
local_vy = -vx * math.sin(angle) + vy * math.cos(angle) | |
# --- Collision detection and response in the local frame --- | |
# The ball must always be within the square’s boundaries. | |
collision = False | |
if local_x < -BOUNDARY: | |
local_x = -BOUNDARY | |
local_vx = -local_vx | |
collision = True | |
if local_x > BOUNDARY: | |
local_x = BOUNDARY | |
local_vx = -local_vx | |
collision = True | |
if local_y < -BOUNDARY: | |
local_y = -BOUNDARY | |
local_vy = -local_vy | |
collision = True | |
if local_y > BOUNDARY: | |
local_y = BOUNDARY | |
local_vy = -local_vy | |
collision = True | |
# (Optional) print("Collision?", collision) | |
# --- Transform the (corrected) local position back to global --- | |
ball_x, ball_y = local_to_global(local_x, local_y) | |
# And update the global velocity from the local velocity. | |
vx, vy = rotate_velocity(local_vx, local_vy) | |
# --- Drawing --- | |
screen.fill((0, 0, 0)) # clear screen with black | |
# Draw the rotating square (yellow outline) | |
vertices = get_square_vertices() | |
pygame.draw.polygon(screen, (255, 255, 0), vertices, 2) | |
# Draw the ball as a yellow circle | |
pygame.draw.circle(screen, (255, 255, 0), (int(ball_x), int(ball_y)), BALL_RADIUS) | |
# --- Flip display --- | |
pygame.display.flip() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment