1#![warn(missing_docs)]
2use std::fmt::Debug;
6use std::fs;
7
8use serde::Serialize;
9
10#[derive(Default, Serialize, Debug, Copy, Clone)]
12pub struct Coordinates {
13 x: f64,
14 y: f64,
15}
16
17#[derive(Default, Serialize, Debug, Copy, Clone)]
19pub struct Rider {
20 #[serde(rename = "startPosition")]
21 start_position: Coordinates,
22 #[serde(rename = "startVelocity")]
23 start_velocity: Coordinates,
24 remountable: usize,
25}
26
27#[derive(Default, Serialize, Debug)]
29pub struct Layer {
30 id: usize,
31 name: String,
32 visible: bool,
33 editable: bool,
34}
35
36impl Layer {
37 pub fn new() -> Self {
39 Layer {
40 id: 0,
41 name: "Base Layer".to_string(),
42 visible: true,
43 editable: true,
44 }
45 }
46}
47
48#[derive(Default, Serialize, Debug, Copy, Clone)]
51pub struct Line {
52 id: Option<usize>,
55 #[serde(rename = "type")] kind: usize,
57 x1: f64,
58 y1: f64,
59 x2: f64,
60 y2: f64,
61 flipped: bool,
63 #[serde(rename = "leftExtended")] left_extended: bool,
65 #[serde(rename = "rightExtended")]
66 right_extended: bool,
67}
68
69#[derive(Serialize, Debug)]
71pub struct Version(String);
72
73impl Default for Version {
75 fn default() -> Self {
76 Self("6.2".into())
77 }
78}
79
80#[derive(Default, Serialize, Debug)]
82pub struct Game {
83 label: String,
84 creator: String,
85 description: String,
86 duration: usize,
87 version: Version,
88 audio: Option<String>,
89 #[serde(rename = "startPosition")] start_position: Coordinates,
91 riders: Vec<Rider>,
92 layers: Vec<Layer>,
93 lines: Vec<Line>,
94}
95
96impl Game {
97 pub fn new() -> Self {
99 Game {
100 label: "Track created by lr-rust".to_string(),
101 creator: "lr-rust".to_string(),
102 duration: 120,
103 layers: Vec::from([Layer::new()]),
104 ..Game::default()
105 }
106 }
107
108 pub fn add_line(&mut self, line: &Line) {
110 let line_with_id = Line {
111 id: Some(self.lines.len() + 1),
112 ..*line
113 };
114 self.lines.push(line_with_id);
115 }
116
117 pub fn add_lines<'a, T: Iterator<Item = &'a Line>>(&mut self, lines: T) {
119 for line in lines {
120 self.add_line(line);
121 }
122 }
123
124 pub fn add_rider(&mut self, rider: &Rider) {
126 self.riders.push(*rider);
127 }
128
129 pub fn add_riders<'a, T: Iterator<Item = &'a Rider>>(&mut self, riders: T) {
131 for rider in riders {
132 self.add_rider(rider);
133 }
134 }
135
136 pub fn construct_game(&self) -> String {
138 serde_json::to_string(&self).unwrap()
139 }
140
141 pub fn write_to_file(&self, filename: &str) -> std::io::Result<()> {
143 fs::write(filename, self.construct_game())?;
144 Ok(())
145 }
146}
147
148pub mod extension {
150 use std::ops::Range;
151
152 use crate::*;
153
154 #[derive(Clone, Copy)]
156 pub enum CoordOptions {
157 Rand,
159 RandRange(Coordinates, Coordinates),
161 Other(Option<Coordinates>),
163 EvenlySpaced(Coordinates, Coordinates),
165 }
166
167 pub fn create_riders(n: usize, start_position: CoordOptions, speed_range: CoordOptions, remountable: Option<usize>) -> Vec<Rider> {
169 fn check_min_max(min: f64, max: f64) {
170 if max < min {
172 panic!("Max ({:.}) is less than min ({:.})", max, min);
173 }
174 }
175 fn even_spaced_rider(min: Coordinates, max: Coordinates, i: usize, n: usize) -> Coordinates {
176 check_min_max(min.x, max.x);
177 check_min_max(min.y, max.y);
178
179 Coordinates {
180 x: min.x + i as f64 * (max.x - min.x) / (n - 1) as f64,
181 y: min.y + i as f64 * (max.y - min.y) / (n - 1) as f64,
182 }
183 }
184 fn coord_between(min: f64, max: f64) -> f64 {
186 check_min_max(min, max);
187 min + rand::random::<f64>() * (max - min)
188 }
189
190 fn match_coords(coordinates: CoordOptions, i: usize, n: usize) -> Option<Coordinates> {
191 match coordinates {
192 CoordOptions::Rand => Some(Coordinates {
193 x: coord_between(-10.0, 10.0),
194 y: coord_between(-10.0, 10.0),
195 }),
196
197 CoordOptions::RandRange(min, max) => Some(Coordinates {
198 x: coord_between(min.x, max.x),
199 y: coord_between(min.y, max.y),
200 }),
201
202 CoordOptions::EvenlySpaced(min, max) => Some(even_spaced_rider(min, max, i, n)),
203 CoordOptions::Other(x) => x,
204 }
205 }
206
207 let mut riders: Vec<Rider> = Vec::new();
208
209 for i in 0..n {
210 let start_position: Option<Coordinates> = match_coords(start_position, i, n);
211 let start_velocity: Option<Coordinates> = match_coords(speed_range, i, n);
212
213 riders.push(Rider {
214 start_position: start_position.unwrap_or_default(),
215 start_velocity: start_velocity.unwrap_or_default(),
216 remountable: remountable.unwrap_or_default(),
217 });
218 }
219
220 riders
221 }
222
223 pub fn polygon_lines(sides: u16, radius: u16, start_position: Option<Coordinates>, rotation: Option<f64>, kind: usize) -> Vec<Line> {
226 let center = start_position.unwrap_or_default();
227
228 let mut polygon_lines: Vec<Line> = Vec::new();
229
230 let vertex_degree: f64 = std::f64::consts::TAU / sides as f64;
231 let initial_angle: f64 = vertex_degree / 2_f64 + rotation.unwrap_or_default();
232
233 let mut first_point: Coordinates = Coordinates {
235 x: center.x + radius as f64 * initial_angle.cos(),
236 y: center.y + radius as f64 * initial_angle.sin(),
237 };
238
239 for i in 1..sides + 1 {
240 let second_point: Coordinates = Coordinates {
241 x: center.x + radius as f64 * (initial_angle + i as f64 * vertex_degree).cos(),
242 y: center.y + radius as f64 * (initial_angle + i as f64 * vertex_degree).sin(),
243 };
244
245 polygon_lines.push(Line {
246 id: None,
247 kind,
248 x1: first_point.x,
249 y1: first_point.y,
250 x2: second_point.x,
251 y2: second_point.y,
252 flipped: true,
253 left_extended: true,
254 right_extended: true,
255 });
256
257 first_point = second_point;
258 }
259
260 polygon_lines
261 }
262
263 pub fn thick_polygon_lines(
265 sides: u16,
266 radius: u16,
267 start_position: Option<Coordinates>,
268 rotation: Option<f64>,
269 thickness: u16,
270 kind: usize,
271 ) -> Vec<Line> {
272 let mut single_polygon_lines: Vec<Line> = Vec::new();
273
274 for i in 0..thickness {
275 single_polygon_lines.extend(polygon_lines(sides, radius + i, start_position, rotation, kind));
276 }
277
278 single_polygon_lines
279 }
280
281 pub fn function_lines(func: fn(f64) -> f64, range: Range<i64>, iterations: Option<u8>, kind: Option<usize>) -> Vec<Line> {
284 let mut function_lines: Vec<Line> = Vec::new();
285 let num_iterations = iterations.unwrap_or(10);
286
287 let mut last_x: f64 = range.start as f64;
288 let mut last_y: f64 = func(last_x);
289
290 for i in range {
291 for j in (1..num_iterations).rev() {
292 let x = i as f64 + (j as f64 / num_iterations as f64);
294 let y = func(x);
295 function_lines.push(Line {
296 kind: kind.unwrap_or(1),
297 x1: last_x,
298 y1: last_y,
299 x2: x,
300 y2: y,
301 ..Line::default()
302 });
303 last_x = x;
304 last_y = y;
305 }
306 }
307
308 function_lines
309 }
310}