embedded_charts/data/
point.rs1use crate::error::DataResult;
4
5pub trait DataPoint: Copy + Clone + PartialEq {
7 type X: PartialOrd + Copy + Clone;
9 type Y: PartialOrd + Copy + Clone;
11
12 fn x(&self) -> Self::X;
14
15 fn y(&self) -> Self::Y;
17
18 fn new(x: Self::X, y: Self::Y) -> Self;
20}
21
22#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct Point2D {
25 pub x: f32,
27 pub y: f32,
29}
30
31impl Point2D {
32 pub const fn new(x: f32, y: f32) -> Self {
34 Self { x, y }
35 }
36
37 pub fn distance_to(&self, other: &Self) -> f32 {
39 let dx = self.x - other.x;
40 let dy = self.y - other.y;
41 #[cfg(feature = "floating-point")]
42 {
43 micromath::F32Ext::sqrt(dx * dx + dy * dy)
44 }
45 #[cfg(not(feature = "floating-point"))]
46 {
47 let abs_dx = if dx < 0.0 { -dx } else { dx };
49 let abs_dy = if dy < 0.0 { -dy } else { dy };
50 abs_dx + abs_dy
51 }
52 }
53}
54
55impl DataPoint for Point2D {
56 type X = f32;
57 type Y = f32;
58
59 fn x(&self) -> Self::X {
60 self.x
61 }
62
63 fn y(&self) -> Self::Y {
64 self.y
65 }
66
67 fn new(x: Self::X, y: Self::Y) -> Self {
68 Self::new(x, y)
69 }
70}
71
72impl From<(f32, f32)> for Point2D {
73 fn from((x, y): (f32, f32)) -> Self {
74 Self::new(x, y)
75 }
76}
77
78impl From<Point2D> for (f32, f32) {
79 fn from(point: Point2D) -> Self {
80 (point.x, point.y)
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub struct IntPoint {
87 pub x: i32,
89 pub y: i32,
91}
92
93impl IntPoint {
94 pub const fn new(x: i32, y: i32) -> Self {
96 Self { x, y }
97 }
98
99 pub fn to_f32(self) -> Point2D {
101 Point2D::new(self.x as f32, self.y as f32)
102 }
103}
104
105impl DataPoint for IntPoint {
106 type X = i32;
107 type Y = i32;
108
109 fn x(&self) -> Self::X {
110 self.x
111 }
112
113 fn y(&self) -> Self::Y {
114 self.y
115 }
116
117 fn new(x: Self::X, y: Self::Y) -> Self {
118 Self::new(x, y)
119 }
120}
121
122impl From<(i32, i32)> for IntPoint {
123 fn from((x, y): (i32, i32)) -> Self {
124 Self::new(x, y)
125 }
126}
127
128impl From<IntPoint> for (i32, i32) {
129 fn from(point: IntPoint) -> Self {
130 (point.x, point.y)
131 }
132}
133
134#[derive(Debug, Clone, Copy, PartialEq)]
136pub struct TimestampedPoint {
137 pub timestamp: f32,
139 pub value: f32,
141}
142
143impl TimestampedPoint {
144 pub const fn new(timestamp: f32, value: f32) -> Self {
146 Self { timestamp, value }
147 }
148}
149
150impl DataPoint for TimestampedPoint {
151 type X = f32;
152 type Y = f32;
153
154 fn x(&self) -> Self::X {
155 self.timestamp
156 }
157
158 fn y(&self) -> Self::Y {
159 self.value
160 }
161
162 fn new(x: Self::X, y: Self::Y) -> Self {
163 Self::new(x, y)
164 }
165}
166
167impl From<(f32, f32)> for TimestampedPoint {
168 fn from((timestamp, value): (f32, f32)) -> Self {
169 Self::new(timestamp, value)
170 }
171}
172
173#[cfg(feature = "animations")]
175pub trait Interpolatable: DataPoint {
176 fn interpolate(&self, other: &Self, t: f32) -> Self;
182}
183
184#[cfg(feature = "animations")]
185impl Interpolatable for TimestampedPoint {
186 fn interpolate(&self, other: &Self, t: f32) -> Self {
187 let timestamp = self.timestamp + (other.timestamp - self.timestamp) * t;
188 let value = self.value + (other.value - self.value) * t;
189 Self::new(timestamp, value)
190 }
191}
192
193pub fn validate_point<P: DataPoint>(_point: &P) -> DataResult<()>
195where
196 P::X: PartialOrd,
197 P::Y: PartialOrd,
198{
199 #[cfg(feature = "floating-point")]
201 {
202 let (_x, _y) = (_point.x(), _point.y());
203 }
208
209 Ok(())
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_point2d_creation() {
218 let point = Point2D::new(1.0, 2.0);
219 assert_eq!(point.x(), 1.0);
220 assert_eq!(point.y(), 2.0);
221 }
222
223 #[test]
224 fn test_point2d_from_tuple() {
225 let point: Point2D = (3.0, 4.0).into();
226 assert_eq!(point.x(), 3.0);
227 assert_eq!(point.y(), 4.0);
228 }
229
230 #[test]
231 fn test_int_point_creation() {
232 let point = IntPoint::new(10, 20);
233 assert_eq!(point.x(), 10);
234 assert_eq!(point.y(), 20);
235 }
236
237 #[test]
238 fn test_timestamped_point() {
239 let point = TimestampedPoint::new(100.0, 25.5);
240 assert_eq!(point.x(), 100.0);
241 assert_eq!(point.y(), 25.5);
242 }
243
244 #[cfg(feature = "animations")]
245 #[test]
246 fn test_interpolation() {
247 use crate::animation::Interpolatable;
248
249 let p1 = Point2D::new(0.0, 0.0);
250 let p2 = Point2D::new(10.0, 20.0);
251
252 let mid = p1.interpolate(p2, 0.5).unwrap();
253 assert_eq!(mid.x(), 5.0);
254 assert_eq!(mid.y(), 10.0);
255 }
256}