feat(cosmobiologia): Tierra interior — tinte mar/continente + día/noche

La Tierra interior ahora se lee como un planeta:

- Mar y continentes teñidos distinto: el mar es un disco azul, los
  continentes son polígonos rellenos de verde. Para eso se sumó la
  primitiva DrawCommand::Polygon (relleno + trazo) — agnóstica, con su
  traductor GPUI y su emisor SVG.
- Sombreado día/noche según el Sol de la carta: el hemisferio que mira
  al Sol se ilumina (resplandor concéntrico sobre el punto subsolar,
  que se apaga si el Sol queda detrás de la Tierra), el terminador
  marca la línea día/noche, y cada continente se tiñe verde claro u
  oscuro según esté de día o de noche. El observador se atenúa si
  naci­ó de noche.

42 tests verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-22 19:33:46 +00:00
parent 267e54f974
commit cfb37af0cf
3 changed files with 190 additions and 23 deletions
@@ -1138,6 +1138,16 @@ fn render_sphere(
);
}
}
DrawCommand::Polygon { points, fill, stroke, stroke_w } => {
paint_polygon(
window,
points,
ox,
oy,
(*fill).map(rgba_to_hsla),
(*stroke).map(|c| (rgba_to_hsla(c), *stroke_w)),
);
}
DrawCommand::Text { .. } => {}
}
}
@@ -3113,6 +3123,43 @@ fn paint_glow(window: &mut Window, cx: f32, cy: f32, base_r: f32, color: Hsla) {
}
}
/// Pinta un polígono cerrado: relleno y/o trazo. `points` en coords del
/// lienzo (sin el offset del bounds — se le suma `ox`/`oy`).
fn paint_polygon(
window: &mut Window,
points: &[(f32, f32)],
ox: f32,
oy: f32,
fill: Option<Hsla>,
stroke: Option<(Hsla, f32)>,
) {
if points.len() < 3 {
return;
}
if let Some(color) = fill {
let mut b = PathBuilder::fill();
b.move_to(point(px(ox + points[0].0), px(oy + points[0].1)));
for p in &points[1..] {
b.line_to(point(px(ox + p.0), px(oy + p.1)));
}
b.close();
if let Ok(path) = b.build() {
window.paint_path(path, color);
}
}
if let Some((color, w)) = stroke {
let mut b = PathBuilder::stroke(px(w));
b.move_to(point(px(ox + points[0].0), px(oy + points[0].1)));
for p in &points[1..] {
b.line_to(point(px(ox + p.0), px(oy + p.1)));
}
b.line_to(point(px(ox + points[0].0), px(oy + points[0].1)));
if let Ok(path) = b.build() {
window.paint_path(path, color);
}
}
}
fn fill_circle(window: &mut Window, cx: f32, cy: f32, r: f32, color: Hsla) {
const SEGMENTS: usize = 32;
let mut builder = PathBuilder::fill();