1use oxygengine_utils::{grid_2d::Grid2d, noise_map_generator::NoiseMapGenerator, Scalar};
2use psyche_utils::switch::Switch;
3use serde::{Deserialize, Serialize};
4#[cfg(not(feature = "scalar64"))]
5use std::f32::{INFINITY as SCALAR_INFINITY, NEG_INFINITY as SCALAR_NEG_INFINITY};
6#[cfg(feature = "scalar64")]
7use std::f64::{INFINITY as SCALAR_INFINITY, NEG_INFINITY as SCALAR_NEG_INFINITY};
8use std::{any::Any, borrow::Borrow, ops::Range};
9
10pub type World2dField = Switch<Grid2d<Scalar>>;
11
12#[derive(Debug, Clone)]
13pub struct World2dConfig {
14 pub size: usize,
15 pub zoom: Scalar,
16 pub altitude_seed: u32,
17 pub altitude_range: Range<Scalar>,
18 pub temperature_seed: u32,
19 pub temperature_range: Range<Scalar>,
20 pub humidity_seed: u32,
21 pub humidity_range: Range<Scalar>,
22}
23
24impl Default for World2dConfig {
25 fn default() -> Self {
26 Self {
27 size: 100,
28 zoom: 5.0,
29 altitude_seed: 1,
30 altitude_range: 0.0..100.0,
31 temperature_seed: 2,
32 temperature_range: 0.0..100.0,
33 humidity_seed: 3,
34 humidity_range: 0.1..1.0,
35 }
36 }
37}
38
39pub trait World2dSimulation: Any + Send + Sync {
40 fn initialize_world(
41 &mut self,
42 _altitude: &mut Grid2d<Scalar>,
43 _temperature: &mut Grid2d<Scalar>,
44 _humidity: &mut Grid2d<Scalar>,
45 _surface_water: &mut Grid2d<Scalar>,
46 ) {
47 }
48
49 fn process_world(
50 &mut self,
51 altitude: &mut World2dField,
52 temperature: &mut World2dField,
53 humidity: &mut World2dField,
54 surface_water: &mut World2dField,
55 );
56
57 fn as_any(&self) -> &dyn Any;
58}
59
60impl World2dSimulation for () {
61 fn process_world(
62 &mut self,
63 _altitude: &mut World2dField,
64 _temperature: &mut World2dField,
65 _humidity: &mut World2dField,
66 _surface_water: &mut World2dField,
67 ) {
68 }
69
70 fn as_any(&self) -> &dyn Any {
71 self
72 }
73}
74
75#[derive(Debug, Default, Clone)]
76pub struct World2dStats {
77 pub altitude: (Scalar, Scalar, Scalar),
79 pub temperature: (Scalar, Scalar, Scalar),
81 pub humidity: (Scalar, Scalar, Scalar),
83 pub surface_water: (Scalar, Scalar, Scalar),
85}
86
87pub struct World2d {
88 size: usize,
89 altitude: Switch<Grid2d<Scalar>>,
90 temperature: Switch<Grid2d<Scalar>>,
91 humidity: Switch<Grid2d<Scalar>>,
92 surface_water: Switch<Grid2d<Scalar>>,
93 simulation: Box<dyn World2dSimulation>,
94 stats: World2dStats,
95}
96
97impl World2d {
98 pub fn new(config: &World2dConfig, mut simulation: Box<dyn World2dSimulation>) -> Self {
99 let mut altitude = {
100 let gen = NoiseMapGenerator::new(config.altitude_seed, config.size, config.zoom);
101 let diff = config.altitude_range.end - config.altitude_range.start;
102 Switch::new(
103 2,
104 gen.build_chunk((0, 0), 0)
105 .map(|_, _, v| config.altitude_range.start + diff * v),
106 )
107 };
108 let mut temperature = {
109 let gen = NoiseMapGenerator::new(config.temperature_seed, config.size, config.zoom);
110 let diff = config.temperature_range.end - config.temperature_range.start;
111 Switch::new(
112 2,
113 gen.build_chunk((0, 0), 0)
114 .map(|_, _, v| config.temperature_range.start + diff * v),
115 )
116 };
117 let mut humidity = {
118 let gen = NoiseMapGenerator::new(config.humidity_seed, config.size, config.zoom);
119 let diff = config.humidity_range.end - config.humidity_range.start;
120 Switch::new(
121 2,
122 gen.build_chunk((0, 0), 0)
123 .map(|_, _, v| config.humidity_range.start + diff * v),
124 )
125 };
126 let mut surface_water = Switch::new(2, Grid2d::new(config.size, config.size, 0.0));
127 simulation.initialize_world(
128 altitude.get_mut().unwrap(),
129 temperature.get_mut().unwrap(),
130 humidity.get_mut().unwrap(),
131 surface_water.get_mut().unwrap(),
132 );
133 let mut result = Self {
134 size: config.size,
135 altitude,
136 temperature,
137 humidity,
138 surface_water,
139 simulation,
140 stats: Default::default(),
141 };
142 result.calculate_stats();
143 result
144 }
145
146 pub fn generate<FA, FT, FH, FSW>(
147 size: usize,
148 mut simulation: Box<dyn World2dSimulation>,
149 mut altitude_generator: FA,
150 mut temperature_generator: FT,
151 mut humidity_generator: FH,
152 mut surface_water_generator: FSW,
153 ) -> Self
154 where
155 FA: FnMut(usize, usize) -> Scalar,
156 FT: FnMut(usize, usize) -> Scalar,
157 FH: FnMut(usize, usize) -> Scalar,
158 FSW: FnMut(usize, usize) -> Scalar,
159 {
160 let altitude = (0..(size * size))
161 .map(|i| altitude_generator(i % size, i / size))
162 .collect::<Vec<_>>();
163 let temperature = (0..size * size)
164 .map(|i| temperature_generator(i % size, i / size))
165 .collect::<Vec<_>>();
166 let humidity = (0..size * size)
167 .map(|i| humidity_generator(i % size, i / size))
168 .collect::<Vec<_>>();
169 let surface_water = (0..size * size)
170 .map(|i| surface_water_generator(i % size, i / size))
171 .collect::<Vec<_>>();
172 let mut altitude = Switch::new(2, Grid2d::with_cells(size, altitude));
173 let mut temperature = Switch::new(2, Grid2d::with_cells(size, temperature));
174 let mut humidity = Switch::new(2, Grid2d::with_cells(size, humidity));
175 let mut surface_water = Switch::new(2, Grid2d::with_cells(size, surface_water));
176 simulation.initialize_world(
177 altitude.get_mut().unwrap(),
178 temperature.get_mut().unwrap(),
179 humidity.get_mut().unwrap(),
180 surface_water.get_mut().unwrap(),
181 );
182 let mut result = Self {
183 size,
184 altitude,
185 temperature,
186 humidity,
187 surface_water,
188 simulation,
189 stats: Default::default(),
190 };
191 result.calculate_stats();
192 result
193 }
194
195 pub fn stats(&self) -> &World2dStats {
196 &self.stats
197 }
198
199 pub fn size(&self) -> usize {
200 self.size
201 }
202
203 pub fn altitude(&self) -> &Grid2d<Scalar> {
204 self.altitude.get().unwrap()
205 }
206
207 pub fn temperature(&self) -> &Grid2d<Scalar> {
208 self.temperature.get().unwrap()
209 }
210
211 pub fn humidity(&self) -> &Grid2d<Scalar> {
212 self.humidity.get().unwrap()
213 }
214
215 pub fn surface_water(&self) -> &Grid2d<Scalar> {
216 self.surface_water.get().unwrap()
217 }
218
219 pub fn simulation(&self) -> &dyn World2dSimulation {
220 self.simulation.borrow()
221 }
222
223 pub fn as_simulation<T>(&self) -> Option<&T>
224 where
225 T: World2dSimulation,
226 {
227 self.simulation.as_any().downcast_ref::<T>()
228 }
229
230 pub fn process(&mut self) {
231 self.simulation.process_world(
232 &mut self.altitude,
233 &mut self.temperature,
234 &mut self.humidity,
235 &mut self.surface_water,
236 );
237 self.calculate_stats();
238 }
239
240 pub fn remap_region<F, T>(&self, mut range: Range<(usize, usize)>, mut f: F) -> Grid2d<T>
241 where
242 F: FnMut(usize, usize, Scalar, Scalar, Scalar, Scalar) -> T,
244 T: Clone + Send + Sync,
245 {
246 range.end.0 = range.end.0.min(self.size);
247 range.end.1 = range.end.1.min(self.size);
248 range.start.0 = range.start.0.min(range.end.0);
249 range.start.1 = range.start.1.min(range.end.1);
250 let cells = self
251 .altitude
252 .get()
253 .unwrap()
254 .iter_view(range.clone())
255 .zip(self.temperature.get().unwrap().iter_view(range.clone()))
256 .zip(self.humidity.get().unwrap().iter_view(range.clone()))
257 .zip(self.surface_water.get().unwrap().iter_view(range.clone()))
258 .map(|((((col, row, a), (_, _, t)), (_, _, h)), (_, _, sw))| {
259 f(col, row, *a, *t, *h, *sw)
260 })
261 .collect::<Vec<T>>();
262 Grid2d::with_cells(range.end.0 - range.start.0, cells)
263 }
264
265 pub fn resample_region<F, T>(
266 &self,
267 mut range: Range<(usize, usize)>,
268 margin: usize,
269 mut f: F,
270 ) -> Grid2d<T>
271 where
272 F: FnMut(
274 usize,
275 usize,
276 Grid2d<&Scalar>,
277 Grid2d<&Scalar>,
278 Grid2d<&Scalar>,
279 Grid2d<&Scalar>,
280 ) -> T,
281 T: Clone + Send + Sync,
282 {
283 range.end.0 = range.end.0.min(self.size);
284 range.end.1 = range.end.1.min(self.size);
285 range.start.0 = range.start.0.min(range.end.0);
286 range.start.1 = range.start.1.min(range.end.1);
287 let cells = self
288 .altitude
289 .get()
290 .unwrap()
291 .iter_sample(range.clone(), margin)
292 .zip(
293 self.temperature
294 .get()
295 .unwrap()
296 .iter_sample(range.clone(), margin),
297 )
298 .zip(
299 self.humidity
300 .get()
301 .unwrap()
302 .iter_sample(range.clone(), margin),
303 )
304 .zip(
305 self.surface_water
306 .get()
307 .unwrap()
308 .iter_sample(range.clone(), margin),
309 )
310 .map(|((((col, row, a), (_, _, t)), (_, _, h)), (_, _, sw))| f(col, row, a, t, h, sw))
311 .collect::<Vec<T>>();
312 Grid2d::with_cells(range.end.0 - range.start.0, cells)
313 }
314
315 fn calculate_stats(&mut self) {
316 self.stats.altitude = {
317 let (min, max, accum) = self
318 .altitude
319 .get()
320 .unwrap()
321 .iter()
322 .fold((SCALAR_INFINITY, SCALAR_NEG_INFINITY, 0.0), |a, v| {
323 (a.0.min(*v), a.1.max(*v), a.2 + *v)
324 });
325 (
326 min,
327 max,
328 accum / self.altitude.get().unwrap().len() as Scalar,
329 )
330 };
331 self.stats.temperature = {
332 let (min, max, accum) = self
333 .temperature
334 .get()
335 .unwrap()
336 .iter()
337 .fold((SCALAR_INFINITY, SCALAR_NEG_INFINITY, 0.0), |a, v| {
338 (a.0.min(*v), a.1.max(*v), a.2 + *v)
339 });
340 (
341 min,
342 max,
343 accum / self.altitude.get().unwrap().len() as Scalar,
344 )
345 };
346 self.stats.humidity = {
347 let (min, max, accum) = self
348 .humidity
349 .get()
350 .unwrap()
351 .iter()
352 .fold((SCALAR_INFINITY, SCALAR_NEG_INFINITY, 0.0), |a, v| {
353 (a.0.min(*v), a.1.max(*v), a.2 + *v)
354 });
355 (
356 min,
357 max,
358 accum / self.altitude.get().unwrap().len() as Scalar,
359 )
360 };
361 self.stats.surface_water = {
362 let (min, max, accum) = self
363 .surface_water
364 .get()
365 .unwrap()
366 .iter()
367 .fold((SCALAR_INFINITY, SCALAR_NEG_INFINITY, 0.0), |a, v| {
368 (a.0.min(*v), a.1.max(*v), a.2 + *v)
369 });
370 (
371 min,
372 max,
373 accum / self.altitude.get().unwrap().len() as Scalar,
374 )
375 };
376 }
377}
378
379#[derive(Clone, Serialize, Deserialize)]
380pub struct World2dData<S>
381where
382 S: World2dSimulation,
383{
384 size: usize,
385 altitude: Grid2d<Scalar>,
386 temperature: Grid2d<Scalar>,
387 humidity: Grid2d<Scalar>,
388 surface_water: Grid2d<Scalar>,
389 simulation: S,
390}
391
392impl<S> From<&World2d> for World2dData<S>
393where
394 S: World2dSimulation + Clone,
395{
396 fn from(world: &World2d) -> Self {
397 Self {
398 size: world.size,
399 altitude: world.altitude.get().unwrap().clone(),
400 temperature: world.temperature.get().unwrap().clone(),
401 humidity: world.humidity.get().unwrap().clone(),
402 surface_water: world.surface_water.get().unwrap().clone(),
403 simulation: world.as_simulation::<S>().unwrap().clone(),
404 }
405 }
406}
407
408impl<S> From<&World2dData<S>> for World2d
409where
410 S: World2dSimulation + Clone,
411{
412 fn from(data: &World2dData<S>) -> Self {
413 Self {
414 size: data.size,
415 altitude: Switch::new(2, data.altitude.clone()),
416 temperature: Switch::new(2, data.temperature.clone()),
417 humidity: Switch::new(2, data.humidity.clone()),
418 surface_water: Switch::new(2, data.surface_water.clone()),
419 simulation: Box::new(data.simulation.clone()),
420 stats: Default::default(),
421 }
422 }
423}