1use crate::gpu::Viewport;
2
3/// The least and greatest scroll offsets on each axis, in device pixels: how far the
4/// view's corner may sit from the page origin.
5#[derive(Clone, Copy, Debug, PartialEq)]
6pub struct Scroll {
7 pub min: [f32; 2],
8 pub max: [f32; 2],
9}
10
11impl Scroll {
12 /// Each content rectangle comes with the room, in device pixels, OneNote leaves beyond
13 /// it up and toward the page's start: left, or right on a right-to-left page.
14 pub fn new(
15 viewport: Viewport,
16 bounds: impl Iterator<Item = ([f32; 4], f32)>,
17 rtl: bool,
18 ) -> Self {
19 let mut min = [0.0_f32; 2];
20 let mut max = [0.0_f32; 2];
21 let mut end = f32::NEG_INFINITY;
22 for (rect, pad) in bounds {
23 for axis in 0..2 {
24 min[axis] = min[axis].min(rect[axis] * viewport.scale - pad);
25 max[axis] = max[axis].max(rect[axis + 2]);
26 }
27 end = end.max(rect[2] * viewport.scale + pad);
28 }
29 // OneNote stops at the page origin unless content reaches past it; a right-to-left
30 // page ends where its content does.
31 for axis in 0..2 {
32 max[axis] = if rtl && axis == 0 && end.is_finite() {
33 end - viewport.size[0] as f32
34 } else {
35 (max[axis] + 36.0) * viewport.scale - viewport.size[axis] as f32
36 }
37 .max(min[axis]);
38 }
39 Self { min, max }
40 }
41
42 pub(crate) fn clamp(&self, viewport: &mut Viewport) {
43 for axis in 0..2 {
44 viewport.origin[axis] = -(-viewport.origin[axis]).clamp(self.min[axis], self.max[axis]);
45 }
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn bounds_include_negative_objects_and_clamp_both_ends() {
55 let mut viewport = Viewport {
56 size: [800, 600],
57 scale: 2.0,
58 origin: [9999.0; 2],
59 };
60 let scroll = Scroll::new(
61 viewport,
62 [([-80.0, -20.0, 1000.0, 1200.0], 11.0)].into_iter(),
63 false,
64 );
65 scroll.clamp(&mut viewport);
66 assert_eq!(viewport.origin, [171.0, 51.0]);
67 viewport.origin = [-9999.0; 2];
68 scroll.clamp(&mut viewport);
69 assert_eq!(viewport.origin, [-1272.0, -1872.0]);
70 }
71
72 #[test]
73 fn pictures_reach_no_further_than_their_corner() {
74 let viewport = Viewport {
75 size: [800, 600],
76 scale: 2.0,
77 origin: [0.0; 2],
78 };
79 let bounds = [
80 ([-0.5, -22.35, 613.0, 141.3], 0.0),
81 ([36.0, 86.4, 400.0, 180.0], 11.0),
82 ];
83 let scroll = Scroll::new(viewport, bounds.into_iter(), false);
84 assert_eq!(scroll.min, [-1.0, -44.7]);
85 }
86
87 #[test]
88 fn right_to_left_pages_end_at_their_content() {
89 let viewport = Viewport {
90 size: [800, 600],
91 scale: 2.0,
92 origin: [0.0; 2],
93 };
94 let bounds = [
95 ([10835.0, 14.4, 10962.0, 28.0], 11.0),
96 ([11220.0, 84.0, 11334.0, 145.0], 0.0),
97 ];
98 let scroll = Scroll::new(viewport, bounds.into_iter(), true);
99 assert_eq!(scroll.min, [0.0, 0.0]);
100 assert_eq!(scroll.max[0], 11334.0 * 2.0 - 800.0);
101 }
102}