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