feat(cosmobiologia): esfera 3D — switch de constelaciones + luz de la Vía Láctea
- Switch de constelaciones: botón flotante «● Constelaciones» (o tecla B) que las enciende y apaga en la esfera 3D. - La luminosidad se reparte: el brillo especular fijo a la pantalla se bajó mucho (no giraba, se sentía despegado), y en su lugar la Vía Láctea aporta un resplandor difuso a lo largo del plano galáctico — que SÍ gira con la esfera. Más intenso hacia el centro galáctico (Sagitario, como en el cielo real) y atenuado bajo el horizonte local: la franja como se ve desde la Tierra esa noche. 42 tests verdes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -171,6 +171,8 @@ pub struct CanvasState {
|
|||||||
pub sphere_3d: bool,
|
pub sphere_3d: bool,
|
||||||
/// Orientación de la esfera 3D — la muta el drag.
|
/// Orientación de la esfera 3D — la muta el drag.
|
||||||
pub sphere_view: SphereView,
|
pub sphere_view: SphereView,
|
||||||
|
/// Si se dibujan las figuras de constelaciones en la esfera 3D.
|
||||||
|
pub show_constellations: bool,
|
||||||
drag_jog: Option<JogDragState>,
|
drag_jog: Option<JogDragState>,
|
||||||
drag_pan: Option<PanDragState>,
|
drag_pan: Option<PanDragState>,
|
||||||
drag_sphere: Option<SphereDragState>,
|
drag_sphere: Option<SphereDragState>,
|
||||||
@@ -258,6 +260,7 @@ impl Default for CanvasState {
|
|||||||
rectificacion: None,
|
rectificacion: None,
|
||||||
sphere_3d: false,
|
sphere_3d: false,
|
||||||
sphere_view: SphereView::default(),
|
sphere_view: SphereView::default(),
|
||||||
|
show_constellations: true,
|
||||||
drag_jog: None,
|
drag_jog: None,
|
||||||
drag_pan: None,
|
drag_pan: None,
|
||||||
drag_sphere: None,
|
drag_sphere: None,
|
||||||
@@ -383,6 +386,12 @@ impl AstrologyCanvas {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enciende o apaga las figuras de constelaciones en la esfera 3D.
|
||||||
|
pub fn toggle_constellations(&mut self, cx: &mut Context<'_, Self>) {
|
||||||
|
self.state.show_constellations = !self.state.show_constellations;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
/// Resetea zoom y pan a sus defaults (1.0 y 0,0). No toca rotation
|
/// Resetea zoom y pan a sus defaults (1.0 y 0,0). No toca rotation
|
||||||
/// ni time offset — esos son ortogonales y tienen su propio reset.
|
/// ni time offset — esos son ortogonales y tienen su propio reset.
|
||||||
pub fn reset_view(&mut self, cx: &mut Context<'_, Self>) {
|
pub fn reset_view(&mut self, cx: &mut Context<'_, Self>) {
|
||||||
@@ -821,6 +830,10 @@ impl AstrologyCanvas {
|
|||||||
self.toggle_sphere(cx);
|
self.toggle_sphere(cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
"b" | "B" => {
|
||||||
|
self.toggle_constellations(cx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
self.toggle_layer(kind, cx);
|
self.toggle_layer(kind, cx);
|
||||||
@@ -914,6 +927,7 @@ impl Render for AstrologyCanvas {
|
|||||||
&theme,
|
&theme,
|
||||||
render,
|
render,
|
||||||
self.state.sphere_view,
|
self.state.sphere_view,
|
||||||
|
self.state.show_constellations,
|
||||||
self.state.view_scale,
|
self.state.view_scale,
|
||||||
self.state.view_pan_x,
|
self.state.view_pan_x,
|
||||||
self.state.view_pan_y,
|
self.state.view_pan_y,
|
||||||
@@ -965,6 +979,36 @@ impl Render for AstrologyCanvas {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Switch de constelaciones — solo en modo esfera 3D.
|
||||||
|
let constellations_toggle = (matches!(self.state.mode, CanvasMode::Wheel { .. })
|
||||||
|
&& self.state.sphere_3d)
|
||||||
|
.then(|| {
|
||||||
|
let on = self.state.show_constellations;
|
||||||
|
let label = if on {
|
||||||
|
"● Constelaciones"
|
||||||
|
} else {
|
||||||
|
"○ Constelaciones"
|
||||||
|
};
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.top(px(44.0))
|
||||||
|
.right(px(12.0))
|
||||||
|
.px(px(11.0))
|
||||||
|
.py(px(5.0))
|
||||||
|
.rounded(px(6.0))
|
||||||
|
.bg(theme.bg_panel_alt.clone())
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme.border)
|
||||||
|
.text_size(px(11.0))
|
||||||
|
.text_color(if on { theme.fg_text } else { theme.fg_muted })
|
||||||
|
.cursor_pointer()
|
||||||
|
.child(label)
|
||||||
|
.on_mouse_down(
|
||||||
|
MouseButton::Left,
|
||||||
|
cx.listener(|this, _, _w, cx| this.toggle_constellations(cx)),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// Depth field: capa absoluta detrás del body, ocupa todo el
|
// Depth field: capa absoluta detrás del body, ocupa todo el
|
||||||
// canvas. Vignette radial — el centro queda claro y los
|
// canvas. Vignette radial — el centro queda claro y los
|
||||||
// bordes se oscurecen, dando profundidad sin "ruido" de
|
// bordes se oscurecen, dando profundidad sin "ruido" de
|
||||||
@@ -1005,6 +1049,7 @@ impl Render for AstrologyCanvas {
|
|||||||
.child(body),
|
.child(body),
|
||||||
)
|
)
|
||||||
.children(sphere_toggle)
|
.children(sphere_toggle)
|
||||||
|
.children(constellations_toggle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1080,6 +1125,7 @@ fn render_sphere(
|
|||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
render: &RenderModel,
|
render: &RenderModel,
|
||||||
view: SphereView,
|
view: SphereView,
|
||||||
|
show_constellations: bool,
|
||||||
view_scale: f32,
|
view_scale: f32,
|
||||||
view_pan_x: f32,
|
view_pan_x: f32,
|
||||||
view_pan_y: f32,
|
view_pan_y: f32,
|
||||||
@@ -1093,6 +1139,7 @@ fn render_sphere(
|
|||||||
} else {
|
} else {
|
||||||
Palette::light()
|
Palette::light()
|
||||||
},
|
},
|
||||||
|
show_constellations,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let commands = compose_sphere(render, &view, &opts);
|
let commands = compose_sphere(render, &view, &opts);
|
||||||
|
|||||||
@@ -313,15 +313,16 @@ fn add_sphere_shading(
|
|||||||
cy: center,
|
cy: center,
|
||||||
r: rad * (0.95 - 0.95 * t),
|
r: rad * (0.95 - 0.95 * t),
|
||||||
stroke: None,
|
stroke: None,
|
||||||
fill: Some(glow.with_alpha(0.04)),
|
fill: Some(glow.with_alpha(0.028)),
|
||||||
stroke_w: 0.0,
|
stroke_w: 0.0,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// Brillo especular desplazado hacia la luz.
|
// Brillo especular desplazado hacia la luz — tenue: la luminosidad
|
||||||
|
// viva la reparte la Vía Láctea, que sí gira con la esfera.
|
||||||
let hx = center - rad * 0.34;
|
let hx = center - rad * 0.34;
|
||||||
let hy = center - rad * 0.34;
|
let hy = center - rad * 0.34;
|
||||||
const HALO: usize = 7;
|
const HALO: usize = 6;
|
||||||
for i in 0..HALO {
|
for i in 0..HALO {
|
||||||
let t = i as f32 / (HALO - 1) as f32;
|
let t = i as f32 / (HALO - 1) as f32;
|
||||||
items.push((
|
items.push((
|
||||||
@@ -331,7 +332,7 @@ fn add_sphere_shading(
|
|||||||
cy: hy,
|
cy: hy,
|
||||||
r: rad * 0.5 * (1.0 - t),
|
r: rad * 0.5 * (1.0 - t),
|
||||||
stroke: None,
|
stroke: None,
|
||||||
fill: Some(highlight.with_alpha(0.05)),
|
fill: Some(highlight.with_alpha(0.018)),
|
||||||
stroke_w: 0.0,
|
stroke_w: 0.0,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
@@ -562,6 +563,10 @@ fn add_point_marker(
|
|||||||
/// estándar IAU que fija el plano de la Vía Láctea.
|
/// estándar IAU que fija el plano de la Vía Láctea.
|
||||||
const GAL_POLE_RA: f32 = 192.859;
|
const GAL_POLE_RA: f32 = 192.859;
|
||||||
const GAL_POLE_DEC: f32 = 27.128;
|
const GAL_POLE_DEC: f32 = 27.128;
|
||||||
|
/// Centro galáctico (Sgr A*, J2000): AR 266.405°, Dec −28.936°. Hacia
|
||||||
|
/// ahí la Vía Láctea es más brillante.
|
||||||
|
const GAL_CENTER_RA: f32 = 266.405;
|
||||||
|
const GAL_CENTER_DEC: f32 = -28.936;
|
||||||
|
|
||||||
/// Hash entero → f32 en [0,1). Determinista (variante de splitmix32):
|
/// Hash entero → f32 en [0,1). Determinista (variante de splitmix32):
|
||||||
/// la misma entrada da siempre el mismo valor, así el campo de
|
/// la misma entrada da siempre el mismo valor, así el campo de
|
||||||
@@ -659,6 +664,46 @@ fn add_starfield(items: &mut Vec<(f32, DrawCommand)>, proj: &Projector, size: f3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// El resplandor difuso de la Vía Láctea — una luminosidad repartida a
|
||||||
|
/// lo largo del plano galáctico, no un brillo fijo a la pantalla. Gira
|
||||||
|
/// con la esfera. Es más intensa hacia el centro galáctico (en
|
||||||
|
/// Sagitario, como en el cielo real) y, si hay horizonte, se atenúa en
|
||||||
|
/// la parte que queda bajo tierra esa noche — la franja como se ve
|
||||||
|
/// desde la Tierra ese día.
|
||||||
|
fn add_milky_way_glow(
|
||||||
|
items: &mut Vec<(f32, DrawCommand)>,
|
||||||
|
proj: &Projector,
|
||||||
|
eps: f32,
|
||||||
|
size: f32,
|
||||||
|
zenith: Option<Vec3>,
|
||||||
|
) {
|
||||||
|
let gpole = rot_x(equatorial_dir(GAL_POLE_RA, GAL_POLE_DEC), eps);
|
||||||
|
let gcenter = rot_x(equatorial_dir(GAL_CENTER_RA, GAL_CENTER_DEC), eps);
|
||||||
|
let band = Rgba::opaque(0.78, 0.82, 0.96);
|
||||||
|
for p3 in great_circle_perp(gpole, 54) {
|
||||||
|
// Más brillo hacia el centro galáctico.
|
||||||
|
let toward = (p3.dot(gcenter) * 0.5 + 0.5).clamp(0.0, 1.0);
|
||||||
|
let bright = 0.28 + 0.72 * toward * toward;
|
||||||
|
// Atenuada bajo el horizonte local (no se ve esa noche).
|
||||||
|
let vis = match zenith {
|
||||||
|
Some(z) if p3.dot(z) < 0.0 => 0.40,
|
||||||
|
_ => 1.0,
|
||||||
|
};
|
||||||
|
let p = proj.project(p3);
|
||||||
|
items.push((
|
||||||
|
p.depth - 4.0,
|
||||||
|
DrawCommand::Circle {
|
||||||
|
cx: p.x,
|
||||||
|
cy: p.y,
|
||||||
|
r: size * 0.045,
|
||||||
|
stroke: None,
|
||||||
|
fill: Some(band.with_alpha(0.030 * bright * vis * depth_alpha(p.depth))),
|
||||||
|
stroke_w: 0.0,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Estrellas fijas notables ----------------------------------------
|
// --- Estrellas fijas notables ----------------------------------------
|
||||||
|
|
||||||
/// Latitud eclíptica (grados, J2000) de las estrellas fijas notables
|
/// Latitud eclíptica (grados, J2000) de las estrellas fijas notables
|
||||||
@@ -982,8 +1027,9 @@ pub fn compose_sphere(
|
|||||||
// --- Cuerpo de la esfera: sombreado con volumen ---
|
// --- Cuerpo de la esfera: sombreado con volumen ---
|
||||||
add_sphere_shading(&mut items, pal, center, rad);
|
add_sphere_shading(&mut items, pal, center, rad);
|
||||||
|
|
||||||
// --- Cielo de fondo: estrellas + Vía Láctea (solo tema oscuro) ---
|
// --- Cielo de fondo: Vía Láctea + estrellas (solo tema oscuro) ---
|
||||||
if opts.show_sky && pal.is_dark {
|
if opts.show_sky && pal.is_dark {
|
||||||
|
add_milky_way_glow(&mut items, &proj, eps, size, zenith);
|
||||||
add_starfield(&mut items, &proj, size, eps);
|
add_starfield(&mut items, &proj, size, eps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user