fig = go.Figure()
# Trayectoria real
fig.add_trace(go.Scatter3d(
x=x_clean, y=y_clean, z=z_clean,
mode='lines', line=dict(color='crimson', width=4),
name='Trayectoria real'
))
# Puntos originales
fig.add_trace(go.Scatter3d(
x=points[:, 0], y=points[:, 1], z=points[:, 2],
mode='markers', marker=dict(size=2, color='lightgray', opacity=0.4),
name='Mediciones originales'
))
for r in readings:
# Plano del sensor
px_m, py_m, pz_m = plane_mesh(r['point'], r['normal'], size=10)
fig.add_trace(go.Surface(
x=px_m, y=py_m, z=pz_m,
colorscale=[[0, r['color']], [1, r['color']]],
opacity=0.15, showscale=False,
name=f"Plano {r['name']}", showlegend=False
))
# Proyección sobre el plano
fig.add_trace(go.Scatter3d(
x=r['proj3d'][:, 0], y=r['proj3d'][:, 1], z=r['proj3d'][:, 2],
mode='markers', marker=dict(size=2.5, color=r['color'], opacity=0.7),
name=r['name']
))
fig.update_layout(
title='Los tres sensores proyectan la nube sobre sus planos respectivos',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z',
xaxis=dict(range=[-12, 12]),
yaxis=dict(range=[-8, 8])),
legend=dict(x=0.01, y=0.99),
height=560
)
fig.show()