//! 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() } }