Skip to main content

nil_core/continent/
coord.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::continent::ContinentSize;
5use crate::error::Result;
6use glam::u8::U8Vec2;
7use itertools::Itertools;
8use nil_util::ConstDeref;
9use serde::de::{self, Error as _, MapAccess, SeqAccess, Visitor};
10use serde::ser::SerializeStruct;
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12use std::borrow::Cow;
13use std::fmt;
14use std::ops::{Add, AddAssign, Sub, SubAssign};
15
16#[derive(Clone, Copy, PartialEq, Eq, Hash)]
17#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
18#[cfg_attr(feature = "typescript", ts(type = "{ x: number, y: number }"))]
19pub struct Coord(U8Vec2);
20
21impl Coord {
22  #[inline]
23  #[must_use]
24  pub const fn new(x: u8, y: u8) -> Self {
25    Self(U8Vec2::new(x, y))
26  }
27
28  #[inline]
29  #[must_use]
30  pub const fn splat(value: u8) -> Self {
31    Self(U8Vec2::splat(value))
32  }
33
34  #[inline]
35  pub const fn x(&self) -> u8 {
36    self.0.x
37  }
38
39  #[inline]
40  pub const fn y(&self) -> u8 {
41    self.0.y
42  }
43
44  #[inline]
45  pub fn distance(&self, rhs: Coord) -> Distance {
46    Distance::new(self.0.chebyshev_distance(rhs.0))
47  }
48
49  #[inline]
50  pub const fn is_within_continent(&self, size: ContinentSize) -> bool {
51    size > self.x() && size > self.y()
52  }
53
54  pub const fn is_within_distance(&self, other: Coord, distance: Distance) -> bool {
55    let x0 = i16::from(self.x());
56    let y0 = i16::from(self.y());
57    let distance = i16::from(distance);
58
59    let absx = (i16::from(other.x()) - x0).abs();
60    let absy = (i16::from(other.y()) - y0).abs();
61    absx.max(absy) <= distance
62  }
63
64  #[inline]
65  #[must_use]
66  pub fn within_distance(self, distance: Distance) -> Vec<Self> {
67    within_distance(self, distance, false)
68  }
69
70  #[inline]
71  #[must_use]
72  pub fn within_distance_inclusive(self, distance: Distance) -> Vec<Self> {
73    within_distance(self, distance, true)
74  }
75}
76
77fn within_distance(origin: Coord, distance: Distance, inclusive: bool) -> Vec<Coord> {
78  let mut coords = Vec::new();
79  let x0 = i16::from(origin.x());
80  let y0 = i16::from(origin.y());
81  let distance = i16::from(distance);
82
83  for x in (x0 - distance)..=(x0 + distance) {
84    for y in (y0 - distance)..=(y0 + distance) {
85      let absx = (x - x0).abs();
86      let absy = (y - y0).abs();
87      if absx.max(absy) <= distance
88        && (inclusive || x != x0 || y != y0)
89        && let Ok(x) = u8::try_from(x)
90        && let Ok(y) = u8::try_from(y)
91      {
92        coords.push(Coord::new(x, y));
93      }
94    }
95  }
96
97  coords.into_iter().unique().collect()
98}
99
100impl const Add for Coord {
101  type Output = Coord;
102
103  fn add(self, rhs: Self) -> Self::Output {
104    Self(self.0.saturating_add(rhs.0))
105  }
106}
107
108impl const Sub for Coord {
109  type Output = Coord;
110
111  fn sub(self, rhs: Self) -> Self::Output {
112    Self(self.0.saturating_sub(rhs.0))
113  }
114}
115
116impl const From<(u8, u8)> for Coord {
117  fn from((x, y): (u8, u8)) -> Self {
118    Self::new(x, y)
119  }
120}
121
122impl fmt::Debug for Coord {
123  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124    f.debug_tuple("Coord")
125      .field(&self.0.x)
126      .field(&self.0.y)
127      .finish()
128  }
129}
130
131impl fmt::Display for Coord {
132  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133    write!(f, "{:03}|{:03}", self.0.x, self.0.y)
134  }
135}
136
137impl Serialize for Coord {
138  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139  where
140    S: Serializer,
141  {
142    let mut coord = serializer.serialize_struct("Coord", 2)?;
143    coord.serialize_field("x", &self.0.x)?;
144    coord.serialize_field("y", &self.0.y)?;
145    coord.end()
146  }
147}
148
149impl<'de> Deserialize<'de> for Coord {
150  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151  where
152    D: Deserializer<'de>,
153  {
154    deserializer.deserialize_struct("Coord", &CoordVisitor::FIELD, CoordVisitor)
155  }
156}
157
158struct CoordVisitor;
159
160impl CoordVisitor {
161  const FIELD: [&str; 2] = ["x", "y"];
162}
163
164impl<'de> Visitor<'de> for CoordVisitor {
165  type Value = Coord;
166
167  fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
168    formatter.write_str("struct Coord")
169  }
170
171  fn visit_seq<V>(self, mut seq: V) -> Result<Coord, V::Error>
172  where
173    V: SeqAccess<'de>,
174  {
175    let x = seq
176      .next_element()?
177      .ok_or_else(|| de::Error::invalid_length(0, &self))?;
178
179    let y = seq
180      .next_element()?
181      .ok_or_else(|| de::Error::invalid_length(1, &self))?;
182
183    Ok(Coord::new(x, y))
184  }
185
186  fn visit_map<V>(self, mut map: V) -> Result<Coord, V::Error>
187  where
188    V: MapAccess<'de>,
189  {
190    let mut x = None;
191    let mut y = None;
192
193    while let Some(key) = map.next_key::<Cow<'static, str>>()? {
194      match key.as_ref() {
195        "x" => x = Some(map.next_value()?),
196        "y" => y = Some(map.next_value()?),
197        _ => {}
198      }
199    }
200
201    Ok(Coord::new(
202      x.ok_or_else(|| V::Error::missing_field("x"))?,
203      y.ok_or_else(|| V::Error::missing_field("y"))?,
204    ))
205  }
206}
207
208#[derive(Clone, Copy, Debug, Deserialize, Serialize, ConstDeref)]
209#[derive_const(Default, PartialEq, Eq, PartialOrd, Ord)]
210pub struct Distance(u8);
211
212impl Distance {
213  #[inline]
214  pub const fn new(distance: u8) -> Self {
215    Self(distance)
216  }
217}
218
219impl const From<u8> for Distance {
220  fn from(value: u8) -> Self {
221    Self(value)
222  }
223}
224
225impl const From<Distance> for u8 {
226  fn from(value: Distance) -> Self {
227    value.0
228  }
229}
230
231impl const From<Distance> for i16 {
232  fn from(value: Distance) -> Self {
233    i16::from(value.0)
234  }
235}
236
237impl const From<Distance> for f64 {
238  fn from(value: Distance) -> Self {
239    f64::from(value.0)
240  }
241}
242
243impl const PartialEq<u8> for Distance {
244  fn eq(&self, other: &u8) -> bool {
245    self.0.eq(other)
246  }
247}
248
249impl const Add for Distance {
250  type Output = Distance;
251
252  fn add(self, rhs: Self) -> Self::Output {
253    Self(self.0.saturating_add(rhs.0))
254  }
255}
256
257impl const AddAssign for Distance {
258  fn add_assign(&mut self, rhs: Self) {
259    *self = *self + rhs;
260  }
261}
262
263impl const Sub for Distance {
264  type Output = Distance;
265
266  fn sub(self, rhs: Self) -> Self::Output {
267    Self(self.0.saturating_sub(rhs.0))
268  }
269}
270
271impl const SubAssign for Distance {
272  fn sub_assign(&mut self, rhs: Self) {
273    *self = *self - rhs;
274  }
275}
276
277impl const Add<u8> for Distance {
278  type Output = Distance;
279
280  fn add(self, rhs: u8) -> Self::Output {
281    Self(self.0.saturating_add(rhs))
282  }
283}
284
285impl const AddAssign<u8> for Distance {
286  fn add_assign(&mut self, rhs: u8) {
287    *self = *self + rhs;
288  }
289}
290
291impl const Sub<u8> for Distance {
292  type Output = Distance;
293
294  fn sub(self, rhs: u8) -> Self::Output {
295    Self(self.0.saturating_sub(rhs))
296  }
297}
298
299impl const SubAssign<u8> for Distance {
300  fn sub_assign(&mut self, rhs: u8) {
301    *self = *self - rhs;
302  }
303}