tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 73JSON 198595 Definiciones

feat(tahuantinsuyu): cluster shrink, label compacto, hover destacado con z-order

Cuatro ajustes finos al esquema visual de planetas natales/topo:

1. **Discos achicados en cluster**: glyphs en cluster compartido
   (≥2 miembros) llevan un factor adicional `0.86×` sobre el
   shrink residual. Visualmente quedan apenas más pequeños — al
   estar pegados, achicar un poco evita la sensación de
   "amontonamiento" sin perder el unicode.

2. **Pill compartida más chica + libre de "espacios negros"**:
   - Cálculo del ancho ahora usa `text.chars().count()` (era
     `text.len()` en bytes — los chars unicode astronómicos
     cuentan 3 bytes c/u y inflaban el ancho).
   - Mínimo de ancho bajado de `font*2.0` a `font*1.4` y
     padding lateral reducido. Pills con 1-3 chars ya no llevan
     "espacios en negro" que sobrescriben elementos vecinos.
   - Font del label compartido normal bajado a 9.0×s (era 10);
     el hovereado sube a 10×s. Diferencial claro.
   - Label individual también bajó a 8.5×s.

3. **Hover destacado**: nuevo "hovered_idx" identifica el glyph
   bajo el cursor (de `HoverInfo::Body`). El glyph hovereado se
   pinta al FINAL del árbol DOM — queda con z-order encima del
   resto. Border al color pleno (vs 0.85), disco 1.18× y font
   1.12× para destacarlo.

4. **Label del cluster hovereado destacado**: el cluster que
   contiene al planeta bajo el cursor se renderiza con `fg_text`
   (vs `fg_muted` para los demás) y font un punto más grande.

11 tests verdes (sin cambios — los affectados son del path de
render, no del cómputo).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

074d8bcbc8a4c24397e268331deb872f2e243f61 · por sergio · (hace 5 meses) · padre 121f19b91 · ver el árbol en este commit

Delta semántico · 2

crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs

1 archivos · +93 −13

diff --git a/crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs b/crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs
index 334396e..54ab5be 100644
--- a/crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs
+++ b/crates/modules/tahuantinsuyu/tahuantinsuyu-canvas/src/lib.rs
@@ -1212,11 +1212,36 @@ fn render_wheel(
                     }
                 }
 
-                let shrink = (1.0 - residual * 0.30).clamp(0.60, 1.0);
-                let disk_size = disk_size_base * shrink;
-                let font_size_eff = (font_size * shrink).max(11.0);
+                let shrink_residual = (1.0 - residual * 0.30).clamp(0.60, 1.0);
+
+                // El hovered glyph y su cluster reciben tratamiento
+                // especial: lo postponemos para pintarlo al FINAL del
+                // árbol (queda por encima del resto = z-order), y le
+                // damos un border más fuerte. Su label cluster también
+                // se destaca (color fg_text en lugar de fg_muted, font
+                // un punto más grande).
+                let hovered_sym: Option<&str> = match hover {
+                    Some(HoverInfo::Body { symbol, .. }) => Some(symbol.as_str()),
+                    _ => None,
+                };
+                let hovered_idx: Option<usize> = hovered_sym.and_then(|sym| {
+                    layer.glyphs.iter().position(|g| g.symbol == sym)
+                });
+                let hovered_cluster: Option<usize> = hovered_idx.map(|i| cluster_of[i]);
 
                 for (i, g) in layer.glyphs.iter().enumerate() {
+                    if Some(i) == hovered_idx {
+                        continue; // se pinta al final
+                    }
+                    // Achicar discos cuando el glyph está en cluster
+                    // (≥2 miembros) — al estar pegados se ven mejor
+                    // un poco más pequeños.
+                    let cluster_size = clusters[cluster_of[i]].len();
+                    let in_cluster_shrink = if cluster_size >= 2 { 0.86 } else { 1.0 };
+                    let effective_shrink = shrink_residual * in_cluster_shrink;
+                    let disk_size = disk_size_base * effective_shrink;
+                    let font_size_eff = (font_size * effective_shrink).max(11.0);
+
                     let display_deg = display_degs[i];
                     let (x, y) = polar_to_screen(display_deg, asc, rot_offset, ring);
                     let color = with_alpha(planet_color(palette, &g.symbol), alpha);
@@ -1240,7 +1265,6 @@ fn render_wheel(
 
                     // Coord label individual: solo cuando el glyph
                     // está SOLO en su cluster (≥2 ⇒ label compartido).
-                    let cluster_size = clusters[cluster_of[i]].len();
                     if show_coords && (is_natal || is_topo) && cluster_size == 1 {
                         let coord = format_coord_compact(g.deg);
                         let label_r = ring - disk_size * 1.3;
@@ -1252,20 +1276,21 @@ fn render_wheel(
                             coord.into(),
                             theme.fg_muted,
                             halo_bg,
-                            9.5 * s,
+                            8.5 * s,
                         ));
                     }
                 }
 
                 // Label compartido para CADA cluster con ≥2 miembros.
-                // Texto = símbolos concatenados + coord del centroide
-                // real. Posicionado sobre el centroide DISPLAY (donde
-                // se ven los discos tras el shift).
+                // El del cluster hovereado se destaca: color fg_text
+                // (vs fg_muted) y font un punto más grande.
                 if show_coords && (is_natal || is_topo) {
+                    let disk_size_typical = disk_size_base * shrink_residual * 0.86;
                     for (ci, c) in clusters.iter().enumerate() {
                         if c.len() < 2 {
                             continue;
                         }
+                        let highlighted = Some(ci) == hovered_cluster;
                         let center_display_deg = display_centroids[ci];
                         let center_real_deg = cluster_centroids[ci];
                         let symbols: String = c
@@ -1275,17 +1300,68 @@ fn render_wheel(
                             .join(" ");
                         let coord = format_coord_compact(center_real_deg);
                         let text = format!("{}  {}", symbols, coord);
-                        let label_r = ring - disk_size * 1.5;
+                        let label_r = ring - disk_size_typical * 1.5;
                         let (lx, ly) = polar_to_screen(
                             center_display_deg,
                             asc,
                             rot_offset,
                             label_r,
                         );
+                        let (fg, font_sz) = if highlighted {
+                            (theme.fg_text, 10.0 * s)
+                        } else {
+                            (theme.fg_muted, 9.0 * s)
+                        };
                         wheel = wheel.child(coord_label(
                             cx_center + lx,
                             cy_center + ly,
                             text.into(),
+                            fg,
+                            halo_bg,
+                            font_sz,
+                        ));
+                    }
+                }
+
+                // Render del glyph hovered al FINAL: queda encima del
+                // resto en z-order. Disco un poco más grande y border
+                // más prominente para destacar.
+                if let Some(hi) = hovered_idx {
+                    let g = &layer.glyphs[hi];
+                    let display_deg = display_degs[hi];
+                    let (x, y) = polar_to_screen(display_deg, asc, rot_offset, ring);
+                    let color = with_alpha(planet_color(palette, &g.symbol), alpha);
+                    let mut glyph_text = planet_unicode(&g.symbol).to_string();
+                    if g.retrograde {
+                        glyph_text.push('ᴿ');
+                    }
+                    if let Some(marker) = &g.dignity_marker {
+                        glyph_text.push_str(marker);
+                    }
+                    let disk_size = disk_size_base * shrink_residual * 1.18;
+                    let font_size_eff = font_size * shrink_residual * 1.12;
+                    wheel = wheel.child(planet_glyph(
+                        cx_center + x,
+                        cy_center + y,
+                        disk_size,
+                        font_size_eff,
+                        glyph_text.into(),
+                        color,
+                        halo_bg,
+                        color, // border al color pleno (no .85) — destaca
+                    ));
+                    // Si el hovered no está en cluster compartido,
+                    // pintamos su coord individual destacada acá.
+                    let cluster_size = clusters[cluster_of[hi]].len();
+                    if show_coords && (is_natal || is_topo) && cluster_size == 1 {
+                        let coord = format_coord_compact(g.deg);
+                        let label_r = ring - disk_size * 1.3;
+                        let (lx, ly) =
+                            polar_to_screen(display_deg, asc, rot_offset, label_r);
+                        wheel = wheel.child(coord_label(
+                            cx_center + lx,
+                            cy_center + ly,
+                            coord.into(),
                             theme.fg_text,
                             halo_bg,
                             10.0 * s,
@@ -2862,10 +2938,14 @@ fn coord_label(
     halo_bg: Hsla,
     font_size: f32,
 ) -> gpui::Div {
-    // Estimación gruesa del ancho (caracteres × ~5.5 px a font 9.5).
-    // Suficiente para no recortar; el flex centra dentro.
-    let w = (text.len() as f32 * (font_size * 0.58)).max(font_size * 2.0);
-    let h = font_size + 6.0;
+    // Estimación del ancho basada en `chars().count()` (NO `text.len()`
+    // — los chars unicode astronómicos cuentan 3 bytes pero ocupan
+    // ~1 columna de fuente). Padding lateral muy pequeño en lugar de
+    // un mínimo grande: pills con 1-3 chars no llevan "espacios en
+    // negro" que sobrescriben elementos vecinos.
+    let char_count = text.chars().count() as f32;
+    let w = (char_count * font_size * 0.62 + font_size * 0.5).max(font_size * 1.4);
+    let h = font_size + 5.0;
     div()
         .absolute()
         .left(px(x - w / 2.0))