11import matplotlib .pyplot as plt
22import matplotlib .animation as animation
33import math
4+ from matplotlib .widgets import Button
45
56# --- Physics constants ---
67G = 6.67430e-11 # gravitational constant
7- dt = 60 * 60 * 24 # time step (24 hours)
8- frame_count = 0 # to animate glow pulsing
8+ dt = 60 * 60 * 24 # 1 day per frame
9+ frame_count = 0
10+ sun_gravity_on = True # Sun gravity toggle
911
1012# --- Planet data ---
1113planets = [
12- {
13- "pos" : [0.39 * 1.496e11 , 0 ], # Mercury
14- "vel" : [0 , 47360 ],
15- "mass" : 3.285e23 ,
16- "x_path" : [],
17- "y_path" : [],
18- "color" : "gray"
19- },
20- {
21- "pos" : [0.72 * 1.496e11 , 0 ], # Venus
22- "vel" : [0 , 35020 ],
23- "mass" : 4.867e24 ,
24- "x_path" : [],
25- "y_path" : [],
26- "color" : "orange"
27- },
28- {
29- "pos" : [1.496e11 , 0 ], # Earth
30- "vel" : [0 , 29780 ],
31- "mass" : 5.972e24 ,
32- "x_path" : [],
33- "y_path" : [],
34- "color" : "blue"
35- },
36- {
37- "pos" : [1.52 * 1.496e11 , 0 ], # Mars
38- "vel" : [0 , 24077 ],
39- "mass" : 6.417e23 ,
40- "x_path" : [],
41- "y_path" : [],
42- "color" : "red"
43- }
14+ {"name" : "Mercury" , "pos" : [0.39 * 1.496e11 , 0 ], "vel" : [0 , 47360 ],
15+ "mass" : 3.285e23 , "mass_on" : True , "x_path" : [], "y_path" : [], "color" : "gray" },
16+ {"name" : "Venus" , "pos" : [0.72 * 1.496e11 , 0 ], "vel" : [0 , 35020 ],
17+ "mass" : 4.867e24 , "mass_on" : True , "x_path" : [], "y_path" : [], "color" : "orange" },
18+ {"name" : "Earth" , "pos" : [1.496e11 , 0 ], "vel" : [0 , 29780 ],
19+ "mass" : 5.972e24 , "mass_on" : True , "x_path" : [], "y_path" : [], "color" : "blue" },
20+ {"name" : "Mars" , "pos" : [1.52 * 1.496e11 , 0 ], "vel" : [0 , 24077 ],
21+ "mass" : 6.417e23 , "mass_on" : True , "x_path" : [], "y_path" : [], "color" : "red" },
22+ {"name" : "Jupiter" , "pos" : [5.2 * 1.496e11 , 0 ], "vel" : [0 , 13070 ],
23+ "mass" : 1.898e27 , "mass_on" : True , "x_path" : [], "y_path" : [], "color" : "brown" }
4424]
4525
26+ # --- Store initial positions/velocities for reset ---
27+ initial_states = []
28+ for p in planets :
29+ initial_states .append ({"pos" : p ["pos" ][:], "vel" : p ["vel" ][:]})
30+
4631# --- Setup figure ---
4732fig , ax = plt .subplots ()
4833fig .patch .set_facecolor ('black' )
4934ax .set_facecolor ("black" )
5035ax .set_aspect ('equal' , adjustable = 'box' )
5136
37+ # --- Physics update ---
5238def update_positions ():
5339 for i , p1 in enumerate (planets ):
5440 fx = fy = 0
5541 for j , p2 in enumerate (planets ):
56- if i != j :
42+ if i != j and p2 [ "mass_on" ] :
5743 dx = p2 ["pos" ][0 ] - p1 ["pos" ][0 ]
5844 dy = p2 ["pos" ][1 ] - p1 ["pos" ][1 ]
5945 dist = math .sqrt (dx ** 2 + dy ** 2 )
6046 F = G * p1 ["mass" ] * p2 ["mass" ] / dist ** 2
6147 fx += F * dx / dist
6248 fy += F * dy / dist
6349
64- # Add Sun gravity
65- dx = 0 - p1 ["pos" ][0 ]
66- dy = 0 - p1 ["pos" ][1 ]
67- dist = math .sqrt (dx ** 2 + dy ** 2 )
68- F = G * p1 ["mass" ] * 1.989e30 / dist ** 2
69- fx += F * dx / dist
70- fy += F * dy / dist
50+ # Sun gravity (toggleable)
51+ if sun_gravity_on :
52+ dx = 0 - p1 ["pos" ][0 ]
53+ dy = 0 - p1 ["pos" ][1 ]
54+ dist = math .sqrt (dx ** 2 + dy ** 2 )
55+ F = G * p1 ["mass" ] * 1.989e30 / dist ** 2
56+ fx += F * dx / dist
57+ fy += F * dy / dist
7158
7259 # Update velocity & position
7360 p1 ["vel" ][0 ] += fx / p1 ["mass" ] * dt
@@ -79,35 +66,75 @@ def update_positions():
7966 p1 ["x_path" ].append (p1 ["pos" ][0 ])
8067 p1 ["y_path" ].append (p1 ["pos" ][1 ])
8168
69+ # --- Animation ---
8270def animate (frame ):
8371 global frame_count
8472 frame_count += 1
8573 ax .clear ()
8674 ax .set_facecolor ("black" )
8775 ax .set_aspect ('equal' , adjustable = 'box' )
88- ax .set_xlim (- 2.5e11 , 2.5e11 )
89- ax .set_ylim (- 2.5e11 , 2.5e11 )
9076 ax .axis ('off' )
9177
9278 update_positions ()
9379
94- # --- Sun breathing glow ---
95- glow_alpha = 0.3 + 0.25 * (math .sin (frame_count * 0.05 ) + 1 ) / 2
96- ax .scatter (0 , 0 , color = 'yellow' , s = 200 , zorder = 3 )
97- ax .scatter (0 , 0 , color = 'yellow' , s = 900 , alpha = glow_alpha , zorder = 1 )
80+ # Automatic scaling
81+ max_distance = max (math .sqrt (p ["pos" ][0 ]** 2 + p ["pos" ][1 ]** 2 ) for p in planets )
82+ margin = 0.3 * max_distance
83+ ax .set_xlim (- max_distance - margin , max_distance + margin )
84+ ax .set_ylim (- max_distance - margin , max_distance + margin )
85+
86+ # Sun core & subtle halo
87+ glow_alpha = 0.2 + 0.1 * (math .sin (frame_count * 0.05 ) + 1 ) / 2
88+ ax .scatter (0 , 0 , color = 'yellow' , s = 200 , zorder = 3 ) # Sun core
89+ ax .scatter (0 , 0 , color = 'yellow' , s = 100 , alpha = glow_alpha , zorder = 1 ) # subtle halo
9890
99- # --- Planets ---
91+ # Planets
10092 for p in planets :
10193 # Orbit path
10294 ax .plot (p ["x_path" ], p ["y_path" ], color = p ["color" ], lw = 1 )
95+ # Planet core
96+ ax .scatter (p ["pos" ][0 ], p ["pos" ][1 ], color = p ["color" ], s = 30 , zorder = 4 )
97+ # Planet glow halo
98+ planet_alpha = 0.1 + 0.05 * (math .sin (frame_count * 0.1 ) + 1 ) / 2
99+ ax .scatter (p ["pos" ][0 ], p ["pos" ][1 ], color = p ["color" ], s = 150 , alpha = planet_alpha , zorder = 2 )
100+
101+ # --- Button callbacks ---
102+ def reset (event ):
103+ for i , p in enumerate (planets ):
104+ p ["pos" ] = initial_states [i ]["pos" ][:]
105+ p ["vel" ] = initial_states [i ]["vel" ][:]
106+ p ["x_path" ].clear ()
107+ p ["y_path" ].clear ()
108+
109+ def toggle_mass (planet_index ):
110+ def inner (event ):
111+ planets [planet_index ]["mass_on" ] = not planets [planet_index ]["mass_on" ]
112+ status = "ON" if planets [planet_index ]["mass_on" ] else "OFF"
113+ print (f"{ planets [planet_index ]['name' ]} mass toggled { status } " )
114+ return inner
115+
116+ def toggle_sun_gravity (event ):
117+ global sun_gravity_on
118+ sun_gravity_on = not sun_gravity_on
119+ status = "ON" if sun_gravity_on else "OFF"
120+ print (f"Sun gravity toggled { status } " )
121+
122+ # --- Add buttons ---
123+ ax_reset = plt .axes ([0.81 , 0.05 , 0.1 , 0.05 ])
124+ btn_reset = Button (ax_reset , 'Reset' )
125+ btn_reset .on_clicked (reset )
103126
104- # Planet
105- ax .scatter (p ["pos" ][0 ], p ["pos" ][1 ], color = p ["color" ], s = 30 , zorder = 3 )
127+ # Buttons for each planet
128+ for i , p in enumerate (planets ):
129+ ax_btn = plt .axes ([0.01 , 0.05 + i * 0.06 , 0.1 , 0.05 ])
130+ btn = Button (ax_btn , p ["name" ])
131+ btn .on_clicked (toggle_mass (i ))
106132
107- # Glow halo
108- planet_alpha = 0.3 + 0.25 * ( math . sin ( frame_count * 0.1 ) + 1 ) / 2
109- ax . scatter ( p [ "pos" ][ 0 ], p [ "pos" ][ 1 ],
110- color = p [ "color" ], s = 200 , alpha = planet_alpha , zorder = 1 )
133+ # Sun gravity toggle button
134+ ax_sun = plt . axes ([ 0.81 , 0.12 , 0.1 , 0.05 ])
135+ btn_sun = Button ( ax_sun , "Sun Gravity" )
136+ btn_sun . on_clicked ( toggle_sun_gravity )
111137
138+ # --- Run animation ---
112139ani = animation .FuncAnimation (fig , animate , frames = 360 , interval = 20 )
113140plt .show ()
0 commit comments