Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import numpy as np | |
| import plotly.graph_objects as go | |
| import time | |
| G = 6.674e-11 | |
| c = 3e8 | |
| M_sun = 1.989e30 | |
| def black_hole_advanced(mass): | |
| # internal time (auto animation feel) | |
| t = time.time() % 100 | |
| M = mass * M_sun | |
| rs = (2 * G * M) / (c**2) / 1000 # km | |
| fig = go.Figure() | |
| # =============================== | |
| # EVENT HORIZON (PURE BLACK) | |
| # =============================== | |
| u = np.linspace(0, 2*np.pi, 60) | |
| v = np.linspace(0, np.pi, 60) | |
| x = rs * np.outer(np.cos(u), np.sin(v)) | |
| y = rs * np.outer(np.sin(u), np.sin(v)) | |
| z = rs * np.outer(np.ones_like(u), np.cos(v)) | |
| fig.add_surface( | |
| x=x, y=y, z=z, | |
| colorscale=[[0, "black"], [1, "black"]], | |
| showscale=False, | |
| opacity=1.0 | |
| ) | |
| # =============================== | |
| # ACCRETION DISK | |
| # =============================== | |
| r_disk = np.linspace(1.7*rs, 5*rs, 80) | |
| theta = np.linspace(0, 2*np.pi, 160) | |
| R, T = np.meshgrid(r_disk, theta) | |
| Xd = R * np.cos(T) | |
| Yd = R * np.sin(T) | |
| Zd = 0.05 * rs * np.sin(T * 3) | |
| disk_intensity = np.exp(-R / (3*rs)) | |
| fig.add_surface( | |
| x=Xd, y=Yd, z=Zd, | |
| surfacecolor=disk_intensity, | |
| colorscale="Inferno", | |
| opacity=0.9, | |
| showscale=False | |
| ) | |
| # =============================== | |
| # AUTO INFALLING STREAMS (NO UI CONTROL) | |
| # =============================== | |
| n_particles = 60 | |
| angles = np.linspace(0, 2*np.pi, n_particles) | |
| radii = np.linspace(2.5*rs, 6*rs, n_particles) | |
| radii = radii - (t * 0.03 * rs) | |
| radii = np.mod(radii - 1.1*rs, 5*rs) + 1.1*rs | |
| xp = radii * np.cos(angles + t * 0.25) | |
| yp = radii * np.sin(angles + t * 0.25) | |
| zp = np.random.normal(0, 0.035*rs, n_particles) | |
| fig.add_trace(go.Scatter3d( | |
| x=xp, | |
| y=yp, | |
| z=zp, | |
| mode="markers", | |
| marker=dict( | |
| size=2, | |
| color="orange", | |
| opacity=0.5 | |
| ), | |
| showlegend=False | |
| )) | |
| # =============================== | |
| # LIGHT BENDING | |
| # =============================== | |
| # =============================== | |
| # CORRECTED GRAVITATIONAL LENSING | |
| # =============================== | |
| y_vals = np.linspace(-4*rs, 4*rs, 9) | |
| for y0 in y_vals: | |
| x_ray = np.linspace(-8*rs, 8*rs, 500) | |
| y_ray, z_ray = [], [] | |
| for x0 in x_ray: | |
| r = np.sqrt(x0**2 + y0**2) | |
| if r <= 1.02 * rs: | |
| y_ray.append(np.nan) | |
| z_ray.append(np.nan) | |
| continue | |
| # Strong relativistic-style bending | |
| b = abs(y0) + 0.2 * rs # impact parameter | |
| bend_strength = (rs / r) * (rs / b) | |
| y_deflect = bend_strength * y0 * 0.9 | |
| z_deflect = bend_strength * rs * 0.7 | |
| y_ray.append(y0 - y_deflect) | |
| z_ray.append(z_deflect) | |
| fig.add_trace(go.Scatter3d( | |
| x=x_ray, | |
| y=y_ray, | |
| z=z_ray, | |
| mode="lines", | |
| line=dict(width=2), | |
| opacity=0.5, | |
| showlegend=False | |
| )) | |
| # =============================== | |
| # LAYOUT | |
| # =============================== | |
| fig.update_layout( | |
| title="Advanced 3D Black Hole Simulation", | |
| scene=dict( | |
| xaxis=dict(visible=False), | |
| yaxis=dict(visible=False), | |
| zaxis=dict(visible=False), | |
| aspectmode="data", | |
| camera=dict(eye=dict(x=1.6, y=1.6, z=1.1)) | |
| ), | |
| margin=dict(l=0, r=0, b=0, t=40), | |
| paper_bgcolor="black", | |
| plot_bgcolor="black" | |
| ) | |
| return fig | |
| # =============================== | |
| # CLEAN INTERFACE (ONE CONTROL ONLY) | |
| # =============================== | |
| gr.Interface( | |
| fn=black_hole_advanced, | |
| inputs=gr.Slider(5, 50, value=15, label="Black Hole Mass (Solar Masses)"), | |
| outputs=gr.Plot(label="3D Black Hole"), | |
| title="Advanced Black Hole Simulator", | |
| description="Pure black event horizon with automatic infalling matter and accretion disk." | |
| ).launch() |