1use flo_curves::{Coordinate, Coordinate2D};
2use num_traits::Float;
3use std::{convert::{From, Into}, fmt::Display, ops::*};
4
5#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub struct Point2<T> {
8 pub x: T,
9 pub y: T,
10}
11
12pub trait PointType: Default + Copy {
13 fn from<P: PointType>(p: &P) -> Self;
14
15 fn to<T: PointType>(&self) -> T {
16 T::from(self)
17 }
18
19 fn to_point_f64(&self) -> PointF64;
20
21 fn to_point_i32(&self) -> PointI32;
22}
23
24pub trait ToSvgString {
25 fn to_svg_string(&self, precision: Option<u32>) -> String;
26}
27
28impl<T> ToSvgString for Point2<T>
29where
30 T: Copy + NumberFormat
31{
32 fn to_svg_string(&self, precision: Option<u32>) -> String {
33 format!("{},{}", Self::number_format(self.x, precision), Self::number_format(self.y, precision))
34 }
35}
36
37pub trait NumberFormat: Display {
38 fn number_format(num: Self, precision: Option<u32>) -> String;
39}
40
41impl NumberFormat for i32 {
42 fn number_format(num: Self, _precision: Option<u32>) -> String {
43 format!("{}", num)
44 }
45}
46
47impl NumberFormat for f64 {
48 fn number_format(num: Self, precision: Option<u32>) -> String {
49 match precision {
50 None => format!("{}", num),
51 Some(0) => format!("{1:.0$}", 0, num),
52 Some(p) => {
53 let mut string: String = format!("{1:.0$}", p as usize, num);
54 string = string.trim_end_matches('0').trim_end_matches('.').to_owned();
55 string
56 },
57 }
58 }
59}
60
61impl<T> Point2<T>
62where
63 T: NumberFormat,
64{
65 #[inline]
66 pub(crate) fn number_format(num: T, precision: Option<u32>) -> String {
67 NumberFormat::number_format(num, precision)
68 }
69}
70
71impl<T> Point2<T> {
72 #[inline]
73 pub const fn new(x: T, y: T) -> Self {
74 Self { x, y }
75 }
76}
77
78impl<T> Point2<T>
79where
80 T: Add<Output = T> + Mul<Output = T>,
81{
82 #[inline]
83 pub fn dot(self, v: Self) -> T {
84 self.x * v.x + self.y * v.y
85 }
86}
87
88impl<T> Point2<T>
89where
90 T: Add<Output = T>
91{
92 #[inline]
93 pub fn translate(self, vector: Self) -> Self {
94 self + vector
95 }
96}
97
98impl<T> Point2<T>
99where
100 T: Add<Output = T> + Copy + Neg<Output = T> + Sub<Output = T>,
101{
102 #[inline]
103 pub fn rotate_90deg(&self, origin: Self, clockwise: bool) -> Self {
106 let o = origin;
107
108 if !clockwise {
109 Self {
110 x: (self.y - o.y) + o.x,
111 y: -(self.x - o.x) + o.y,
112 }
113 } else {
114 Self {
115 x: -(self.y - o.y) + o.x,
116 y: (self.x - o.x) + o.y,
117 }
118 }
119 }
120}
121
122impl<T> Point2<T>
123where
124 T: Float,
125{
126 #[inline]
127 pub fn rotate(&self, origin: Self, angle: T) -> Self {
128 let o = origin;
129 let a = angle;
130 Self {
131 x: a.cos() * (self.x - o.x) - a.sin() * (self.y - o.y) + o.x,
132 y: a.sin() * (self.x - o.x) + a.cos() * (self.y - o.y) + o.y,
133 }
134 }
135
136 #[inline]
137 pub fn norm(self) -> T {
139 self.dot(self).sqrt()
140 }
141
142 #[inline]
143 pub fn distance_to(&self, other: Point2<T>) -> T {
145 (*self - other).norm()
146 }
147}
148
149impl<T> Point2<T>
150where
151 T: Default + Float,
152{
153 #[inline]
154 pub fn get_normalized(&self) -> Self {
155 let norm = self.norm();
156 if norm != T::zero() {
157 *self / norm
158 } else {
159 Self::default()
160 }
161 }
162
163}
164
165impl<T> Neg for Point2<T>
166where
167 T: Neg<Output = T>,
168{
169 type Output = Self;
170 #[inline]
171 fn neg(self) -> Self::Output {
172 Self {
173 x: self.x.neg(),
174 y: self.y.neg(),
175 }
176 }
177}
178
179impl<T> Add for Point2<T>
180where
181 T: Add<Output = T>,
182{
183 type Output = Self;
184 #[inline]
185 fn add(self, other: Self) -> Self {
186 Self {
187 x: self.x.add(other.x),
188 y: self.y.add(other.y),
189 }
190 }
191}
192
193impl<T> AddAssign for Point2<T>
194where
195 T: AddAssign,
196{ #[inline]
197 fn add_assign(&mut self, other: Self) {
198 self.x.add_assign(other.x);
199 self.y.add_assign(other.y);
200 }
201}
202
203impl<T> Sub for Point2<T>
204where
205 T: Sub<Output = T>,
206{
207 type Output = Self;
208 #[inline]
209 fn sub(self, other: Self) -> Self {
210 Self {
211 x: self.x.sub(other.x),
212 y: self.y.sub(other.y),
213 }
214 }
215}
216
217impl<T> SubAssign for Point2<T>
218where
219 T: SubAssign,
220{
221 #[inline]
222 fn sub_assign(&mut self, other: Self) {
223 self.x.sub_assign(other.x);
224 self.y.sub_assign(other.y);
225 }
226}
227
228impl<T, F> Mul<F> for Point2<T>
229where
230 T: Mul<F, Output = T>,
231 F: Float,
232{
233 type Output = Self;
234
235 fn mul(self, rhs: F) -> Self::Output {
236 Self {
237 x: self.x.mul(rhs),
238 y: self.y.mul(rhs),
239 }
240 }
241}
242
243impl<T, F> MulAssign<F> for Point2<T>
244where
245 T: MulAssign<F>,
246 F: Float,
247{
248 fn mul_assign(&mut self, rhs: F) {
249 self.x.mul_assign(rhs);
250 self.y.mul_assign(rhs);
251 }
252}
253
254impl<T, F> Div<F> for Point2<T>
255where
256 T: Div<F, Output = T>,
257 F: Float,
258{
259 type Output = Self;
260
261 #[inline]
262 fn div(self, rhs: F) -> Self::Output {
263 Self {
264 x: self.x.div(rhs),
265 y: self.y.div(rhs),
266 }
267 }
268}
269
270impl<T, F> DivAssign<F> for Point2<T>
271where
272 T: DivAssign<F>,
273 F: Float,
274{
275 #[inline]
276 fn div_assign(&mut self, rhs: F) {
277 self.x.div_assign(rhs);
278 self.y.div_assign(rhs);
279 }
280}
281
282impl<F> Coordinate2D for Point2<F>
283where
284 F: Copy + Into<f64>,
285{
286 fn x(&self) -> f64 {
287 self.x.into()
288 }
289
290 fn y(&self) -> f64 {
291 self.y.into()
292 }
293}
294
295impl<F> Coordinate for Point2<F>
296where
297 F: Add<Output = F> + Copy + Default + Float + From<f64> + Into<f64> + Mul<f64, Output = F> + PartialEq + Sub<Output = F>,
298{
299 #[inline]
300 fn from_components(components: &[f64]) -> Self {
301 Self::new(components[0].into(), components[1].into())
302 }
303
304 #[inline]
305 fn origin() -> Self {
306 Self::default()
307 }
308
309 #[inline]
310 fn len() -> usize {
311 2
312 }
313
314 #[inline]
315 fn get(&self, index: usize) -> f64 {
316 match index {
317 0 => self.x.into(),
318 1 => self.y.into(),
319 _ => panic!("Point2 only has two components")
320 }
321 }
322
323 fn from_biggest_components(p1: Self, p2: Self) -> Self {
324 Self::new(
325 f64::from_biggest_components(p1.x.into(), p2.x.into()).into(),
326 f64::from_biggest_components(p1.y.into(), p2.y.into()).into(),
327 )
328 }
329
330 fn from_smallest_components(p1: Self, p2: Self) -> Self {
331 Self::new(
332 f64::from_smallest_components(p1.x.into(), p2.x.into()).into(),
333 f64::from_smallest_components(p1.y.into(), p2.y.into()).into(),
334 )
335 }
336}
337
338pub type PointU8 = Point2<u8>;
340pub type PointUsize = Point2<usize>;
342pub type PointI32 = Point2<i32>;
344pub type PointF32 = Point2<f32>;
346pub type PointF64 = Point2<f64>;
348
349impl PointI32 {
350 pub fn to_point_usize(&self) -> PointUsize {
351 PointUsize {x: self.x as usize, y: self.y as usize}
352 }
353
354 pub fn to_point_f64(&self) -> PointF64 {
355 PointF64 { x: self.x as f64, y: self.y as f64 }
356 }
357}
358
359impl PointF64 {
360 pub fn to_point_i32(&self) -> PointI32 {
361 PointI32 { x: self.x as i32, y: self.y as i32 }
362 }
363
364 pub fn to_point_f32(&self) -> PointF32 {
365 PointF32 { x: self.x as f32, y: self.y as f32 }
366 }
367}
368
369impl PointType for PointI32 {
370 fn from<P: PointType>(p: &P) -> Self {
371 p.to_point_i32()
372 }
373
374 #[inline]
375 fn to_point_f64(&self) -> PointF64 {
376 self.to_point_f64()
377 }
378
379 #[inline]
380 fn to_point_i32(&self) -> PointI32 {
381 *self
382 }
383}
384
385impl PointType for PointF64 {
386 fn from<P: PointType>(p: &P) -> Self {
387 p.to_point_f64()
388 }
389
390 #[inline]
391 fn to_point_f64(&self) -> PointF64 {
392 *self
393 }
394
395 #[inline]
396 fn to_point_i32(&self) -> PointI32 {
397 self.to_point_i32()
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404
405 #[test]
406 fn pointf64_rotate() {
408 let p = PointF64 { x: 1.0, y: 0.0 };
409 let r = p.rotate(PointF64 { x: 0.0, y: 0.0 }, std::f64::consts::PI / 2.0);
410 assert!(-0.000000001 < r.x && r.x < 0.000000001);
412 assert!(1.0 - 0.000000001 < r.y && r.y < 1.0 + 0.000000001);
413 }
414
415 #[test]
416 fn test_round_i32() {
417 let z = PointI32 { x: 0, y: 2 };
418 assert_eq!(z.to_svg_string(None), "0,2");
419 assert_eq!(z.to_svg_string(Some(5)), "0,2");
420
421 let r = PointI32 { x: 1, y: 2 };
422 assert_eq!(r.to_svg_string(None), "1,2");
423 assert_eq!(r.to_svg_string(Some(5)), "1,2");
424 }
425
426 #[test]
427 fn test_round_f64() {
428 let z = PointF64 { x: 0.0, y: 0.1 };
429 assert_eq!(z.to_svg_string(Some(0)), "0,0");
430 assert_eq!(z.to_svg_string(Some(1)), "0,0.1");
431 assert_eq!(z.to_svg_string(Some(2)), "0,0.1");
432 assert_eq!(z.to_svg_string(None), "0,0.1");
433
434 let p = PointF64 { x: 1.21786434, y: 2.98252586 };
435 assert_eq!(p.to_svg_string(Some(0)), "1,3");
436 assert_eq!(p.to_svg_string(Some(1)), "1.2,3");
437 assert_eq!(p.to_svg_string(Some(2)), "1.22,2.98");
438 assert_eq!(p.to_svg_string(Some(3)), "1.218,2.983");
439 assert_eq!(p.to_svg_string(Some(4)), "1.2179,2.9825");
440 assert_eq!(p.to_svg_string(Some(5)), "1.21786,2.98253");
441 assert_eq!(p.to_svg_string(Some(6)), "1.217864,2.982526");
442 assert_eq!(p.to_svg_string(Some(7)), "1.2178643,2.9825259");
443 assert_eq!(p.to_svg_string(None), "1.21786434,2.98252586");
444 }
445
446 #[test]
447 fn pointi32_rotate() {
449 let p = PointI32 { x: 1, y: 0 };
450 let r = p.rotate_90deg(PointI32::default(), true);
451 assert_eq!(PointI32::new(0, 1), r);
452 }
453}