Update app.py
Browse files
app.py
CHANGED
|
@@ -15,7 +15,7 @@ from PIL import Image
|
|
| 15 |
def bernstein_poly(i, n, t):
|
| 16 |
return comb(n, i) * (t**i) * ((1-t)**(n-i))
|
| 17 |
|
| 18 |
-
def bezier_curve(points, n_times=20):
|
| 19 |
points = np.array(points)
|
| 20 |
t = np.linspace(0, 1, n_times)
|
| 21 |
n = len(points) - 1
|
|
@@ -34,7 +34,7 @@ def learn_from_echo(echo_points):
|
|
| 34 |
return p2 - p1
|
| 35 |
|
| 36 |
# ============================================================
|
| 37 |
-
# SIMULAÇÃO COM RASTRO IDÊNTICO AO ORIGINAL
|
| 38 |
# ============================================================
|
| 39 |
|
| 40 |
class CometSimulation:
|
|
@@ -69,24 +69,26 @@ class CometSimulation:
|
|
| 69 |
dpi = 100
|
| 70 |
fig = plt.figure(figsize=(12, 8), dpi=dpi)
|
| 71 |
|
| 72 |
-
# Layout: 3D à esquerda,
|
| 73 |
gs = fig.add_gridspec(2, 3, hspace=0.3, wspace=0.3)
|
| 74 |
|
| 75 |
ax_3d = fig.add_subplot(gs[:, 0:2], projection='3d')
|
| 76 |
-
ax_info = fig.add_subplot(gs[0, 2])
|
| 77 |
-
ax_stats = fig.add_subplot(gs[1, 2])
|
| 78 |
|
| 79 |
bg_color = '#0a0a0a'
|
| 80 |
fig.patch.set_facecolor(bg_color)
|
| 81 |
for ax in [ax_3d, ax_info, ax_stats]:
|
| 82 |
ax.set_facecolor(bg_color)
|
| 83 |
|
| 84 |
-
#
|
|
|
|
| 85 |
u = np.linspace(0, 2*np.pi, 20)
|
| 86 |
v = np.linspace(0, np.pi, 15)
|
| 87 |
sphere_x = 28 * np.outer(np.cos(u), np.sin(v))
|
| 88 |
sphere_y = 28 * np.outer(np.sin(u), np.sin(v))
|
| 89 |
sphere_z = 28 * np.outer(np.ones_like(u), np.cos(v))
|
|
|
|
| 90 |
|
| 91 |
cycle = 0
|
| 92 |
frame_global = 0
|
|
@@ -94,7 +96,7 @@ class CometSimulation:
|
|
| 94 |
while self.running:
|
| 95 |
cycle += 1
|
| 96 |
|
| 97 |
-
# Ângulo da câmera
|
| 98 |
if auto_rotate:
|
| 99 |
current_angle = (camera_angle + frame_global * 0.8) % 360
|
| 100 |
else:
|
|
@@ -146,19 +148,17 @@ class CometSimulation:
|
|
| 146 |
ax_3d.set_yticklabels([])
|
| 147 |
ax_3d.set_zticklabels([])
|
| 148 |
|
| 149 |
-
#
|
| 150 |
ax_3d.plot_wireframe(sphere_x, sphere_y, sphere_z,
|
| 151 |
color='#4488ff', alpha=0.1, linewidth=0.5)
|
|
|
|
| 152 |
|
| 153 |
# ===== ELEMENTOS ESTÁTICOS (igual ao original) =====
|
| 154 |
-
|
| 155 |
-
ax_3d.scatter(*
|
| 156 |
-
|
| 157 |
-
# Alvo - X vermelho
|
| 158 |
-
ax_3d.scatter(*target, s=150, c='red', marker='X', alpha=0.9)
|
| 159 |
|
| 160 |
# ===== RASTRO IDÊNTICO AO ORIGINAL =====
|
| 161 |
-
#
|
| 162 |
trail_end = len(history) - len(x_cycle) + frame_idx
|
| 163 |
trail_start = max(0, trail_end - 12)
|
| 164 |
trail_segment = history[trail_start:trail_end+1]
|
|
@@ -166,7 +166,7 @@ class CometSimulation:
|
|
| 166 |
if len(trail_segment) > 1:
|
| 167 |
for i in range(len(trail_segment) - 1):
|
| 168 |
p1, p2 = trail_segment[i], trail_segment[i+1]
|
| 169 |
-
# Alpha = 0.8 * (i/12)
|
| 170 |
alpha = 0.8 * (i / 12)
|
| 171 |
ax_3d.plot([p1[0], p2[0]], [p1[1], p2[1]], [p1[2], p2[2]],
|
| 172 |
color='#ff4500', linewidth=4, alpha=alpha)
|
|
@@ -181,35 +181,28 @@ class CometSimulation:
|
|
| 181 |
color='white', fontsize=10,
|
| 182 |
bbox=dict(boxstyle='round', facecolor='#222222', alpha=0.7))
|
| 183 |
|
| 184 |
-
# ===== PAINEL INFO =====
|
| 185 |
-
ax_info.set_title('📊
|
| 186 |
ax_info.axis('off')
|
| 187 |
-
|
| 188 |
-
inertia_text = f"""INÉRCIA:
|
| 189 |
({inertia[0]:.1f}, {inertia[1]:.1f}, {inertia[2]:.1f})
|
| 190 |
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
ECO: {len(echo_points)} pontos"""
|
| 194 |
-
|
| 195 |
ax_info.text(0.1, 0.8, inertia_text, transform=ax_info.transAxes,
|
| 196 |
color='#ffaa00', fontsize=9, verticalalignment='top')
|
| 197 |
|
| 198 |
-
# ===== PAINEL STATS =====
|
| 199 |
-
ax_stats.set_title('📍 POSIÇÃO
|
| 200 |
ax_stats.axis('off')
|
| 201 |
-
|
| 202 |
pos_text = f"""X: {x_cycle[frame_idx]:.1f}
|
| 203 |
Y: {y_cycle[frame_idx]:.1f}
|
| 204 |
Z: {z_cycle[frame_idx]:.1f}"""
|
| 205 |
-
|
| 206 |
ax_stats.text(0.1, 0.6, pos_text, transform=ax_stats.transAxes,
|
| 207 |
color='#88ccff', fontsize=12, fontweight='bold')
|
| 208 |
|
| 209 |
-
# Renderiza
|
| 210 |
fig.canvas.draw()
|
| 211 |
-
|
| 212 |
-
# Converte para array
|
| 213 |
buf = io.BytesIO()
|
| 214 |
fig.savefig(buf, format='png', dpi=dpi,
|
| 215 |
facecolor=bg_color, bbox_inches='tight',
|
|
@@ -220,19 +213,18 @@ Z: {z_cycle[frame_idx]:.1f}"""
|
|
| 220 |
self.queue.put(buf)
|
| 221 |
|
| 222 |
time.sleep(0.01) # igual ao original
|
| 223 |
-
|
| 224 |
# Prepara próximo ciclo
|
| 225 |
pos = target
|
| 226 |
|
| 227 |
# ============================================================
|
| 228 |
-
# INTERFACE
|
| 229 |
# ============================================================
|
| 230 |
|
| 231 |
simulation = CometSimulation()
|
| 232 |
|
| 233 |
def get_frame(camera_angle, auto_rotate):
|
| 234 |
queue = simulation.start(camera_angle, auto_rotate)
|
| 235 |
-
|
| 236 |
try:
|
| 237 |
while True:
|
| 238 |
buf = queue.get(timeout=1.0)
|
|
@@ -243,18 +235,13 @@ def get_frame(camera_angle, auto_rotate):
|
|
| 243 |
finally:
|
| 244 |
simulation.stop()
|
| 245 |
|
| 246 |
-
|
| 247 |
-
with gr.Blocks(theme=gr.themes.Base(
|
| 248 |
-
primary_hue="purple",
|
| 249 |
-
secondary_hue="orange",
|
| 250 |
-
)) as demo:
|
| 251 |
-
|
| 252 |
gr.HTML("""
|
| 253 |
<div style="text-align: center; margin-bottom: 15px;">
|
| 254 |
<h1 style="color: #ff4500; font-size: 2.2rem; margin: 0;">
|
| 255 |
✨ CAUSAL CONVERGENCE SIMULATOR ✨
|
| 256 |
</h1>
|
| 257 |
-
<p style="color: #cccccc;">rastro
|
| 258 |
</div>
|
| 259 |
""")
|
| 260 |
|
|
|
|
| 15 |
def bernstein_poly(i, n, t):
|
| 16 |
return comb(n, i) * (t**i) * ((1-t)**(n-i))
|
| 17 |
|
| 18 |
+
def bezier_curve(points, n_times=20):
|
| 19 |
points = np.array(points)
|
| 20 |
t = np.linspace(0, 1, n_times)
|
| 21 |
n = len(points) - 1
|
|
|
|
| 34 |
return p2 - p1
|
| 35 |
|
| 36 |
# ============================================================
|
| 37 |
+
# SIMULAÇÃO COM RASTRO IDÊNTICO AO ORIGINAL + ESFERA TRANSPARENTE
|
| 38 |
# ============================================================
|
| 39 |
|
| 40 |
class CometSimulation:
|
|
|
|
| 69 |
dpi = 100
|
| 70 |
fig = plt.figure(figsize=(12, 8), dpi=dpi)
|
| 71 |
|
| 72 |
+
# Layout: 3D à esquerda (2/3), painéis à direita (1/3)
|
| 73 |
gs = fig.add_gridspec(2, 3, hspace=0.3, wspace=0.3)
|
| 74 |
|
| 75 |
ax_3d = fig.add_subplot(gs[:, 0:2], projection='3d')
|
| 76 |
+
ax_info = fig.add_subplot(gs[0, 2]) # estatísticas de inércia
|
| 77 |
+
ax_stats = fig.add_subplot(gs[1, 2]) # posição atual
|
| 78 |
|
| 79 |
bg_color = '#0a0a0a'
|
| 80 |
fig.patch.set_facecolor(bg_color)
|
| 81 |
for ax in [ax_3d, ax_info, ax_stats]:
|
| 82 |
ax.set_facecolor(bg_color)
|
| 83 |
|
| 84 |
+
# ========== ESFERA TRANSPARENTE (MELHORIA VISUAL) ==========
|
| 85 |
+
# Malha da esfera – referência espacial sem poluir
|
| 86 |
u = np.linspace(0, 2*np.pi, 20)
|
| 87 |
v = np.linspace(0, np.pi, 15)
|
| 88 |
sphere_x = 28 * np.outer(np.cos(u), np.sin(v))
|
| 89 |
sphere_y = 28 * np.outer(np.sin(u), np.sin(v))
|
| 90 |
sphere_z = 28 * np.outer(np.ones_like(u), np.cos(v))
|
| 91 |
+
# ============================================================
|
| 92 |
|
| 93 |
cycle = 0
|
| 94 |
frame_global = 0
|
|
|
|
| 96 |
while self.running:
|
| 97 |
cycle += 1
|
| 98 |
|
| 99 |
+
# Ângulo da câmera (com rotação automática opcional)
|
| 100 |
if auto_rotate:
|
| 101 |
current_angle = (camera_angle + frame_global * 0.8) % 360
|
| 102 |
else:
|
|
|
|
| 148 |
ax_3d.set_yticklabels([])
|
| 149 |
ax_3d.set_zticklabels([])
|
| 150 |
|
| 151 |
+
# ========== DESENHA A ESFERA TRANSPARENTE ==========
|
| 152 |
ax_3d.plot_wireframe(sphere_x, sphere_y, sphere_z,
|
| 153 |
color='#4488ff', alpha=0.1, linewidth=0.5)
|
| 154 |
+
# ===================================================
|
| 155 |
|
| 156 |
# ===== ELEMENTOS ESTÁTICOS (igual ao original) =====
|
| 157 |
+
ax_3d.scatter(*current_point, s=150, c='lime', alpha=0.7) # ponto inicial do ciclo
|
| 158 |
+
ax_3d.scatter(*target, s=150, c='red', marker='X', alpha=0.9) # alvo
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
# ===== RASTRO IDÊNTICO AO ORIGINAL =====
|
| 161 |
+
# Índices: últimos 12 pontos antes do frame atual
|
| 162 |
trail_end = len(history) - len(x_cycle) + frame_idx
|
| 163 |
trail_start = max(0, trail_end - 12)
|
| 164 |
trail_segment = history[trail_start:trail_end+1]
|
|
|
|
| 166 |
if len(trail_segment) > 1:
|
| 167 |
for i in range(len(trail_segment) - 1):
|
| 168 |
p1, p2 = trail_segment[i], trail_segment[i+1]
|
| 169 |
+
# Alpha = 0.8 * (i/12) – exatamente como no original
|
| 170 |
alpha = 0.8 * (i / 12)
|
| 171 |
ax_3d.plot([p1[0], p2[0]], [p1[1], p2[1]], [p1[2], p2[2]],
|
| 172 |
color='#ff4500', linewidth=4, alpha=alpha)
|
|
|
|
| 181 |
color='white', fontsize=10,
|
| 182 |
bbox=dict(boxstyle='round', facecolor='#222222', alpha=0.7))
|
| 183 |
|
| 184 |
+
# ===== PAINEL INFO (MELHORIA) =====
|
| 185 |
+
ax_info.set_title('📊 INÉRCIA', color='white', fontsize=11)
|
| 186 |
ax_info.axis('off')
|
| 187 |
+
inertia_text = f"""Vetor:
|
|
|
|
| 188 |
({inertia[0]:.1f}, {inertia[1]:.1f}, {inertia[2]:.1f})
|
| 189 |
|
| 190 |
+
Histórico: {len(history)} pontos
|
| 191 |
+
Eco: {len(echo_points)} pontos"""
|
|
|
|
|
|
|
| 192 |
ax_info.text(0.1, 0.8, inertia_text, transform=ax_info.transAxes,
|
| 193 |
color='#ffaa00', fontsize=9, verticalalignment='top')
|
| 194 |
|
| 195 |
+
# ===== PAINEL STATS (MELHORIA) =====
|
| 196 |
+
ax_stats.set_title('📍 POSIÇÃO', color='white', fontsize=11)
|
| 197 |
ax_stats.axis('off')
|
|
|
|
| 198 |
pos_text = f"""X: {x_cycle[frame_idx]:.1f}
|
| 199 |
Y: {y_cycle[frame_idx]:.1f}
|
| 200 |
Z: {z_cycle[frame_idx]:.1f}"""
|
|
|
|
| 201 |
ax_stats.text(0.1, 0.6, pos_text, transform=ax_stats.transAxes,
|
| 202 |
color='#88ccff', fontsize=12, fontweight='bold')
|
| 203 |
|
| 204 |
+
# Renderiza e envia
|
| 205 |
fig.canvas.draw()
|
|
|
|
|
|
|
| 206 |
buf = io.BytesIO()
|
| 207 |
fig.savefig(buf, format='png', dpi=dpi,
|
| 208 |
facecolor=bg_color, bbox_inches='tight',
|
|
|
|
| 213 |
self.queue.put(buf)
|
| 214 |
|
| 215 |
time.sleep(0.01) # igual ao original
|
| 216 |
+
|
| 217 |
# Prepara próximo ciclo
|
| 218 |
pos = target
|
| 219 |
|
| 220 |
# ============================================================
|
| 221 |
+
# INTERFACE GRADIO (COM ROTAÇÃO E PAINÉIS)
|
| 222 |
# ============================================================
|
| 223 |
|
| 224 |
simulation = CometSimulation()
|
| 225 |
|
| 226 |
def get_frame(camera_angle, auto_rotate):
|
| 227 |
queue = simulation.start(camera_angle, auto_rotate)
|
|
|
|
| 228 |
try:
|
| 229 |
while True:
|
| 230 |
buf = queue.get(timeout=1.0)
|
|
|
|
| 235 |
finally:
|
| 236 |
simulation.stop()
|
| 237 |
|
| 238 |
+
with gr.Blocks(theme=gr.themes.Base(primary_hue="purple", secondary_hue="orange")) as demo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
gr.HTML("""
|
| 240 |
<div style="text-align: center; margin-bottom: 15px;">
|
| 241 |
<h1 style="color: #ff4500; font-size: 2.2rem; margin: 0;">
|
| 242 |
✨ CAUSAL CONVERGENCE SIMULATOR ✨
|
| 243 |
</h1>
|
| 244 |
+
<p style="color: #cccccc;">rastro original • esfera transparente • estatísticas</p>
|
| 245 |
</div>
|
| 246 |
""")
|
| 247 |
|