refactor(tahuantinsuyu): aros a/b/c/d/e canónicos + un solo anillo por bloque

Reorganización de los radios siguiendo nomenclatura clara del
usuario (de afuera hacia adentro):

  Aro A (1.00·r)  externo zodiaco
  Zona AB         signos … (sign dial)
  Aro B (0.92·r)  interno zodiaco / externo bloque ascensional
  Zona BC         casas topo (cusps b→c) + planetas topo + coords
  Aro C (0.78·r)  separador ascensional / casas geo
  Zona CD         casas geo (cusps c→d) + sus coords
  Aro D (0.62·r)  externo planetas natales
  (junto a D)     planetas natales + coords
  Aro E (0.49·r)  anclaje invisible de líneas de aspecto

Overlays opcionales (transits, midpoints, progression, solar arc,
composite) ahora viven todos INTERIORES al aro E — solo se pintan
cuando su módulo está activo, así no compiten con el layout base.

Cambios concretos en Radii:
- Doc `Radii` reescrito con la nomenclatura a/b/c/d/e arriba.
- Eliminado `bodies_inner` (la idea del "carril doble" confundía
  con el sistema de casas; ahora hay un único anillo por bloque).
- Coord labels uniformes — `label_r = ring - disk_size * 0.7`
  (hacia adentro) tanto para natal como para topocéntrico, ya que
  cada bloque tiene su propia zona radial bien definida.
- Coord pills de cusps de casa ahora se posan dentro de su propia
  zona (`r_in + (r_out - r_in) * 0.18`) — no se salen del bloque.
- Stroke 3D del bloque de planetas natales se mueve a `houses_inner`
  (= aro D), que es el verdadero borde visible del cinturón.

Si el usuario quiere un anillo adicional para algo en particular
(p. ej. transits clásico afuera del zodiaco), se agrega cuando
ese módulo se active.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-18 18:25:07 +00:00
parent 7eb620aa17
commit a7214e0498
@@ -1067,13 +1067,25 @@ fn render_wheel(
} }
// House numbers + (opcional) coord del cusp. // House numbers + (opcional) coord del cusp.
//
// El layer `natal` usa Zona CD (entre aros C y D); `topocentric`
// usa Zona BC (entre aros B y C). Los house numbers se posan al
// centro de la zona; las coord pills se posan adyacentes al aro
// interior de la propia zona, así no se sale del bloque.
if visible.get(&LayerKind::Houses).copied().unwrap_or(true) { if visible.get(&LayerKind::Houses).copied().unwrap_or(true) {
let house_label_r = (radii.houses_outer + radii.houses_inner) / 2.0;
let house_label_color = house_ring_color(palette); let house_label_color = house_ring_color(palette);
for layer in &render.layers { for layer in &render.layers {
if matches!(layer.kind, LayerKind::Houses) { if matches!(layer.kind, LayerKind::Houses) {
let is_topo = layer.module_id == "topocentric";
let (r_out, r_in) = if is_topo {
(radii.topo_houses_outer, radii.topo_houses_inner)
} else {
(radii.houses_outer, radii.houses_inner)
};
let label_r = (r_out + r_in) / 2.0;
let coord_r = r_in + (r_out - r_in) * 0.18;
for g in &layer.glyphs { for g in &layer.glyphs {
let (x, y) = polar_to_screen(g.deg, asc, rot_offset, house_label_r); let (x, y) = polar_to_screen(g.deg, asc, rot_offset, label_r);
if let Some(h) = g.house { if let Some(h) = g.house {
wheel = wheel.child(centered_glyph( wheel = wheel.child(centered_glyph(
cx_center + x, cx_center + x,
@@ -1083,14 +1095,10 @@ fn render_wheel(
format!("{}", h).into(), format!("{}", h).into(),
house_label_color, house_label_color,
)); ));
// Coord del cusp justo dentro del anillo de
// casas — los grados se imprimen en una pill
// pequeña pegada al radio del cusp.
if show_coords { if show_coords {
let coord = format_coord_compact(g.deg); let coord = format_coord_compact(g.deg);
let label_r = radii.houses_inner - 8.0 * s;
let (lx, ly) = let (lx, ly) =
polar_to_screen(g.deg, asc, rot_offset, label_r); polar_to_screen(g.deg, asc, rot_offset, coord_r);
wheel = wheel.child(coord_label( wheel = wheel.child(coord_label(
cx_center + lx, cx_center + lx,
cy_center + ly, cy_center + ly,
@@ -1167,23 +1175,14 @@ fn render_wheel(
with_alpha(color, 0.85), with_alpha(color, 0.85),
)); ));
// Coord label: grado dentro del signo + glyph del // Coord label en pill — junto al planeta, dentro
// signo, pintado afuera del disco del planeta // de su propia zona radial. Topo en zona BC
// (radialmente). Se pinta para el natal (afuera) // (label hacia C, lado interior); natal en
// y para el topocéntrico (más afuera aún, hacia // zona cerca de D (label hacia E, lado interior
// el sign dial)los dos sistemas conviven con // del cinturón natal — del lado de los aspectos).
// sus coords. Otros overlays (progression, solar
// arc) usan badges en el footer.
if show_coords && (is_natal || is_topo) { if show_coords && (is_natal || is_topo) {
let coord = format_coord_compact(g.deg); let coord = format_coord_compact(g.deg);
// Topo: label hacia ADENTRO (entre planeta y let label_r = ring - disk_size * 0.7;
// casas P-P arriba); natal: label hacia
// AFUERA (entre planeta y casas Placidus).
let label_r = if is_topo {
ring - disk_size * 0.7
} else {
ring + disk_size * 0.7
};
let (lx, ly) = polar_to_screen(g.deg, asc, rot_offset, label_r); let (lx, ly) = polar_to_screen(g.deg, asc, rot_offset, label_r);
wheel = wheel.child(coord_label( wheel = wheel.child(coord_label(
cx_center + lx, cx_center + lx,
@@ -1615,88 +1614,82 @@ fn format_offset(minutes: i64) -> String {
// Painting // Painting
// ===================================================================== // =====================================================================
/// Geometría radial canónica de la rueda. Aros nombrados según
/// convención del usuario, de afuera hacia adentro:
///
/// * **Aro A** (`sign_outer`) — externo del zodiaco.
/// * **Zona AB** — sign dial: glyphs de signos zodiacales.
/// * **Aro B** (`sign_inner` = `topo_houses_outer`) — interno del
/// zodiaco / externo del bloque ascensional.
/// * **Zona BC** — casas topocéntricas (cusps b→c) + planetas
/// topocéntricos, ambos con sus coordenadas.
/// * **Aro C** (`topo_houses_inner` = `houses_outer`) — separador
/// ascensional / casas geo.
/// * **Zona CD** — casas geocéntricas (cusps c→d) + sus coordenadas.
/// * **Aro D** (`houses_inner`) — externo de los planetas natales.
/// Junto a D, hacia adentro, se posan los planetas natales y sus
/// coordenadas.
/// * **Aro E** (`aspects`) — el más interno. Desde aquí nacen las
/// líneas de aspecto / relaciones / overlays opcionales.
///
/// Los overlays adicionales (transits, midpoints, progression, solar
/// arc, composite) viven INTERIORES al aro E — solo se pintan
/// cuando el módulo correspondiente está activo, así no compiten
/// con el layout base.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct Radii { struct Radii {
// Layout outer→inner. La capa **ascensional** (sistema topocéntrico sign_outer: f32, // Aro A
// Polich-Page) va pegada al sign dial — su lógica nace del eje sign_inner: f32, // Aro B
// ASC/MC y de la ascensión recta del observador, conceptualmente topo_houses_outer: f32, // = Aro B
// hermana del zodiaco. La capa **geocéntrica** clásica topocentric: f32, // Zona BC: planetas topo
// (casas Placidus + planetas tropicales sin paralaje) vive más topo_houses_inner: f32, // Aro C
// adentro, alrededor del cinturón de planetas natales. houses_outer: f32, // = Aro C
sign_outer: f32, houses_inner: f32, // Aro D
sign_inner: f32, bodies: f32, // Zona D-E: planetas natales (junto a D)
/// Anillo exterior de las casas Polich-Page (topocéntricas), pegado pd_direct: f32, // GR (cuando activo): exterior al cinturón natal
/// al sign dial. Los cusps llegan hasta el `sign_inner`. pd_converse: f32, // GR (cuando activo): interior al cinturón natal
topo_houses_outer: f32, aspects: f32, // Aro E (invisible, ancla de líneas)
topo_houses_inner: f32, // Overlays adicionales — todos interiores a E.
/// Carril de planetas topocéntricos — apenas dentro del anillo
/// de casas P-P. Lleva sus propios coord labels.
topocentric: f32,
/// Anillo de glifos de tránsito (cuando el overlay está activo).
transits: f32, transits: f32,
/// Casas geocéntricas (Placidus default) — más adentro que las
/// topocéntricas, claramente diferenciables.
houses_outer: f32,
houses_inner: f32,
/// Anillo de midpoints — entre cuerpos geocéntricos y `houses_inner`.
midpoints: f32, midpoints: f32,
/// Anillo principal de cuerpos natales geocéntricos.
bodies: f32,
/// Borde interior del cinturón de planetas geocéntricos.
bodies_inner: f32,
/// Direcciones Primarias DIRECTAS (Sistema GR): ring exterior del
/// "abrazo" GR — justo afuera del cinturón natal.
pd_direct: f32,
/// Direcciones Primarias CONVERSAS (Sistema GR): ring interior —
/// el cinturón natal queda entre `pd_direct` (afuera) y
/// `pd_converse` (dentro), formando el dual-ring de rectificación.
pd_converse: f32,
/// Anillo interno con cuerpos progresados (overlay opcional).
progression: f32, progression: f32,
/// Anillo más interno con cuerpos dirigidos por Solar Arc.
solar_arc: f32, solar_arc: f32,
/// Anillo de carta compuesta (midpoint Davison) con un partner.
composite: f32, composite: f32,
/// Círculo donde anclan las líneas de aspecto entre cuerpos
/// natales. Justo dentro del cinturón de planetas.
aspects: f32,
} }
impl Radii { impl Radii {
fn from_outer(r: f32) -> Self { fn from_outer(r: f32) -> Self {
Self { Self {
// Aro A — externo zodiaco.
sign_outer: r, sign_outer: r,
sign_inner: r * 0.88, // Aro B — interno zodiaco / externo bloque ascensional.
// Ascensional (Polich-Page): pegado al sign dial. sign_inner: r * 0.92,
topo_houses_outer: r * 0.875, topo_houses_outer: r * 0.92,
topo_houses_inner: r * 0.79, // Zona BC: planetas topocéntricos centrados.
// Carril topocéntrico de planetas: justo dentro de las topocentric: r * 0.85,
// casas P-P. Lleva sus coord labels al borde interior. // Aro C — separador ascensional / casas geo.
topocentric: r * 0.755, topo_houses_inner: r * 0.78,
// Tránsitos: ring intermedio entre las dos coronas (topo y houses_outer: r * 0.78,
// geo), apenas debajo del topocéntrico. // Aro D — externo planetas natales.
transits: r * 0.71, houses_inner: r * 0.62,
// Capa geocéntrica clásica más adentro — claramente // Planetas natales justo dentro de D.
// separada del bloque ascensional. bodies: r * 0.57,
houses_outer: r * 0.66, // GR dual-ring (cuando se activa): abraza el cinturón
houses_inner: r * 0.54, // natal por afuera (`pd_direct`) y por adentro
midpoints: r * 0.50, // (`pd_converse`). Si GR está OFF, ninguno de los dos se
bodies: r * 0.47, // pinta — no compite con el layout base.
bodies_inner: r * 0.44, pd_direct: r * 0.545,
// Dual-ring GR — abraza el cinturón natal por afuera y por pd_converse: r * 0.515,
// adentro. Para Saturno los dos rings caen casi en la misma // Aro E — anclaje invisible de las líneas de aspecto.
// posición angular (poca rotación diurna afecta su lon en aspects: r * 0.49,
// años humanos); para luminarias los rings divergen // Overlays adicionales — todos INTERIORES al aro E. Se
// visiblemente y leen "directa rumbo a este grado, conversa // pintan solo cuando el módulo correspondiente está
// hacia este otro". // activo, así no compiten con el layout base.
pd_direct: r * 0.495, transits: r * 0.43,
pd_converse: r * 0.425, midpoints: r * 0.39,
// aspects justo bajo el carril natal — las líneas entran progression: r * 0.33,
// a este radio sin pintar el círculo (sería ruido extra). solar_arc: r * 0.27,
aspects: r * 0.41, composite: r * 0.21,
progression: r * 0.36,
solar_arc: r * 0.30,
composite: r * 0.24,
} }
} }
@@ -1917,22 +1910,13 @@ fn paint_wheel(
} }
} }
// 2.5. Carril de planetas. `bodies` + `bodies_inner` muy juntos // Aro D — único anillo visible del bloque de planetas natales
// delimitan la franja estrecha donde viven los glyphs natales. // (la idea del "carril doble" se descartó: confundía con el
// El círculo de aspectos NO se pinta — `radii.aspects` solo // sistema de casas). El aro E (`radii.aspects`) no se pinta por
// existe como punto donde se anclan las líneas; un stroke ahí // diseño; solo es ancla invisible de las líneas.
// sería un anillo extra que confunde sin aportar.
if show(LayerKind::Bodies) { if show(LayerKind::Bodies) {
let belt_color = with_alpha(palette.dial_ring, 0.55); let belt_color = with_alpha(palette.dial_ring, 0.55);
stroke_circle_3d(window, cx, cy, radii.bodies, 0.9, belt_color, theme); stroke_circle_3d(window, cx, cy, radii.houses_inner, 0.9, belt_color, theme);
stroke_circle(
window,
cx,
cy,
radii.bodies_inner,
0.6,
with_alpha(palette.dial_ring, 0.35),
);
// GR dual-ring: si las capas de direcciones primarias están // GR dual-ring: si las capas de direcciones primarias están
// presentes, marcar sus anillos para que el visual lea como // presentes, marcar sus anillos para que el visual lea como
// "abrazo" del cinturón natal. La directa va punteada, // "abrazo" del cinturón natal. La directa va punteada,