From a48b9362567b990529205f7b172ccb76bda95980 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Mon, 7 Oct 2024 22:49:11 -0600 Subject: [PATCH 1/5] Move logger to within window and switch to egui light theme --- anise-gui/Cargo.toml | 11 ++++------ anise-gui/src/main.rs | 12 +++++++++-- anise-gui/src/ui.rs | 49 +++++++++++++++---------------------------- 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/anise-gui/Cargo.toml b/anise-gui/Cargo.toml index b7c92d11..cec603aa 100644 --- a/anise-gui/Cargo.toml +++ b/anise-gui/Cargo.toml @@ -14,14 +14,11 @@ hifitime = { workspace = true } log = { workspace = true } bytes = { workspace = true } pretty_env_logger = { workspace = true } -egui-toast = "0.14" -eframe = { version = "0.28" } -egui = { version = "0.28" } -egui_extras = { version = "0.28", features = ["datepicker", "http", "image"] } -catppuccin-egui = { version = "5.1", default-features = false, features = [ - "egui28", -] } +eframe = { version = "0.29" } +egui = { version = "0.29" } +egui_extras = { version = "0.29", features = ["datepicker", "http", "image"] } rfd = { version = "0.15.0" } +egui_logger = "0.6.1" [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen-futures = "0.4" diff --git a/anise-gui/src/main.rs b/anise-gui/src/main.rs index 07504d04..24b5ac63 100644 --- a/anise-gui/src/main.rs +++ b/anise-gui/src/main.rs @@ -12,11 +12,19 @@ fn main() { set_var(LOG_VAR, "INFO"); } - let _ = pretty_env_logger::try_init_custom_env(LOG_VAR).is_err(); + // Initialize the logger + egui_logger::builder() + .init() + .expect("Error initializing logger"); + + let opts = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default().with_maximized(true), + ..Default::default() + }; let _ = eframe::run_native( "ANISE by Nyx Space", - eframe::NativeOptions::default(), + opts, Box::new(|cc| Ok(Box::new(UiApp::new(cc)))), ); } diff --git a/anise-gui/src/ui.rs b/anise-gui/src/ui.rs index 313a314a..3333fc73 100644 --- a/anise-gui/src/ui.rs +++ b/anise-gui/src/ui.rs @@ -3,16 +3,14 @@ use anise::{ naif::daf::NAIFSummaryRecord, }; use eframe::egui; -use egui::Align2; +use egui::Theme; use egui_extras::{Column, TableBuilder}; -use egui_toast::{Toast, ToastKind, ToastOptions, ToastStyle, Toasts}; use hifitime::TimeScale; +use log::error; #[cfg(target_arch = "wasm32")] use poll_promise::Promise; -use catppuccin_egui::FRAPPE; - #[cfg(target_arch = "wasm32")] type AlmanacFile = Option<(String, Vec)>; @@ -33,12 +31,12 @@ enum FileLoadResult { } impl UiApp { - pub fn new(_cc: &eframe::CreationContext<'_>) -> Self { + pub fn new(cc: &eframe::CreationContext<'_>) -> Self { // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals. // Restore app state using cc.storage (requires the "persistence" feature). // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use // for e.g. egui::PaintCallback. - // _cc.egui_ctx.set_style(Arc::new(MOCHA)); + cc.egui_ctx.set_theme(Theme::Light); Self::default() } @@ -82,25 +80,28 @@ impl UiApp { impl eframe::App for UiApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - catppuccin_egui::set_theme(ctx, FRAPPE); ctx.set_pixels_per_point(1.25); - let mut toasts = Toasts::new() - .anchor(Align2::RIGHT_BOTTOM, (-10.0, -10.0)) // 10 units from the bottom right corner - .direction(egui::Direction::BottomUp); - egui::TopBottomPanel::top("header").show(ctx, |ui| { ui.horizontal_centered(|ui| { ui.vertical_centered(|ui| { ui.heading("ANISE v0.4"); - ui.label("A modern rewrite of NAIF SPICE"); + ui.label("A modern rewrite of NASA's SPICE toolkit"); ui.hyperlink_to("Contact us", "https://7ug5imdtt8v.typeform.com/to/neFvVW3p"); - ui.hyperlink("https://www.nyxspace.com"); + ui.hyperlink_to("https://www.nyxspace.com", "https://www.nyxspace.com?utm_source=gui"); ui.label("ANISE is open-sourced under the Mozilla Public License 2.0"); }); }); }); + egui::TopBottomPanel::bottom("log").show(ctx, |ui| { + // draws the actual logger ui + egui_logger::LoggerUi::default() + .enable_ctx_menu(false) + .enable_regex(false) + .show(ui) + }); + egui::CentralPanel::default().show(ctx, |ui| { egui::ScrollArea::both().show(ui, |ui| { ui.horizontal_centered(|ui| { @@ -125,26 +126,12 @@ impl eframe::App for UiApp { FileLoadResult::NoFileSelectedYet => { } FileLoadResult::Ok((path, almanac)) => { - toasts.add(Toast { - text: format!("Loaded {path:?}").into(), - kind: ToastKind::Success, - style: ToastStyle::default(), - options: ToastOptions::default() - .duration_in_seconds(15.0) - .show_progress(true), - }); + self.almanac = almanac; self.path = Some(path); } FileLoadResult::Error(e) => { - toasts.add(Toast { - text: format!("{e}").into(), - kind: ToastKind::Error, - style: ToastStyle::default(), - options: ToastOptions::default() - .duration_in_seconds(15.0) - .show_progress(true), - }); + error!("{e}"); } } } @@ -669,10 +656,8 @@ impl eframe::App for UiApp { }; }); }); - - // Show and update all toasts }); - toasts.show(ctx); + }); } } From 5cda3a918c98df4cd12247f963dd183cacd25c30 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 8 Oct 2024 20:27:33 -0600 Subject: [PATCH 2/5] Black theme + windows supression --- anise-gui/src/main.rs | 3 ++- anise-gui/src/ui.rs | 48 +++++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/anise-gui/src/main.rs b/anise-gui/src/main.rs index 24b5ac63..d922efc1 100644 --- a/anise-gui/src/main.rs +++ b/anise-gui/src/main.rs @@ -1,3 +1,4 @@ +#![windows_subsystem = "windows"] #[allow(dead_code)] const LOG_VAR: &str = "ANISE_LOG"; @@ -18,7 +19,7 @@ fn main() { .expect("Error initializing logger"); let opts = eframe::NativeOptions { - viewport: egui::ViewportBuilder::default().with_maximized(true), + viewport: egui::ViewportBuilder::default().with_inner_size([1024.0, 768.0]), ..Default::default() }; diff --git a/anise-gui/src/ui.rs b/anise-gui/src/ui.rs index 3333fc73..9d34e33d 100644 --- a/anise-gui/src/ui.rs +++ b/anise-gui/src/ui.rs @@ -14,7 +14,6 @@ use poll_promise::Promise; #[cfg(target_arch = "wasm32")] type AlmanacFile = Option<(String, Vec)>; -#[derive(Default)] pub struct UiApp { selected_time_scale: TimeScale, show_unix: bool, @@ -24,6 +23,19 @@ pub struct UiApp { promise: Option>, } +impl Default for UiApp { + fn default() -> Self { + Self { + selected_time_scale: TimeScale::UTC, + show_unix: false, + almanac: Default::default(), + path: None, + #[cfg(target_arch = "wasm32")] + promise: Default::default(), + } + } +} + enum FileLoadResult { NoFileSelectedYet, Ok((String, Almanac)), @@ -36,7 +48,7 @@ impl UiApp { // Restore app state using cc.storage (requires the "persistence" feature). // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use // for e.g. egui::PaintCallback. - cc.egui_ctx.set_theme(Theme::Light); + cc.egui_ctx.set_theme(Theme::Dark); Self::default() } @@ -87,20 +99,28 @@ impl eframe::App for UiApp { ui.vertical_centered(|ui| { ui.heading("ANISE v0.4"); ui.label("A modern rewrite of NASA's SPICE toolkit"); - ui.hyperlink_to("Contact us", "https://7ug5imdtt8v.typeform.com/to/neFvVW3p"); - ui.hyperlink_to("https://www.nyxspace.com", "https://www.nyxspace.com?utm_source=gui"); - ui.label("ANISE is open-sourced under the Mozilla Public License 2.0"); + ui.hyperlink_to("Contact", "https://7ug5imdtt8v.typeform.com/to/neFvVW3p"); + ui.hyperlink_to( + "https://www.nyxspace.com", + "https://www.nyxspace.com?utm_source=gui", + ); }); }); }); - egui::TopBottomPanel::bottom("log").show(ctx, |ui| { - // draws the actual logger ui - egui_logger::LoggerUi::default() - .enable_ctx_menu(false) - .enable_regex(false) - .show(ui) - }); + egui::TopBottomPanel::bottom("bottom_panel") + .resizable(false) + .min_height(0.0) + .show(ctx, |ui| { + ui.vertical_centered(|ui| { + ui.heading("Run log"); + }); + + egui_logger::LoggerUi::default() + .enable_ctx_menu(false) + .enable_regex(false) + .show(ui); + }); egui::CentralPanel::default().show(ctx, |ui| { egui::ScrollArea::both().show(ui, |ui| { @@ -126,7 +146,6 @@ impl eframe::App for UiApp { FileLoadResult::NoFileSelectedYet => { } FileLoadResult::Ok((path, almanac)) => { - self.almanac = almanac; self.path = Some(path); } @@ -201,7 +220,7 @@ impl eframe::App for UiApp { ui.checkbox( &mut self.show_unix, - "Show UNIX timestamps (in seconds)", + "UNIX timestamps", ); }); } @@ -657,7 +676,6 @@ impl eframe::App for UiApp { }); }); }); - }); } } From 6c6f471373b67b0d6fd729fe7a00f599e2fb616c Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Wed, 9 Oct 2024 21:34:55 -0600 Subject: [PATCH 3/5] Cleanup GUI --- anise-gui/src/bpc.rs | 116 ++++++++++++++++ anise-gui/src/main.rs | 5 +- anise-gui/src/spk.rs | 107 +++++++++++++++ anise-gui/src/ui.rs | 308 +++--------------------------------------- 4 files changed, 247 insertions(+), 289 deletions(-) create mode 100644 anise-gui/src/bpc.rs create mode 100644 anise-gui/src/spk.rs diff --git a/anise-gui/src/bpc.rs b/anise-gui/src/bpc.rs new file mode 100644 index 00000000..2c459911 --- /dev/null +++ b/anise-gui/src/bpc.rs @@ -0,0 +1,116 @@ +use anise::{ + constants::orientations::orientation_name_from_id, + prelude::{Almanac, NAIFSummaryRecord}, +}; +use egui_extras::{Column, TableBuilder}; +use hifitime::{TimeScale, Unit}; + +pub fn bpc_ui( + ui: &mut egui::Ui, + almanac: &Almanac, + show_unix: bool, + selected_time_scale: TimeScale, +) { + // We can use the summary + TableBuilder::new(ui) + .column(Column::auto().at_least(125.0).resizable(true)) + .column(Column::auto().at_least(125.0).resizable(true)) + .column(Column::auto().at_least(250.0).resizable(true)) + .column(Column::auto().at_least(250.0).resizable(true)) + .column(Column::auto().at_least(200.0).resizable(true)) + .column(Column::auto().at_least(150.0).resizable(true)) + .column(Column::remainder()) + .header(20.0, |mut header| { + header.col(|ui| { + ui.heading("Frame"); + }); + header.col(|ui| { + ui.heading("Segment name"); + }); + header.col(|ui| { + ui.heading("Start"); + }); + header.col(|ui| { + ui.heading("End"); + }); + header.col(|ui| { + ui.heading("Inertial frame"); + }); + header.col(|ui| { + ui.heading("Domain"); + }); + header.col(|ui| { + ui.heading("Type"); + }); + }) + .body(|mut body| { + let pck = almanac.bpc_data[0].as_ref().unwrap(); + + for (sno, summary) in pck.data_summaries().unwrap().iter().enumerate() { + let name_rcrd = pck.name_record().unwrap(); + let name = name_rcrd.nth_name(sno, pck.file_record().unwrap().summary_size()); + if summary.is_empty() { + continue; + } + + body.row(30.0, |mut row| { + row.col(|ui| { + ui.label(name); + }); + + row.col(|ui| { + if show_unix { + ui.text_edit_singleline(&mut format!( + "{}", + summary.start_epoch().to_unix_seconds() + )); + } else { + ui.label(summary.start_epoch().to_gregorian_str(selected_time_scale)); + }; + }); + + row.col(|ui| { + if show_unix { + ui.text_edit_singleline(&mut format!( + "{}", + summary.end_epoch().to_unix_seconds() + )); + } else { + ui.label(summary.end_epoch().to_gregorian_str(selected_time_scale)); + }; + }); + + row.col(|ui| match orientation_name_from_id(summary.frame_id) { + Some(name) => { + ui.label(format!("{name} ({})", summary.frame_id)); + } + None => { + ui.label(format!("{}", summary.frame_id)); + } + }); + + row.col( + |ui| match orientation_name_from_id(summary.inertial_frame_id) { + Some(name) => { + ui.label(format!("{name} ({})", summary.inertial_frame_id)); + } + None => { + ui.label(format!("{}", summary.inertial_frame_id)); + } + }, + ); + + row.col(|ui| { + ui.label(format!( + "{}", + (summary.end_epoch() - summary.start_epoch()).round(Unit::Second * 1) + )); + }); + + row.col(|ui| { + ui.label(format!("{}", summary.data_type().unwrap())); + }); + }); + } + }); +} diff --git a/anise-gui/src/main.rs b/anise-gui/src/main.rs index d922efc1..89a95948 100644 --- a/anise-gui/src/main.rs +++ b/anise-gui/src/main.rs @@ -5,6 +5,9 @@ const LOG_VAR: &str = "ANISE_LOG"; mod ui; use ui::UiApp; +mod bpc; +mod spk; + #[cfg(not(target_arch = "wasm32"))] fn main() { use std::env::{set_var, var}; @@ -19,7 +22,7 @@ fn main() { .expect("Error initializing logger"); let opts = eframe::NativeOptions { - viewport: egui::ViewportBuilder::default().with_inner_size([1024.0, 768.0]), + viewport: egui::ViewportBuilder::default().with_inner_size([1024.0, 640.0]), ..Default::default() }; diff --git a/anise-gui/src/spk.rs b/anise-gui/src/spk.rs new file mode 100644 index 00000000..dc7b0b5f --- /dev/null +++ b/anise-gui/src/spk.rs @@ -0,0 +1,107 @@ +use anise::prelude::{Almanac, NAIFSummaryRecord}; +use egui_extras::{Column, TableBuilder}; +use hifitime::{TimeScale, Unit}; + +pub fn spk_ui( + ui: &mut egui::Ui, + almanac: &Almanac, + show_unix: bool, + selected_time_scale: TimeScale, +) { + TableBuilder::new(ui) + .column(Column::auto().at_least(150.0).resizable(true)) + .column(Column::auto().at_least(150.0).resizable(true)) + .column(Column::auto().at_least(250.0).resizable(true)) + .column(Column::auto().at_least(250.0).resizable(true)) + .column(Column::auto().at_least(200.0).resizable(true)) + .column(Column::auto().at_least(150.0).resizable(true)) + .column(Column::remainder()) + .header(20.0, |mut header| { + header.col(|ui| { + ui.heading("Target"); + }); + header.col(|ui| { + ui.heading("Name"); + }); + header.col(|ui| { + ui.heading("Start"); + }); + header.col(|ui| { + ui.heading("End"); + }); + header.col(|ui| { + ui.heading("Center"); + }); + header.col(|ui| { + ui.heading("Domain"); + }); + header.col(|ui| { + ui.heading("Type"); + }); + }) + .body(|mut body| { + let spk = almanac.spk_data[0].as_ref().unwrap(); + + for (sno, summary) in spk.data_summaries().unwrap().iter().enumerate() { + let name_rcrd = spk.name_record().unwrap(); + let name = name_rcrd.nth_name(sno, spk.file_record().unwrap().summary_size()); + if summary.is_empty() { + continue; + } + + body.row(30.0, |mut row| { + row.col(|ui| { + ui.label(format!( + "{} ({})", + summary.target_frame(), + summary.target_id + )); + }); + row.col(|ui| { + ui.label(name); + }); + + row.col(|ui| { + if show_unix { + ui.text_edit_singleline(&mut format!( + "{}", + summary.start_epoch().to_unix_seconds() + )); + } else { + ui.label(summary.start_epoch().to_gregorian_str(selected_time_scale)); + }; + }); + + row.col(|ui| { + if show_unix { + ui.text_edit_singleline(&mut format!( + "{}", + summary.end_epoch().to_unix_seconds() + )); + } else { + ui.label(summary.end_epoch().to_gregorian_str(selected_time_scale)); + }; + }); + + row.col(|ui| { + ui.label(format!( + "{} ({})", + summary.center_frame(), + summary.center_id + )); + }); + + row.col(|ui| { + ui.label(format!( + "{}", + (summary.end_epoch() - summary.start_epoch()).round(Unit::Second * 1) + )); + }); + + row.col(|ui| { + ui.label(format!("{}", summary.data_type().unwrap())); + }); + }); + } + }); +} diff --git a/anise-gui/src/ui.rs b/anise-gui/src/ui.rs index 9d34e33d..5c8b4aef 100644 --- a/anise-gui/src/ui.rs +++ b/anise-gui/src/ui.rs @@ -1,7 +1,4 @@ -use anise::{ - almanac::Almanac, constants::orientations::orientation_name_from_id, errors::AlmanacError, - naif::daf::NAIFSummaryRecord, -}; +use anise::{almanac::Almanac, errors::AlmanacError}; use eframe::egui; use egui::Theme; use egui_extras::{Column, TableBuilder}; @@ -11,14 +8,16 @@ use log::error; #[cfg(target_arch = "wasm32")] use poll_promise::Promise; +use crate::{bpc::bpc_ui, spk::spk_ui}; + #[cfg(target_arch = "wasm32")] type AlmanacFile = Option<(String, Vec)>; pub struct UiApp { - selected_time_scale: TimeScale, - show_unix: bool, - almanac: Almanac, - path: Option, + pub selected_time_scale: TimeScale, + pub show_unix: bool, + pub almanac: Almanac, + pub path: Option, #[cfg(target_arch = "wasm32")] promise: Option>, } @@ -188,10 +187,19 @@ impl eframe::App for UiApp { ui.horizontal(|ui| { ui.label("File type"); ui.label(label); - }); - ui.horizontal(|ui| { + ui.label("CRC32"); ui.text_edit_singleline(&mut format!("{crc}")); + + if label.ends_with("SPK") { + let num_summaries = self.almanac.spk_domains().unwrap().len(); + ui.label("Number of summaries"); + ui.label(format!("{num_summaries}")); + } else if label.ends_with("BPC") { + let num_summaries = self.almanac.bpc_domains().unwrap().len(); + ui.label("Number of summaries"); + ui.label(format!("{num_summaries}")); + } }); if label.starts_with("DAF/") { @@ -227,285 +235,9 @@ impl eframe::App for UiApp { // Now display the data if label == "DAF/PCK" { - // We can use the summary - TableBuilder::new(ui) - .column(Column::auto().at_least(125.0).resizable(true)) - .column(Column::auto().at_least(225.0).resizable(true)) - .column(Column::auto().at_least(225.0).resizable(true)) - .column(Column::auto().at_least(50.0).resizable(true)) - .column(Column::auto().at_least(200.0).resizable(true)) - .column(Column::auto().at_least(150.0).resizable(true)) - .column(Column::remainder()) - .header(20.0, |mut header| { - header.col(|ui| { - ui.heading("Segment name"); - }); - header.col(|ui| { - ui.heading("Start epoch"); - }); - header.col(|ui| { - ui.heading("End epoch"); - }); - header.col(|ui| { - ui.heading("Frame"); - }); - header.col(|ui| { - ui.heading("Inertial frame"); - }); - header.col(|ui| { - ui.heading("Validity"); - }); - header.col(|ui| { - ui.heading("Kind"); - }); - }) - .body(|mut body| { - let pck = - self.almanac.bpc_data[0].as_ref().unwrap(); - - for (sno, summary) in - pck.data_summaries().unwrap().iter().enumerate() - { - let name_rcrd = pck.name_record().unwrap(); - let name = name_rcrd.nth_name( - sno, - pck.file_record().unwrap().summary_size(), - ); - if summary.is_empty() { - continue; - } - - body.row(30.0, |mut row| { - row.col(|ui| { - ui.label(name); - }); - - row.col(|ui| { - if self.show_unix { - ui.text_edit_singleline( - &mut format!( - "{}", - summary - .start_epoch() - .to_unix_seconds() - ), - ); - } else { - ui.label( - summary - .start_epoch() - .to_gregorian_str( - self.selected_time_scale, - ), - ); - }; - }); - - row.col(|ui| { - if self.show_unix { - ui.text_edit_singleline( - &mut format!( - "{}", - summary - .end_epoch() - .to_unix_seconds() - ), - ); - } else { - ui.label( - summary - .end_epoch() - .to_gregorian_str( - self.selected_time_scale, - ), - ); - }; - }); - - row.col( - |ui| match orientation_name_from_id( - summary.frame_id, - ) { - Some(name) => { - ui.label(format!( - "{name} ({})", - summary.frame_id - )); - } - None => { - ui.label(format!( - "{}", - summary.frame_id - )); - } - }, - ); - - row.col( - |ui| match orientation_name_from_id( - summary.inertial_frame_id, - ) { - Some(name) => { - ui.label(format!( - "{name} ({})", - summary.inertial_frame_id - )); - } - None => { - ui.label(format!( - "{}", - summary.inertial_frame_id - )); - } - }, - ); - - row.col(|ui| { - ui.label(format!( - "{}", - summary.end_epoch() - - summary.start_epoch() - )); - }); - - row.col(|ui| { - ui.label(format!( - "{}", - summary.data_type().unwrap() - )); - }); - }); - } - }); + bpc_ui(ui, &self.almanac, self.show_unix, self.selected_time_scale); } else if label == "DAF/SPK" { - TableBuilder::new(ui) - .column(Column::auto().at_least(125.0).resizable(true)) - .column(Column::auto().at_least(225.0).resizable(true)) - .column(Column::auto().at_least(225.0).resizable(true)) - .column(Column::auto().at_least(150.0).resizable(true)) - .column(Column::auto().at_least(200.0).resizable(true)) - .column(Column::auto().at_least(150.0).resizable(true)) - .column(Column::remainder()) - .header(20.0, |mut header| { - header.col(|ui| { - ui.heading("Segment name"); - }); - header.col(|ui| { - ui.heading("Start epoch"); - }); - header.col(|ui| { - ui.heading("End epoch"); - }); - - header.col(|ui| { - ui.heading("Target"); - }); - header.col(|ui| { - ui.heading("Center"); - }); - header.col(|ui| { - ui.heading("Validity"); - }); - header.col(|ui| { - ui.heading("Kind"); - }); - }) - .body(|mut body| { - let spk = - self.almanac.spk_data[0].as_ref().unwrap(); - - for (sno, summary) in - spk.data_summaries().unwrap().iter().enumerate() - { - let name_rcrd = spk.name_record().unwrap(); - let name = name_rcrd.nth_name( - sno, - spk.file_record().unwrap().summary_size(), - ); - if summary.is_empty() { - continue; - } - - body.row(30.0, |mut row| { - row.col(|ui| { - ui.label(name); - }); - - row.col(|ui| { - if self.show_unix { - ui.text_edit_singleline( - &mut format!( - "{}", - summary - .start_epoch() - .to_unix_seconds() - ), - ); - } else { - ui.label( - summary - .start_epoch() - .to_gregorian_str( - self.selected_time_scale, - ), - ); - }; - }); - - row.col(|ui| { - if self.show_unix { - ui.text_edit_singleline( - &mut format!( - "{}", - summary - .end_epoch() - .to_unix_seconds() - ), - ); - } else { - ui.label( - summary - .end_epoch() - .to_gregorian_str( - self.selected_time_scale, - ), - ); - }; - }); - - row.col(|ui| { - ui.label(format!( - "{} ({})", - summary.target_frame(), - summary.target_id - )); - }); - - row.col(|ui| { - ui.label(format!( - "{} ({})", - summary.center_frame(), - summary.center_id - )); - }); - - row.col(|ui| { - ui.label(format!( - "{}", - summary.end_epoch() - - summary.start_epoch() - )); - }); - - row.col(|ui| { - ui.label(format!( - "{}", - summary.data_type().unwrap() - )); - }); - }); - } - }); + spk_ui(ui, &self.almanac, self.show_unix, self.selected_time_scale) } else if label == "ANISE/PCA" { TableBuilder::new(ui) .column(Column::auto().at_least(100.0).resizable(true)) From 3528c9dfec0e709d0c7eaeead56fab443f709d9a Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Fri, 11 Oct 2024 08:55:46 -0600 Subject: [PATCH 4/5] Fix num summaries --- anise-gui/src/ui.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/anise-gui/src/ui.rs b/anise-gui/src/ui.rs index 5c8b4aef..2a0dfbe9 100644 --- a/anise-gui/src/ui.rs +++ b/anise-gui/src/ui.rs @@ -192,11 +192,11 @@ impl eframe::App for UiApp { ui.text_edit_singleline(&mut format!("{crc}")); if label.ends_with("SPK") { - let num_summaries = self.almanac.spk_domains().unwrap().len(); + let num_summaries = self.almanac.spk_data[0].as_ref().unwrap().data_summaries().unwrap().len(); ui.label("Number of summaries"); ui.label(format!("{num_summaries}")); - } else if label.ends_with("BPC") { - let num_summaries = self.almanac.bpc_domains().unwrap().len(); + } else if label.ends_with("PCK") { + let num_summaries = self.almanac.bpc_data[0].as_ref().unwrap().data_summaries().unwrap().len(); ui.label("Number of summaries"); ui.label(format!("{num_summaries}")); } From 232b0b1509afea2e3095bfed6539ec4192b6857b Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Fri, 11 Oct 2024 09:09:04 -0600 Subject: [PATCH 5/5] Add windows GUI icon --- anise-gui/Cargo.toml | 14 ++++++++++++++ anise-gui/build.rs | 9 +++++++++ anise-gui/icon.ico | Bin 0 -> 363526 bytes 3 files changed, 23 insertions(+) create mode 100644 anise-gui/build.rs create mode 100644 anise-gui/icon.ico diff --git a/anise-gui/Cargo.toml b/anise-gui/Cargo.toml index cec603aa..75ffbdc5 100644 --- a/anise-gui/Cargo.toml +++ b/anise-gui/Cargo.toml @@ -7,6 +7,7 @@ homepage = { workspace = true } license = { workspace = true } repository = { workspace = true } description = "A graphical user interface for ANISE" +build = "build.rs" [dependencies] anise = { workspace = true } @@ -23,3 +24,16 @@ egui_logger = "0.6.1" [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen-futures = "0.4" poll-promise = { version = "0.3.0", features = ["web"] } + + +[target.'cfg(windows)'.build-dependencies] +tauri-winres = "0.1" + +[package.metadata.tauri-winres] +FileDescription = "Inspect SPICE SPK and PCK binary files" +FileVersion = "0.4" +InternalName = "ANISE-GUI.EXE" +OriginalFilename = "ANISE-GUI.EXE" +ProductName = "ANISE" +ProductVersion = "0.4" +LegalCopyright = "Copyright (C) 2021-onward Christopher Rabotin" diff --git a/anise-gui/build.rs b/anise-gui/build.rs new file mode 100644 index 00000000..146f9f92 --- /dev/null +++ b/anise-gui/build.rs @@ -0,0 +1,9 @@ +#[cfg(windows)] +fn main() { + let mut res = tauri_winres::WindowsResource::new(); + res.set_icon("icon.ico"); + res.compile().unwrap(); +} + +#[cfg(not(windows))] +fn main() {} diff --git a/anise-gui/icon.ico b/anise-gui/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c300dc7f47194858374a071744c810fa11b9b2db GIT binary patch literal 363526 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 Rw02$(Y&~C4Ij39g{|DP-@nHY} literal 0 HcmV?d00001