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>
6e30dc2d7288b691aec553b8261f818c6f9dbba9 · ruwaq sergio · 2026-05-22T19:40:11Z (5 killa ñawpaq) · tayta cfb37af0c · kay commitpi sach'ata qhaway
Semántico sayk'uy · 12
crates/modules/cosmobiologia/cosmobiologia-canvas/src/lib.rs
crates/modules/cosmobiologia/cosmobiologia-render/src/sphere3d.rs
diff --git a/crates/modules/cosmobiologia/cosmobiologia-canvas/src/lib.rs b/crates/modules/cosmobiologia/cosmobiologia-canvas/src/lib.rs
index 3855b35..43952af 100644
--- a/crates/modules/cosmobiologia/cosmobiologia-canvas/src/lib.rs
+++ b/crates/modules/cosmobiologia/cosmobiologia-canvas/src/lib.rs
@@ -171,6 +171,8 @@ pub struct CanvasState {
pub sphere_3d: bool,
/// Orientación de la esfera 3D — la muta el drag.
pub sphere_view: SphereView,
+ /// Si se dibujan las figuras de constelaciones en la esfera 3D.
+ pub show_constellations: bool,
drag_jog: Option<JogDragState>,
drag_pan: Option<PanDragState>,
drag_sphere: Option<SphereDragState>,
@@ -258,6 +260,7 @@ impl Default for CanvasState {
rectificacion: None,
sphere_3d: false,
sphere_view: SphereView::default(),
+ show_constellations: true,
drag_jog: None,
drag_pan: None,
drag_sphere: None,
@@ -383,6 +386,12 @@ impl AstrologyCanvas {
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
/// ni time offset — esos son ortogonales y tienen su propio reset.
pub fn reset_view(&mut self, cx: &mut Context<'_, Self>) {
@@ -821,6 +830,10 @@ impl AstrologyCanvas {
self.toggle_sphere(cx);
return;
}
+ "b" | "B" => {
+ self.toggle_constellations(cx);
+ return;
+ }
_ => return,
};
self.toggle_layer(kind, cx);
@@ -914,6 +927,7 @@ impl Render for AstrologyCanvas {
&theme,
render,
self.state.sphere_view,
+ self.state.show_constellations,
self.state.view_scale,
self.state.view_pan_x,
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
// canvas. Vignette radial — el centro queda claro y los
// bordes se oscurecen, dando profundidad sin "ruido" de
@@ -1005,6 +1049,7 @@ impl Render for AstrologyCanvas {
.child(body),
)
.children(sphere_toggle)
+ .children(constellations_toggle)
}
}
@@ -1080,6 +1125,7 @@ fn render_sphere(
theme: &Theme,
render: &RenderModel,
view: SphereView,
+ show_constellations: bool,
view_scale: f32,
view_pan_x: f32,
view_pan_y: f32,
@@ -1093,6 +1139,7 @@ fn render_sphere(
} else {
Palette::light()
},
+ show_constellations,
..Default::default()
};
let commands = compose_sphere(render, &view, &opts);
diff --git a/crates/modules/cosmobiologia/cosmobiologia-render/src/sphere3d.rs b/crates/modules/cosmobiologia/cosmobiologia-render/src/sphere3d.rs
index 2508d98..a18f8a4 100644
--- a/crates/modules/cosmobiologia/cosmobiologia-render/src/sphere3d.rs
+++ b/crates/modules/cosmobiologia/cosmobiologia-render/src/sphere3d.rs
@@ -313,15 +313,16 @@ fn add_sphere_shading(
cy: center,
r: rad * (0.95 - 0.95 * t),
stroke: None,
- fill: Some(glow.with_alpha(0.04)),
+ fill: Some(glow.with_alpha(0.028)),
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 hy = center - rad * 0.34;
- const HALO: usize = 7;
+ const HALO: usize = 6;
for i in 0..HALO {
let t = i as f32 / (HALO - 1) as f32;
items.push((
@@ -331,7 +332,7 @@ fn add_sphere_shading(
cy: hy,
r: rad * 0.5 * (1.0 - t),
stroke: None,
- fill: Some(highlight.with_alpha(0.05)),
+ fill: Some(highlight.with_alpha(0.018)),
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.
const GAL_POLE_RA: f32 = 192.859;
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):
/// 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 ----------------------------------------
/// 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 ---
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 {
+ add_milky_way_glow(&mut items, &proj, eps, size, zenith);
add_starfield(&mut items, &proj, size, eps);
}