Skip to content

Instantly share code, notes, and snippets.

@anadim
Created April 15, 2025 15:51
Show Gist options
  • Save anadim/668040e2c7fbac513bd48fac4923bd21 to your computer and use it in GitHub Desktop.
Save anadim/668040e2c7fbac513bd48fac4923bd21 to your computer and use it in GitHub Desktop.
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