fork download
  1. import math
  2. import random
  3. import time # For potential future use, though tkinter's after handles timing
  4.  
  5. # --- Configuration ---
  6. WIDTH = 600
  7. HEIGHT = 600
  8. CENTER_X = WIDTH // 2
  9. CENTER_Y = HEIGHT // 2
  10. HEX_SIZE = 150
  11. HEX_LINE_WIDTH = 3
  12. ROTATION_SPEED = 0.02 # Radians per frame
  13.  
  14. BALL_RADIUS = 15
  15. GRAVITY = 0.5
  16. BOUNCE_DAMPING = 0.8 # How much velocity is lost on bounce
  17. INITIAL_BALL_SPEED_X = 5
  18. INITIAL_BALL_SPEED_Y = 0
  19.  
  20. # --- Tkinter setup (if allowed) ---
  21. try:
  22. import tkinter as tk
  23. root = tk.Tk()
  24. root.title("Rotating Hexagon with Bouncing Ball")
  25. canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg="black")
  26. canvas.pack()
  27. except ImportError:
  28. print("Tkinter is not available. This script requires a graphical environment.")
  29. print("Without a graphical library, drawing a rotating hexagon and bouncing ball")
  30. print("realistically is not possible in a meaningful way.")
  31. exit()
  32.  
  33. # --- Global Variables ---
  34. hex_angle = 0
  35. ball_x = CENTER_X
  36. ball_y = CENTER_Y - HEX_SIZE + BALL_RADIUS + 50 # Start slightly above bottom hex
  37. ball_vx = INITIAL_BALL_SPEED_X
  38. ball_vy = INITIAL_BALL_SPEED_Y
  39. ball_color = "white"
  40.  
  41. # --- Utility Functions ---
  42. def get_hexagon_vertices(center_x, center_y, size, angle_offset):
  43. vertices = []
  44. for i in range(6):
  45. angle_deg = 60 * i - 30 # -30 makes one flat side horizontal
  46. angle_rad = math.radians(angle_deg) + angle_offset
  47. x = center_x + size * math.cos(angle_rad)
  48. y = center_y + size * math.sin(angle_rad)
  49. vertices.append((x, y))
  50. return vertices
  51.  
  52. def draw_hexagon():
  53. canvas.delete("hexagon") # Clear previous hexagon
  54. vertices = get_hexagon_vertices(CENTER_X, CENTER_Y, HEX_SIZE, hex_angle)
  55. # Flatten the list of tuples for create_polygon
  56. flat_vertices = [coord for vertex in vertices for coord in vertex]
  57. canvas.create_polygon(flat_vertices, outline="blue", width=HEX_LINE_WIDTH, tag="hexagon")
  58.  
  59. def draw_ball():
  60. canvas.delete("ball") # Clear previous ball
  61. canvas.create_oval(ball_x - BALL_RADIUS, ball_y - BALL_RADIUS,
  62. ball_x + BALL_RADIUS, ball_y + BALL_RADIUS,
  63. fill=ball_color, outline="white", tag="ball")
  64.  
  65. def get_random_color():
  66. # Generate a random hex color code
  67. return f"#{random.randint(0, 0xFFFFFF):06x}"
  68.  
  69. # --- Animation Logic ---
  70. def animate():
  71. global hex_angle, ball_x, ball_y, ball_vx, ball_vy, ball_color
  72.  
  73. # 1. Update Hexagon Rotation
  74. hex_angle += ROTATION_SPEED
  75. draw_hexagon()
  76.  
  77. # 2. Update Ball Physics
  78. ball_vy += GRAVITY # Apply gravity
  79. ball_x += ball_vx
  80. ball_y += ball_vy
  81.  
  82. # Collision with Canvas Edges (for debugging or if ball leaves hex)
  83. if ball_x - BALL_RADIUS < 0 or ball_x + BALL_RADIUS > WIDTH:
  84. ball_vx *= -BOUNCE_DAMPING
  85. ball_x = max(BALL_RADIUS, min(WIDTH - BALL_RADIUS, ball_x)) # Keep in bounds
  86. ball_color = get_random_color()
  87. if ball_y - BALL_RADIUS < 0 or ball_y + BALL_RADIUS > HEIGHT:
  88. ball_vy *= -BOUNCE_DAMPING
  89. ball_y = max(BALL_RADIUS, min(HEIGHT - BALL_RADIUS, ball_y)) # Keep in bounds
  90. ball_color = get_random_color()
  91.  
  92. # Collision with Hexagon (Simplified)
  93. # This is a very simplified collision check. For perfect realism,
  94. # you'd need to calculate intersection with each line segment and reflect
  95. # based on the normal of that segment.
  96. # Here, we'll check if the ball is generally "inside" the hexagon's bounding circle
  97. # and then check for proximity to the vertices or edges in a simpler way.
  98.  
  99. hex_vertices = get_hexagon_vertices(CENTER_X, CENTER_Y, HEX_SIZE, hex_angle)
  100.  
  101. # Simple distance check from ball to hexagon center (acts like a bounding circle)
  102. dist_to_center = math.sqrt((ball_x - CENTER_X)**2 + (ball_y - CENTER_Y)**2)
  103.  
  104. # If the ball is outside a certain radius, or close to an edge
  105. if dist_to_center >= HEX_SIZE - BALL_RADIUS:
  106. # More precise collision detection with edges would involve:
  107. # 1. Iterating through each edge (line segment) of the hexagon.
  108. # 2. Finding the closest point on that line segment to the ball's center.
  109. # 3. If the distance from the ball's center to that closest point is <= BALL_RADIUS,
  110. # then a collision occurred.
  111. # 4. Calculate the normal of the hit edge and reflect the ball's velocity.
  112.  
  113. # For simplicity, we'll approximate: if it hits the general boundary, reverse.
  114. # This will not be perfectly realistic for complex angles.
  115. # A better approach would require vector math for line-circle intersection.
  116.  
  117. collided = False
  118. for i in range(6):
  119. p1 = hex_vertices[i]
  120. p2 = hex_vertices[(i + 1) % 6]
  121.  
  122. # Calculate the vector of the edge
  123. edge_vx = p2[0] - p1[0]
  124. edge_vy = p2[1] - p1[1]
  125.  
  126. # Calculate the vector from p1 to the ball center
  127. ball_to_p1_x = ball_x - p1[0]
  128. ball_to_p1_y = ball_y - p1[1]
  129.  
  130. # Project ball_to_p1 onto the edge vector
  131. dot_product = ball_to_p1_x * edge_vx + ball_to_p1_y * edge_vy
  132. len_sq = edge_vx**2 + edge_vy**2
  133. t = dot_product / len_sq if len_sq != 0 else 0
  134. t = max(0, min(1, t)) # Clamp t between 0 and 1 (point on segment)
  135.  
  136. # Closest point on the segment to the ball center
  137. closest_x = p1[0] + t * edge_vx
  138. closest_y = p1[1] + t * edge_vy
  139.  
  140. # Distance from ball center to closest point
  141. dist_x = ball_x - closest_x
  142. dist_y = ball_y - closest_y
  143. distance = math.sqrt(dist_x**2 + dist_y**2)
  144.  
  145. if distance <= BALL_RADIUS:
  146. collided = True
  147. # Calculate normal vector for reflection
  148. normal_x = -edge_vy # Perpendicular to edge (swapped and one negated)
  149. normal_y = edge_vx
  150. norm_length = math.sqrt(normal_x**2 + normal_y**2)
  151. if norm_length != 0:
  152. normal_x /= norm_length
  153. normal_y /= norm_length
  154.  
  155. # Ensure normal points outwards from hexagon center
  156. # This is crucial for correct reflection
  157. center_to_ball_x = ball_x - CENTER_X
  158. center_to_ball_y = ball_y - CENTER_Y
  159. if (center_to_ball_x * normal_x + center_to_ball_y * normal_y) < 0:
  160. normal_x *= -1
  161. normal_y *= -1
  162.  
  163. # Reflect velocity
  164. dot_product_vel_norm = ball_vx * normal_x + ball_vy * normal_y
  165. ball_vx = (ball_vx - 2 * dot_product_vel_norm * normal_x) * BOUNCE_DAMPING
  166. ball_vy = (ball_vy - 2 * dot_product_vel_norm * normal_y) * BOUNCE_DAMPING
  167.  
  168. # Move ball slightly out of collision to prevent sticking
  169. overlap = BALL_RADIUS - distance
  170. ball_x += normal_x * overlap * 1.1 # Move a bit more than overlap
  171. ball_y += normal_y * overlap * 1.1
  172. break # Only handle one collision per frame
  173.  
  174. if collided:
  175. ball_color = get_random_color()
  176.  
  177.  
  178. draw_ball()
  179.  
  180. # Schedule next frame
  181. canvas.after(20, animate) # 20ms = 50 frames per second
  182.  
  183. # --- Initial Draw and Start Animation ---
  184. draw_hexagon()
  185. draw_ball()
  186. animate()
  187.  
  188. root.mainloop()
Success #stdin #stdout 0.08s 14316KB
stdin
Standard input is empty
stdout
Tkinter is not available. This script requires a graphical environment.
Without a graphical library, drawing a rotating hexagon and bouncing ball
realistically is not possible in a meaningful way.