feat(cosmobiologia-render): compose_wheel rico con palette + dial 3D + spread + coord labels
El render agnóstico ya no es un esqueleto — porta al WASM la mayoría de los detalles visuales que tenía solo el canvas gpui nativo: - palette.rs: Palette dark/light replicando AstroPalette del theme nativo, pero en Rgba (no Hsla de gpui). Métodos planet/aspect/sign para resolver color por id simbólico, + house_ring con hue-shift. - CompositionOpts extendido: palette, dial_3d, draw_ascensional_cross, show_coord_labels, show_minor_aspects. Defaults razonables. - compose_wheel ahora dibuja: background panel, dial 3D bevel (4 strokes concéntricos con alpha decreciente), subdivisiones cada 10° con sign boundaries reforzados, signos con color elemental, casas topocéntricas + geocéntricas en sus rings canónicos, cuerpos con spread anti-solapamiento + clusters + disco coloreado por planeta, coord labels "DD°MM'♈" en natal, aspectos con width inversa al orbe + filtrado opcional de minors, cruz ascensional dashed + pills ASC/MC/DESC/IC. - cosmobiologia-web: nuevo render_model_to_svg_themed(dark: bool) para que el cliente JS elija palette según preferencia del UA. Tests del módulo math siguen verdes (10/10). Smoke test del server: /api/sky.svg ahora emite 22 circles, 77 lines, 52 texts con paleta real (vs ~6 circles, 24 lines, 36 texts del esqueleto previo). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
//! Paleta astrológica agnóstica (`Rgba`, no `Hsla` de gpui). Replica
|
||||
//! los slots de `cosmobiologia-theme::AstroPalette` con `dark()` y
|
||||
//! `light()`, sin arrastrar dependencia de gpui. El canvas nativo
|
||||
//! traduce desde su `AstroPalette` Hsla; el cliente WASM usa esta
|
||||
//! palette directamente.
|
||||
|
||||
use crate::draw::Rgba;
|
||||
|
||||
/// Color en HSL `[0..1]^4`, helper local para construir la palette
|
||||
/// con la misma convención que el theme nativo (que era Hsla de gpui).
|
||||
fn hsla(h_deg: f32, s: f32, l: f32, a: f32) -> Rgba {
|
||||
// Conversión HSL → RGB (algoritmo estándar). H en grados.
|
||||
let h = h_deg / 360.0;
|
||||
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
|
||||
let h6 = (h * 6.0).rem_euclid(6.0);
|
||||
let x = c * (1.0 - (h6 % 2.0 - 1.0).abs());
|
||||
let (r1, g1, b1) = match h6 as i32 {
|
||||
0 => (c, x, 0.0),
|
||||
1 => (x, c, 0.0),
|
||||
2 => (0.0, c, x),
|
||||
3 => (0.0, x, c),
|
||||
4 => (x, 0.0, c),
|
||||
_ => (c, 0.0, x),
|
||||
};
|
||||
let m = l - c / 2.0;
|
||||
Rgba {
|
||||
r: (r1 + m).clamp(0.0, 1.0),
|
||||
g: (g1 + m).clamp(0.0, 1.0),
|
||||
b: (b1 + m).clamp(0.0, 1.0),
|
||||
a,
|
||||
}
|
||||
}
|
||||
|
||||
/// Paleta astrológica completa. Mismos slots que el theme nativo —
|
||||
/// permite que el cliente WASM y el canvas gpui generen las mismas
|
||||
/// decisiones de color en su superficie.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Palette {
|
||||
pub is_dark: bool,
|
||||
// Elementos
|
||||
pub fire: Rgba,
|
||||
pub earth: Rgba,
|
||||
pub air: Rgba,
|
||||
pub water: Rgba,
|
||||
// Planetas
|
||||
pub sun: Rgba,
|
||||
pub moon: Rgba,
|
||||
pub mercury: Rgba,
|
||||
pub venus: Rgba,
|
||||
pub mars: Rgba,
|
||||
pub jupiter: Rgba,
|
||||
pub saturn: Rgba,
|
||||
pub uranus: Rgba,
|
||||
pub neptune: Rgba,
|
||||
pub pluto: Rgba,
|
||||
pub chiron: Rgba,
|
||||
pub north_node: Rgba,
|
||||
pub south_node: Rgba,
|
||||
pub lilith: Rgba,
|
||||
// Aspectos
|
||||
pub conjunction: Rgba,
|
||||
pub sextile: Rgba,
|
||||
pub square: Rgba,
|
||||
pub trine: Rgba,
|
||||
pub opposition: Rgba,
|
||||
pub minor_aspect: Rgba,
|
||||
// Estructura
|
||||
pub dial_ring: Rgba,
|
||||
pub house_cusp: Rgba,
|
||||
pub angle_highlight: Rgba,
|
||||
// Estructura del lienzo (background panel / texto)
|
||||
pub bg_panel: Rgba,
|
||||
pub fg_text: Rgba,
|
||||
pub fg_muted: Rgba,
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
/// Paleta dark — equivalente a `AstroPalette::dark()` del theme
|
||||
/// nativo.
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
is_dark: true,
|
||||
fire: hsla(11.0, 0.78, 0.58, 1.0),
|
||||
earth: hsla(95.0, 0.40, 0.48, 1.0),
|
||||
air: hsla(48.0, 0.72, 0.66, 1.0),
|
||||
water: hsla(210.0, 0.68, 0.58, 1.0),
|
||||
sun: hsla(45.0, 0.92, 0.62, 1.0),
|
||||
moon: hsla(220.0, 0.25, 0.85, 1.0),
|
||||
mercury: hsla(140.0, 0.40, 0.62, 1.0),
|
||||
venus: hsla(330.0, 0.55, 0.70, 1.0),
|
||||
mars: hsla(8.0, 0.78, 0.55, 1.0),
|
||||
jupiter: hsla(38.0, 0.72, 0.62, 1.0),
|
||||
saturn: hsla(28.0, 0.20, 0.50, 1.0),
|
||||
uranus: hsla(195.0, 0.65, 0.62, 1.0),
|
||||
neptune: hsla(225.0, 0.55, 0.66, 1.0),
|
||||
pluto: hsla(280.0, 0.40, 0.45, 1.0),
|
||||
chiron: hsla(75.0, 0.30, 0.55, 1.0),
|
||||
north_node: hsla(35.0, 0.35, 0.70, 1.0),
|
||||
south_node: hsla(35.0, 0.20, 0.45, 1.0),
|
||||
lilith: hsla(310.0, 0.45, 0.40, 1.0),
|
||||
conjunction: hsla(50.0, 0.65, 0.70, 0.85),
|
||||
sextile: hsla(195.0, 0.60, 0.62, 0.75),
|
||||
square: hsla(8.0, 0.75, 0.58, 0.85),
|
||||
trine: hsla(140.0, 0.55, 0.55, 0.80),
|
||||
opposition: hsla(280.0, 0.55, 0.62, 0.85),
|
||||
minor_aspect: hsla(220.0, 0.20, 0.55, 0.55),
|
||||
dial_ring: hsla(40.0, 0.18, 0.78, 0.85),
|
||||
house_cusp: hsla(40.0, 0.12, 0.55, 0.60),
|
||||
angle_highlight: hsla(50.0, 0.95, 0.65, 1.0),
|
||||
bg_panel: hsla(245.0, 0.28, 0.10, 1.0),
|
||||
fg_text: hsla(210.0, 0.35, 0.88, 1.0),
|
||||
fg_muted: hsla(215.0, 0.22, 0.58, 1.0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Paleta light — análoga a `AstroPalette::light()`.
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
is_dark: false,
|
||||
fire: hsla(11.0, 0.65, 0.42, 1.0),
|
||||
earth: hsla(95.0, 0.45, 0.30, 1.0),
|
||||
air: hsla(48.0, 0.55, 0.42, 1.0),
|
||||
water: hsla(210.0, 0.60, 0.38, 1.0),
|
||||
sun: hsla(38.0, 0.85, 0.45, 1.0),
|
||||
moon: hsla(220.0, 0.22, 0.45, 1.0),
|
||||
mercury: hsla(140.0, 0.45, 0.36, 1.0),
|
||||
venus: hsla(330.0, 0.55, 0.45, 1.0),
|
||||
mars: hsla(8.0, 0.75, 0.40, 1.0),
|
||||
jupiter: hsla(38.0, 0.72, 0.42, 1.0),
|
||||
saturn: hsla(28.0, 0.25, 0.30, 1.0),
|
||||
uranus: hsla(195.0, 0.65, 0.40, 1.0),
|
||||
neptune: hsla(225.0, 0.55, 0.42, 1.0),
|
||||
pluto: hsla(280.0, 0.45, 0.30, 1.0),
|
||||
chiron: hsla(75.0, 0.32, 0.35, 1.0),
|
||||
north_node: hsla(35.0, 0.45, 0.45, 1.0),
|
||||
south_node: hsla(35.0, 0.20, 0.30, 1.0),
|
||||
lilith: hsla(310.0, 0.50, 0.30, 1.0),
|
||||
conjunction: hsla(45.0, 0.70, 0.38, 0.95),
|
||||
sextile: hsla(195.0, 0.65, 0.36, 0.90),
|
||||
square: hsla(8.0, 0.80, 0.38, 0.95),
|
||||
trine: hsla(140.0, 0.60, 0.32, 0.92),
|
||||
opposition: hsla(280.0, 0.60, 0.40, 0.95),
|
||||
minor_aspect: hsla(220.0, 0.30, 0.38, 0.75),
|
||||
dial_ring: hsla(40.0, 0.20, 0.28, 0.95),
|
||||
house_cusp: hsla(40.0, 0.15, 0.32, 0.80),
|
||||
angle_highlight: hsla(38.0, 0.90, 0.38, 1.0),
|
||||
bg_panel: hsla(40.0, 0.25, 0.97, 1.0),
|
||||
fg_text: hsla(30.0, 0.15, 0.18, 1.0),
|
||||
fg_muted: hsla(30.0, 0.12, 0.40, 1.0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Color del planeta por su id simbólico (`"sun"`, `"moon"`, …).
|
||||
pub fn planet(&self, sym: &str) -> Rgba {
|
||||
match sym {
|
||||
"sun" => self.sun,
|
||||
"moon" => self.moon,
|
||||
"mercury" => self.mercury,
|
||||
"venus" => self.venus,
|
||||
"mars" => self.mars,
|
||||
"jupiter" => self.jupiter,
|
||||
"saturn" => self.saturn,
|
||||
"uranus" => self.uranus,
|
||||
"neptune" => self.neptune,
|
||||
"pluto" => self.pluto,
|
||||
"chiron" => self.chiron,
|
||||
"north_node" => self.north_node,
|
||||
"south_node" => self.south_node,
|
||||
"lilith" => self.lilith,
|
||||
_ => self.fg_muted,
|
||||
}
|
||||
}
|
||||
|
||||
/// Color del aspecto por su kind.
|
||||
pub fn aspect(&self, kind: &str) -> Rgba {
|
||||
match kind {
|
||||
"conjunction" => self.conjunction,
|
||||
"sextile" => self.sextile,
|
||||
"square" => self.square,
|
||||
"trine" => self.trine,
|
||||
"opposition" => self.opposition,
|
||||
_ => self.minor_aspect,
|
||||
}
|
||||
}
|
||||
|
||||
/// Color del signo zodiacal por su elemento (fire/earth/air/water).
|
||||
pub fn sign(&self, sym: &str) -> Rgba {
|
||||
match sym {
|
||||
"aries" | "leo" | "sagittarius" => self.fire,
|
||||
"taurus" | "virgo" | "capricorn" => self.earth,
|
||||
"gemini" | "libra" | "aquarius" => self.air,
|
||||
"cancer" | "scorpio" | "pisces" => self.water,
|
||||
_ => self.fg_muted,
|
||||
}
|
||||
}
|
||||
|
||||
/// Color del anillo de casas (sistema ascensional Polich-Page).
|
||||
/// Hue-shift de 140° respecto a `house_cusp` para diferenciar
|
||||
/// del dial zodiacal — replica el shift que hace el canvas
|
||||
/// nativo via `house_ring_color`.
|
||||
pub fn house_ring(&self) -> Rgba {
|
||||
// Aproximación rápida en HSL: rotamos el hue por 140°
|
||||
// manteniendo aspecto similar.
|
||||
let base = self.house_cusp;
|
||||
// Conversión RGB → HSL → shift → RGB. Para no agregar
|
||||
// dependencias, lo aproximamos con un mix simple hacia el
|
||||
// verde/teal de la palette base.
|
||||
let target = if self.is_dark {
|
||||
hsla(170.0, 0.30, 0.55, base.a)
|
||||
} else {
|
||||
hsla(170.0, 0.40, 0.32, base.a)
|
||||
};
|
||||
target
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Palette {
|
||||
fn default() -> Self {
|
||||
Self::dark()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user