feat(tahuantinsuyu): GR dual-ring + topo ascensional pegado al dial + coords

Dos cambios mayores que cierran el sistema GR/ascensional:

1. Reordenamiento radial — la capa ascensional (topocéntrico
   Polich-Page) se ubica AHORA pegada al sign dial, y la
   geocéntrica clásica queda más adentro. Layout outer→inner:
   - sign_dial (1.00 → 0.88)
   - topo_houses_outer (0.875) / topo_houses_inner (0.79)  ← P-P pegadas al zodiaco
   - topocentric (0.755)                                    ← planetas topo con coords
   - transits (0.71)
   - houses_outer (0.66) / houses_inner (0.54)             ← Placidus geo
   - midpoints (0.50) / bodies (0.47) / bodies_inner (0.44) ← natal geo con coords
   - pd_direct (0.495) / pd_converse (0.425)               ← dual-ring GR
   - aspects (0.41) / progression (0.36) / solar_arc (0.30)

   Topocéntrico default ON (era OFF en la fase previa).
   Coord labels ahora se pintan también en planetas topocéntricos
   (label hacia adentro, no afuera, para no chocar con casas P-P).

2. Sistema GR Direcciones Primarias (dual-ring):
   - Nuevo `PipelineRequest::PrimaryDirections { target_age_years }`.
   - `build_primary_directions_overlay` proyecta cada cuerpo natal
     con `directed_longitude` (key Naibod) en dos direcciones —
     directa y conversa — y emite dos Layer Bodies con
     `module_id` "pd_direct" / "pd_converse".
   - Canvas: nuevos `pd_direct` y `pd_converse` en Radii; en el
     render de Bodies disco más chico y alpha 0.80. Los dos anillos
     se marcan con punteado fino que "abraza" el cinturón natal
     por afuera y por adentro — el natal queda en el centro.
   - Nuevo `PrimaryDirectionsModule` con toggle + slider de edad
     (0..120, step 0.05a). Activable desde el panel.

Tests: 6 shell + 5 coord siguen verdes; el motor matemático
(eternal-astrology directed_longitude) y house system Polich-Page
están testeados desde el commit `e385ab2` en eternal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-18 18:08:29 +00:00
parent 1d49b9ff88
commit 7eb620aa17
5 changed files with 323 additions and 84 deletions
@@ -143,6 +143,7 @@ impl Registry {
r.register(Box::new(lots::LotsModule));
r.register(Box::new(fixed_stars::FixedStarsModule));
r.register(Box::new(topocentric::TopocentricModule));
r.register(Box::new(primary_directions::PrimaryDirectionsModule));
r
}
@@ -840,13 +841,13 @@ pub mod topocentric {
matches!(kind, ChartKind::Natal)
}
fn enabled_by_default(&self) -> bool {
false
true
}
fn controls(&self) -> Vec<Control> {
vec![Control::Toggle {
key: "enabled".into(),
label: "Activar".into(),
default: false,
default: true,
hotkey: None,
}]
}
@@ -855,3 +856,59 @@ pub mod topocentric {
}
}
}
// =====================================================================
// PrimaryDirectionsModule — GR dual-ring (Direct + Converse)
// =====================================================================
pub mod primary_directions {
use super::*;
/// Direcciones Primarias del Sistema GR (García Rosas): cada
/// cuerpo natal se proyecta en dos rings — directa (rotación
/// diurna forward) y conversa (rotación inversa). El usuario
/// scrubea `target_age_years` para ver el movimiento en vivo.
/// Útil para rectificación: un evento real debe coincidir con
/// arcos directos y conversos consistentes si la hora natal es
/// correcta.
pub struct PrimaryDirectionsModule;
impl Module for PrimaryDirectionsModule {
fn id(&self) -> &'static str {
"primary_directions"
}
fn label(&self) -> &'static str {
"Direcciones primarias (GR)"
}
fn description(&self) -> &'static str {
"Dual-ring directas + conversas para rectificación en vivo."
}
fn applies_to(&self, kind: ChartKind) -> bool {
matches!(kind, ChartKind::Natal)
}
fn enabled_by_default(&self) -> bool {
false
}
fn controls(&self) -> Vec<Control> {
vec![
Control::Toggle {
key: "enabled".into(),
label: "Activar".into(),
default: false,
hotkey: None,
},
Control::Slider {
key: "target_age_years".into(),
label: "Edad (años)".into(),
min: 0.0,
max: 120.0,
step: 0.05,
default: 30.0,
},
]
}
fn compute_layers(&self, _chart: &Chart, _cfg: &serde_json::Value) -> Vec<Layer> {
Vec::new()
}
}
}