use crate::gui::all_libs_gui;
use crate::gui::combustion;
use crate::gui::experimental_kinetics_gui::experimental_kinetics_gui_main;
use crate::gui::gui_main::egui::IconData;
use crate::gui::gui_solid_ivp;
use crate::gui::kinetics_gui;
use crate::gui::settings_gui;
use crate::gui::thermochemistry_gui;
use crate::gui::transport_gui;
use eframe::CreationContext;
use eframe::egui;
use egui::ColorImage;
use egui::TextureHandle;
pub fn gui_main() -> Result<(), eframe::Error> {
let icon = match std::fs::read("src/assets/icon.png") {
Ok(icon_bytes) => match eframe::icon_data::from_png_bytes(&icon_bytes) {
Ok(icon_data) => icon_data,
Err(_) => create_programmatic_icon(),
},
Err(_) => create_programmatic_icon(),
};
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([800.0, 600.0])
.with_title("Main menu ")
.with_icon(icon),
..Default::default()
};
eframe::run_native(
"Chemical Analysis Suite",
options,
Box::new(|cc: &eframe::CreationContext<'_>| Ok(Box::new(MainApp::new(cc)))),
)
}
fn create_programmatic_icon() -> IconData {
let size = 32;
let mut rgba = vec![0u8; size * size * 4];
for y in 0..size {
for x in 0..size {
let idx = (y * size + x) * 4;
let center_x = size as f32 / 2.0;
let center_y = size as f32 / 2.0;
let dist = ((x as f32 - center_x).powi(2) + (y as f32 - center_y).powi(2)).sqrt();
if dist < 12.0 {
rgba[idx] = 255;
rgba[idx + 1] = (200.0 * (1.0 - dist / 12.0)) as u8;
rgba[idx + 2] = 0;
rgba[idx + 3] = 255;
}
}
}
IconData {
rgba,
width: 32,
height: 32,
}
}
#[derive(Default)]
struct MainApp {
kinetics_open: bool,
kinetics_app: Option<kinetics_gui::KineticsApp>,
combustion_open: bool,
combustion_app: Option<combustion::CombustionApp>,
thermochemistry_open: bool,
thermochemistry_app: Option<thermochemistry_gui::ThermochemistryApp>,
transport_open: bool,
transport_app: Option<transport_gui::TransportApp>,
settings_open: bool,
settings_app: Option<settings_gui::SettingsGui>,
all_libs_open: bool,
all_libs_app: Option<all_libs_gui::AllLibsGui>,
solid_ivp_open: bool,
solid_ivp_app: Option<gui_solid_ivp::SolidIVPApp>,
experimental_kinetics_open: bool,
experimental_kinetics_app: Option<experimental_kinetics_gui_main::PlotApp>,
logo_texture: Option<egui::TextureHandle>,
}
impl MainApp {
pub fn new(cc: &CreationContext<'_>) -> Self {
let ctx = &cc.egui_ctx;
Self::setup_visuals(ctx);
Self::setup_fonts(ctx);
let logo_texture = Self::load_logo_texture(ctx);
Self {
kinetics_open: false,
kinetics_app: None,
combustion_open: false,
combustion_app: None,
thermochemistry_open: false,
thermochemistry_app: None,
transport_open: false,
transport_app: None,
settings_open: false,
settings_app: None,
all_libs_open: false,
all_libs_app: None,
solid_ivp_open: false,
solid_ivp_app: None,
experimental_kinetics_open: false,
experimental_kinetics_app: None,
logo_texture,
}
}
fn setup_visuals(ctx: &egui::Context) {
use egui::Visuals;
ctx.set_visuals(Visuals::dark()); }
fn setup_fonts(ctx: &egui::Context) {
use egui::FontDefinitions;
let mut fonts = FontDefinitions::default();
ctx.set_fonts(fonts);
}
fn load_logo_texture(ctx: &egui::Context) -> Option<TextureHandle> {
if let Ok(logo_bytes) = std::fs::read("src/assets/logo.png") {
if let Ok(color_image) = eframe::icon_data::from_png_bytes(&logo_bytes) {
let egui_image = ColorImage::from_rgba_unmultiplied(
[color_image.width as usize, color_image.height as usize],
&color_image.rgba,
);
return Some(ctx.load_texture(
"kithe_logo",
egui_image,
egui::TextureOptions::default(),
));
}
}
None
}
}
impl eframe::App for MainApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
ctx.style_mut(|style| {
style.visuals.widgets.inactive.fg_stroke.color = egui::Color32::BLACK;
style.visuals.widgets.hovered.fg_stroke.color = egui::Color32::BLACK;
style.visuals.widgets.active.fg_stroke.color = egui::Color32::WHITE;
style.text_styles.insert(
egui::TextStyle::Button,
egui::FontId::new(16.0, egui::FontFamily::Proportional),
);
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.vertical_centered(|ui| {
ui.add_space(50.0);
ui.heading("Heat and mass transfer, chemical reactors \ncombustion and macrokinetics suite");
ui.add_space(30.0);
ui.horizontal(|ui| {
ui.add_space(50.0);
ui.vertical(|ui| {
ui.horizontal(|ui| {
if ui.add_sized([200.0, 80.0], egui::Button::new("🧪 Kinetics!")).clicked() {
self.kinetics_open = true;
if self.kinetics_app.is_none() {
self.kinetics_app = Some(kinetics_gui::KineticsApp::new());
}
}
ui.add_space(20.0);
if ui.add_sized([200.0, 80.0], egui::Button::new("🌡️ Thermodynamical properties!")).clicked() {
self.thermochemistry_open = true;
if self.thermochemistry_app.is_none() {
self.thermochemistry_app = Some(thermochemistry_gui::ThermochemistryApp::new());
}
}
});
ui.add_space(20.0);
ui.horizontal(|ui| {
if ui.add_sized([200.0, 80.0], egui::Button::new("🚚 Transport properties!")).clicked() {
self.transport_open = true;
if self.transport_app.is_none() {
self.transport_app = Some(transport_gui::TransportApp::new());
}
}
ui.add_space(20.0);
if ui.add_sized([200.0, 80.0], egui::Button::new("🔥 Gas-phase combustuion/steady state plug flow")).clicked() {
self.combustion_open = true;
if self.combustion_app.is_none() {
self.combustion_app = Some(combustion::CombustionApp::new());
}
}
});
ui.add_space(20.0);
ui.horizontal(|ui| {
if ui.add_sized([200.0, 80.0], egui::Button::new("📚 Library Review")).clicked() {
self.all_libs_open = true;
if self.all_libs_app.is_none() {
self.all_libs_app = Some(all_libs_gui::AllLibsGui::new());
}
}
ui.add_space(20.0);
if ui.add_sized([200.0, 80.0], egui::Button::new("⚙️ Settings")).clicked() {
self.settings_open = true;
if self.settings_app.is_none() {
self.settings_app = Some(settings_gui::SettingsGui::new());
}
}
});
ui.add_space(20.0);
ui.horizontal(|ui| {
if ui.add_sized([200.0, 80.0], egui::Button::new("Solid-state kinetics")).clicked() {
self.solid_ivp_open = true;
if self.solid_ivp_app.is_none() {
self.solid_ivp_app = Some(gui_solid_ivp::SolidIVPApp::new());
}
}
ui.add_space(20.0);
if ui.add_sized([200.0, 80.0], egui::Button::new("📊 Experimental Kinetics")).clicked() {
self.experimental_kinetics_open = true;
if self.experimental_kinetics_app.is_none() {
self.experimental_kinetics_app = Some(experimental_kinetics_gui_main::PlotApp::new());
}
}
});
});
});
ui.add_space(50.0);
ui.separator();
ui.add_space(20.0);
if self.logo_texture.is_none() {
if let Ok(logo_bytes) = std::fs::read("src/assets/logo.png") {
if let Ok(color_image) = eframe::icon_data::from_png_bytes(&logo_bytes) {
let egui_image = egui::ColorImage::from_rgba_unmultiplied(
[color_image.width as usize, color_image.height as usize],
&color_image.rgba
);
self.logo_texture = Some(ctx.load_texture("logo", egui_image, egui::TextureOptions::default()));
}
}
}
if let Some(texture) = &self.logo_texture {
ui.add(egui::Image::new(texture).max_width(200.0));
ui.add_space(10.0);
} else {
ui.label("Logo not found");
}
ui.label("developed by Gleb E. Zaslavsky 2024-2026 (c)");
}) ;
});
if self.kinetics_open {
if let Some(kinetics_app) = &mut self.kinetics_app {
kinetics_app.show(ctx, &mut self.kinetics_open);
}
}
if self.combustion_open {
if let Some(combustion_app) = &mut self.combustion_app {
combustion_app.show(ctx, &mut self.combustion_open);
}
}
if self.thermochemistry_open {
if let Some(thermochemistry_app) = &mut self.thermochemistry_app {
thermochemistry_app.show(ctx, &mut self.thermochemistry_open);
}
}
if self.transport_open {
if let Some(transport_app) = &mut self.transport_app {
transport_app.show(ctx, &mut self.transport_open);
}
}
if self.settings_open {
if let Some(settings_app) = &mut self.settings_app {
settings_app.show(ctx, &mut self.settings_open);
}
}
if self.all_libs_open {
if let Some(all_libs_app) = &mut self.all_libs_app {
all_libs_app.show(ctx, &mut self.all_libs_open);
}
}
if self.solid_ivp_open {
if let Some(solid_ivp_app) = &mut self.solid_ivp_app {
solid_ivp_app.show(ctx, &mut self.solid_ivp_open)
}
}
if self.experimental_kinetics_open {
if let Some(experimental_kinetics_app) = &mut self.experimental_kinetics_app {
experimental_kinetics_app.show(ctx, &mut self.experimental_kinetics_open);
}
}
}
}