[lab3] Ants similar visualization

This commit is contained in:
Andrew Golovashevich 2026-02-10 13:31:47 +03:00
parent 6b74ba6590
commit d2ba913525
8 changed files with 238 additions and 70 deletions

View File

@ -9,10 +9,4 @@ pub struct EdgeExtraData {
pub type Edge = _BaseEdge<EdgeExtraData>; pub type Edge = _BaseEdge<EdgeExtraData>;
pub type EdgesVec = _BaseEdgesVec<EdgeExtraData>; pub type EdgesVec = _BaseEdgesVec<EdgeExtraData>;
pub use state::{AntsSimulationConfig, AntsSimulationState, updateState, Ant};
pub struct AntsSimulationConfig {
pub ferment_weight: f64,
pub heuristic_coefficient: f64,
pub q: f64,
pub r: f64,
}

View File

@ -1,6 +1,8 @@
use super::EdgesVec; use super::EdgesVec;
use rand::Rng;
use std::collections::HashSet; use std::collections::HashSet;
use std::ops::Mul; use std::ops::Mul;
use tsp_utility::graph::VerticesVec;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum AntDirection { pub enum AntDirection {
@ -8,11 +10,24 @@ pub enum AntDirection {
ToFirst, ToFirst,
} }
#[derive(Clone)]
pub struct Ant { pub struct Ant {
allowed_vertices: HashSet<usize>, allowed_vertices: HashSet<usize>,
location: AntLocation, location: AntLocation,
} }
impl Ant {
pub fn new(vertex: usize, allowed_vertices: HashSet<usize>) -> Self {
return Self {
allowed_vertices: allowed_vertices,
location: AntLocation::OnVertex {
vertex_index: vertex,
},
};
}
}
#[derive(Clone)]
pub enum AntLocation { pub enum AntLocation {
OnEdge { OnEdge {
edge_index: usize, edge_index: usize,
@ -24,20 +39,32 @@ pub enum AntLocation {
}, },
} }
pub struct AntsSimulationState<'a, Rng: rand::Rng> { pub struct AntsSimulationState {
rng: Rng, pub vertices: VerticesVec,
vertices: &'a Vec<HashSet<usize>>, pub edges: EdgesVec,
edges: &'a mut EdgesVec, pub ants: Vec<Ant>,
ants: &'a mut [Ant],
} }
pub fn updateState<Rng: rand::Rng>( pub struct AntsSimulationConfig {
state: &mut AntsSimulationState<Rng>, pub ferment_weight: f64,
ferment_weight: f64, pub heuristic_coefficient: f64,
heuristic_coefficient: f64, pub q: f64,
q: f64, pub r: f64,
r: f64, }
speed: u16,
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 { ) -> bool {
let mut finished_ants_count = 0usize; let mut finished_ants_count = 0usize;
for ant in state.ants.iter_mut() { for ant in state.ants.iter_mut() {
@ -46,11 +73,9 @@ pub fn updateState<Rng: rand::Rng>(
edge_index, edge_index,
offset, offset,
direction, direction,
} => match offset.checked_add(speed) { } => {
Some(newOffset) => *offset = newOffset,
None => {
let edge = &mut state.edges[*edge_index]; let edge = &mut state.edges[*edge_index];
edge.extra.ferment_intensity += (q / edge.length) * r; edge.extra.ferment_intensity += (cfg.q / edge.length) * cfg.r;
let vertex_index; let vertex_index;
match direction { match direction {
AntDirection::ToSecond => vertex_index = edge.vertex2_index, AntDirection::ToSecond => vertex_index = edge.vertex2_index,
@ -58,7 +83,6 @@ pub fn updateState<Rng: rand::Rng>(
} }
ant.location = AntLocation::OnVertex { vertex_index } ant.location = AntLocation::OnVertex { vertex_index }
} }
},
AntLocation::OnVertex { vertex_index } => { AntLocation::OnVertex { vertex_index } => {
let allowed_outbounds = state.vertices[*vertex_index] let allowed_outbounds = state.vertices[*vertex_index]
.iter() .iter()
@ -75,8 +99,8 @@ pub fn updateState<Rng: rand::Rng>(
return ( return (
e.0, e.0,
(e.2, e.3), (e.2, e.3),
e.1.extra.ferment_intensity.powf(ferment_weight) e.1.extra.ferment_intensity.powf(cfg.ferment_weight)
* (1.0 / e.1.length).powf(heuristic_coefficient), * (1.0 / e.1.length).powf(cfg.heuristic_coefficient),
); );
}) })
.collect::<Vec<(usize, (usize, AntDirection), f64)>>(); .collect::<Vec<(usize, (usize, AntDirection), f64)>>();
@ -84,7 +108,7 @@ pub fn updateState<Rng: rand::Rng>(
let mut accumulator: f64 = allowed_outbounds let mut accumulator: f64 = allowed_outbounds
.iter() .iter()
.fold(0.0, |a, e| a + e.2) .fold(0.0, |a, e| a + e.2)
.mul(state.rng.random::<f64>()); .mul(rng.random::<f64>());
let target_vertex_index = allowed_outbounds.iter().find(|e| { let target_vertex_index = allowed_outbounds.iter().find(|e| {
accumulator -= e.2; accumulator -= e.2;
return accumulator <= 0.0; return accumulator <= 0.0;

View File

@ -2,13 +2,18 @@
mod algo; 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;
use eframe::egui::{Frame, Ui}; use eframe::egui::{Frame, Ui};
use eframe::emath::Numeric; use eframe::emath::Numeric;
use std::cmp::max;
use std::collections::HashSet; use std::collections::HashSet;
use std::ops::RangeInclusive; 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 { fn main() -> eframe::Result {
let options = eframe::NativeOptions { let options = eframe::NativeOptions {
@ -22,33 +27,29 @@ fn main() -> eframe::Result {
) )
} }
enum ViewState {
Stop,
Running { lastUpdateTimestamp: u64 },
VertexMove { vertexId: usize },
}
enum GlobalState { enum GlobalState {
Edit {}, Edit,
Running { view_state: ViewState }, Running,
} }
struct MyApp { struct MyApp {
edges: EdgesVec, simulation: AntsSimulationState,
vertices: VerticesVec,
config: AntsSimulationConfig, config: AntsSimulationConfig,
vertex_update: UpdatePending, vertex_update: UpdatePending,
speed: u32,
state: GlobalState, state: GlobalState,
vertex_locations: Vec<(f32, f32)>,
ants_per_vertex: usize,
} }
impl Default for MyApp { impl Default for MyApp {
fn default() -> Self { fn default() -> Self {
return Self { return Self {
simulation: AntsSimulationState {
edges: EdgesVec::new(), edges: EdgesVec::new(),
vertices: VerticesVec::new(), vertices: VerticesVec::new(),
ants: Vec::new(),
},
vertex_update: UpdatePending::NoChange, vertex_update: UpdatePending::NoChange,
speed: 1,
config: AntsSimulationConfig { config: AntsSimulationConfig {
ferment_weight: 0.5, ferment_weight: 0.5,
heuristic_coefficient: 0.5, heuristic_coefficient: 0.5,
@ -56,6 +57,8 @@ impl Default for MyApp {
r: 0.5, r: 0.5,
}, },
state: GlobalState::Edit {}, state: GlobalState::Edit {},
vertex_locations: Vec::new(),
ants_per_vertex: 1,
}; };
} }
} }
@ -85,13 +88,13 @@ impl eframe::App for MyApp {
match self.vertex_update { match self.vertex_update {
UpdatePending::NoChange => {} UpdatePending::NoChange => {}
UpdatePending::Add => { UpdatePending::Add => {
let new_vi = self.vertices.add(HashSet::new()); let new_vi = self.simulation.vertices.add(HashSet::new());
let mut newEdgesSet = 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) { if (vi == new_vi) {
continue; continue;
} }
let ei = self.edges.add( let ei = self.simulation.edges.add(
new_vi, new_vi,
vi, vi,
1.0, 1.0,
@ -102,19 +105,20 @@ impl eframe::App for MyApp {
newEdgesSet.insert(ei); newEdgesSet.insert(ei);
v.insert(ei); v.insert(ei);
} }
self.vertices[new_vi] = newEdgesSet; self.simulation.vertices[new_vi] = newEdgesSet;
self.vertex_update = UpdatePending::NoChange; self.vertex_update = UpdatePending::NoChange;
} }
UpdatePending::Remove(vi) => { UpdatePending::Remove(vi) => {
let mut eis = Vec::with_capacity(self.vertices[vi].len()); let mut eis = Vec::with_capacity(self.simulation.vertices[vi].len());
for ei in self.vertices[vi].iter() { for ei in self.simulation.vertices[vi].iter() {
eis.push(*ei) eis.push(*ei)
} }
for ei in eis { for ei in eis {
self.vertices[self.edges[ei].another(vi)].remove(&ei); self.simulation.vertices[self.simulation.edges[ei].another(vi)]
self.edges.remove(ei); .remove(&ei);
self.simulation.edges.remove(ei);
} }
self.vertices.remove(vi); self.simulation.vertices.remove(vi);
self.vertex_update = UpdatePending::NoChange; self.vertex_update = UpdatePending::NoChange;
} }
} }
@ -140,7 +144,7 @@ impl eframe::App for MyApp {
} }
fn edit_panel(data: &mut MyApp, ui: &mut Ui) { fn edit_panel(data: &mut MyApp, ui: &mut Ui) {
let run: bool; let mut run: bool = false;
_slider( _slider(
ui, ui,
@ -162,30 +166,97 @@ fn edit_panel(data: &mut MyApp, ui: &mut Ui) {
ui.label(""); ui.label("");
_slider(ui, "r", &mut data.config.r, 0.0..=1.0, 0.001); _slider(ui, "r", &mut data.config.r, 0.0..=1.0, 0.001);
ui.label(""); ui.label("");
_slider(
ui,
"Ants per vertex",
&mut data.ants_per_vertex,
1..=100,
1.0,
);
ui.label("");
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.button("Add vertex").clicked() { if ui.button("Add vertex").clicked() {
data.vertex_update = UpdatePending::Add; data.vertex_update = UpdatePending::Add;
} }
ui.separator(); ui.separator();
let run = ui.button("Run").clicked(); run = ui.button("Run").clicked();
}); });
draw_lengths_table( draw_lengths_table(
ui, ui,
&mut data.vertices, &mut data.simulation.vertices,
&mut data.edges, &mut data.simulation.edges,
&mut data.vertex_update, &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::<f32>() * 0.8 + 0.1,
rand::random::<f32>() * 0.8 + 0.1,
) )
} }
fn visualization_panel(data: &mut MyApp, ui: &mut Ui) { data.state = GlobalState::Running;
ui.horizontal(|ui| { data.vertex_locations = coords;
if ui.button("Exit").clicked() {}
if ui.button("Pause").clicked() {} let allowed_locations = data
.simulation
.vertices
.iter_indexed()
.map(|(i, _)| i)
.collect::<HashSet<usize>>();
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.vertical(|ui| {
ui.horizontal(|ui| {
if ui.button("Exit").clicked() {
data.state = GlobalState::Edit
}
if ui.button("Step").clicked() {
updateState(&mut data.simulation, &mut data.config, &mut rand::rng());
}
ui.label(""); ui.label("");
_slider(ui, "Ant speed", &mut data.speed, 1..=10, 1.0); });
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,
)
}

View File

@ -91,6 +91,10 @@ impl<T> _PreserveIndexVec<T> {
} }
}) })
} }
pub fn capacity(&self) -> usize {
return self.buffer.len();
}
} }
impl<T> Index<usize> for _PreserveIndexVec<T> { impl<T> Index<usize> for _PreserveIndexVec<T> {

View File

@ -51,6 +51,10 @@ impl<D> EdgesVec<D> {
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Edge<D>> { pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Edge<D>> {
return self.data.iter_mut(); return self.data.iter_mut();
} }
pub fn capacity(&self) -> usize {
return self.data.capacity()
}
} }
impl <D> Index<usize> for EdgesVec<D> { impl <D> Index<usize> for EdgesVec<D> {

View File

@ -33,6 +33,10 @@ impl VerticesVec {
pub fn iter_indexed_mut(&mut self) -> impl Iterator<Item = (usize, &mut HashSet<usize>)> { pub fn iter_indexed_mut(&mut self) -> impl Iterator<Item = (usize, &mut HashSet<usize>)> {
return self.data.iter_indexed_mut(); return self.data.iter_indexed_mut();
} }
pub fn capacity(&self) -> usize {
return self.data.capacity()
}
} }
impl Index<usize> for VerticesVec { impl Index<usize> for VerticesVec {

View File

@ -1 +1,2 @@
pub mod lengths_table; pub mod lengths_table;
pub mod render;

View File

@ -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<D>(
ui: &mut Ui,
vertices: &VerticesVec,
vertex_locations: &mut [(f32, f32)],
edges: &EdgesVec<D>,
normalized_intensity: impl Fn(&Edge<D>) -> 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!(),
}
}