From 837dfe9ba0f540ee00c79cf8bb830d221cdb5b70 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Mon, 16 Feb 2026 00:49:13 +0300 Subject: [PATCH] [lab2] Gui decomposition --- lab2/src/gui/data.rs | 54 +++++++++++++++++ lab2/src/gui/input.rs | 111 ++++++++++++++++++++++++++++++++++ lab2/src/gui/mod.rs | 5 ++ lab2/src/main.rs | 137 +++--------------------------------------- 4 files changed, 177 insertions(+), 130 deletions(-) create mode 100644 lab2/src/gui/data.rs create mode 100644 lab2/src/gui/input.rs create mode 100644 lab2/src/gui/mod.rs diff --git a/lab2/src/gui/data.rs b/lab2/src/gui/data.rs new file mode 100644 index 0000000..840b746 --- /dev/null +++ b/lab2/src/gui/data.rs @@ -0,0 +1,54 @@ +use crate::algo::BitVector; +use bgtu_ai_utility::{UpdatePending, UpdateTarget}; + +pub(crate) struct MyApp { + pub _isFirstFrame: bool, + pub bitCount: usize, + pub beta: usize, + pub attentiveness: f64, + pub data: Vec, + pub result: Option>, + pub columnUpdate: UpdatePending, + pub rowUpdate: UpdatePending, +} + +impl MyApp { + pub(crate) fn new() -> Self { + return Self { + _isFirstFrame: true, + bitCount: 0, + beta: 1, + attentiveness: 0.5, + data: Vec::new(), + result: None, + columnUpdate: UpdatePending::NoChange, + rowUpdate: UpdatePending::NoChange, + }; + } + + pub fn update_columns<'s>(&'s mut self) -> ColumnsUpdateTarget<'s> { + return ColumnsUpdateTarget { data: self }; + } +} + +struct ColumnsUpdateTarget<'d> { + data: &'d mut MyApp, +} + +impl UpdateTarget for ColumnsUpdateTarget<'_> { + type Elem = bool; + + fn add(&mut self, value: Self::Elem) { + for bv in self.data.data.iter_mut() { + UpdateTarget::add(bv, value) + } + self.data.bitCount += 1; + } + + fn remove(&mut self, index: usize) { + for bv in self.data.data.iter_mut() { + UpdateTarget::remove(bv, index) + } + self.data.bitCount -= 1; + } +} diff --git a/lab2/src/gui/input.rs b/lab2/src/gui/input.rs new file mode 100644 index 0000000..4f34cfc --- /dev/null +++ b/lab2/src/gui/input.rs @@ -0,0 +1,111 @@ +use super::MyApp; +use crate::algo; +use bgtu_ai_utility::UpdatePending; +use bgtu_ai_utility::gui::labeled_slider; +use eframe::egui::Ui; +use egui_extras::{Column, TableBuilder}; + +pub(crate) fn input(ui: &mut Ui, data: &mut MyApp) { + ui.add_enabled_ui(matches!(data.result, None), |ui| { + labeled_slider(ui, "beta", &mut data.beta, 1..=10, 1f64); + ui.label(""); + labeled_slider( + ui, + "attentiveness", + &mut data.attentiveness, + 0f64..=1f64, + 0.001, + ); + ui.label(""); + }); + + ui.horizontal(|ui| { + ui.add_enabled_ui(matches!(data.result, None), |ui| { + if ui.button("Add row").clicked() { + data.rowUpdate = UpdatePending::Add; + } + if ui.button("Add column").clicked() { + data.columnUpdate = UpdatePending::Add; + } + }); + ui.separator(); + + ui.add_enabled_ui(matches!(data.result, None), |ui| { + if ui.button("Classify").clicked() { + data.result = Some(algo::adaptiveResonanceTheoryImpl( + data.data.as_slice(), + data.beta, + data.attentiveness, + )) + } + }); + + ui.add_enabled_ui(!matches!(data.result, None), |ui| { + if ui.button("Edit").clicked() { + data.result = None; + } + }); + }); + + ui.label(""); + + table(ui, data); +} + +fn table(ui: &mut Ui, data: &mut MyApp) { + TableBuilder::new(ui) + .striped(true) // Alternating row colors + .resizable(true) + .column(Column::remainder()) + .column(Column::remainder()) + .columns(Column::remainder(), data.bitCount) + .header(20.0, |mut header| { + header.col(|ui| { + ui.horizontal(|ui| { + ui.label("#"); + }); + }); + header.col(|ui| { + ui.label("Group"); + }); + for i in 0..data.bitCount { + header.col(|ui| { + ui.horizontal(|ui| { + ui.label(i.to_string()); + ui.add_enabled_ui(matches!(data.result, None), |ui| { + if ui.button("-").clicked() { + data.columnUpdate = UpdatePending::Remove(i); + } + }); + }); + }); + } + }) + .body(|body| { + body.rows(20.0, data.data.len(), |mut row| { + let i = row.index(); + row.col(|ui| { + ui.horizontal(|ui| { + if ui.button("-").clicked() { + data.rowUpdate = UpdatePending::Remove(i); + } + ui.label(i.to_string()); + }); + }); + row.col(|ui| { + if let Some(result) = &data.result { + ui.label(result[i].to_string()); + } else { + ui.label("?"); + } + }); + for j in 0..data.bitCount { + row.col(|ui| { + ui.add_enabled_ui(matches!(data.result, None), |ui| { + ui.checkbox(&mut data.data[i][j], ""); + }); + }); + } + }); + }); +} diff --git a/lab2/src/gui/mod.rs b/lab2/src/gui/mod.rs new file mode 100644 index 0000000..d91c486 --- /dev/null +++ b/lab2/src/gui/mod.rs @@ -0,0 +1,5 @@ +mod data; +mod input; + +pub(crate) use data::MyApp; +pub(crate) use input::input; \ No newline at end of file diff --git a/lab2/src/main.rs b/lab2/src/main.rs index 114612a..8c85d6b 100644 --- a/lab2/src/main.rs +++ b/lab2/src/main.rs @@ -1,48 +1,22 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release mod algo; +mod gui; use crate::algo::BitVector; -use bgtu_ai_utility::gui::{boot_eframe, labeled_slider}; +use bgtu_ai_utility::gui::boot_eframe; use eframe::egui; -use egui_extras::{Column, TableBuilder}; -use bgtu_ai_utility::{UpdatePending, UpdateTarget}; fn main() -> eframe::Result { - return boot_eframe(|| MyApp::new()); + return boot_eframe(|| gui::MyApp::new()); } -struct MyApp { - _isFirstFrame: bool, - bitCount: usize, - beta: usize, - attentiveness: f64, - data: Vec, - result: Option>, - columnUpdate: UpdatePending, - rowUpdate: UpdatePending, -} -impl MyApp { - fn new() -> Self { - return Self { - _isFirstFrame: true, - bitCount: 0, - beta: 1, - attentiveness: 0.5, - data: Vec::new(), - result: None, - columnUpdate: UpdatePending::NoChange, - rowUpdate: UpdatePending::NoChange, - }; - } -} - -impl eframe::App for MyApp { +impl eframe::App for gui::MyApp { fn update(&mut self, ui: &eframe::egui::Context, _frame: &mut eframe::Frame) { - self.columnUpdate.apply_many_and_clear( - self.data.iter_mut(), + self.columnUpdate.apply( + &mut self.update_columns(), || false ); @@ -52,104 +26,7 @@ impl eframe::App for MyApp { ); egui::CentralPanel::default().show(ui, |ui| { - ui.add_enabled_ui(matches!(self.result, None), |ui| { - labeled_slider(ui, "beta", &mut self.beta, 1..=10, 1f64); - ui.label(""); - labeled_slider( - ui, - "attentiveness", - &mut self.attentiveness, - 0f64..=1f64, - 0.001, - ); - ui.label(""); - }); - - ui.horizontal(|ui| { - ui.add_enabled_ui(matches!(self.result, None), |ui| { - if ui.button("Add row").clicked() { - self.rowUpdate = UpdatePending::Add; - } - if ui.button("Add column").clicked() { - self.columnUpdate = UpdatePending::Add; - } - }); - ui.separator(); - - ui.add_enabled_ui(matches!(self.result, None), |ui| { - if ui.button("Classify").clicked() { - self.result = Some(algo::adaptiveResonanceTheoryImpl( - self.data.as_slice(), - self.beta, - self.attentiveness, - )) - } - }); - - ui.add_enabled_ui(!matches!(self.result, None), |ui| { - if ui.button("Edit").clicked() { - self.result = None; - } - }); - }); - - ui.label(""); - - TableBuilder::new(ui) - .striped(true) // Alternating row colors - .resizable(true) - .column(Column::remainder()) - .column(Column::remainder()) - .columns(Column::remainder(), self.bitCount) - .header(20.0, |mut header| { - header.col(|ui| { - ui.horizontal(|ui| { - ui.label("#"); - }); - }); - header.col(|ui| { - ui.label("Group"); - }); - for i in 0..self.bitCount { - header.col(|ui| { - ui.horizontal(|ui| { - ui.label(i.to_string()); - ui.add_enabled_ui(matches!(self.result, None), |ui| { - if ui.button("-").clicked() { - self.columnUpdate = UpdatePending::Remove(i); - } - }); - }); - }); - } - }) - .body(|body| { - body.rows(20.0, self.data.len(), |mut row| { - let i = row.index(); - row.col(|ui| { - ui.horizontal(|ui| { - if ui.button("-").clicked() { - self.rowUpdate = UpdatePending::Remove(i); - } - ui.label(i.to_string()); - }); - }); - row.col(|ui| { - if let Some(result) = &self.result { - ui.label(result[i].to_string()); - } else { - ui.label("?"); - } - }); - for j in 0..self.bitCount { - row.col(|ui| { - ui.add_enabled_ui(matches!(self.result, None), |ui| { - ui.checkbox(&mut self.data[i][j], ""); - }); - }); - } - }); - }); + gui::input(ui, self); }); } }