1#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct Range {
12 pub min: f64,
14 pub max: f64,
16}
17
18impl Range {
19 pub fn new(mut min: f64, mut max: f64) -> Self {
21 if min > max {
22 std::mem::swap(&mut min, &mut max);
23 }
24 Self { min, max }
25 }
26
27 pub fn span(&self) -> f64 {
29 self.max - self.min
30 }
31
32 pub fn is_finite(&self) -> bool {
34 self.min.is_finite() && self.max.is_finite()
35 }
36
37 pub fn is_valid(&self) -> bool {
39 self.is_finite() && self.span() > 0.0
40 }
41
42 pub fn expand_to_include(&mut self, value: f64) {
46 if !value.is_finite() {
47 return;
48 }
49 if value < self.min {
50 self.min = value;
51 }
52 if value > self.max {
53 self.max = value;
54 }
55 }
56
57 pub fn union(a: Self, b: Self) -> Option<Self> {
59 if !a.is_finite() || !b.is_finite() {
60 return None;
61 }
62 Some(Self {
63 min: a.min.min(b.min),
64 max: a.max.max(b.max),
65 })
66 }
67
68 pub fn clamp(&self, value: f64) -> f64 {
70 value.max(self.min).min(self.max)
71 }
72
73 pub fn padded(&self, frac: f64, min_padding: f64) -> Self {
77 let span = self.span().abs();
78 let padding = (span * frac).max(min_padding);
79 Self {
80 min: self.min - padding,
81 max: self.max + padding,
82 }
83 }
84
85 pub fn with_min_span(&self, min_span: f64) -> Self {
87 let span = self.span();
88 if span >= min_span {
89 return *self;
90 }
91 let center = (self.min + self.max) * 0.5;
92 let half = min_span * 0.5;
93 Self {
94 min: center - half,
95 max: center + half,
96 }
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub enum View {
107 AutoAll {
109 auto_x: bool,
111 auto_y: bool,
113 },
114 Manual,
116 FollowLastN {
118 points: usize,
120 },
121 FollowLastNXY {
123 points: usize,
125 },
126}
127
128impl Default for View {
129 fn default() -> Self {
130 Self::AutoAll {
131 auto_x: true,
132 auto_y: true,
133 }
134 }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq)]
142pub struct Viewport {
143 pub x: Range,
145 pub y: Range,
147}
148
149impl Viewport {
150 pub fn new(x: Range, y: Range) -> Self {
152 Self { x, y }
153 }
154
155 pub fn is_valid(&self) -> bool {
157 self.x.is_valid() && self.y.is_valid()
158 }
159
160 pub fn padded(&self, frac: f64, min_padding: f64) -> Self {
162 Self {
163 x: self.x.padded(frac, min_padding),
164 y: self.y.padded(frac, min_padding),
165 }
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn range_with_min_span_expands() {
175 let range = Range::new(2.0, 2.0);
176 let expanded = range.with_min_span(1.0);
177 assert!(expanded.span() >= 1.0);
178 assert!((expanded.min + expanded.max) * 0.5 - 2.0 < 1e-9);
179 }
180}