1use std::fmt;
9use std::ops::Add;
10use std::ops::Mul;
11use std::ops::Sub;
12
13const BW_TO_METERS: f32 = 30.0;
15
16const BW_TO_SHIP: f32 = 15.0;
20
21#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
25pub struct Meters(f32);
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
31pub struct BigWorldDistance(f32);
32
33#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
40pub struct ShipModelDistance(f32);
41
42#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
46pub struct Km(f32);
47
48#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
51#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
52pub struct Millimeters(f32);
53
54#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
56#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
57#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
58pub struct MetersPerSecond(f32);
59
60impl From<f32> for Meters {
61 fn from(v: f32) -> Self {
62 Self(v)
63 }
64}
65impl From<i32> for Meters {
66 fn from(v: i32) -> Self {
67 Self(v as f32)
68 }
69}
70
71impl From<f32> for BigWorldDistance {
72 fn from(v: f32) -> Self {
73 Self(v)
74 }
75}
76impl From<i32> for BigWorldDistance {
77 fn from(v: i32) -> Self {
78 Self(v as f32)
79 }
80}
81
82impl From<f32> for Km {
83 fn from(v: f32) -> Self {
84 Self(v)
85 }
86}
87impl From<i32> for Km {
88 fn from(v: i32) -> Self {
89 Self(v as f32)
90 }
91}
92
93impl From<f32> for Millimeters {
94 fn from(v: f32) -> Self {
95 Self(v)
96 }
97}
98impl From<i32> for Millimeters {
99 fn from(v: i32) -> Self {
100 Self(v as f32)
101 }
102}
103
104impl From<f32> for MetersPerSecond {
105 fn from(v: f32) -> Self {
106 Self(v)
107 }
108}
109impl From<i32> for MetersPerSecond {
110 fn from(v: i32) -> Self {
111 Self(v as f32)
112 }
113}
114
115impl Meters {
116 pub const fn new(v: f32) -> Self {
118 Self(v)
119 }
120
121 pub fn value(self) -> f32 {
122 self.0
123 }
124 pub fn to_bigworld(self) -> BigWorldDistance {
125 BigWorldDistance(self.0 / BW_TO_METERS)
126 }
127 pub fn to_ship_model(self) -> ShipModelDistance {
131 ShipModelDistance(self.0 * BW_TO_SHIP / BW_TO_METERS)
132 }
133 pub fn to_km(self) -> Km {
134 Km(self.0 / 1000.0)
135 }
136 pub fn to_mm(self) -> Millimeters {
137 Millimeters(self.0 * 1000.0)
138 }
139}
140
141impl BigWorldDistance {
142 pub fn value(self) -> f32 {
143 self.0
144 }
145 pub fn to_meters(self) -> Meters {
146 Meters(self.0 * BW_TO_METERS)
147 }
148 pub fn to_km(self) -> Km {
149 self.to_meters().to_km()
150 }
151}
152
153impl ShipModelDistance {
154 pub fn value(self) -> f32 {
155 self.0
156 }
157 pub fn to_meters(self) -> Meters {
158 Meters(self.0 * BW_TO_METERS / BW_TO_SHIP)
159 }
160 pub fn to_bigworld(self) -> BigWorldDistance {
161 BigWorldDistance(self.0 / BW_TO_SHIP)
162 }
163}
164
165impl Km {
166 pub const fn new(v: f32) -> Self {
168 Self(v)
169 }
170
171 pub fn value(self) -> f32 {
172 self.0
173 }
174 pub fn to_meters(self) -> Meters {
175 Meters(self.0 * 1000.0)
176 }
177 pub fn to_bigworld(self) -> BigWorldDistance {
178 self.to_meters().to_bigworld()
179 }
180}
181
182impl Millimeters {
183 pub const fn new(v: f32) -> Self {
185 Self(v)
186 }
187
188 pub fn value(self) -> f32 {
189 self.0
190 }
191 pub fn to_meters(self) -> Meters {
192 Meters(self.0 / 1000.0)
193 }
194 pub fn to_bigworld(self) -> BigWorldDistance {
195 self.to_meters().to_bigworld()
196 }
197}
198
199impl MetersPerSecond {
200 pub const fn new(v: f32) -> Self {
202 Self(v)
203 }
204
205 pub fn value(self) -> f32 {
206 self.0
207 }
208}
209
210impl fmt::Display for Meters {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 write!(f, "{:.0} m", self.0)
214 }
215}
216
217impl fmt::Display for Km {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 write!(f, "{:.1} km", self.0)
221 }
222}
223
224impl fmt::Display for Millimeters {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(f, "{:.0} mm", self.0)
228 }
229}
230
231impl fmt::Display for MetersPerSecond {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 write!(f, "{:.0} m/s", self.0)
235 }
236}
237
238impl Mul<f32> for Meters {
239 type Output = Meters;
240 fn mul(self, rhs: f32) -> Meters {
241 Meters(self.0 * rhs)
242 }
243}
244
245impl Mul<f32> for BigWorldDistance {
246 type Output = BigWorldDistance;
247 fn mul(self, rhs: f32) -> BigWorldDistance {
248 BigWorldDistance(self.0 * rhs)
249 }
250}
251
252impl Mul<f32> for Km {
253 type Output = Km;
254 fn mul(self, rhs: f32) -> Km {
255 Km(self.0 * rhs)
256 }
257}
258
259impl Mul<f32> for Millimeters {
260 type Output = Millimeters;
261 fn mul(self, rhs: f32) -> Millimeters {
262 Millimeters(self.0 * rhs)
263 }
264}
265
266impl Add for Meters {
267 type Output = Meters;
268 fn add(self, rhs: Meters) -> Meters {
269 Meters(self.0 + rhs.0)
270 }
271}
272impl Sub for Meters {
273 type Output = Meters;
274 fn sub(self, rhs: Meters) -> Meters {
275 Meters(self.0 - rhs.0)
276 }
277}
278
279impl Add for BigWorldDistance {
280 type Output = BigWorldDistance;
281 fn add(self, rhs: BigWorldDistance) -> BigWorldDistance {
282 BigWorldDistance(self.0 + rhs.0)
283 }
284}
285impl Sub for BigWorldDistance {
286 type Output = BigWorldDistance;
287 fn sub(self, rhs: BigWorldDistance) -> BigWorldDistance {
288 BigWorldDistance(self.0 - rhs.0)
289 }
290}
291
292impl Add for Km {
293 type Output = Km;
294 fn add(self, rhs: Km) -> Km {
295 Km(self.0 + rhs.0)
296 }
297}
298impl Sub for Km {
299 type Output = Km;
300 fn sub(self, rhs: Km) -> Km {
301 Km(self.0 - rhs.0)
302 }
303}
304
305impl Add for Millimeters {
306 type Output = Millimeters;
307 fn add(self, rhs: Millimeters) -> Millimeters {
308 Millimeters(self.0 + rhs.0)
309 }
310}
311impl Sub for Millimeters {
312 type Output = Millimeters;
313 fn sub(self, rhs: Millimeters) -> Millimeters {
314 Millimeters(self.0 - rhs.0)
315 }
316}
317
318impl Add<BigWorldDistance> for Meters {
319 type Output = Meters;
320 fn add(self, rhs: BigWorldDistance) -> Meters {
321 Meters(self.0 + rhs.to_meters().0)
322 }
323}
324impl Sub<BigWorldDistance> for Meters {
325 type Output = Meters;
326 fn sub(self, rhs: BigWorldDistance) -> Meters {
327 Meters(self.0 - rhs.to_meters().0)
328 }
329}
330
331impl Add<Meters> for BigWorldDistance {
332 type Output = BigWorldDistance;
333 fn add(self, rhs: Meters) -> BigWorldDistance {
334 BigWorldDistance(self.0 + rhs.to_bigworld().0)
335 }
336}
337impl Sub<Meters> for BigWorldDistance {
338 type Output = BigWorldDistance;
339 fn sub(self, rhs: Meters) -> BigWorldDistance {
340 BigWorldDistance(self.0 - rhs.to_bigworld().0)
341 }
342}
343
344impl Add<Km> for Meters {
345 type Output = Meters;
346 fn add(self, rhs: Km) -> Meters {
347 Meters(self.0 + rhs.to_meters().0)
348 }
349}
350impl Sub<Km> for Meters {
351 type Output = Meters;
352 fn sub(self, rhs: Km) -> Meters {
353 Meters(self.0 - rhs.to_meters().0)
354 }
355}
356
357impl Add<Meters> for Km {
358 type Output = Km;
359 fn add(self, rhs: Meters) -> Km {
360 Km(self.0 + rhs.to_km().0)
361 }
362}
363impl Sub<Meters> for Km {
364 type Output = Km;
365 fn sub(self, rhs: Meters) -> Km {
366 Km(self.0 - rhs.to_km().0)
367 }
368}
369
370impl std::ops::Div<f32> for Meters {
371 type Output = Meters;
372 fn div(self, rhs: f32) -> Meters {
373 Meters(self.0 / rhs)
374 }
375}
376
377impl std::ops::Div<f32> for BigWorldDistance {
378 type Output = BigWorldDistance;
379 fn div(self, rhs: f32) -> BigWorldDistance {
380 BigWorldDistance(self.0 / rhs)
381 }
382}
383
384impl std::ops::Div<f32> for Km {
385 type Output = Km;
386 fn div(self, rhs: f32) -> Km {
387 Km(self.0 / rhs)
388 }
389}
390
391impl std::ops::Div<f32> for Millimeters {
392 type Output = Millimeters;
393 fn div(self, rhs: f32) -> Millimeters {
394 Millimeters(self.0 / rhs)
395 }
396}
397
398impl std::iter::Sum for Meters {
399 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
400 Meters(iter.map(|m| m.0).sum())
401 }
402}
403
404impl std::iter::Sum for BigWorldDistance {
405 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
406 BigWorldDistance(iter.map(|d| d.0).sum())
407 }
408}
409
410impl std::iter::Sum for Km {
411 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
412 Km(iter.map(|k| k.0).sum())
413 }
414}
415
416impl std::iter::Sum for Millimeters {
417 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
418 Millimeters(iter.map(|m| m.0).sum())
419 }
420}
421
422impl PartialEq<BigWorldDistance> for Meters {
423 fn eq(&self, other: &BigWorldDistance) -> bool {
424 self.0 == other.to_meters().0
425 }
426}
427impl PartialOrd<BigWorldDistance> for Meters {
428 fn partial_cmp(&self, other: &BigWorldDistance) -> Option<std::cmp::Ordering> {
429 self.0.partial_cmp(&other.to_meters().0)
430 }
431}
432
433impl PartialEq<Meters> for BigWorldDistance {
434 fn eq(&self, other: &Meters) -> bool {
435 self.0 == other.to_bigworld().0
436 }
437}
438impl PartialOrd<Meters> for BigWorldDistance {
439 fn partial_cmp(&self, other: &Meters) -> Option<std::cmp::Ordering> {
440 self.0.partial_cmp(&other.to_bigworld().0)
441 }
442}
443
444#[cfg(test)]
445mod tests {
446 use super::*;
447
448 #[test]
449 fn meters_display_rounds_to_whole() {
450 assert_eq!(Meters::from(740.6).to_string(), "741 m");
451 }
452
453 #[test]
454 fn km_display_one_decimal() {
455 assert_eq!(Km::from(10.5).to_string(), "10.5 km");
456 }
457
458 #[test]
459 fn millimeters_display_rounds_to_whole() {
460 assert_eq!(Millimeters::from(406.0).to_string(), "406 mm");
461 }
462
463 #[test]
464 fn meters_per_second_display_rounds_to_whole() {
465 assert_eq!(MetersPerSecond::from(820.0).to_string(), "820 m/s");
466 }
467}