//! `cosmobiologia-theme` — paleta simbólica + presets místicos. //! //! Una capa fina sobre [`nahual_theme::Theme`]: el theme base aporta los //! slots de panel/foreground/accent; nosotros agregamos paletas //! semánticas para los elementos (fuego/tierra/aire/agua), los modos //! (cardinal/fijo/mutable), los planetas y los aspectos. //! //! El canvas pide colores por símbolo (`palette.element(Element::Fire)`), //! nunca hex directos. Así una sola tabla controla tanto el dark como el //! light, y cambiar la paleta no requiere tocar el render. #![forbid(unsafe_code)] #![warn(rust_2018_idioms)] use gpui::{Hsla, hsla}; // ===================================================================== // Símbolos // ===================================================================== #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Element { Fire, Earth, Air, Water, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Modality { Cardinal, Fixed, Mutable, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Planet { Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Chiron, NorthNode, SouthNode, Lilith, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum AspectKind { Conjunction, Sextile, Square, Trine, Opposition, Quincunx, Semisextile, Semisquare, Sesquisquare, Quintile, Biquintile, } // ===================================================================== // Paleta // ===================================================================== /// Paleta completa de símbolos astrológicos resuelta a colores HSLA. Las /// dos variantes (`dark` / `light`) comparten estructura — el canvas /// elige según `nahual_theme::Theme::is_dark`. #[derive(Debug, Clone)] pub struct AstroPalette { pub is_dark: bool, pub fire: Hsla, pub earth: Hsla, pub air: Hsla, pub water: Hsla, pub cardinal: Hsla, pub fixed: Hsla, pub mutable: Hsla, pub sun: Hsla, pub moon: Hsla, pub mercury: Hsla, pub venus: Hsla, pub mars: Hsla, pub jupiter: Hsla, pub saturn: Hsla, pub uranus: Hsla, pub neptune: Hsla, pub pluto: Hsla, pub chiron: Hsla, pub north_node: Hsla, pub south_node: Hsla, pub lilith: Hsla, pub conjunction: Hsla, pub sextile: Hsla, pub square: Hsla, pub trine: Hsla, pub opposition: Hsla, pub minor_aspect: Hsla, /// Color del dial zodiacal (anillo exterior). pub dial_ring: Hsla, /// Cusps de casas. pub house_cusp: Hsla, /// Resaltado del ascendente / MC. pub angle_highlight: Hsla, } impl AstroPalette { /// Variante oscura — calibrada para sentirse cálida y mística sin /// caer en saturación de carnaval. Las cusps quedan apenas más /// claras que el fondo, los planetas tienen luminancia media-alta /// para destacar sin glow falso. pub fn dark() -> Self { Self { is_dark: true, // Elementos — saturación alta + luminancia media. Familiares // al símbolo pero suaves para coexistir. fire: hsla(11.0 / 360.0, 0.78, 0.58, 1.0), earth: hsla(95.0 / 360.0, 0.40, 0.48, 1.0), air: hsla(48.0 / 360.0, 0.72, 0.66, 1.0), water: hsla(210.0 / 360.0, 0.68, 0.58, 1.0), cardinal: hsla(340.0 / 360.0, 0.55, 0.62, 1.0), fixed: hsla(258.0 / 360.0, 0.48, 0.58, 1.0), mutable: hsla(170.0 / 360.0, 0.42, 0.55, 1.0), sun: hsla(45.0 / 360.0, 0.92, 0.62, 1.0), moon: hsla(220.0 / 360.0, 0.25, 0.85, 1.0), mercury: hsla(140.0 / 360.0, 0.40, 0.62, 1.0), venus: hsla(330.0 / 360.0, 0.55, 0.70, 1.0), mars: hsla(8.0 / 360.0, 0.78, 0.55, 1.0), jupiter: hsla(38.0 / 360.0, 0.72, 0.62, 1.0), saturn: hsla(28.0 / 360.0, 0.20, 0.50, 1.0), uranus: hsla(195.0 / 360.0, 0.65, 0.62, 1.0), neptune: hsla(225.0 / 360.0, 0.55, 0.66, 1.0), pluto: hsla(280.0 / 360.0, 0.40, 0.45, 1.0), chiron: hsla(75.0 / 360.0, 0.30, 0.55, 1.0), north_node: hsla(35.0 / 360.0, 0.35, 0.70, 1.0), south_node: hsla(35.0 / 360.0, 0.20, 0.45, 1.0), lilith: hsla(310.0 / 360.0, 0.45, 0.40, 1.0), conjunction: hsla(50.0 / 360.0, 0.65, 0.70, 0.85), sextile: hsla(195.0 / 360.0, 0.60, 0.62, 0.75), square: hsla(8.0 / 360.0, 0.75, 0.58, 0.85), trine: hsla(140.0 / 360.0, 0.55, 0.55, 0.80), opposition: hsla(280.0 / 360.0, 0.55, 0.62, 0.85), minor_aspect: hsla(220.0 / 360.0, 0.20, 0.55, 0.55), dial_ring: hsla(40.0 / 360.0, 0.18, 0.78, 0.85), house_cusp: hsla(40.0 / 360.0, 0.12, 0.55, 0.60), angle_highlight: hsla(50.0 / 360.0, 0.95, 0.65, 1.0), } } /// Variante clara — desaturada y con luminancias bajas para que los /// símbolos no compitan con el fondo blanco. Pensada para imprimir. pub fn light() -> Self { Self { is_dark: false, fire: hsla(11.0 / 360.0, 0.65, 0.42, 1.0), earth: hsla(95.0 / 360.0, 0.45, 0.30, 1.0), air: hsla(48.0 / 360.0, 0.55, 0.42, 1.0), water: hsla(210.0 / 360.0, 0.60, 0.38, 1.0), cardinal: hsla(340.0 / 360.0, 0.55, 0.42, 1.0), fixed: hsla(258.0 / 360.0, 0.45, 0.40, 1.0), mutable: hsla(170.0 / 360.0, 0.42, 0.35, 1.0), sun: hsla(38.0 / 360.0, 0.85, 0.45, 1.0), moon: hsla(220.0 / 360.0, 0.22, 0.45, 1.0), mercury: hsla(140.0 / 360.0, 0.45, 0.36, 1.0), venus: hsla(330.0 / 360.0, 0.55, 0.45, 1.0), mars: hsla(8.0 / 360.0, 0.75, 0.40, 1.0), jupiter: hsla(38.0 / 360.0, 0.72, 0.42, 1.0), saturn: hsla(28.0 / 360.0, 0.25, 0.30, 1.0), uranus: hsla(195.0 / 360.0, 0.65, 0.40, 1.0), neptune: hsla(225.0 / 360.0, 0.55, 0.42, 1.0), pluto: hsla(280.0 / 360.0, 0.45, 0.30, 1.0), chiron: hsla(75.0 / 360.0, 0.32, 0.35, 1.0), north_node: hsla(35.0 / 360.0, 0.45, 0.45, 1.0), south_node: hsla(35.0 / 360.0, 0.20, 0.30, 1.0), lilith: hsla(310.0 / 360.0, 0.50, 0.30, 1.0), // Aspectos en light: alpha alta y luminancia media-baja para // que las líneas tengan presencia contra fondo claro. En dark // las alphas pueden ser más bajas porque el contraste contra // el fondo oscuro ya las hace destacar. conjunction: hsla(45.0 / 360.0, 0.70, 0.38, 0.95), sextile: hsla(195.0 / 360.0, 0.65, 0.36, 0.90), square: hsla(8.0 / 360.0, 0.80, 0.38, 0.95), trine: hsla(140.0 / 360.0, 0.60, 0.32, 0.92), opposition: hsla(280.0 / 360.0, 0.60, 0.40, 0.95), minor_aspect: hsla(220.0 / 360.0, 0.30, 0.38, 0.75), // dial_ring: luminancia baja (oscuro sobre blanco) para que // el anillo de signos tenga peso. house_cusp: subimos alpha // y bajamos luminancia para que las cúspides no se laven en // un beige translúcido. dial_ring: hsla(40.0 / 360.0, 0.20, 0.28, 0.95), house_cusp: hsla(40.0 / 360.0, 0.15, 0.32, 0.80), angle_highlight: hsla(38.0 / 360.0, 0.90, 0.38, 1.0), } } /// Variante "papel coloreado" — para preview de impresión. Hue de /// cada slot mantenido; luminancia 0.26-0.34 y saturación alta /// para que sobreviva el ink-bleed sin perder identidad. Sin glow. pub fn print_color() -> Self { Self { is_dark: false, fire: hsla(11.0 / 360.0, 0.78, 0.34, 1.0), earth: hsla(95.0 / 360.0, 0.55, 0.26, 1.0), air: hsla(48.0 / 360.0, 0.78, 0.34, 1.0), water: hsla(210.0 / 360.0, 0.72, 0.32, 1.0), cardinal: hsla(340.0 / 360.0, 0.65, 0.34, 1.0), fixed: hsla(258.0 / 360.0, 0.55, 0.32, 1.0), mutable: hsla(170.0 / 360.0, 0.55, 0.28, 1.0), sun: hsla(35.0 / 360.0, 0.95, 0.34, 1.0), moon: hsla(220.0 / 360.0, 0.35, 0.34, 1.0), mercury: hsla(140.0 / 360.0, 0.55, 0.28, 1.0), venus: hsla(330.0 / 360.0, 0.65, 0.36, 1.0), mars: hsla(8.0 / 360.0, 0.85, 0.34, 1.0), jupiter: hsla(38.0 / 360.0, 0.85, 0.34, 1.0), saturn: hsla(28.0 / 360.0, 0.30, 0.26, 1.0), uranus: hsla(195.0 / 360.0, 0.75, 0.34, 1.0), neptune: hsla(225.0 / 360.0, 0.65, 0.34, 1.0), pluto: hsla(280.0 / 360.0, 0.55, 0.28, 1.0), chiron: hsla(75.0 / 360.0, 0.42, 0.30, 1.0), north_node: hsla(35.0 / 360.0, 0.55, 0.36, 1.0), south_node: hsla(35.0 / 360.0, 0.30, 0.28, 1.0), lilith: hsla(310.0 / 360.0, 0.60, 0.26, 1.0), conjunction: hsla(45.0 / 360.0, 0.75, 0.32, 1.0), sextile: hsla(195.0 / 360.0, 0.70, 0.32, 1.0), square: hsla(8.0 / 360.0, 0.85, 0.34, 1.0), trine: hsla(140.0 / 360.0, 0.65, 0.28, 1.0), opposition: hsla(280.0 / 360.0, 0.65, 0.36, 1.0), minor_aspect: hsla(220.0 / 360.0, 0.40, 0.40, 0.85), dial_ring: hsla(40.0 / 360.0, 0.30, 0.22, 1.0), house_cusp: hsla(40.0 / 360.0, 0.20, 0.28, 0.90), angle_highlight: hsla(15.0 / 360.0, 0.85, 0.36, 1.0), } } /// Variante "papel B&N" — preview de impresión monocromática. /// TODO los slots de planeta y aspecto se reducen a niveles de /// gris. El canvas se encarga de diferenciar aspectos por dash /// pattern y planetas por glyph (el unicode astronómico es /// distintivo aunque pierda color). pub fn print_bw() -> Self { // Tres niveles funcionales: muy oscuro (texto, glyphs // principales), medio (cusps, líneas), claro (fondos, minors). let ink_strong = hsla(0.0, 0.0, 0.10, 1.0); let ink_mid = hsla(0.0, 0.0, 0.30, 1.0); let ink_soft = hsla(0.0, 0.0, 0.50, 0.90); let ink_faint = hsla(0.0, 0.0, 0.55, 0.75); Self { is_dark: false, fire: ink_strong, earth: ink_strong, air: ink_strong, water: ink_strong, cardinal: ink_mid, fixed: ink_mid, mutable: ink_mid, // Planetas: todos en ink_strong para que los glyphs se // lean fuerte. El usuario distingue por el unicode // astronómico, no por hue. sun: ink_strong, moon: ink_strong, mercury: ink_strong, venus: ink_strong, mars: ink_strong, jupiter: ink_strong, saturn: ink_strong, uranus: ink_strong, neptune: ink_strong, pluto: ink_strong, chiron: ink_mid, north_node: ink_mid, south_node: ink_mid, lilith: ink_mid, // Aspectos: el color es uniforme; la diferenciación es por // dash pattern en el painter (square=dashed, trine=solid, // sextile=dotted, etc.). Acá solo damos el "intensity" // base que el painter modula. conjunction: ink_strong, sextile: ink_mid, square: ink_strong, trine: ink_mid, opposition: ink_strong, minor_aspect: ink_faint, dial_ring: ink_mid, house_cusp: ink_soft, angle_highlight: ink_strong, } } pub fn for_theme(theme: &nahual_theme::Theme) -> Self { // Dispatcher por nombre para los themes "papel"; el resto cae // al binary dark/light según `is_dark`. Mantenemos el match // case-insensitive por defensa contra cambios de naming. match theme.name { "Print Color" => Self::print_color(), "Print B&W" => Self::print_bw(), _ if theme.is_dark => Self::dark(), _ => Self::light(), } } /// Devuelve `true` si la paleta es monocromática — los painters /// la usan para activar dash patterns en lugar de diferenciar /// aspectos por color. pub fn is_monochrome(&self) -> bool { // Heurística simple: si conjunction y square (que en color // siempre tienen hues distintos) tienen el mismo hue, // estamos en BW. (self.conjunction.h - self.square.h).abs() < 1e-3 } pub fn element(&self, e: Element) -> Hsla { match e { Element::Fire => self.fire, Element::Earth => self.earth, Element::Air => self.air, Element::Water => self.water, } } pub fn modality(&self, m: Modality) -> Hsla { match m { Modality::Cardinal => self.cardinal, Modality::Fixed => self.fixed, Modality::Mutable => self.mutable, } } pub fn planet(&self, p: Planet) -> Hsla { match p { Planet::Sun => self.sun, Planet::Moon => self.moon, Planet::Mercury => self.mercury, Planet::Venus => self.venus, Planet::Mars => self.mars, Planet::Jupiter => self.jupiter, Planet::Saturn => self.saturn, Planet::Uranus => self.uranus, Planet::Neptune => self.neptune, Planet::Pluto => self.pluto, Planet::Chiron => self.chiron, Planet::NorthNode => self.north_node, Planet::SouthNode => self.south_node, Planet::Lilith => self.lilith, } } pub fn aspect(&self, a: AspectKind) -> Hsla { match a { AspectKind::Conjunction => self.conjunction, AspectKind::Sextile => self.sextile, AspectKind::Square => self.square, AspectKind::Trine => self.trine, AspectKind::Opposition => self.opposition, _ => self.minor_aspect, } } } /// Resuelve un símbolo zodiacal (string) a su elemento. /// Ej. `"aries" → Fire`, `"taurus" → Earth`, … pub fn element_for_sign(sign: &str) -> Option { Some(match sign.to_ascii_lowercase().as_str() { "aries" | "leo" | "sagittarius" => Element::Fire, "taurus" | "virgo" | "capricorn" => Element::Earth, "gemini" | "libra" | "aquarius" => Element::Air, "cancer" | "scorpio" | "pisces" => Element::Water, _ => return None, }) } #[cfg(test)] mod tests { use super::*; #[test] fn element_lookup() { assert_eq!(element_for_sign("aries"), Some(Element::Fire)); assert_eq!(element_for_sign("CAPRICORN"), Some(Element::Earth)); assert_eq!(element_for_sign("zod"), None); } #[test] fn palette_indexes() { let p = AstroPalette::dark(); assert_eq!(p.planet(Planet::Sun), p.sun); assert_eq!(p.element(Element::Water), p.water); } }