From d2ba913525c94037006a00f3dd6ebf6cdd848c8a Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Tue, 10 Feb 2026 13:31:47 +0300 Subject: [PATCH] [lab3] Ants similar visualization --- lab3/src/algo/mod.rs | 8 +- lab3/src/algo/state.rs | 78 ++++++---- lab3/src/main.rs | 143 ++++++++++++++----- tsp-utility/src/graph/_preserve_index_vec.rs | 4 + tsp-utility/src/graph/edges.rs | 4 + tsp-utility/src/graph/vertices.rs | 4 + tsp-utility/src/gui/mod.rs | 1 + tsp-utility/src/gui/render.rs | 66 +++++++++ 8 files changed, 238 insertions(+), 70 deletions(-) create mode 100644 tsp-utility/src/gui/render.rs diff --git a/lab3/src/algo/mod.rs b/lab3/src/algo/mod.rs index ca75a6a..d90b307 100644 --- a/lab3/src/algo/mod.rs +++ b/lab3/src/algo/mod.rs @@ -9,10 +9,4 @@ pub struct EdgeExtraData { pub type Edge = _BaseEdge; pub type EdgesVec = _BaseEdgesVec; - -pub struct AntsSimulationConfig { - pub ferment_weight: f64, - pub heuristic_coefficient: f64, - pub q: f64, - pub r: f64, -} +pub use state::{AntsSimulationConfig, AntsSimulationState, updateState, Ant}; \ No newline at end of file diff --git a/lab3/src/algo/state.rs b/lab3/src/algo/state.rs index 0139271..48cc604 100644 --- a/lab3/src/algo/state.rs +++ b/lab3/src/algo/state.rs @@ -1,6 +1,8 @@ use super::EdgesVec; +use rand::Rng; use std::collections::HashSet; use std::ops::Mul; +use tsp_utility::graph::VerticesVec; #[derive(Clone, Copy)] pub enum AntDirection { @@ -8,11 +10,24 @@ pub enum AntDirection { ToFirst, } +#[derive(Clone)] pub struct Ant { allowed_vertices: HashSet, location: AntLocation, } +impl Ant { + pub fn new(vertex: usize, allowed_vertices: HashSet) -> Self { + return Self { + allowed_vertices: allowed_vertices, + location: AntLocation::OnVertex { + vertex_index: vertex, + }, + }; + } +} + +#[derive(Clone)] pub enum AntLocation { OnEdge { edge_index: usize, @@ -24,20 +39,32 @@ pub enum AntLocation { }, } -pub struct AntsSimulationState<'a, Rng: rand::Rng> { - rng: Rng, - vertices: &'a Vec>, - edges: &'a mut EdgesVec, - ants: &'a mut [Ant], +pub struct AntsSimulationState { + pub vertices: VerticesVec, + pub edges: EdgesVec, + pub ants: Vec, } -pub fn updateState( - state: &mut AntsSimulationState, - ferment_weight: f64, - heuristic_coefficient: f64, - q: f64, - r: f64, - speed: u16, +pub struct AntsSimulationConfig { + pub ferment_weight: f64, + pub heuristic_coefficient: f64, + pub q: f64, + pub r: f64, +} + +pub fn updateState( + state: &mut AntsSimulationState, + cfg: &AntsSimulationConfig, + rng: &mut impl Rng, +) { + _updateState(state, cfg, rng); + _updateState(state, cfg, rng); +} + +fn _updateState( + state: &mut AntsSimulationState, + cfg: &AntsSimulationConfig, + rng: &mut impl Rng, ) -> bool { let mut finished_ants_count = 0usize; for ant in state.ants.iter_mut() { @@ -46,19 +73,16 @@ pub fn updateState( edge_index, offset, direction, - } => match offset.checked_add(speed) { - Some(newOffset) => *offset = newOffset, - None => { - let edge = &mut state.edges[*edge_index]; - edge.extra.ferment_intensity += (q / edge.length) * r; - let vertex_index; - match direction { - AntDirection::ToSecond => vertex_index = edge.vertex2_index, - AntDirection::ToFirst => vertex_index = edge.vertex1_index, - } - ant.location = AntLocation::OnVertex { vertex_index } + } => { + let edge = &mut state.edges[*edge_index]; + edge.extra.ferment_intensity += (cfg.q / edge.length) * cfg.r; + let vertex_index; + match direction { + AntDirection::ToSecond => vertex_index = edge.vertex2_index, + AntDirection::ToFirst => vertex_index = edge.vertex1_index, } - }, + ant.location = AntLocation::OnVertex { vertex_index } + } AntLocation::OnVertex { vertex_index } => { let allowed_outbounds = state.vertices[*vertex_index] .iter() @@ -75,8 +99,8 @@ pub fn updateState( return ( e.0, (e.2, e.3), - e.1.extra.ferment_intensity.powf(ferment_weight) - * (1.0 / e.1.length).powf(heuristic_coefficient), + e.1.extra.ferment_intensity.powf(cfg.ferment_weight) + * (1.0 / e.1.length).powf(cfg.heuristic_coefficient), ); }) .collect::>(); @@ -84,7 +108,7 @@ pub fn updateState( let mut accumulator: f64 = allowed_outbounds .iter() .fold(0.0, |a, e| a + e.2) - .mul(state.rng.random::()); + .mul(rng.random::()); let target_vertex_index = allowed_outbounds.iter().find(|e| { accumulator -= e.2; return accumulator <= 0.0; diff --git a/lab3/src/main.rs b/lab3/src/main.rs index ad945f2..7a67ed5 100644 --- a/lab3/src/main.rs +++ b/lab3/src/main.rs @@ -2,13 +2,18 @@ mod algo; -use crate::algo::{AntsSimulationConfig, EdgeExtraData, EdgesVec, VerticesVec}; +use crate::algo::{ + Ant, AntsSimulationConfig, AntsSimulationState, EdgeExtraData, EdgesVec, VerticesVec, + updateState, +}; use eframe::egui; use eframe::egui::{Frame, Ui}; use eframe::emath::Numeric; +use std::cmp::max; use std::collections::HashSet; use std::ops::RangeInclusive; -use tsp_utility::gui::lengths_table::{draw_lengths_table, UpdatePending}; +use tsp_utility::gui::lengths_table::{UpdatePending, draw_lengths_table}; +use tsp_utility::gui::render::render_graph; fn main() -> eframe::Result { let options = eframe::NativeOptions { @@ -22,33 +27,29 @@ fn main() -> eframe::Result { ) } -enum ViewState { - Stop, - Running { lastUpdateTimestamp: u64 }, - VertexMove { vertexId: usize }, -} - enum GlobalState { - Edit {}, - Running { view_state: ViewState }, + Edit, + Running, } struct MyApp { - edges: EdgesVec, - vertices: VerticesVec, + simulation: AntsSimulationState, config: AntsSimulationConfig, vertex_update: UpdatePending, - speed: u32, state: GlobalState, + vertex_locations: Vec<(f32, f32)>, + ants_per_vertex: usize, } impl Default for MyApp { fn default() -> Self { return Self { - edges: EdgesVec::new(), - vertices: VerticesVec::new(), + simulation: AntsSimulationState { + edges: EdgesVec::new(), + vertices: VerticesVec::new(), + ants: Vec::new(), + }, vertex_update: UpdatePending::NoChange, - speed: 1, config: AntsSimulationConfig { ferment_weight: 0.5, heuristic_coefficient: 0.5, @@ -56,6 +57,8 @@ impl Default for MyApp { r: 0.5, }, state: GlobalState::Edit {}, + vertex_locations: Vec::new(), + ants_per_vertex: 1, }; } } @@ -85,13 +88,13 @@ impl eframe::App for MyApp { match self.vertex_update { UpdatePending::NoChange => {} UpdatePending::Add => { - let new_vi = self.vertices.add(HashSet::new()); + let new_vi = self.simulation.vertices.add(HashSet::new()); let mut newEdgesSet = HashSet::new(); - for (vi, v) in self.vertices.iter_indexed_mut() { + for (vi, v) in self.simulation.vertices.iter_indexed_mut() { if (vi == new_vi) { continue; } - let ei = self.edges.add( + let ei = self.simulation.edges.add( new_vi, vi, 1.0, @@ -102,19 +105,20 @@ impl eframe::App for MyApp { newEdgesSet.insert(ei); v.insert(ei); } - self.vertices[new_vi] = newEdgesSet; + self.simulation.vertices[new_vi] = newEdgesSet; self.vertex_update = UpdatePending::NoChange; } UpdatePending::Remove(vi) => { - let mut eis = Vec::with_capacity(self.vertices[vi].len()); - for ei in self.vertices[vi].iter() { + let mut eis = Vec::with_capacity(self.simulation.vertices[vi].len()); + for ei in self.simulation.vertices[vi].iter() { eis.push(*ei) } for ei in eis { - self.vertices[self.edges[ei].another(vi)].remove(&ei); - self.edges.remove(ei); + self.simulation.vertices[self.simulation.edges[ei].another(vi)] + .remove(&ei); + self.simulation.edges.remove(ei); } - self.vertices.remove(vi); + self.simulation.vertices.remove(vi); self.vertex_update = UpdatePending::NoChange; } } @@ -140,7 +144,7 @@ impl eframe::App for MyApp { } fn edit_panel(data: &mut MyApp, ui: &mut Ui) { - let run: bool; + let mut run: bool = false; _slider( ui, @@ -162,30 +166,97 @@ fn edit_panel(data: &mut MyApp, ui: &mut Ui) { ui.label(""); _slider(ui, "r", &mut data.config.r, 0.0..=1.0, 0.001); ui.label(""); + _slider( + ui, + "Ants per vertex", + &mut data.ants_per_vertex, + 1..=100, + 1.0, + ); + ui.label(""); ui.horizontal(|ui| { if ui.button("Add vertex").clicked() { data.vertex_update = UpdatePending::Add; } ui.separator(); - let run = ui.button("Run").clicked(); + run = ui.button("Run").clicked(); }); draw_lengths_table( ui, - &mut data.vertices, - &mut data.edges, + &mut data.simulation.vertices, + &mut data.simulation.edges, &mut data.vertex_update, - ) + ); + + if run { + let mut coords = vec![(0.0, 0.0); data.simulation.vertices.capacity()]; + + for (i, _) in data.simulation.vertices.iter_indexed() { + coords[i] = ( + rand::random::() * 0.8 + 0.1, + rand::random::() * 0.8 + 0.1, + ) + } + + data.state = GlobalState::Running; + data.vertex_locations = coords; + + let allowed_locations = data + .simulation + .vertices + .iter_indexed() + .map(|(i, _)| i) + .collect::>(); + + let mut ants = Vec::new(); + for (i, _) in data.simulation.vertices.iter_indexed() { + for _ in 0..data.ants_per_vertex { + ants.push(Ant::new(i, allowed_locations.clone())) + } + } + data.simulation.ants = ants; + } } fn visualization_panel(data: &mut MyApp, ui: &mut Ui) { - ui.horizontal(|ui| { - if ui.button("Exit").clicked() {} + ui.vertical(|ui| { + ui.horizontal(|ui| { + if ui.button("Exit").clicked() { + data.state = GlobalState::Edit + } - if ui.button("Pause").clicked() {} - ui.label(""); - _slider(ui, "Ant speed", &mut data.speed, 1..=10, 1.0); + if ui.button("Step").clicked() { + updateState(&mut data.simulation, &mut data.config, &mut rand::rng()); + } + ui.label(""); + }); + draw_ants(data, ui); + if ui.input(|i| i.viewport().close_requested()) { + data.state = GlobalState::Edit + } }); } -fn draw_ants(data: &mut MyApp, ui: &mut Ui) {} +fn draw_ants(data: &mut MyApp, ui: &mut Ui) { + let mut cap: f64 = 0.000000000001; + + for w in data + .simulation + .edges + .iter() + .map(|e| e.extra.ferment_intensity) + { + if w > cap { + cap = w + } + } + + render_graph( + ui, + &data.simulation.vertices, + data.vertex_locations.as_mut_slice(), + &data.simulation.edges, + |e| e.extra.ferment_intensity / cap, + ) +} diff --git a/tsp-utility/src/graph/_preserve_index_vec.rs b/tsp-utility/src/graph/_preserve_index_vec.rs index 99f2098..e0df4bd 100644 --- a/tsp-utility/src/graph/_preserve_index_vec.rs +++ b/tsp-utility/src/graph/_preserve_index_vec.rs @@ -91,6 +91,10 @@ impl _PreserveIndexVec { } }) } + + pub fn capacity(&self) -> usize { + return self.buffer.len(); + } } impl Index for _PreserveIndexVec { diff --git a/tsp-utility/src/graph/edges.rs b/tsp-utility/src/graph/edges.rs index e71c255..c62d0bb 100644 --- a/tsp-utility/src/graph/edges.rs +++ b/tsp-utility/src/graph/edges.rs @@ -51,6 +51,10 @@ impl EdgesVec { pub fn iter_mut(&mut self) -> impl Iterator> { return self.data.iter_mut(); } + + pub fn capacity(&self) -> usize { + return self.data.capacity() + } } impl Index for EdgesVec { diff --git a/tsp-utility/src/graph/vertices.rs b/tsp-utility/src/graph/vertices.rs index 4402be7..b44a5bd 100644 --- a/tsp-utility/src/graph/vertices.rs +++ b/tsp-utility/src/graph/vertices.rs @@ -33,6 +33,10 @@ impl VerticesVec { pub fn iter_indexed_mut(&mut self) -> impl Iterator)> { return self.data.iter_indexed_mut(); } + + pub fn capacity(&self) -> usize { + return self.data.capacity() + } } impl Index for VerticesVec { diff --git a/tsp-utility/src/gui/mod.rs b/tsp-utility/src/gui/mod.rs index 8c8f9bc..d444494 100644 --- a/tsp-utility/src/gui/mod.rs +++ b/tsp-utility/src/gui/mod.rs @@ -1 +1,2 @@ pub mod lengths_table; +pub mod render; diff --git a/tsp-utility/src/gui/render.rs b/tsp-utility/src/gui/render.rs new file mode 100644 index 0000000..30592c9 --- /dev/null +++ b/tsp-utility/src/gui/render.rs @@ -0,0 +1,66 @@ +use crate::graph::{Edge, EdgesVec, VerticesVec}; +use eframe::egui::{Color32, Pos2, Rect, Ui}; +use eframe::epaint::{CornerRadius, Stroke}; + +pub fn render_graph( + ui: &mut Ui, + vertices: &VerticesVec, + vertex_locations: &mut [(f32, f32)], + edges: &EdgesVec, + normalized_intensity: impl Fn(&Edge) -> f64, +) { + let canvas = ui.painter(); + let rect = ui.available_size(); + let start = ui.cursor(); + + let rect = Rect::from_min_max(start.min, rect.to_pos2()); + canvas.rect_filled(rect, CornerRadius::same(0), Color32::from_rgb(0, 0, 0)); + + for e in edges.iter() { + let p1 = vertex_locations[e.vertex1_index]; + let p2 = vertex_locations[e.vertex2_index]; + + canvas.line_segment( + [ + Pos2::new( + p1.0 * rect.width() + rect.min.x, + p1.1 * rect.height() + rect.min.y, + ), + Pos2::new( + p2.0 * rect.width() + rect.min.x, + p2.1 * rect.height() + rect.min.y, + ), + ], + Stroke::new(1.0, intensity2color(normalized_intensity(e))), + ); + } +} + +fn intensity2color(intensity: f64) -> Color32 { + let scale = |base: f64, coefficient: u8| -> u8 { + return ((intensity - base) * 5.0 * (coefficient as f64)) as u8; + }; + + match intensity { + f64::NEG_INFINITY..0.2 => { + return Color32::from_rgb( + 50 - scale(0.0, 50), + 50 - scale(0.0, 50), + 50 + scale(0.0, 155), + ); + } + 0.2..0.4 => { + return Color32::from_rgb(0, scale(0.2, 255), 255); + } + 0.4..0.6 => { + return Color32::from_rgb(0, 255, 255 - scale(0.4, 255)); + } + 0.6..0.8 => { + return Color32::from_rgb(scale(0.6, 255) as u8, 255, 0); + } + 0.8..f64::INFINITY => { + return Color32::from_rgb(255, 255 - scale(0.8, 255), 0); + } + _ => unreachable!(), + } +}