Skip to main content

wows_core/
units.rs

1//! Distance newtypes and their unit conversions.
2//!
3//! The game mixes several distance units: real meters, BigWorld engine units
4//! (1 BW unit = 30 m), ship-model units (used by armor/hull geometry), plus
5//! kilometers and millimeters. These newtypes keep units honest at the type
6//! level; cross-unit arithmetic and comparison convert to a common unit.
7
8use std::ops::Add;
9use std::ops::Mul;
10use std::ops::Sub;
11
12/// Conversion factor: 1 BigWorld unit = 30 meters.
13const BW_TO_METERS: f32 = 30.0;
14
15/// Conversion factor: 1 BigWorld unit = 15 ship-model units.
16/// Ship geometry (armor meshes, hull models) uses this coordinate space
17/// where 1 ship-model unit = 2 real meters (= 30 / 15).
18const BW_TO_SHIP: f32 = 15.0;
19
20/// Distance in meters.
21#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
24pub struct Meters(f32);
25
26/// Distance in BigWorld coordinate units (1 BW unit = 30 meters).
27#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
30pub struct BigWorldDistance(f32);
31
32/// Distance in ship-model coordinate units (1 unit = 2 meters).
33/// Ship geometry (armor meshes, hull visual models) uses this coordinate space.
34/// The game defines BW_TO_SHIP = 15, meaning 1 BigWorld unit = 15 ship-model units,
35/// so 1 ship-model unit = BW_TO_METERS / BW_TO_SHIP = 30 / 15 = 2 meters.
36#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
39pub struct ShipModelDistance(f32);
40
41/// Distance in kilometers.
42#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
45pub struct Km(f32);
46
47/// Distance in millimeters.
48#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
51pub struct Millimeters(f32);
52
53impl From<f32> for Meters {
54    fn from(v: f32) -> Self {
55        Self(v)
56    }
57}
58impl From<i32> for Meters {
59    fn from(v: i32) -> Self {
60        Self(v as f32)
61    }
62}
63
64impl From<f32> for BigWorldDistance {
65    fn from(v: f32) -> Self {
66        Self(v)
67    }
68}
69impl From<i32> for BigWorldDistance {
70    fn from(v: i32) -> Self {
71        Self(v as f32)
72    }
73}
74
75impl From<f32> for Km {
76    fn from(v: f32) -> Self {
77        Self(v)
78    }
79}
80impl From<i32> for Km {
81    fn from(v: i32) -> Self {
82        Self(v as f32)
83    }
84}
85
86impl From<f32> for Millimeters {
87    fn from(v: f32) -> Self {
88        Self(v)
89    }
90}
91impl From<i32> for Millimeters {
92    fn from(v: i32) -> Self {
93        Self(v as f32)
94    }
95}
96
97impl Meters {
98    /// Const constructor for use in static/const contexts.
99    pub const fn new(v: f32) -> Self {
100        Self(v)
101    }
102
103    pub fn value(self) -> f32 {
104        self.0
105    }
106    pub fn to_bigworld(self) -> BigWorldDistance {
107        BigWorldDistance(self.0 / BW_TO_METERS)
108    }
109    /// Convert to ship-model units (1 unit = 2 meters).
110    /// Use this for distances that will be compared against ship geometry
111    /// (armor meshes, hull models), which are in ship-model coordinates.
112    pub fn to_ship_model(self) -> ShipModelDistance {
113        ShipModelDistance(self.0 * BW_TO_SHIP / BW_TO_METERS)
114    }
115    pub fn to_km(self) -> Km {
116        Km(self.0 / 1000.0)
117    }
118    pub fn to_mm(self) -> Millimeters {
119        Millimeters(self.0 * 1000.0)
120    }
121}
122
123impl BigWorldDistance {
124    pub fn value(self) -> f32 {
125        self.0
126    }
127    pub fn to_meters(self) -> Meters {
128        Meters(self.0 * BW_TO_METERS)
129    }
130    pub fn to_km(self) -> Km {
131        self.to_meters().to_km()
132    }
133}
134
135impl ShipModelDistance {
136    pub fn value(self) -> f32 {
137        self.0
138    }
139    pub fn to_meters(self) -> Meters {
140        Meters(self.0 * BW_TO_METERS / BW_TO_SHIP)
141    }
142    pub fn to_bigworld(self) -> BigWorldDistance {
143        BigWorldDistance(self.0 / BW_TO_SHIP)
144    }
145}
146
147impl Km {
148    /// Const constructor for use in static/const contexts.
149    pub const fn new(v: f32) -> Self {
150        Self(v)
151    }
152
153    pub fn value(self) -> f32 {
154        self.0
155    }
156    pub fn to_meters(self) -> Meters {
157        Meters(self.0 * 1000.0)
158    }
159    pub fn to_bigworld(self) -> BigWorldDistance {
160        self.to_meters().to_bigworld()
161    }
162}
163
164impl Millimeters {
165    /// Const constructor for use in static/const contexts.
166    pub const fn new(v: f32) -> Self {
167        Self(v)
168    }
169
170    pub fn value(self) -> f32 {
171        self.0
172    }
173    pub fn to_meters(self) -> Meters {
174        Meters(self.0 / 1000.0)
175    }
176    pub fn to_bigworld(self) -> BigWorldDistance {
177        self.to_meters().to_bigworld()
178    }
179}
180
181impl Mul<f32> for Meters {
182    type Output = Meters;
183    fn mul(self, rhs: f32) -> Meters {
184        Meters(self.0 * rhs)
185    }
186}
187
188impl Mul<f32> for BigWorldDistance {
189    type Output = BigWorldDistance;
190    fn mul(self, rhs: f32) -> BigWorldDistance {
191        BigWorldDistance(self.0 * rhs)
192    }
193}
194
195impl Mul<f32> for Km {
196    type Output = Km;
197    fn mul(self, rhs: f32) -> Km {
198        Km(self.0 * rhs)
199    }
200}
201
202impl Mul<f32> for Millimeters {
203    type Output = Millimeters;
204    fn mul(self, rhs: f32) -> Millimeters {
205        Millimeters(self.0 * rhs)
206    }
207}
208
209impl Add for Meters {
210    type Output = Meters;
211    fn add(self, rhs: Meters) -> Meters {
212        Meters(self.0 + rhs.0)
213    }
214}
215impl Sub for Meters {
216    type Output = Meters;
217    fn sub(self, rhs: Meters) -> Meters {
218        Meters(self.0 - rhs.0)
219    }
220}
221
222impl Add for BigWorldDistance {
223    type Output = BigWorldDistance;
224    fn add(self, rhs: BigWorldDistance) -> BigWorldDistance {
225        BigWorldDistance(self.0 + rhs.0)
226    }
227}
228impl Sub for BigWorldDistance {
229    type Output = BigWorldDistance;
230    fn sub(self, rhs: BigWorldDistance) -> BigWorldDistance {
231        BigWorldDistance(self.0 - rhs.0)
232    }
233}
234
235impl Add for Km {
236    type Output = Km;
237    fn add(self, rhs: Km) -> Km {
238        Km(self.0 + rhs.0)
239    }
240}
241impl Sub for Km {
242    type Output = Km;
243    fn sub(self, rhs: Km) -> Km {
244        Km(self.0 - rhs.0)
245    }
246}
247
248impl Add for Millimeters {
249    type Output = Millimeters;
250    fn add(self, rhs: Millimeters) -> Millimeters {
251        Millimeters(self.0 + rhs.0)
252    }
253}
254impl Sub for Millimeters {
255    type Output = Millimeters;
256    fn sub(self, rhs: Millimeters) -> Millimeters {
257        Millimeters(self.0 - rhs.0)
258    }
259}
260
261impl Add<BigWorldDistance> for Meters {
262    type Output = Meters;
263    fn add(self, rhs: BigWorldDistance) -> Meters {
264        Meters(self.0 + rhs.to_meters().0)
265    }
266}
267impl Sub<BigWorldDistance> for Meters {
268    type Output = Meters;
269    fn sub(self, rhs: BigWorldDistance) -> Meters {
270        Meters(self.0 - rhs.to_meters().0)
271    }
272}
273
274impl Add<Meters> for BigWorldDistance {
275    type Output = BigWorldDistance;
276    fn add(self, rhs: Meters) -> BigWorldDistance {
277        BigWorldDistance(self.0 + rhs.to_bigworld().0)
278    }
279}
280impl Sub<Meters> for BigWorldDistance {
281    type Output = BigWorldDistance;
282    fn sub(self, rhs: Meters) -> BigWorldDistance {
283        BigWorldDistance(self.0 - rhs.to_bigworld().0)
284    }
285}
286
287impl Add<Km> for Meters {
288    type Output = Meters;
289    fn add(self, rhs: Km) -> Meters {
290        Meters(self.0 + rhs.to_meters().0)
291    }
292}
293impl Sub<Km> for Meters {
294    type Output = Meters;
295    fn sub(self, rhs: Km) -> Meters {
296        Meters(self.0 - rhs.to_meters().0)
297    }
298}
299
300impl Add<Meters> for Km {
301    type Output = Km;
302    fn add(self, rhs: Meters) -> Km {
303        Km(self.0 + rhs.to_km().0)
304    }
305}
306impl Sub<Meters> for Km {
307    type Output = Km;
308    fn sub(self, rhs: Meters) -> Km {
309        Km(self.0 - rhs.to_km().0)
310    }
311}
312
313impl std::ops::Div<f32> for Meters {
314    type Output = Meters;
315    fn div(self, rhs: f32) -> Meters {
316        Meters(self.0 / rhs)
317    }
318}
319
320impl std::ops::Div<f32> for BigWorldDistance {
321    type Output = BigWorldDistance;
322    fn div(self, rhs: f32) -> BigWorldDistance {
323        BigWorldDistance(self.0 / rhs)
324    }
325}
326
327impl std::ops::Div<f32> for Km {
328    type Output = Km;
329    fn div(self, rhs: f32) -> Km {
330        Km(self.0 / rhs)
331    }
332}
333
334impl std::ops::Div<f32> for Millimeters {
335    type Output = Millimeters;
336    fn div(self, rhs: f32) -> Millimeters {
337        Millimeters(self.0 / rhs)
338    }
339}
340
341impl std::iter::Sum for Meters {
342    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
343        Meters(iter.map(|m| m.0).sum())
344    }
345}
346
347impl std::iter::Sum for BigWorldDistance {
348    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
349        BigWorldDistance(iter.map(|d| d.0).sum())
350    }
351}
352
353impl std::iter::Sum for Km {
354    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
355        Km(iter.map(|k| k.0).sum())
356    }
357}
358
359impl std::iter::Sum for Millimeters {
360    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
361        Millimeters(iter.map(|m| m.0).sum())
362    }
363}
364
365impl PartialEq<BigWorldDistance> for Meters {
366    fn eq(&self, other: &BigWorldDistance) -> bool {
367        self.0 == other.to_meters().0
368    }
369}
370impl PartialOrd<BigWorldDistance> for Meters {
371    fn partial_cmp(&self, other: &BigWorldDistance) -> Option<std::cmp::Ordering> {
372        self.0.partial_cmp(&other.to_meters().0)
373    }
374}
375
376impl PartialEq<Meters> for BigWorldDistance {
377    fn eq(&self, other: &Meters) -> bool {
378        self.0 == other.to_bigworld().0
379    }
380}
381impl PartialOrd<Meters> for BigWorldDistance {
382    fn partial_cmp(&self, other: &Meters) -> Option<std::cmp::Ordering> {
383        self.0.partial_cmp(&other.to_bigworld().0)
384    }
385}