feat(lapaloma-demo): pan + zoom + double-click reset interactivos

- viewport.rs: `pan_fraction(fx, fy)` — pan en fracción del viewport
  independiente del plot_rect. Útil cuando el handler GPUI trabaja
  en coords de window y no conoce el rect interno del chart.
- lapaloma-demo: state machine de drag (DragAnchor con snapshot del
  viewport al click) + handlers on_mouse_down/move/up para pan,
  on_scroll_wheel con sensitivity exponencial 0.0015 para zoom
  anchor-preserving al cursor, on_click con click_count >= 2 para
  reset al viewport inicial. El header muestra estado dragging.
- Maneja ScrollDelta::Pixels (trackpad) y ::Lines (mouse wheel
  tradicional) unificando con line-height 16px.

46 tests verdes en lapaloma-{core,cartesian,render}.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-13 02:52:51 +00:00
parent fd25369715
commit 138307c2ba
2 changed files with 150 additions and 23 deletions
@@ -58,6 +58,14 @@ impl ChartViewport {
self.pan(dx, dy);
}
/// Pan en **fracción del viewport**. `fx = 0.5` arrastra medio
/// span hacia la izquierda. Útil cuando el caller no conoce el
/// `plot_rect` exacto y trabaja con coords normalizadas
/// (drag dividido por el ancho de la window).
pub fn pan_fraction(&mut self, fx: f64, fy: f64) {
self.pan(-fx * self.x_span(), fy * self.y_span());
}
/// Zoom anchor-preserving (sección 5.3 del ARCHITECTURE.md).
/// `anchor_norm` es la posición del ancla **normalizada al
/// viewport** en `[0, 1]` por eje (típicamente: la posición
@@ -127,4 +135,13 @@ mod tests {
assert!((v.x_min - (-5.0)).abs() < 1e-9);
assert!((v.x_max - 5.0).abs() < 1e-9);
}
#[test]
fn pan_fraction_es_independiente_de_plot() {
let mut v = ChartViewport::new(0.0, 10.0, 0.0, 10.0);
// 50% del span hacia la derecha = viewport se mueve -5 en X.
v.pan_fraction(0.5, 0.0);
assert!((v.x_min - (-5.0)).abs() < 1e-9);
assert!((v.x_max - 5.0).abs() < 1e-9);
}
}