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