From 0e66cda079bcb2c3f7b282c73a799f270cee3537 Mon Sep 17 00:00:00 2001 From: sergio Date: Mon, 18 May 2026 19:11:54 +0000 Subject: [PATCH] fix(tahuantinsuyu): hit-test del hover usa display_deg post-spread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El hover hacía hit-test contra la posición REAL del planeta (`g.deg`) en vez de la posición de pintura (post-spread). Con clusters esto generaba que el cursor sobre el disco visible NO disparara el hover — había que apuntar al grado real (zona vacía) para activarlo. Fix: `on_hover_check` ahora corre el mismo `spread_angles` que `render_wheel` con los inputs equivalentes, y compara la posición del mouse contra `display_degs[i]` en lugar de `g.deg`. Nuevo helper `body_disk_base(module_id, kind, view_scale)` centraliza el cálculo del disco base — render y hit-test ambos lo usan, así no divergen si más adelante se ajusta el tamaño por tipo de capa. 11 tests verdes. Co-Authored-By: Claude Opus 4.7 --- .../tahuantinsuyu-canvas/src/lib.rs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs b/crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs index 54ab5be..b2814c0 100644 --- a/crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs +++ b/crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs @@ -459,6 +459,13 @@ impl AstrologyCanvas { let mut best: Option<(f32, HoverInfo)> = None; // 1) Body glyphs (incluye natal, overlays, midpoints). + // + // Importante: el hit-test debe usar `display_deg` (post-spread) + // y no `g.deg` (raw) — el spread mueve los discos para evitar + // solapes y si el hover sigue al raw, el usuario tendría que + // apuntar a una zona vacía para activarlo. Calculamos los + // displays con la misma función que render_wheel. + let view_scale = self.state.view_scale; for layer in &render.layers { let ring = match layer.kind { LayerKind::Bodies => radii.body_ring(&layer.module_id), @@ -468,8 +475,13 @@ impl AstrologyCanvas { } _ => continue, }; - for g in &layer.glyphs { - let (gx, gy) = polar_to_screen(g.deg, asc, rot, ring); + let disk_base = body_disk_base(&layer.module_id, layer.kind, view_scale); + let raw_degs: Vec = layer.glyphs.iter().map(|g| g.deg).collect(); + let disk_angular = (disk_base / (std::f32::consts::TAU * ring)) * 360.0; + let (display_degs, _) = + spread_angles(&raw_degs, disk_angular, disk_angular); + for (i, g) in layer.glyphs.iter().enumerate() { + let (gx, gy) = polar_to_screen(display_degs[i], asc, rot, ring); let dx = mx - (cx_px + gx); let dy = my - (cy_px + gy); let dist = (dx * dx + dy * dy).sqrt(); @@ -2641,6 +2653,24 @@ fn planet_glyph( .child(text) } +/// Disco base (px) de un body glyph según `module_id` y kind. Lo +/// usan render_wheel (para pintar) y on_hover_check (para +/// hit-testear) — ambos deben coincidir o el hover apunta a una +/// posición distinta a donde se pinta el disco. +fn body_disk_base(module_id: &str, kind: LayerKind, view_scale: f32) -> f32 { + let base = match kind { + LayerKind::Outer => 20.0, + LayerKind::Midpoints => 16.0, + _ => match module_id { + "natal" => 26.0, + "topocentric" => 22.0, + "pd_direct" | "pd_converse" => 20.0, + _ => 22.0, + }, + }; + base * view_scale +} + /// Reposiciona angularmente un conjunto de longitudes para que pares /// adyacentes mantengan al menos `min_sep_deg` de separación, **sin /// que ningún glyph se aleje más de `max_shift_deg` de su posición