[lab2] Gui decomposition

This commit is contained in:
Andrew Golovashevich 2026-02-16 00:49:13 +03:00
parent 60450ee342
commit 837dfe9ba0
4 changed files with 177 additions and 130 deletions

54
lab2/src/gui/data.rs Normal file
View File

@ -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<BitVector>,
pub result: Option<Box<[usize]>>,
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;
}
}

111
lab2/src/gui/input.rs Normal file
View File

@ -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], "");
});
});
}
});
});
}

5
lab2/src/gui/mod.rs Normal file
View File

@ -0,0 +1,5 @@
mod data;
mod input;
pub(crate) use data::MyApp;
pub(crate) use input::input;

View File

@ -1,48 +1,22 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
mod algo; mod algo;
mod gui;
use crate::algo::BitVector; use crate::algo::BitVector;
use bgtu_ai_utility::gui::{boot_eframe, labeled_slider}; use bgtu_ai_utility::gui::boot_eframe;
use eframe::egui; use eframe::egui;
use egui_extras::{Column, TableBuilder};
use bgtu_ai_utility::{UpdatePending, UpdateTarget};
fn main() -> eframe::Result { 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<BitVector>,
result: Option<Box<[usize]>>,
columnUpdate: UpdatePending,
rowUpdate: UpdatePending,
}
impl MyApp { impl eframe::App for gui::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 {
fn update(&mut self, ui: &eframe::egui::Context, _frame: &mut eframe::Frame) { fn update(&mut self, ui: &eframe::egui::Context, _frame: &mut eframe::Frame) {
self.columnUpdate.apply_many_and_clear( self.columnUpdate.apply(
self.data.iter_mut(), &mut self.update_columns(),
|| false || false
); );
@ -52,104 +26,7 @@ impl eframe::App for MyApp {
); );
egui::CentralPanel::default().show(ui, |ui| { egui::CentralPanel::default().show(ui, |ui| {
ui.add_enabled_ui(matches!(self.result, None), |ui| { gui::input(ui, self);
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], "");
});
});
}
});
});
}); });
} }
} }