diff --git a/src/state.rs b/src/state.rs index d17aeda..53b6dd9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::sync::atomic::AtomicUsize; use std::sync::{Arc, RwLock}; use crate::dijkstra::{GlobePoints, GridPoint}; @@ -44,7 +45,7 @@ impl Default for Config { } } -#[derive(PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash, Clone, Copy)] pub struct Rail { pub from: GridPoint, pub to: GridPoint, @@ -52,6 +53,7 @@ pub struct Rail { pub struct RailInfo { pub entity: Entity, + pub counter: AtomicUsize, // Other details such as how frequently the rail is used can come here. } @@ -67,4 +69,5 @@ pub struct State { pub rails: Rails, pub rng: rand::rngs::StdRng, pub create_new_city_next: bool, + pub max_rail_usage: AtomicUsize, } diff --git a/src/train.rs b/src/train.rs index 41662fc..49ecf94 100644 --- a/src/train.rs +++ b/src/train.rs @@ -1,8 +1,10 @@ +use crate::state::{Rail, State}; use bevy::prelude::*; +use std::sync::atomic::Ordering; #[derive(Component)] pub struct Train { - pub transforms: Vec, + pub transforms: Vec<(Transform, Rail)>, pub idx: usize, pub next_idx: usize, pub forward: bool, @@ -14,7 +16,7 @@ pub struct Train { pub struct SelectedTrain; impl Train { - pub fn new(transforms: Vec) -> Option { + pub fn new(transforms: Vec<(Transform, Rail)>) -> Option { if transforms.len() < 2 { return None; } @@ -29,7 +31,7 @@ impl Train { } fn transform_at(&self, idx: i32) -> Transform { - self.transforms[idx.clamp(0, self.transforms.len() as i32 - 1) as usize] + self.transforms[idx.clamp(0, self.transforms.len() as i32 - 1) as usize].0 } fn duration_between(&self, start: &Transform, end: &Transform) -> f32 { @@ -107,7 +109,14 @@ impl Train { } } - pub fn update(&mut self, transform: &mut Transform, time_passed_seconds: f32) { + pub fn update( + &mut self, + transform: &mut Transform, + time_passed_seconds: f32, + state: &State, + commands: &mut Commands, + materials: &mut Assets, + ) { if self.segment_duration.is_none() { self.compute_segment_duration(); } @@ -117,6 +126,26 @@ impl Train { // Move to the next segment. self.idx = self.next_idx; + let rail_info = state.rails.rails.get(&self.transforms[self.idx].1).unwrap(); + + let count = rail_info.counter.fetch_add(1, Ordering::Relaxed) + 1; + + let max_rail_usage = + count.max(state.max_rail_usage.fetch_max(count, Ordering::Relaxed)); + + let color = (((max_rail_usage - count) as f32 / max_rail_usage as f32) * 255.) as u8; + + let material = materials.add(StandardMaterial { + base_color: Color::srgb_u8(255, color, color), + perceptual_roughness: 0.0, + metallic: 0.0, + ..default() + }); + + commands + .entity(rail_info.entity) + .insert((MeshMaterial3d(material),)); + // Account for the remaining time. self.seconds_spent_within_segment = self.segment_duration.unwrap() - self.seconds_spent_within_segment; diff --git a/src/ui.rs b/src/ui.rs index 30495d4..fd03a4b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -62,6 +62,7 @@ pub fn init() { crate::state::Config::default().perlin_config.seed as u64, ), create_new_city_next: true, + max_rail_usage: 0.into(), }) .insert_resource(SelectedCity::default()) .add_systems(Update, draw_pointer) @@ -200,7 +201,7 @@ fn create_path_if_dijkstra_ready( meshes: Res, custom_materials: Res, ) { - let Some(task) = dijkstra_communication.task else { + let Some(_) = dijkstra_communication.task else { return; }; let Ok(dijkstra_result) = dijkstra_communication.receiver.try_recv() else { @@ -214,19 +215,11 @@ fn create_path_if_dijkstra_ready( // Clear task to signal Dijkstra is ready for another task. dijkstra_communication.task = None; - let start = task.0; - let end = task.1; - let path_mesh = meshes.path.clone(); let train_mesh = meshes.train.clone(); - // Create a custom color for the path based on start and end points. - let r = ((27 * (start.1 + end.1)) % 256) as u8; - let g = ((51 * (start.2 + end.2)) % 256) as u8; - let b = ((11 * (start.1 + end.2)) % 256) as u8; - let material = materials.add(StandardMaterial { - base_color: Color::srgb_u8(r, g, b), + base_color: Color::srgb_u8(255, 255, 255), perceptual_roughness: 0.0, metallic: 0.0, ..default() @@ -283,36 +276,27 @@ fn create_path_if_dijkstra_ready( let rotation = Quat::from_mat3(&Mat3::from_cols(Vec3::cross(dir_norm, up), dir_norm, up)); - train_transforms - .push(Transform::from_translation(mid_point * 1.005).with_rotation(rotation)); - // If this piece of rail already exists, just change its material // corresponding to the current path. // We don't _really_ need this, but this demonstrates how to update // existig rail piece entities. - if let Some(rail_info) = state.rails.rails.get(&rail) { - commands - .entity(rail_info.entity) - .insert((MeshMaterial3d(material.clone()),)); - continue; - } - // Otherwise, create a new entity for the rail and store it in the - // Rails resource. - // - // We first create an empty entity in order to already have its ID - // which we can key the RailInfo with. - // - // We'll update it with all the details the same way as we've updated - // the existing rail piece above. - let entity = commands.spawn_empty().id(); - state.rails.rails.insert( - rail, - RailInfo { - entity, - // Other details can be added here. - }, - ); - + if let std::collections::hash_map::Entry::Vacant(e) = state.rails.rails.entry(rail) { + // Otherwise, create a new entity for the rail and store it in the + // Rails resource. + // + // We first create an empty entity in order to already have its ID + // which we can key the RailInfo with. + // + // We'll update it with all the details the same way as we've updated + // the existing rail piece above. + let entity = commands.spawn_empty().id(); + e.insert( + RailInfo { + entity, + counter: 0.into(), + // Other details can be added here. + }, + ); commands.entity(entity).insert(( Mesh3d(path_mesh.clone()), MeshMaterial3d(material.clone()), @@ -325,6 +309,13 @@ fn create_path_if_dijkstra_ready( .with_rotation(rotation), PointerInteraction::default(), )); + } + + train_transforms.push(( + Transform::from_translation(mid_point * 1.005).with_rotation(rotation), + rail, + )); + } } } @@ -686,11 +677,23 @@ fn highlight_city( } } -fn move_trains(time: Res