1use std::ops::RangeInclusive;
2
3use egui::{pos2, remap, Pos2, Rect, Vec2};
4
5use crate::items::Value;
6
7#[derive(Clone, Copy, Debug, serde::Deserialize, PartialEq, serde::Serialize)]
10pub struct Bounds {
11 pub min: [f64; 2],
12 pub max: [f64; 2],
13}
14
15impl Bounds {
16 pub const NOTHING: Self = Self {
17 min: [f64::INFINITY; 2],
18 max: [-f64::INFINITY; 2],
19 };
20
21 pub fn new_symmetrical(half_extent: f64) -> Self {
22 Self {
23 min: [-half_extent; 2],
24 max: [half_extent; 2],
25 }
26 }
27
28 pub fn is_finite(&self) -> bool {
29 self.min[0].is_finite()
30 && self.min[1].is_finite()
31 && self.max[0].is_finite()
32 && self.max[1].is_finite()
33 }
34
35 pub fn is_valid(&self) -> bool {
36 self.is_finite() && self.width() > 0.0 && self.height() > 0.0
37 }
38
39 pub fn width(&self) -> f64 {
40 self.max[0] - self.min[0]
41 }
42
43 pub fn height(&self) -> f64 {
44 self.max[1] - self.min[1]
45 }
46
47 pub fn extend_with(&mut self, value: &Value) {
48 self.extend_with_x(value.x);
49 self.extend_with_y(value.y);
50 }
51
52 pub fn extend_with_x(&mut self, x: f64) {
54 self.min[0] = self.min[0].min(x);
55 self.max[0] = self.max[0].max(x);
56 }
57
58 pub fn extend_with_y(&mut self, y: f64) {
60 self.min[1] = self.min[1].min(y);
61 self.max[1] = self.max[1].max(y);
62 }
63
64 pub fn expand_x(&mut self, pad: f64) {
65 self.min[0] -= pad;
66 self.max[0] += pad;
67 }
68
69 pub fn expand_y(&mut self, pad: f64) {
70 self.min[1] -= pad;
71 self.max[1] += pad;
72 }
73
74 pub fn merge(&mut self, other: &Bounds) {
75 self.min[0] = self.min[0].min(other.min[0]);
76 self.min[1] = self.min[1].min(other.min[1]);
77 self.max[0] = self.max[0].max(other.max[0]);
78 self.max[1] = self.max[1].max(other.max[1]);
79 }
80
81 pub fn translate_x(&mut self, delta: f64) {
82 self.min[0] += delta;
83 self.max[0] += delta;
84 }
85
86 pub fn translate_y(&mut self, delta: f64) {
87 self.min[1] += delta;
88 self.max[1] += delta;
89 }
90
91 pub fn translate(&mut self, delta: Vec2) {
92 self.translate_x(delta.x as f64);
93 self.translate_y(delta.y as f64);
94 }
95
96 pub fn add_relative_margin(&mut self, margin_fraction: Vec2) {
97 let width = self.width().max(0.0);
98 let height = self.height().max(0.0);
99 self.expand_x(margin_fraction.x as f64 * width);
100 self.expand_y(margin_fraction.y as f64 * height);
101 }
102
103 pub fn range_x(&self) -> RangeInclusive<f64> {
104 self.min[0]..=self.max[0]
105 }
106
107 pub fn make_x_symmetrical(&mut self) {
108 let x_abs = self.min[0].abs().max(self.max[0].abs());
109 self.min[0] = -x_abs;
110 self.max[0] = x_abs;
111 }
112
113 pub fn make_y_symmetrical(&mut self) {
114 let y_abs = self.min[1].abs().max(self.max[1].abs());
115 self.min[1] = -y_abs;
116 self.max[1] = y_abs;
117 }
118}
119
120#[derive(Clone)]
123pub struct ScreenTransform {
124 frame: Rect,
126 bounds: Bounds,
128 x_centered: bool,
130 y_centered: bool,
132}
133
134impl ScreenTransform {
135 pub fn new(frame: Rect, bounds: Bounds, x_centered: bool, y_centered: bool) -> Self {
136 Self {
137 frame,
138 bounds,
139 x_centered,
140 y_centered,
141 }
142 }
143
144 pub fn frame(&self) -> &Rect {
145 &self.frame
146 }
147
148 pub fn bounds(&self) -> &Bounds {
149 &self.bounds
150 }
151
152 pub fn translate_bounds(&mut self, mut delta_pos: Vec2) {
153 if self.x_centered {
154 delta_pos.x = 0.;
155 }
156 if self.y_centered {
157 delta_pos.y = 0.;
158 }
159 delta_pos.x *= self.dvalue_dpos()[0] as f32;
160 delta_pos.y *= self.dvalue_dpos()[1] as f32;
161 self.bounds.translate(delta_pos);
162 }
163
164 pub fn zoom(&mut self, zoom_factor: Vec2, center: Pos2) {
166 let center = self.value_from_position(center);
167
168 let mut new_bounds = self.bounds;
169 new_bounds.min[0] = center.x + (new_bounds.min[0] - center.x) / (zoom_factor.x as f64);
170 new_bounds.max[0] = center.x + (new_bounds.max[0] - center.x) / (zoom_factor.x as f64);
171 new_bounds.min[1] = center.y + (new_bounds.min[1] - center.y) / (zoom_factor.y as f64);
172 new_bounds.max[1] = center.y + (new_bounds.max[1] - center.y) / (zoom_factor.y as f64);
173
174 if new_bounds.is_valid() {
175 self.bounds = new_bounds;
176 }
177 }
178
179 pub fn position_from_value(&self, value: &Value) -> Pos2 {
180 let x = remap(
181 value.x,
182 self.bounds.min[0]..=self.bounds.max[0],
183 (self.frame.left() as f64)..=(self.frame.right() as f64),
184 );
185 let y = remap(
186 value.y,
187 self.bounds.min[1]..=self.bounds.max[1],
188 (self.frame.bottom() as f64)..=(self.frame.top() as f64), );
190 pos2(x as f32, y as f32)
191 }
192
193 pub fn value_from_position(&self, pos: Pos2) -> Value {
194 let x = remap(
195 pos.x as f64,
196 (self.frame.left() as f64)..=(self.frame.right() as f64),
197 self.bounds.min[0]..=self.bounds.max[0],
198 );
199 let y = remap(
200 pos.y as f64,
201 (self.frame.bottom() as f64)..=(self.frame.top() as f64), self.bounds.min[1]..=self.bounds.max[1],
203 );
204 Value::new(x, y)
205 }
206
207 pub fn dpos_dvalue_x(&self) -> f64 {
209 self.frame.width() as f64 / self.bounds.width()
210 }
211
212 pub fn dpos_dvalue_y(&self) -> f64 {
214 -self.frame.height() as f64 / self.bounds.height() }
216
217 pub fn dpos_dvalue(&self) -> [f64; 2] {
219 [self.dpos_dvalue_x(), self.dpos_dvalue_y()]
220 }
221
222 pub fn dvalue_dpos(&self) -> [f64; 2] {
224 [1.0 / self.dpos_dvalue_x(), 1.0 / self.dpos_dvalue_y()]
225 }
226
227 pub fn get_aspect(&self) -> f64 {
228 let rw = self.frame.width() as f64;
229 let rh = self.frame.height() as f64;
230 (self.bounds.width() / rw) / (self.bounds.height() / rh)
231 }
232
233 pub fn set_aspect(&mut self, aspect: f64) {
234 let epsilon = 1e-5;
235 let current_aspect = self.get_aspect();
236 if current_aspect < aspect - epsilon {
237 self.bounds
238 .expand_x((aspect / current_aspect - 1.0) * self.bounds.width() * 0.5);
239 } else if current_aspect > aspect + epsilon {
240 self.bounds
241 .expand_y((current_aspect / aspect - 1.0) * self.bounds.height() * 0.5);
242 }
243 }
244}