diff --git a/Cargo.lock b/Cargo.lock index 31ac5a0a..cf237db0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2509,6 +2509,16 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "duplicate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", +] + [[package]] name = "ecolor" version = "0.26.2" @@ -2540,6 +2550,17 @@ dependencies = [ "egui", ] +[[package]] +name = "egui_dock" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a062ac200c9f3ddf120ffcc5582f9fbd5d8fbd046d2eed215ed5426f56513d0" +dependencies = [ + "duplicate", + "egui", + "paste", +] + [[package]] name = "egui_extras" version = "0.26.2" @@ -2576,6 +2597,19 @@ dependencies = [ "egui", ] +[[package]] +name = "egui_tiles" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb10cef7bdbd1adb158aec9cca20f34779fd40ea126e02662ab558189f1c435" +dependencies = [ + "ahash 0.8.11", + "egui", + "itertools", + "log", + "serde", +] + [[package]] name = "ehttp" version = "0.4.0" @@ -3048,9 +3082,11 @@ dependencies = [ "dark-light", "derive_more", "directories", + "egui_dock", "egui_extras", "egui_graphs", "egui_plot", + "egui_tiles", "embed-resource 2.4.2", "gbp_linalg", "gbp_multivariate_normal", @@ -4557,6 +4593,30 @@ dependencies = [ "toml_edit 0.21.1", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.79" diff --git a/Cargo.toml b/Cargo.toml index cf285903..75ef1763 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ cargo-features = [ [workspace] resolver = "2" # Important! wgpu/bevy needs this! -members = [ "crates/angle", +members = [ + "crates/angle", "crates/min_len_vec", "crates/percentage", "crates/unit_interval", @@ -13,7 +14,11 @@ members = [ "crates/angle", # "crates/bevy_undo_redo", "crates/gbpplanner-rs", "crates/bevy_notify", - "crates/units", "crates/repeating_array", + "crates/units", + "crates/repeating_array", + "crates/units", + "crates/units", + "crates/bevy_fullscreen", ] [workspace.package] diff --git a/crates/bevy_notify/src/lib.rs b/crates/bevy_notify/src/lib.rs index d23cebfd..63f1e380 100644 --- a/crates/bevy_notify/src/lib.rs +++ b/crates/bevy_notify/src/lib.rs @@ -45,6 +45,7 @@ pub mod prelude { /// Adds event `ToastEvent` to be used in systems. /// Uses a `Update` system to render the toasts on the screen at the specified /// anchor +#[derive(Debug)] pub struct NotifyPlugin { /// Anchor for where to render the toasts in the main window /// Defaults to `Anchor::TopCenter` @@ -108,7 +109,7 @@ impl Toasts { } /// Event for creating a toast -#[derive(Event)] +#[derive(Debug, Event)] pub struct ToastEvent { /// The caption of the toast pub caption: String, diff --git a/crates/gbp_linalg/src/lib.rs b/crates/gbp_linalg/src/lib.rs index 7039ce17..3292127c 100644 --- a/crates/gbp_linalg/src/lib.rs +++ b/crates/gbp_linalg/src/lib.rs @@ -5,6 +5,8 @@ pub mod pretty_print; /// `use gbp_linalg::prelude::*` to import all the common symbols from this /// crate pub mod prelude { + // pub use ndarray::{array, concatenate, s, Axis}; + pub use super::{ pretty_print::*, Float, GbpFloat, Matrix, MatrixView, NdarrayVectorExt, Vector, VectorNorm, VectorView, @@ -41,12 +43,16 @@ pub type VectorView<'a, T> = ndarray::ArrayView1<'a, T>; /// Type alias for `ndarray::ArrayView2` pub type MatrixView<'a, T> = ndarray::ArrayView2<'a, T>; -/// +/// Trait for vector norms pub trait VectorNorm { + /// The scalar type of the vector type Scalar: GbpFloat; + /// Calculate the Euclidean norm of the vector fn euclidean_norm(&self) -> Self::Scalar; + /// Calculate the L1 norm of the vector fn l1_norm(&self) -> Self::Scalar; + /// Calculate the L2 norm of the vector #[inline(always)] fn l2_norm(&self) -> Self::Scalar { self.euclidean_norm() @@ -73,8 +79,11 @@ macro_rules! vector_norm_trait_impl { vector_norm_trait_impl!(f32); vector_norm_trait_impl!(f64); +/// Extension trait for `ndarray::Array1` pub trait NdarrayVectorExt: Clone + VectorNorm { + /// The scalar type of the vector type Scalar: GbpFloat; + /// Normalize the vector in place. fn normalize(&mut self); /// Return a normalized copy of the vector. fn normalized(&self) -> Self { diff --git a/crates/gbp_linalg/src/pretty_print.rs b/crates/gbp_linalg/src/pretty_print.rs index f92bda21..23178868 100644 --- a/crates/gbp_linalg/src/pretty_print.rs +++ b/crates/gbp_linalg/src/pretty_print.rs @@ -63,6 +63,7 @@ pub fn num_of_integral_digits(mut f: f64) -> Option { Some(count) } +/// Map a floating point number to a ansi color string. fn float_color(f: f64) -> &'static str { if f.is_nan() { MAGENTA_TEXT @@ -190,6 +191,8 @@ pub fn _pretty_print_matrix( ); } +/// Internal function to pretty print a vector +/// Use by the [`pretty_print_vector!`] macro pub fn _pretty_print_vector( vector: &V, name: Option<&str>, @@ -302,6 +305,7 @@ pub fn _pretty_print_vector( ); } +/// Extension trait that adds a [`pretty_print`] method to vectors pub trait PrettyPrintVector: Sized { /// Returns the length of the vector. #[allow(clippy::len_without_is_empty)] @@ -309,6 +313,7 @@ pub trait PrettyPrintVector: Sized { /// Returns the element at index `i`. fn at(&self, i: usize) -> T; + /// Pretty prints the vector. #[inline(always)] fn pretty_print(&self) { _pretty_print_vector(self, None, None, None); @@ -326,12 +331,14 @@ impl PrettyPrintVector for Vector { self[i] } } +/// Extension trait that adds a [`pretty_print`] method to matrices pub trait PrettyPrintMatrix: Sized { /// Returns the shape of the matrix as a tuple `(nrows, ncols)`. fn shape(&self) -> (usize, usize); /// Returns the element at index `(i, j)`. fn at(&self, i: usize, j: usize) -> T; + /// Pretty prints the matrix. #[inline(always)] fn pretty_print(&self) { _pretty_print_matrix(self, None, None, None); @@ -350,6 +357,7 @@ impl PrettyPrintMatrix for Matrix { } } +/// Pretty prints a vector #[macro_export] macro_rules! pretty_print_vector { ($name:expr) => { @@ -370,6 +378,7 @@ macro_rules! pretty_print_vector { }; } +/// Pretty prints a matrix #[macro_export] macro_rules! pretty_print_matrix { ($name:expr) => { diff --git a/crates/gbp_multivariate_normal/src/lib.rs b/crates/gbp_multivariate_normal/src/lib.rs index 3ffec224..28985c07 100644 --- a/crates/gbp_multivariate_normal/src/lib.rs +++ b/crates/gbp_multivariate_normal/src/lib.rs @@ -1,30 +1,38 @@ -// #![warn(missing_docs)] +//! Multivariate normal distribution type use gbp_linalg::{Float, Matrix, Vector}; use ndarray_inverse::Inverse; +/// Error type use by this module #[derive(Debug, thiserror::Error)] pub enum MultivariateNormalError { + /// The precision matrix is not square #[error("the precision matrix is not square, it has shape {0}x{1}")] NonSquarePrecisionMatrix(usize, usize), #[error( "the length of the information vector ({0}) is not equal to the number of rows ({1}) or \ columns ({2}) of the precision matrix" )] + /// The length of the information vector is not equal to the number of rows + /// or columns of the precision matrix VectorLengthNotEqualMatrixShape(usize, usize, usize), #[error( "the covariance matrix is not invertible, which is required to calculate the precision \ matrix" )] + /// The covariance matrix is not invertible NonInvertibleCovarianceMatrix, #[error( "the precision matrix is not invertible, which is required to calculate the covariance \ matrix" )] + /// The precision matrix is not invertible NonInvertiblePrecisionMatrix, } +/// Result type used by this module pub type Result = std::result::Result; +/// Multivariate normal distribution stored in information form #[allow(clippy::len_without_is_empty)] #[derive(Debug, Clone)] pub struct MultivariateNormal { @@ -145,11 +153,15 @@ impl MultivariateNormal { &self.precision } + /// Set the information vector of the multivariate normal distribution + /// Updates the cached mean internally. pub fn update_information_vector(&mut self, value: &Vector) { self.information.clone_from(value); self.update(); } + /// Set the precision matrix of the multivariate normal distribution + /// Updates the cached mean internally. pub fn update_precision_matrix(&mut self, value: &Matrix) -> Result<()> { // if value.det() == Float::zero() { if value.det() == 0.0 { diff --git a/crates/gbpplanner-rs/Cargo.toml b/crates/gbpplanner-rs/Cargo.toml index ceaa24cf..f03211cf 100644 --- a/crates/gbpplanner-rs/Cargo.toml +++ b/crates/gbpplanner-rs/Cargo.toml @@ -30,8 +30,11 @@ bevy = { version = "0.13.0", default-features = true, features = [ # "wayland", # "dynamic_linking", ] } + bevy-inspector-egui = "0.23.2" -bevy_egui = "0.26.0" +bevy_egui = "0.26.0" +egui_dock = "0.11.4" +egui_tiles = "0.7.2" # bevy_egui = { git = "https://github.com/mvlabat/bevy_egui", branch = "main" } # 0.26 does not support egui 0.27, which egui-notify needs @@ -104,6 +107,8 @@ serde_yaml = "0.9.34" colored = "2.1.0" directories = "5.0.1" chrono = "0.4.37" + + # font-kit = { version = "0.13.0", features = ["freetype"] } dark-light = "1.1.1" diff --git a/crates/gbpplanner-rs/src/bevy_utils.rs b/crates/gbpplanner-rs/src/bevy_utils.rs index 1eb21b01..d4bb95e1 100644 --- a/crates/gbpplanner-rs/src/bevy_utils.rs +++ b/crates/gbpplanner-rs/src/bevy_utils.rs @@ -58,3 +58,26 @@ impl BevyPluginExt for bevy::app::App { self } } + +pub mod run_conditions { + use bevy::{ + ecs::system::Res, + input::{keyboard::KeyCode, ButtonInput}, + }; + + // pub fn any_input_just_pressed( + // // inputs: impl IntoIterator>, + // // inputs: impl IntoIterator, + // // inputs: Vec, + // ) -> impl Fn(Res>) -> bool + // // where + // // T: Copy + Eq + Send + Sync + 'static, + // { + // move |keyboard_input: Res>| + // keyboard_input.any_pressed(inputs) + + // // move |keyboard_input: Res>| { + // // inputs.into_iter().any(|it| + // keyboard_input.just_pressed(it)) // } + // } +} diff --git a/crates/gbpplanner-rs/src/bin/console.rs b/crates/gbpplanner-rs/src/bin/console.rs index 84ca2503..64daba06 100644 --- a/crates/gbpplanner-rs/src/bin/console.rs +++ b/crates/gbpplanner-rs/src/bin/console.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use bevy::{log::LogPlugin, prelude::*}; use bevy_dev_console::prelude::*; diff --git a/crates/gbpplanner-rs/src/bin/derive_more.rs b/crates/gbpplanner-rs/src/bin/derive_more.rs deleted file mode 100644 index 0453bf25..00000000 --- a/crates/gbpplanner-rs/src/bin/derive_more.rs +++ /dev/null @@ -1,18 +0,0 @@ -#[derive(derive_more::Display)] -#[display(fmt = "hello")] -struct Foo; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, derive_more::From)] -struct VariableIndex(usize); - -fn main() -> Result<(), Box> { - let foo = Foo; - - println!("{}", foo); - - let vi: VariableIndex = 42usize.into(); - - println!("{:?}", vi); - - Ok(()) -} diff --git a/crates/gbpplanner-rs/src/bin/draw2d.rs b/crates/gbpplanner-rs/src/bin/draw2d.rs index 89cc067a..baf0578e 100644 --- a/crates/gbpplanner-rs/src/bin/draw2d.rs +++ b/crates/gbpplanner-rs/src/bin/draw2d.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use bevy::{prelude::*, window::WindowResized}; // use std::f32::consts::{PI, TAU}; diff --git a/crates/gbpplanner-rs/src/bin/gizmos.rs b/crates/gbpplanner-rs/src/bin/gizmos.rs index 2836a2e1..645e1fd4 100644 --- a/crates/gbpplanner-rs/src/bin/gizmos.rs +++ b/crates/gbpplanner-rs/src/bin/gizmos.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, unused_macros)] +#![allow(dead_code, unused_macros, missing_docs)] use bevy::{prelude::*, window::WindowResized}; // use std::f32::consts::{PI, TAU}; diff --git a/crates/gbpplanner-rs/src/bin/hover-ui.rs b/crates/gbpplanner-rs/src/bin/hover-ui.rs deleted file mode 100644 index 93b8b7d5..00000000 --- a/crates/gbpplanner-rs/src/bin/hover-ui.rs +++ /dev/null @@ -1,199 +0,0 @@ -#![allow(missing_docs)] - -use bevy::{prelude::*, window::PrimaryWindow}; - -fn main() -> Result<(), Box> { - let mut app = App::new(); - - app.add_plugins(DefaultPlugins) - .add_systems(Startup, setup) - .add_systems(Update, move_hover_window); - - app.run(); - Ok(()) -} - -// #[inline] -// fn px(logical_pixels: f32) -> Val { -// Val::Px(logical_pixels) -// } - -trait ValExt { - fn px(self) -> Val; - fn percent(self) -> Val; -} - -impl ValExt for f32 { - fn px(self) -> Val { - Val::Px(self) - } - - fn percent(self) -> Val { - if !(0.0..=100.0).contains(&self) { - panic!("value outside interval [0.0, 100.0]"); - } - Val::Percent(self) - } -} - -impl ValExt for i32 { - fn px(self) -> Val { - Val::Px(self as f32) - } - - fn percent(self) -> Val { - if !(0..=100).contains(&self) { - panic!("value outside interval [0, 100]"); - } - - Val::Percent(self as f32) - } -} - -fn relative_cursor_position_in_window(window: &Window) -> Option { - let cursor_position = window.cursor_position()?; - let relative_cursor_position = Vec2::new( - cursor_position.x / window.width(), - cursor_position.y / window.height(), - ); - - Some(relative_cursor_position) -} - -#[derive(Component)] -pub struct HoverWindow; - -fn setup(mut commands: Commands, query_window: Query<&Window, With>) { - let Ok(window) = query_window.get_single() else { - error!("no primary window"); - return; - }; - - let Some(relative_cursor_position) = relative_cursor_position_in_window(window) else { - warn!("cursor position is outside primary window"); - return; - }; - - commands.spawn(Camera2dBundle::default()); - - let root = commands - .spawn((HoverWindow, NodeBundle { - style: Style { - position_type: PositionType::Absolute, - top: 5.0.percent(), - // top: percent(5.0), - left: 5.0.percent(), - - width: 200.px(), - height: 400.px(), - ..default() - }, - background_color: Color::RED.into(), - border_color: Color::GREEN.into(), - ..default() - })) - .id(); - - // all the different combinations of border edges - // these correspond to the labels above - let borders = [ - UiRect::default(), - UiRect::all(Val::Px(10.)), - UiRect::left(Val::Px(10.)), - UiRect::right(Val::Px(10.)), - UiRect::top(Val::Px(10.)), - UiRect::bottom(Val::Px(10.)), - UiRect::horizontal(Val::Px(10.)), - UiRect::vertical(Val::Px(10.)), - UiRect { - left: Val::Px(10.), - top: Val::Px(10.), - ..Default::default() - }, - UiRect { - left: Val::Px(10.), - bottom: Val::Px(10.), - ..Default::default() - }, - UiRect { - right: Val::Px(10.), - top: Val::Px(10.), - ..Default::default() - }, - UiRect { - right: Val::Px(10.), - bottom: Val::Px(10.), - ..Default::default() - }, - UiRect { - right: Val::Px(10.), - top: Val::Px(10.), - bottom: Val::Px(10.), - ..Default::default() - }, - UiRect { - left: Val::Px(10.), - top: Val::Px(10.), - bottom: Val::Px(10.), - ..Default::default() - }, - UiRect { - left: Val::Px(10.), - right: Val::Px(10.), - top: Val::Px(10.), - ..Default::default() - }, - UiRect { - left: Val::Px(10.), - right: Val::Px(10.), - bottom: Val::Px(10.), - ..Default::default() - }, - ]; - - let title = commands - .spawn(TextBundle::from_section("variable", TextStyle { - font_size: 10.0, - ..Default::default() - })) - .id(); - - let container = commands - .spawn(NodeBundle { - style: Style { - flex_direction: FlexDirection::Column, - align_items: AlignItems::Center, - ..Default::default() - }, - ..Default::default() - }) - .push_children(&[title]) - .id(); - - commands.entity(root).add_child(container); -} - -fn primary_window_exists(query_window: Query<&Window, With>) -> bool { - query_window.get_single().is_ok() -} - -fn move_hover_window( - mut query_hover_window: Query<&mut Transform, With>, - query_window: Query<&Window, With>, -) { - let window = query_window.single(); - - let Some(relative_cursor_position) = relative_cursor_position_in_window(window) else { - warn!("cursor outside primary window"); - return; - }; - - let mut transform = query_hover_window.single_mut(); - - let x = relative_cursor_position.x * window.width(); - let y = relative_cursor_position.y * window.height(); - - info!("x = {}, y = {}", x, y); - - transform.translation = Vec3::new(x, 0.0, y); -} diff --git a/crates/gbpplanner-rs/src/bin/many-animated-sprites.rs b/crates/gbpplanner-rs/src/bin/many-animated-sprites.rs deleted file mode 100644 index 0b63fd12..00000000 --- a/crates/gbpplanner-rs/src/bin/many-animated-sprites.rs +++ /dev/null @@ -1,152 +0,0 @@ -//! Renders a lot of animated sprites to allow performance testing. -//! -//! This example sets up many animated sprites in different sizes, rotations, -//! and scales in the world. It also moves the camera over them to see how well -//! frustum culling works. - -use std::time::Duration; - -use bevy::{ - diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, - math::Quat, - prelude::*, - render::camera::Camera, - window::{PresentMode, WindowResolution}, - winit::{UpdateMode, WinitSettings}, -}; -use rand::Rng; - -const CAMERA_SPEED: f32 = 1000.0; - -fn main() { - App::new() - // Since this is also used as a benchmark, we want it to display performance data. - .add_plugins(( - LogDiagnosticsPlugin::default(), - FrameTimeDiagnosticsPlugin, - DefaultPlugins.set(WindowPlugin { - primary_window: Some(Window { - present_mode: PresentMode::AutoNoVsync, - resolution: WindowResolution::new(1920.0, 1080.0) - .with_scale_factor_override(1.0), - ..default() - }), - ..default() - }), - )) - .insert_resource(WinitSettings { - focused_mode: UpdateMode::Continuous, - unfocused_mode: UpdateMode::Continuous, - }) - .add_systems(Startup, setup) - .add_systems( - Update, - ( - animate_sprite, - print_sprite_count, - move_camera.after(print_sprite_count), - ), - ) - .run(); -} - -fn setup( - mut commands: Commands, - assets: Res, - mut texture_atlases: ResMut>, -) { - // warn!(include_str!("warning_string.txt")); - - let mut rng = rand::thread_rng(); - - let tile_size = Vec2::splat(64.0); - let map_size = Vec2::splat(320.0); - - let half_x = (map_size.x / 2.0) as i32; - let half_y = (map_size.y / 2.0) as i32; - - let texture_handle = assets.load("textures/rpg/chars/gabe/gabe-idle-run.png"); - let texture_atlas = TextureAtlasLayout::from_grid(Vec2::new(24.0, 24.0), 7, 1, None, None); - let texture_atlas_handle = texture_atlases.add(texture_atlas); - - // Spawns the camera - - commands.spawn(Camera2dBundle::default()); - - // Builds and spawns the sprites - for y in -half_y..half_y { - for x in -half_x..half_x { - let position = Vec2::new(x as f32, y as f32); - let translation = (position * tile_size).extend(rng.gen::()); - let rotation = Quat::from_rotation_z(rng.gen::()); - let scale = Vec3::splat(rng.gen::() * 2.0); - let mut timer = Timer::from_seconds(0.1, TimerMode::Repeating); - timer.set_elapsed(Duration::from_secs_f32(rng.gen::())); - - commands.spawn(( - SpriteSheetBundle { - texture: texture_handle.clone(), - atlas: TextureAtlas { - layout: texture_atlas_handle.clone(), - ..Default::default() - }, - transform: Transform { - translation, - rotation, - scale, - }, - sprite: Sprite { - custom_size: Some(tile_size), - ..default() - }, - ..default() - }, - AnimationTimer(timer), - )); - } - } -} - -// System for rotating and translating the camera -fn move_camera(time: Res