[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 EdgesVec = _BaseEdgesVec<EdgeExtraData>;
pub struct AntsSimulationConfig {
pub ferment_weight: f64,
pub heuristic_coefficient: f64,
pub q: f64,
pub r: f64,
}
pub use state::{AntsSimulationConfig, AntsSimulationState, updateState, Ant};

View File

@ -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<usize>,
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 {
OnEdge {
edge_index: usize,
@ -24,20 +39,32 @@ pub enum AntLocation {
},
}
pub struct AntsSimulationState<'a, Rng: rand::Rng> {
rng: Rng,
vertices: &'a Vec<HashSet<usize>>,
edges: &'a mut EdgesVec,
ants: &'a mut [Ant],
pub struct AntsSimulationState {
pub vertices: VerticesVec,
pub edges: EdgesVec,
pub ants: Vec<Ant>,
}
pub fn updateState<Rng: rand::Rng>(
state: &mut AntsSimulationState<Rng>,
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,11 +73,9 @@ pub fn updateState<Rng: rand::Rng>(
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;
edge.extra.ferment_intensity += (cfg.q / edge.length) * cfg.r;
let vertex_index;
match direction {
AntDirection::ToSecond => vertex_index = edge.vertex2_index,
@ -58,7 +83,6 @@ pub fn updateState<Rng: rand::Rng>(
}
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<Rng: rand::Rng>(
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::<Vec<(usize, (usize, AntDirection), f64)>>();
@ -84,7 +108,7 @@ pub fn updateState<Rng: rand::Rng>(
let mut accumulator: f64 = allowed_outbounds
.iter()
.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| {
accumulator -= e.2;
return accumulator <= 0.0;

View File

@ -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 {
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::<f32>() * 0.8 + 0.1,
rand::random::<f32>() * 0.8 + 0.1,
)
}
fn visualization_panel(data: &mut MyApp, ui: &mut Ui) {
ui.horizontal(|ui| {
if ui.button("Exit").clicked() {}
data.state = GlobalState::Running;
data.vertex_locations = coords;
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("");
_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> {

View File

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

View File

@ -1 +1,2 @@
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!(),
}
}