1use std::ops::RangeInclusive;
2
3use ahash::HashMap;
4use egui::Id;
5use emath::Pos2;
6use emath::Vec2;
7use emath::Vec2b;
8
9#[derive(Clone, Copy, Debug, PartialEq)]
14pub struct PlotPoint {
15 pub x: f64,
18
19 pub y: f64,
21}
22
23impl From<[f64; 2]> for PlotPoint {
24 #[inline]
25 fn from([x, y]: [f64; 2]) -> Self {
26 Self { x, y }
27 }
28}
29
30impl PlotPoint {
31 #[inline(always)]
32 pub fn new(x: impl Into<f64>, y: impl Into<f64>) -> Self {
33 Self {
34 x: x.into(),
35 y: y.into(),
36 }
37 }
38
39 #[inline(always)]
40 pub fn to_pos2(self) -> Pos2 {
41 Pos2::new(self.x as f32, self.y as f32)
42 }
43
44 #[inline(always)]
45 pub fn to_vec2(self) -> Vec2 {
46 Vec2::new(self.x as f32, self.y as f32)
47 }
48}
49
50#[derive(Clone, Copy, PartialEq, Debug)]
54#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
55pub struct PlotBounds {
56 pub(crate) min: [f64; 2],
57 pub(crate) max: [f64; 2],
58}
59
60impl PlotBounds {
61 pub const NOTHING: Self = Self {
62 min: [f64::INFINITY; 2],
63 max: [-f64::INFINITY; 2],
64 };
65
66 #[inline]
67 pub fn from_min_max(min: [f64; 2], max: [f64; 2]) -> Self {
68 Self { min, max }
69 }
70
71 #[inline]
72 pub fn min(&self) -> [f64; 2] {
73 self.min
74 }
75
76 #[inline]
77 pub fn max(&self) -> [f64; 2] {
78 self.max
79 }
80
81 #[inline]
82 pub fn new_symmetrical(half_extent: f64) -> Self {
83 Self {
84 min: [-half_extent; 2],
85 max: [half_extent; 2],
86 }
87 }
88
89 #[inline]
90 pub fn is_finite(&self) -> bool {
91 self.min[0].is_finite() && self.min[1].is_finite() && self.max[0].is_finite() && self.max[1].is_finite()
92 }
93
94 #[inline]
95 pub fn is_finite_x(&self) -> bool {
96 self.min[0].is_finite() && self.max[0].is_finite()
97 }
98
99 #[inline]
100 pub fn is_finite_y(&self) -> bool {
101 self.min[1].is_finite() && self.max[1].is_finite()
102 }
103
104 #[inline]
105 pub fn is_valid(&self) -> bool {
106 self.is_finite() && self.width() > 0.0 && self.height() > 0.0
107 }
108
109 #[inline]
110 pub fn is_valid_x(&self) -> bool {
111 self.is_finite_x() && self.width() > 0.0
112 }
113
114 #[inline]
115 pub fn is_valid_y(&self) -> bool {
116 self.is_finite_y() && self.height() > 0.0
117 }
118
119 #[inline]
120 pub fn width(&self) -> f64 {
121 self.max[0] - self.min[0]
122 }
123
124 #[inline]
125 pub fn height(&self) -> f64 {
126 self.max[1] - self.min[1]
127 }
128
129 #[inline]
130 pub fn center(&self) -> PlotPoint {
131 [
132 emath::fast_midpoint(self.min[0], self.max[0]),
133 emath::fast_midpoint(self.min[1], self.max[1]),
134 ]
135 .into()
136 }
137
138 #[inline]
140 pub fn extend_with(&mut self, value: &PlotPoint) {
141 self.extend_with_x(value.x);
142 self.extend_with_y(value.y);
143 }
144
145 #[inline]
147 pub fn extend_with_x(&mut self, x: f64) {
148 self.min[0] = self.min[0].min(x);
149 self.max[0] = self.max[0].max(x);
150 }
151
152 #[inline]
154 pub fn extend_with_y(&mut self, y: f64) {
155 self.min[1] = self.min[1].min(y);
156 self.max[1] = self.max[1].max(y);
157 }
158
159 #[inline]
160 fn clamp_to_finite(&mut self) {
161 for d in 0..2 {
162 self.min[d] = self.min[d].clamp(f64::MIN, f64::MAX);
163 if self.min[d].is_nan() {
164 self.min[d] = 0.0;
165 }
166
167 self.max[d] = self.max[d].clamp(f64::MIN, f64::MAX);
168 if self.max[d].is_nan() {
169 self.max[d] = 0.0;
170 }
171 }
172 }
173
174 #[inline]
175 pub fn expand_x(&mut self, pad: f64) {
176 if pad.is_finite() {
177 self.min[0] -= pad;
178 self.max[0] += pad;
179 self.clamp_to_finite();
180 }
181 }
182
183 #[inline]
184 pub fn expand_y(&mut self, pad: f64) {
185 if pad.is_finite() {
186 self.min[1] -= pad;
187 self.max[1] += pad;
188 self.clamp_to_finite();
189 }
190 }
191
192 #[inline]
193 pub fn merge_x(&mut self, other: &Self) {
194 self.min[0] = self.min[0].min(other.min[0]);
195 self.max[0] = self.max[0].max(other.max[0]);
196 }
197
198 #[inline]
199 pub fn merge_y(&mut self, other: &Self) {
200 self.min[1] = self.min[1].min(other.min[1]);
201 self.max[1] = self.max[1].max(other.max[1]);
202 }
203
204 #[inline]
205 pub fn set_x(&mut self, other: &Self) {
206 self.min[0] = other.min[0];
207 self.max[0] = other.max[0];
208 }
209
210 #[inline]
211 pub fn set_x_center_width(&mut self, x: f64, width: f64) {
212 self.min[0] = x - width / 2.0;
213 self.max[0] = x + width / 2.0;
214 }
215
216 #[inline]
217 pub fn set_y(&mut self, other: &Self) {
218 self.min[1] = other.min[1];
219 self.max[1] = other.max[1];
220 }
221
222 #[inline]
223 pub fn set_y_center_height(&mut self, y: f64, height: f64) {
224 self.min[1] = y - height / 2.0;
225 self.max[1] = y + height / 2.0;
226 }
227
228 #[inline]
229 pub fn merge(&mut self, other: &Self) {
230 self.min[0] = self.min[0].min(other.min[0]);
231 self.min[1] = self.min[1].min(other.min[1]);
232 self.max[0] = self.max[0].max(other.max[0]);
233 self.max[1] = self.max[1].max(other.max[1]);
234 }
235
236 #[inline]
237 pub fn translate_x(&mut self, delta: f64) {
238 if delta.is_finite() {
239 self.min[0] += delta;
240 self.max[0] += delta;
241 self.clamp_to_finite();
242 }
243 }
244
245 #[inline]
246 pub fn translate_y(&mut self, delta: f64) {
247 if delta.is_finite() {
248 self.min[1] += delta;
249 self.max[1] += delta;
250 self.clamp_to_finite();
251 }
252 }
253
254 #[inline]
255 pub fn translate(&mut self, delta: (f64, f64)) {
256 self.translate_x(delta.0);
257 self.translate_y(delta.1);
258 }
259
260 #[inline]
261 pub fn zoom(&mut self, zoom_factor: Vec2, center: PlotPoint) {
262 self.min[0] = center.x + (self.min[0] - center.x) / (zoom_factor.x as f64);
263 self.max[0] = center.x + (self.max[0] - center.x) / (zoom_factor.x as f64);
264 self.min[1] = center.y + (self.min[1] - center.y) / (zoom_factor.y as f64);
265 self.max[1] = center.y + (self.max[1] - center.y) / (zoom_factor.y as f64);
266 }
267
268 #[inline]
269 pub fn add_relative_margin_x(&mut self, margin_fraction: Vec2) {
270 let width = self.width().max(0.0);
271 self.expand_x(margin_fraction.x as f64 * width);
272 }
273
274 #[inline]
275 pub fn add_relative_margin_y(&mut self, margin_fraction: Vec2) {
276 let height = self.height().max(0.0);
277 self.expand_y(margin_fraction.y as f64 * height);
278 }
279
280 #[inline]
281 pub fn range_x(&self) -> RangeInclusive<f64> {
282 self.min[0]..=self.max[0]
283 }
284
285 #[inline]
286 pub fn range_y(&self) -> RangeInclusive<f64> {
287 self.min[1]..=self.max[1]
288 }
289
290 #[inline]
291 pub fn make_x_symmetrical(&mut self) {
292 let x_abs = self.min[0].abs().max(self.max[0].abs());
293 self.min[0] = -x_abs;
294 self.max[0] = x_abs;
295 }
296
297 #[inline]
298 pub fn make_y_symmetrical(&mut self) {
299 let y_abs = self.min[1].abs().max(self.max[1].abs());
300 self.min[1] = -y_abs;
301 self.max[1] = y_abs;
302 }
303}
304
305#[derive(Clone)]
306pub struct LinkedBounds {
307 pub bounds: PlotBounds,
308 pub auto_bounds: Vec2b,
309}
310
311#[derive(Default, Clone)]
312pub struct BoundsLinkGroups(pub HashMap<Id, LinkedBounds>);
313
314pub enum BoundsModification {
318 SetX(RangeInclusive<f64>),
319 SetY(RangeInclusive<f64>),
320 Translate(Vec2),
321 AutoBounds(Vec2b),
322 Zoom(Vec2, PlotPoint),
323}