1use bevy::prelude::*;
2use derive_more::{Add, AddAssign, Sub, SubAssign};
3
4#[derive(
6 Debug,
7 Clone,
8 Copy,
9 PartialEq,
10 Eq,
11 Hash,
12 Ord,
13 PartialOrd,
14 Default,
15 Reflect,
16 Add,
17 Sub,
18 AddAssign,
19 SubAssign,
20)]
21pub struct TimetableTime(pub i32);
22
23impl TimetableTime {
24 #[inline]
25 pub fn from_hms<T: Into<i32>>(h: T, m: T, s: T) -> Self {
26 TimetableTime(h.into() * 3600 + m.into() * 60 + s.into())
27 }
28 #[inline]
29 pub fn from_str(s: &str) -> Option<Self> {
30 let positive = s.contains("+");
31 let parts: Vec<&str> = s.trim().split(|c| c == '+' || c == '-').collect();
32 let time_part = parts.get(0)?;
33 let time_parts: Vec<&str> = time_part.split(':').collect();
34 let base_time = match time_parts.len() {
35 2 => {
36 let h = time_parts[0].parse::<i32>().ok()?;
37 let m = time_parts[1].parse::<i32>().ok()?;
38 Some(TimetableTime::from_hms(h, m, 0))
39 }
40 3 => {
41 let h = time_parts[0].parse::<i32>().ok()?;
42 let m = time_parts[1].parse::<i32>().ok()?;
43 let s = time_parts[2].parse::<i32>().ok()?;
44 Some(TimetableTime::from_hms(h, m, s))
45 }
46 _ => None,
47 };
48 match (base_time, parts.get(1)) {
49 (Some(t), Some(day_str)) => {
50 let day_offset: i32 = day_str.trim().parse().ok()?;
51 Some(if positive {
52 t + TimetableTime(day_offset * 86400)
53 } else {
54 t - TimetableTime(day_offset * 86400)
55 })
56 }
57 (Some(t), None) => Some(t),
58 _ => None,
59 }
60 }
61 #[inline]
62 pub fn from_oud2_str(s: &str) -> Option<Self> {
63 match s.len() {
64 3 => {
65 let h = s[0..1].parse::<i32>().ok()?;
66 let m = s[1..3].parse::<i32>().ok()?;
67 Some(TimetableTime::from_hms(h, m, 0))
68 }
69 4 => {
70 let h = s[0..2].parse::<i32>().ok()?;
71 let m = s[2..4].parse::<i32>().ok()?;
72 Some(TimetableTime::from_hms(h, m, 0))
73 }
74 5 => {
75 let h = s[0..1].parse::<i32>().ok()?;
76 let m = s[1..3].parse::<i32>().ok()?;
77 let sec = s[3..5].parse::<i32>().ok()?;
78 Some(TimetableTime::from_hms(h, m, sec))
79 }
80 6 => {
81 let h = s[0..2].parse::<i32>().ok()?;
82 let m = s[2..4].parse::<i32>().ok()?;
83 let sec = s[4..6].parse::<i32>().ok()?;
84 Some(TimetableTime::from_hms(h, m, sec))
85 }
86 _ => None,
87 }
88 }
89 #[inline]
90 pub fn to_hhmm_string_no_colon(self) -> String {
91 let total_seconds = self.0;
92 let hours = total_seconds / 3600;
93 let minutes = (total_seconds % 3600) / 60;
94 if hours < 0 || minutes < 0 {
95 warn!("Negative time encountered in to_hhmm_string_no_colon()");
96 }
97 format!("{:>2}{:02}", hours, minutes)
98 }
99 #[inline]
100 pub fn to_mmss_string(self) -> String {
101 let total_seconds = self.0;
102 let minutes = total_seconds / 60;
103 let seconds = total_seconds % 60;
104 if minutes < 0 || seconds < 0 {
105 warn!("Negative time encountered in to_mmss_string()");
106 }
107 format!("{:02}:{:02}", minutes, seconds)
108 }
109 #[inline]
114 pub fn to_hhmmss_string(self) -> String {
115 let total_seconds = self.0;
116 let hours = total_seconds / 3600;
117 let minutes = (total_seconds % 3600) / 60;
118 let seconds = total_seconds % 60;
119 if hours < 0 || minutes < 0 || seconds < 0 {
120 warn!("Negative time encountered in to_hhmmss_string()");
121 }
122 format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
123 }
124 #[inline]
125 pub fn to_hmsd_parts(self) -> (i32, i32, i32, i32) {
126 let days = self.0.div_euclid(24 * 3600);
127 let seconds_of_day = self.0.rem_euclid(24 * 3600);
128
129 let hours = seconds_of_day / 3600;
130 let minutes = (seconds_of_day % 3600) / 60;
131 let seconds = seconds_of_day % 60;
132
133 (hours, minutes, seconds, days)
134 }
135 #[inline]
136 pub fn to_ms_parts(self) -> (i32, i32) {
137 let total_seconds = self.0;
138 let minutes = total_seconds / 60;
139 let seconds = total_seconds % 60;
140 (minutes, seconds)
141 }
142}
143
144impl std::fmt::Display for TimetableTime {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 let days = self.0.div_euclid(24 * 3600);
147 let seconds_of_day = self.0.rem_euclid(24 * 3600);
148
149 let hours = seconds_of_day / 3600;
150 let minutes = (seconds_of_day % 3600) / 60;
151 let seconds = seconds_of_day % 60;
152
153 write!(f, "{:02}:{:02}:{:02}", hours, minutes, seconds)?;
154
155 if days != 0 {
156 let sign = if days > 0 { '+' } else { '-' };
157 write!(f, "{}{}", sign, days.abs())?;
158 }
159
160 Ok(())
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Default, Reflect)]
166pub struct TrackDistance(i32);
167
168impl TrackDistance {
169 #[inline(always)]
170 pub fn from_km<T: Into<f64>>(km: T) -> Self {
171 TrackDistance((km.into() * 1000.0).round() as i32)
172 }
173 #[inline(always)]
174 pub fn from_m<T: Into<i32>>(m: T) -> Self {
175 TrackDistance(m.into())
176 }
177 #[inline(always)]
182 pub fn as_m(&self) -> i32 {
183 self.0
184 }
185}
186
187impl std::fmt::Display for TrackDistance {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 if self.0 >= 1000 {
190 write!(f, "{:.2} km", self.0 as f64 / 1000.0)
191 } else {
192 write!(f, "{} m", self.0)
193 }
194 }
195}
196
197#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Reflect)]
199pub struct CanvasDistance(f32);
200impl CanvasDistance {
201 #[inline(always)]
202 pub fn from_mm<T: Into<f32>>(mm: T, factor: T) -> Self {
203 CanvasDistance(mm.into() * factor.into())
204 }
205 #[inline(always)]
214 pub fn as_mm(&self) -> f32 {
215 self.0
216 }
217}
218
219impl From<TrackDistance> for CanvasDistance {
220 fn from(distance: TrackDistance) -> Self {
221 CanvasDistance(distance.0 as f32)
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Reflect)]
227pub struct Speed(f32);
228impl Speed {
229 }
242
243impl std::fmt::Display for Speed {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 write!(f, "{:.2} km/h", self.0)
246 }
247}
248
249impl std::ops::Div<TimetableTime> for TrackDistance {
250 type Output = Speed;
251
252 fn div(self, rhs: TimetableTime) -> Self::Output {
253 Speed(self.0 as f32 / 1000.0 / rhs.0 as f32 * 3600.0)
254 }
255}
256
257#[derive(Component, Debug)]
260pub struct Note {
261 pub text: String,
263 pub modified_time: i64,
265 pub created_time: i64,
267}