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