import math
import random
import time # For potential future use, though tkinter's after handles timing
# --- Configuration ---
WIDTH = 600
HEIGHT = 600
CENTER_X = WIDTH // 2
CENTER_Y = HEIGHT // 2
HEX_SIZE = 150
HEX_LINE_WIDTH = 3
ROTATION_SPEED = 0.02 # Radians per frame
BALL_RADIUS = 15
GRAVITY = 0.5
BOUNCE_DAMPING = 0.8 # How much velocity is lost on bounce
INITIAL_BALL_SPEED_X = 5
INITIAL_BALL_SPEED_Y = 0
# --- Tkinter setup (if allowed) ---
try:
import tkinter as tk
root = tk.Tk()
root.title("Rotating Hexagon with Bouncing Ball")
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg="black")
canvas.pack()
except ImportError:
print("Tkinter is not available. This script requires a graphical environment.")
print("Without a graphical library, drawing a rotating hexagon and bouncing ball")
print("realistically is not possible in a meaningful way.")
exit()
# --- Global Variables ---
hex_angle = 0
ball_x = CENTER_X
ball_y = CENTER_Y - HEX_SIZE + BALL_RADIUS + 50 # Start slightly above bottom hex
ball_vx = INITIAL_BALL_SPEED_X
ball_vy = INITIAL_BALL_SPEED_Y
ball_color = "white"
# --- Utility Functions ---
def get_hexagon_vertices(center_x, center_y, size, angle_offset):
vertices = []
for i in range(6):
angle_deg = 60 * i - 30 # -30 makes one flat side horizontal
angle_rad = math.radians(angle_deg) + angle_offset
x = center_x + size * math.cos(angle_rad)
y = center_y + size * math.sin(angle_rad)
vertices.append((x, y))
return vertices
def draw_hexagon():
canvas.delete("hexagon") # Clear previous hexagon
vertices = get_hexagon_vertices(CENTER_X, CENTER_Y, HEX_SIZE, hex_angle)
# Flatten the list of tuples for create_polygon
flat_vertices = [coord for vertex in vertices for coord in vertex]
canvas.create_polygon(flat_vertices, outline="blue", width=HEX_LINE_WIDTH, tag="hexagon")
def draw_ball():
canvas.delete("ball") # Clear previous ball
canvas.create_oval(ball_x - BALL_RADIUS, ball_y - BALL_RADIUS,
ball_x + BALL_RADIUS, ball_y + BALL_RADIUS,
fill=ball_color, outline="white", tag="ball")
def get_random_color():
# Generate a random hex color code
return f"#{random.randint(0, 0xFFFFFF):06x}"
# --- Animation Logic ---
def animate():
global hex_angle, ball_x, ball_y, ball_vx, ball_vy, ball_color
# 1. Update Hexagon Rotation
hex_angle += ROTATION_SPEED
draw_hexagon()
# 2. Update Ball Physics
ball_vy += GRAVITY # Apply gravity
ball_x += ball_vx
ball_y += ball_vy
# Collision with Canvas Edges (for debugging or if ball leaves hex)
if ball_x - BALL_RADIUS < 0 or ball_x + BALL_RADIUS > WIDTH:
ball_vx *= -BOUNCE_DAMPING
ball_x = max(BALL_RADIUS, min(WIDTH - BALL_RADIUS, ball_x)) # Keep in bounds
ball_color = get_random_color()
if ball_y - BALL_RADIUS < 0 or ball_y + BALL_RADIUS > HEIGHT:
ball_vy *= -BOUNCE_DAMPING
ball_y = max(BALL_RADIUS, min(HEIGHT - BALL_RADIUS, ball_y)) # Keep in bounds
ball_color = get_random_color()
# Collision with Hexagon (Simplified)
# This is a very simplified collision check. For perfect realism,
# you'd need to calculate intersection with each line segment and reflect
# based on the normal of that segment.
# Here, we'll check if the ball is generally "inside" the hexagon's bounding circle
# and then check for proximity to the vertices or edges in a simpler way.
hex_vertices = get_hexagon_vertices(CENTER_X, CENTER_Y, HEX_SIZE, hex_angle)
# Simple distance check from ball to hexagon center (acts like a bounding circle)
dist_to_center = math.sqrt((ball_x - CENTER_X)**2 + (ball_y - CENTER_Y)**2)
# If the ball is outside a certain radius, or close to an edge
if dist_to_center >= HEX_SIZE - BALL_RADIUS:
# More precise collision detection with edges would involve:
# 1. Iterating through each edge (line segment) of the hexagon.
# 2. Finding the closest point on that line segment to the ball's center.
# 3. If the distance from the ball's center to that closest point is <= BALL_RADIUS,
# then a collision occurred.
# 4. Calculate the normal of the hit edge and reflect the ball's velocity.
# For simplicity, we'll approximate: if it hits the general boundary, reverse.
# This will not be perfectly realistic for complex angles.
# A better approach would require vector math for line-circle intersection.
collided = False
for i in range(6):
p1 = hex_vertices[i]
p2 = hex_vertices[(i + 1) % 6]
# Calculate the vector of the edge
edge_vx = p2[0] - p1[0]
edge_vy = p2[1] - p1[1]
# Calculate the vector from p1 to the ball center
ball_to_p1_x = ball_x - p1[0]
ball_to_p1_y = ball_y - p1[1]
# Project ball_to_p1 onto the edge vector
dot_product = ball_to_p1_x * edge_vx + ball_to_p1_y * edge_vy
len_sq = edge_vx**2 + edge_vy**2
t = dot_product / len_sq if len_sq != 0 else 0
t = max(0, min(1, t)) # Clamp t between 0 and 1 (point on segment)
# Closest point on the segment to the ball center
closest_x = p1[0] + t * edge_vx
closest_y = p1[1] + t * edge_vy
# Distance from ball center to closest point
dist_x = ball_x - closest_x
dist_y = ball_y - closest_y
distance = math.sqrt(dist_x**2 + dist_y**2)
if distance <= BALL_RADIUS:
collided = True
# Calculate normal vector for reflection
normal_x = -edge_vy # Perpendicular to edge (swapped and one negated)
normal_y = edge_vx
norm_length = math.sqrt(normal_x**2 + normal_y**2)
if norm_length != 0:
normal_x /= norm_length
normal_y /= norm_length
# Ensure normal points outwards from hexagon center
# This is crucial for correct reflection
center_to_ball_x = ball_x - CENTER_X
center_to_ball_y = ball_y - CENTER_Y
if (center_to_ball_x * normal_x + center_to_ball_y * normal_y) < 0:
normal_x *= -1
normal_y *= -1
# Reflect velocity
dot_product_vel_norm = ball_vx * normal_x + ball_vy * normal_y
ball_vx = (ball_vx - 2 * dot_product_vel_norm * normal_x) * BOUNCE_DAMPING
ball_vy = (ball_vy - 2 * dot_product_vel_norm * normal_y) * BOUNCE_DAMPING
# Move ball slightly out of collision to prevent sticking
overlap = BALL_RADIUS - distance
ball_x += normal_x * overlap * 1.1 # Move a bit more than overlap
ball_y += normal_y * overlap * 1.1
break # Only handle one collision per frame
if collided:
ball_color = get_random_color()
draw_ball()
# Schedule next frame
canvas.after(20, animate) # 20ms = 50 frames per second
# --- Initial Draw and Start Animation ---
draw_hexagon()
draw_ball()
animate()
root.mainloop()