1#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct HoverState {
10 pub index: usize,
12 pub client_x: f64,
14 pub client_y: f64,
16}
17
18#[must_use]
24pub fn pixel_to_index(client_x: f64, plot_left: f64, plot_width: f64, n_points: usize) -> usize {
25 if n_points <= 1 || plot_width <= 0.0 {
26 return 0;
27 }
28 let last = n_points - 1;
29 let fraction = ((client_x - plot_left) / plot_width).clamp(0.0, 1.0);
30 let raw = fraction * last as f64;
31 let snapped = raw.round() as usize;
32 snapped.min(last)
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 #[test]
40 fn pixel_to_index_snaps_to_nearest() {
41 assert_eq!(pixel_to_index(0.0, 0.0, 1000.0, 10), 0);
43 assert_eq!(pixel_to_index(1000.0, 0.0, 1000.0, 10), 9);
44 assert_eq!(pixel_to_index(500.0, 0.0, 1000.0, 10), 5);
46 assert_eq!(pixel_to_index(56.0, 0.0, 1000.0, 10), 1);
48 assert_eq!(pixel_to_index(54.0, 0.0, 1000.0, 10), 0);
49 }
50
51 #[test]
52 fn pixel_to_index_clamps_at_bounds() {
53 assert_eq!(pixel_to_index(-100.0, 0.0, 1000.0, 5), 0);
54 assert_eq!(pixel_to_index(2000.0, 0.0, 1000.0, 5), 4);
55 }
56
57 #[test]
58 fn pixel_to_index_handles_single_point() {
59 assert_eq!(pixel_to_index(500.0, 0.0, 1000.0, 1), 0);
60 }
61
62 #[test]
63 fn pixel_to_index_respects_plot_left_offset() {
64 assert_eq!(pixel_to_index(600.0, 200.0, 800.0, 11), 5);
67 }
68
69 #[test]
70 fn pixel_to_index_handles_zero_width() {
71 assert_eq!(pixel_to_index(500.0, 0.0, 0.0, 5), 0);
72 }
73}