1use crate::coord;
2#[cfg(feature = "mint")]
3use mint::Point2;
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6use std::ops::{Add, Div, Mul, Neg, Sub};
7
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
26pub struct Coord {
27 pub x: isize,
28 pub y: isize,
29}
30
31impl Coord {
32 #[inline]
34 #[must_use]
35 pub const fn new(x: isize, y: isize) -> Self {
36 Self { x, y }
37 }
38
39 #[must_use]
41 pub fn from_angle<P: Into<Coord>>(center: P, distance: usize, degrees: isize) -> Self {
42 let center = center.into();
43 let distance = distance as f32;
44 let rads = (degrees as f32 - 90.0).to_radians();
45 let x = (distance * rads.cos()).round() as isize;
46 let y = (distance * rads.sin()).round() as isize;
47 coord!(center.x + x, center.y + y)
48 }
49}
50
51impl Coord {
52 #[must_use]
54 pub fn distance<P: Into<Coord>>(self, rhs: P) -> usize {
55 let rhs = rhs.into();
56 let x = (rhs.x - self.x) as f64;
57 let y = (rhs.y - self.y) as f64;
58 x.hypot(y).round().abs() as usize
59 }
60
61 #[must_use]
63 pub fn are_collinear<P1: Into<Coord>, P2: Into<Coord>>(self, b: P1, c: P2) -> bool {
64 let b = b.into();
65 let c = c.into();
66 (b.x - self.x) * (c.y - self.y) == (c.x - self.x) * (b.y - self.y)
67 }
68
69 #[must_use]
71 pub fn is_between<P1: Into<Coord>, P2: Into<Coord>>(self, a: P1, b: P2) -> bool {
72 let a = a.into();
73 let b = b.into();
74 ((a.x <= self.x && self.x <= b.x) && (a.y <= self.y && self.y <= b.y))
75 || ((b.x <= self.x && self.x <= a.x) && (b.y <= self.y && self.y <= a.y))
76 }
77
78 #[must_use]
81 pub fn mid_point<P: Into<Coord>>(self, rhs: P) -> Coord {
82 let rhs = rhs.into();
83 let x = (self.x + rhs.x) / 2;
84 let y = (self.y + rhs.y) / 2;
85 coord!(x, y)
86 }
87
88 #[must_use]
91 pub fn angle_to<P: Into<Coord>>(self, rhs: P) -> isize {
92 let rhs = rhs.into();
93 let x = (rhs.x - self.x) as f32;
94 let y = (rhs.y - self.y) as f32;
95 y.atan2(x).to_degrees().round() as isize + 90
96 }
97
98 #[must_use]
99 pub fn cross_product<P: Into<Coord>>(self, rhs: P) -> isize {
100 let rhs = rhs.into();
101 self.x * rhs.y - self.y * rhs.x
102 }
103
104 #[must_use]
105 pub fn dot_product<P: Into<Coord>>(self, rhs: P) -> isize {
106 let rhs = rhs.into();
107 self.x * rhs.x + self.y * rhs.y
108 }
109
110 #[inline]
112 #[must_use]
113 pub const fn perpendicular(self) -> Coord {
114 Coord::new(self.y, -self.x)
115 }
116
117 #[inline]
119 #[must_use]
120 pub const fn abs(self) -> Coord {
121 Coord {
122 x: self.x.abs(),
123 y: self.y.abs(),
124 }
125 }
126}
127
128impl<P: Into<Coord>> Add<P> for Coord {
129 type Output = Coord;
130
131 #[inline]
132 fn add(self, rhs: P) -> Self::Output {
133 let rhs = rhs.into();
134 Coord {
135 x: self.x + rhs.x,
136 y: self.y + rhs.y,
137 }
138 }
139}
140
141impl Neg for Coord {
142 type Output = Coord;
143
144 #[inline]
145 fn neg(self) -> Self::Output {
146 Coord {
147 x: -self.x,
148 y: -self.y,
149 }
150 }
151}
152
153impl<P: Into<Coord>> Sub<P> for Coord {
154 type Output = Coord;
155
156 #[inline]
157 fn sub(self, rhs: P) -> Self::Output {
158 let rhs = rhs.into();
159 Coord {
160 x: self.x - rhs.x,
161 y: self.y - rhs.y,
162 }
163 }
164}
165
166impl<P: Into<Coord>> Mul<P> for Coord {
167 type Output = Coord;
168
169 #[inline]
170 fn mul(self, rhs: P) -> Self::Output {
171 let rhs = rhs.into();
172 Coord {
173 x: self.x * rhs.x,
174 y: self.y * rhs.y,
175 }
176 }
177}
178
179#[cfg(feature = "mint")]
180impl From<Point2<isize>> for Coord {
181 #[inline]
182 #[must_use]
183 fn from(point: Point2<isize>) -> Self {
184 Coord {
185 x: point.x,
186 y: point.y,
187 }
188 }
189}
190
191#[cfg(feature = "mint")]
192impl From<&Point2<isize>> for Coord {
193 #[inline]
194 #[must_use]
195 fn from(point: &Point2<isize>) -> Self {
196 Coord {
197 x: point.x,
198 y: point.y,
199 }
200 }
201}
202
203#[cfg(feature = "mint")]
204impl From<Coord> for Point2<isize> {
205 #[inline]
206 #[must_use]
207 fn from(coord: Coord) -> Self {
208 Point2 {
209 x: coord.x,
210 y: coord.y,
211 }
212 }
213}
214
215#[cfg(feature = "mint")]
216impl From<&Coord> for Point2<isize> {
217 #[inline]
218 #[must_use]
219 fn from(coord: &Coord) -> Self {
220 Point2 {
221 x: coord.x,
222 y: coord.y,
223 }
224 }
225}
226
227macro_rules! impl_from_num {
228 ($num_type:ty) => {
229 impl From<($num_type, $num_type)> for Coord {
230 #[inline]
231
232 fn from(nums: ($num_type, $num_type)) -> Coord {
233 Coord {
234 x: nums.0 as isize,
235 y: nums.1 as isize,
236 }
237 }
238 }
239
240 impl From<&($num_type, $num_type)> for Coord {
241 #[inline]
242
243 fn from(nums: &($num_type, $num_type)) -> Coord {
244 Coord {
245 x: nums.0 as isize,
246 y: nums.1 as isize,
247 }
248 }
249 }
250
251 impl Add<$num_type> for Coord {
252 type Output = Coord;
253
254 #[inline]
255
256 fn add(self, rhs: $num_type) -> Self::Output {
257 Coord {
258 x: self.x + rhs as isize,
259 y: self.y + rhs as isize,
260 }
261 }
262 }
263
264 impl Sub<$num_type> for Coord {
265 type Output = Coord;
266
267 #[inline]
268
269 fn sub(self, rhs: $num_type) -> Self::Output {
270 Coord {
271 x: self.x - rhs as isize,
272 y: self.y - rhs as isize,
273 }
274 }
275 }
276 };
277}
278
279macro_rules! int_mul {
280 ($num_type:ty) => {
281 impl Mul<$num_type> for Coord {
282 type Output = Coord;
283
284 #[inline]
285
286 fn mul(self, rhs: $num_type) -> Self::Output {
287 Coord {
288 x: self.x * rhs as isize,
289 y: self.y * rhs as isize,
290 }
291 }
292 }
293
294 impl Div<$num_type> for Coord {
295 type Output = Coord;
296
297 #[inline]
298
299 fn div(self, rhs: $num_type) -> Self::Output {
300 Coord {
301 x: self.x / rhs as isize,
302 y: self.y / rhs as isize,
303 }
304 }
305 }
306 };
307}
308
309macro_rules! float_mul {
310 ($num_type:ty) => {
311 impl Mul<$num_type> for Coord {
312 type Output = Coord;
313
314 #[inline]
315
316 fn mul(self, rhs: $num_type) -> Self::Output {
317 Coord {
318 x: ((self.x as $num_type) * rhs).ceil() as isize,
319 y: ((self.y as $num_type) * rhs).ceil() as isize,
320 }
321 }
322 }
323
324 impl Div<$num_type> for Coord {
325 type Output = Coord;
326
327 #[inline]
328
329 fn div(self, rhs: $num_type) -> Self::Output {
330 Coord {
331 x: ((self.x as $num_type) / rhs).ceil() as isize,
332 y: ((self.y as $num_type) / rhs).ceil() as isize,
333 }
334 }
335 }
336 };
337}
338
339impl From<&Coord> for Coord {
340 #[inline]
341 fn from(value: &Coord) -> Self {
342 *value
343 }
344}
345
346#[macro_export]
352macro_rules! coord {
353 ($lhs: expr, $rhs: expr,) => {
354 $crate::coord::Coord::from(($lhs, $rhs))
355 };
356 ($lhs: expr, $rhs: expr) => {
357 $crate::coord::Coord::from(($lhs, $rhs))
358 };
359 ($pair: expr) => {
360 $crate::coord::Coord::from($pair)
361 };
362}
363
364impl_from_num!(u8);
365impl_from_num!(i8);
366impl_from_num!(u16);
367impl_from_num!(i16);
368impl_from_num!(u32);
369impl_from_num!(i32);
370impl_from_num!(u64);
371impl_from_num!(i64);
372impl_from_num!(u128);
373impl_from_num!(i128);
374impl_from_num!(usize);
375impl_from_num!(isize);
376impl_from_num!(f32);
377impl_from_num!(f64);
378int_mul!(u8);
379int_mul!(u16);
380int_mul!(u32);
381int_mul!(u64);
382int_mul!(u128);
383int_mul!(i8);
384int_mul!(i16);
385int_mul!(i32);
386int_mul!(i64);
387int_mul!(i128);
388int_mul!(usize);
389int_mul!(isize);
390float_mul!(f32);
391float_mul!(f64);
392
393#[macro_export]
403macro_rules! coord_vec {
404 () => (
405 Vec::<$crate::coord::Coord>::new()
406 );
407 ($first:expr) => (
408 vec![$crate::coord::Coord::from($first)]
409 );
410 ($first:expr, $($vararg:expr),+) => (
411 vec![$crate::coord::Coord::from($first), $($crate::coord::Coord::from($vararg)),*]
412 );
413}
414
415#[cfg(test)]
416mod test {
417 mod list {
418 #[test]
419 fn empty() {
420 let list = coord_vec![];
421 assert_eq!(list, vec![]);
422 }
423
424 #[test]
425 fn one() {
426 let list = coord_vec![coord!(1, 1)];
427 assert_eq!(list, vec![coord!(1, 1)]);
428
429 let list = coord_vec![(4.0, 2.0)];
430 assert_eq!(list, vec![coord!(4, 2)]);
431 }
432
433 #[test]
434 fn many() {
435 let list = coord_vec![(-1_isize, 1), (9_usize, 4)];
436 assert_eq!(list, vec![coord!(-1, 1), coord!(9, 4)]);
437
438 let list = coord_vec![(-1, 1), (9_usize, 4), (5, 6), (9, 8)];
439 assert_eq!(
440 list,
441 vec![coord!(-1, 1), coord!(9, 4), coord!(5, 6), coord!(9, 8)]
442 );
443 }
444 }
445
446 mod point_on_circle {
447 use crate::Coord;
448
449 #[test]
450 fn zero_dist() {
451 let center = coord!(100, 100);
452
453 for i in 0..400 {
454 assert_eq!(Coord::from_angle(center, 0, i), center, "rot: {i}");
455 }
456 }
457
458 #[test]
459 fn twenty_dist_positive_degrees() {
460 let center = coord!(100, 100);
461
462 let zero_degree = Coord::from_angle(center, 20, 0);
463 let ninety_degree = Coord::from_angle(center, 20, 90);
464 let oneeighty_degree = Coord::from_angle(center, 20, 180);
465 let twoseventy_degree = Coord::from_angle(center, 20, 270);
466 let seventwenty_degree = Coord::from_angle(center, 20, 720);
467
468 assert_eq!(zero_degree, (100, 80).into());
469 assert_eq!(ninety_degree, (120, 100).into());
470 assert_eq!(oneeighty_degree, (100, 120).into());
471 assert_eq!(twoseventy_degree, (80, 100).into());
472 assert_eq!(seventwenty_degree, (100, 80).into());
473 }
474
475 #[test]
476 fn twenty_dist_negative_degrees() {
477 let center = coord!(100, 100);
478
479 let zero_degree = Coord::from_angle(center, 20, -0);
480 let ninety_degree = Coord::from_angle(center, 20, -90);
481 let oneeighty_degree = Coord::from_angle(center, 20, -180);
482 let twoseventy_degree = Coord::from_angle(center, 20, -270);
483 let seventwenty_degree = Coord::from_angle(center, 20, -720);
484
485 assert_eq!(zero_degree, (100, 80).into());
486 assert_eq!(ninety_degree, (80, 100).into());
487 assert_eq!(oneeighty_degree, (100, 120).into());
488 assert_eq!(twoseventy_degree, (120, 100).into());
489 assert_eq!(seventwenty_degree, (100, 80).into());
490 }
491
492 #[test]
493 fn eighths() {
494 let center = coord!(100, 100);
495
496 let degree_45 = Coord::from_angle(center, 20, 45);
497 let degree_135 = Coord::from_angle(center, 20, 135);
498 let degree_225 = Coord::from_angle(center, 20, 225);
499 let degree_315 = Coord::from_angle(center, 20, 315);
500
501 assert_eq!(degree_45, (114, 86).into());
502 assert_eq!(degree_135, (114, 114).into());
503 assert_eq!(degree_225, (86, 114).into());
504 assert_eq!(degree_315, (86, 86).into());
505 }
506
507 #[test]
508 fn both_ways() {
509 let center = coord!(60, 60);
510
511 let orig = coord!(90, 60);
512
513 let orig_angle = center.angle_to(orig);
514
515 let rotated = Coord::from_angle(center, center.distance(orig), orig_angle);
516
517 assert_eq!(orig, rotated);
518 }
519 }
520
521 mod methods {
522 #[test]
523 fn dist() {
524 let start = coord!(10, 10);
525
526 assert_eq!(start.distance((20, 10)), 10);
527 assert_eq!(start.distance((0, 10)), 10);
528 assert_eq!(start.distance((10, 0)), 10);
529 assert_eq!(start.distance((10, 20)), 10);
530 assert_eq!(start.distance((20, 20)), 14);
531 assert_eq!(start.distance((0, 0)), 14);
532 assert_eq!(start.distance((20, 0)), 14);
533 assert_eq!(start.distance((0, 20)), 14);
534 }
535
536 #[test]
537 fn angle() {
538 let center = coord!(20, 20);
539
540 assert_eq!(center.angle_to((30, 20)), 90);
541 assert_eq!(center.angle_to((20, 30)), 180);
542 assert_eq!(center.angle_to((10, 20)), 270);
543 assert_eq!(center.angle_to((20, 10)), 0);
544 }
545
546 #[test]
547 fn mid_points() {
548 let start = coord!(10, 10);
549
550 assert_eq!(start.mid_point((20, 10)), (15, 10).into());
551 assert_eq!(start.mid_point((0, 10)), (5, 10).into());
552 assert_eq!(start.mid_point((10, 0)), (10, 5).into());
553 }
554 }
555
556 mod ops {
557 use crate::Coord;
558 use std::ops::{Add, Mul, Neg, Sub};
559
560 #[test]
561 fn simple() {
562 assert_eq!(coord!(1, 1).add((1, 1)), (2, 2).into());
563 assert_eq!(coord!(1, 1).sub((1, 1)), (0, 0).into());
564 assert_eq!(coord!(1, 1).mul((1, 1)), (1, 1).into());
565 assert_eq!(coord!(1, 1).abs(), (1, 1).into());
566 assert_eq!(coord!(1, 1).neg(), (-1, -1).into());
567
568 assert_eq!(coord!(2, 8).add((12, 63)), (14, 71).into());
569 assert_eq!(coord!(3, 7).sub((13, 24)), (-10, -17).into());
570 assert_eq!(coord!(4, 6).mul((11, 21)), (44, 126).into());
571 assert_eq!(coord!(5, -5).abs(), (5, 5).into());
572 assert_eq!(coord!(6, -4).neg(), (-6, 4).into());
573
574 assert_eq!(coord!(4, 8).mul(0.5), (2, 4).into());
575 assert_eq!(coord!(4, 8).mul(Coord::from((0.5, 0.5))), (0, 0).into());
576 assert_eq!(coord!(4, 8).mul(Coord::from((0.4, 0.4))), (0, 0).into());
577 }
578 }
579}