diff --git a/Cargo.lock b/Cargo.lock
index aaf435faea371ab915ebdf982af113c7a6863ca6..00cc84ab9ef8b5f99a1464dadae1933a5bb474d2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,26 +3,16 @@
 version = 3
 
 [[package]]
-name = "ab_glyph"
-version = "0.2.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0"
-dependencies = [
- "ab_glyph_rasterizer",
- "owned_ttf_parser",
-]
-
-[[package]]
-name = "ab_glyph_rasterizer"
-version = "0.1.8"
+name = "accesskit"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
+checksum = "6cf780eb737f2d4a49ffbd512324d53ad089070f813f7be7f99dbd5123a7f448"
 
 [[package]]
 name = "accesskit"
-version = "0.14.0"
+version = "0.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6cf780eb737f2d4a49ffbd512324d53ad089070f813f7be7f99dbd5123a7f448"
+checksum = "d3d3b8f9bae46a948369bc4a03e815d4ed6d616bd00de4051133a5019dc31c5a"
 
 [[package]]
 name = "accesskit_consumer"
@@ -30,7 +20,18 @@ version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3bdfa1638ddd6eb9c752def95568df8b3ad832df252e9156d2eb783b201ca8a9"
 dependencies = [
- "accesskit",
+ "accesskit 0.14.0",
+ "immutable-chunkmap",
+]
+
+[[package]]
+name = "accesskit_consumer"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f47983a1084940ba9a39c077a8c63e55c619388be5476ac04c804cfbd1e63459"
+dependencies = [
+ "accesskit 0.17.1",
+ "hashbrown 0.15.2",
  "immutable-chunkmap",
 ]
 
@@ -40,45 +41,81 @@ version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c236a84ff1111defc280cee755eaa953d0b24398786851b9d28322c6d3bb1ebd"
 dependencies = [
- "accesskit",
- "accesskit_consumer",
+ "accesskit 0.14.0",
+ "accesskit_consumer 0.22.0",
  "objc2",
  "objc2-app-kit",
  "objc2-foundation",
  "once_cell",
 ]
 
+[[package]]
+name = "accesskit_macos"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7329821f3bd1101e03a7d2e03bd339e3ac0dc64c70b4c9f9ae1949e3ba8dece1"
+dependencies = [
+ "accesskit 0.17.1",
+ "accesskit_consumer 0.26.0",
+ "hashbrown 0.15.2",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+]
+
 [[package]]
 name = "accesskit_windows"
 version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5d7f43d24b16b3e76bef248124fbfd2493c3a9860edb5aae1010c890e826de5e"
 dependencies = [
- "accesskit",
- "accesskit_consumer",
+ "accesskit 0.14.0",
+ "accesskit_consumer 0.22.0",
  "paste",
  "static_assertions",
  "windows 0.54.0",
 ]
 
+[[package]]
+name = "accesskit_windows"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24fcd5d23d70670992b823e735e859374d694a3d12bfd8dd32bd3bd8bedb5d81"
+dependencies = [
+ "accesskit 0.17.1",
+ "accesskit_consumer 0.26.0",
+ "hashbrown 0.15.2",
+ "paste",
+ "static_assertions",
+ "windows 0.58.0",
+ "windows-core 0.58.0",
+]
+
 [[package]]
 name = "accesskit_winit"
 version = "0.20.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "755535e6bf711a42dac28b888b884b10fc00ff4010d9d3bd871c5f5beae5aa78"
 dependencies = [
- "accesskit",
- "accesskit_macos",
- "accesskit_windows",
+ "accesskit 0.14.0",
+ "accesskit_macos 0.15.0",
+ "accesskit_windows 0.20.0",
  "raw-window-handle",
  "winit",
 ]
 
 [[package]]
-name = "adler"
-version = "1.0.2"
+name = "accesskit_winit"
+version = "0.23.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "6a6a48dad5530b6deb9fc7a52cc6c3bf72cdd9eb8157ac9d32d69f2427a5e879"
+dependencies = [
+ "accesskit 0.17.1",
+ "accesskit_macos 0.18.1",
+ "accesskit_windows 0.24.1",
+ "raw-window-handle",
+ "winit",
+]
 
 [[package]]
 name = "adler2"
@@ -99,6 +136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
 dependencies = [
  "cfg-if",
+ "const-random",
  "getrandom",
  "once_cell",
  "version_check",
@@ -122,9 +160,9 @@ checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
 
 [[package]]
 name = "allocator-api2"
-version = "0.2.18"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9"
 
 [[package]]
 name = "alsa"
@@ -204,9 +242,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
 
 [[package]]
 name = "anstream"
-version = "0.6.17"
+version = "0.6.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -219,9 +257,9 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.9"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
 
 [[package]]
 name = "anstyle-parse"
@@ -253,9 +291,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.91"
+version = "1.0.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
+checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
 
 [[package]]
 name = "approx"
@@ -268,9 +306,9 @@ dependencies = [
 
 [[package]]
 name = "arbitrary"
-version = "1.3.2"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
 
 [[package]]
 name = "arg_enum_proc_macro"
@@ -280,7 +318,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -310,12 +348,32 @@ dependencies = [
  "libloading 0.7.4",
 ]
 
+[[package]]
+name = "ash"
+version = "0.38.0+1.3.281"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
+dependencies = [
+ "libloading 0.8.5",
+]
+
 [[package]]
 name = "assert_matches"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
 
+[[package]]
+name = "assert_type_match"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+]
+
 [[package]]
 name = "async-broadcast"
 version = "0.5.1"
@@ -385,6 +443,12 @@ version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
 
+[[package]]
+name = "atomicow"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "467163b50876d3a4a44da5f4dbd417537e522fc059ede8d518d57941cfb3d745"
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -449,7 +513,16 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "043c9ad4b6fc4ca52d779873a8ca792a4e37842d07fce95363c9e17e36a1d8a0"
 dependencies = [
- "bevy_internal",
+ "bevy_internal 0.14.2",
+]
+
+[[package]]
+name = "bevy"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6a01cd51a5cd310e4e7aa6e1560b1aabf29efc6a095a01e6daa8bf0a19f1fea"
+dependencies = [
+ "bevy_internal 0.15.0",
 ]
 
 [[package]]
@@ -458,38 +531,53 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ae1a976cb539d6a5a3ff579cdb78187a6bcfbffa7e8224ea28f23d8b983d9389"
 dependencies = [
- "accesskit",
- "bevy_app",
- "bevy_derive",
- "bevy_ecs",
+ "accesskit 0.14.0",
+ "bevy_app 0.14.2",
+ "bevy_derive 0.14.2",
+ "bevy_ecs 0.14.2",
+]
+
+[[package]]
+name = "bevy_a11y"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82c66b5bc82a2660a5663d85b3354ddb72c8ab2c443989333cbea146f39a4e9a"
+dependencies = [
+ "accesskit 0.17.1",
+ "bevy_app 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_reflect 0.15.0",
 ]
 
 [[package]]
 name = "bevy_animation"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93aef7d21a0342c24b05059493aa31d58f1798d34a2236569a8789b74df5a475"
-dependencies = [
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_core",
- "bevy_derive",
- "bevy_ecs",
- "bevy_hierarchy",
- "bevy_log",
- "bevy_math",
- "bevy_reflect",
- "bevy_render",
- "bevy_time",
- "bevy_transform",
- "bevy_utils",
+checksum = "ee48f3fc65f583e5e320e38874053e20e7a71205a62aaace5d607446781bd742"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_core 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_log 0.15.0",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
+ "bevy_time 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
  "blake3",
- "fixedbitset 0.5.7",
+ "derive_more",
+ "downcast-rs",
+ "either",
  "petgraph",
  "ron",
  "serde",
- "thiserror",
+ "smallvec",
  "thread_local",
  "uuid",
 ]
@@ -500,11 +588,11 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a5361d0f8a8677a5d0102cfe7321a7ecd2a8b9a4f887ce0dde1059311cf9cd42"
 dependencies = [
- "bevy_derive",
- "bevy_ecs",
- "bevy_reflect",
- "bevy_tasks",
- "bevy_utils",
+ "bevy_derive 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_tasks 0.14.2",
+ "bevy_utils 0.14.2",
  "console_error_panic_hook",
  "downcast-rs",
  "thiserror",
@@ -512,6 +600,25 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "bevy_app"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "652574e4c10efcfa70f98036709dd5b67e5cb8d46c58087ef48c2ac6b62df9da"
+dependencies = [
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_tasks 0.15.0",
+ "bevy_utils 0.15.0",
+ "console_error_panic_hook",
+ "ctrlc",
+ "derive_more",
+ "downcast-rs",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 name = "bevy_asset"
 version = "0.14.2"
@@ -521,13 +628,13 @@ dependencies = [
  "async-broadcast",
  "async-fs",
  "async-lock",
- "bevy_app",
- "bevy_asset_macros",
- "bevy_ecs",
- "bevy_reflect",
- "bevy_tasks",
- "bevy_utils",
- "bevy_winit",
+ "bevy_app 0.14.2",
+ "bevy_asset_macros 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_tasks 0.14.2",
+ "bevy_utils 0.14.2",
+ "bevy_winit 0.14.2",
  "blake3",
  "crossbeam-channel",
  "downcast-rs",
@@ -544,33 +651,82 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "bevy_asset"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d7d501eda01be6d500d843a06d9b9800c3f0fffaae3c29d17d9e4e172c28d37"
+dependencies = [
+ "async-broadcast",
+ "async-fs",
+ "async-lock",
+ "atomicow",
+ "bevy_app 0.15.0",
+ "bevy_asset_macros 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_tasks 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
+ "bitflags 2.6.0",
+ "blake3",
+ "crossbeam-channel",
+ "derive_more",
+ "disqualified",
+ "downcast-rs",
+ "either",
+ "futures-io",
+ "futures-lite",
+ "js-sys",
+ "parking_lot",
+ "ron",
+ "serde",
+ "stackfuture",
+ "uuid",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
 [[package]]
 name = "bevy_asset_macros"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c9eb05ce838d282f09d83380b4d6432aec7519d421dee8c75cc20e6148237e6e"
 dependencies = [
- "bevy_macro_utils",
+ "bevy_macro_utils 0.14.2",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "bevy_asset_macros"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7474b77fc27db11ec03d49ca04f1a7471f369dc373fd5e091a12ad7ab533d8c8"
+dependencies = [
+ "bevy_macro_utils 0.15.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
 ]
 
 [[package]]
 name = "bevy_audio"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ee31312a0e67f288fe12a1d9aa679dd0ba8a49e1e6fe5fcd2ba1aa1ea34e5ed"
-dependencies = [
- "bevy_app",
- "bevy_asset",
- "bevy_derive",
- "bevy_ecs",
- "bevy_hierarchy",
- "bevy_math",
- "bevy_reflect",
- "bevy_transform",
- "bevy_utils",
+checksum = "20e378c4005d9c47b7ebaf637a6a197e3953463615516ab709ba8b0c3c215c2e"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
  "cpal",
  "rodio",
 ]
@@ -581,13 +737,28 @@ version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "04842e9d38a93f0c75ab46f7f404ea24ef57ad83dbd159e5b4b35318b02257bb"
 dependencies = [
- "bevy_math",
- "bevy_reflect",
+ "bevy_math 0.14.2",
+ "bevy_reflect 0.14.2",
  "bytemuck",
- "encase",
+ "encase 0.8.0",
  "serde",
  "thiserror",
- "wgpu-types",
+ "wgpu-types 0.20.0",
+]
+
+[[package]]
+name = "bevy_color"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87bccacba27db37375eb97ffc86e91a7d95db3f5faa6a834fa7306db02cde327"
+dependencies = [
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bytemuck",
+ "derive_more",
+ "encase 0.10.0",
+ "serde",
+ "wgpu-types 23.0.0",
 ]
 
 [[package]]
@@ -596,11 +767,25 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "de706862871a1fe99ea619bff2f99d73e43ad82f19ef866a9e19a14c957c8537"
 dependencies = [
- "bevy_app",
- "bevy_ecs",
- "bevy_reflect",
- "bevy_tasks",
- "bevy_utils",
+ "bevy_app 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_tasks 0.14.2",
+ "bevy_utils 0.14.2",
+ "uuid",
+]
+
+[[package]]
+name = "bevy_core"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecccf7be33330f58d4c7033b212a25c414d388e3a8d55b61331346da5dbabf22"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_tasks 0.15.0",
+ "bevy_utils 0.15.0",
  "uuid",
 ]
 
@@ -610,17 +795,17 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2f6e1e122ada4cd811442e083fb5ad3e325c59a87271d5ef57193f1c2cad7f8c"
 dependencies = [
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_core",
- "bevy_derive",
- "bevy_ecs",
- "bevy_math",
- "bevy_reflect",
- "bevy_render",
- "bevy_transform",
- "bevy_utils",
+ "bevy_app 0.14.2",
+ "bevy_asset 0.14.2",
+ "bevy_color 0.14.3",
+ "bevy_core 0.14.2",
+ "bevy_derive 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_math 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_render 0.14.2",
+ "bevy_transform 0.14.2",
+ "bevy_utils 0.14.2",
  "bitflags 2.6.0",
  "nonmax",
  "radsort",
@@ -629,15 +814,53 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "bevy_core_pipeline"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a3fb9f84fa60c2006d4a15e039c3d08d4d10599441b9175907341a77a69d627"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_core 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_image",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
+ "bitflags 2.6.0",
+ "derive_more",
+ "nonmax",
+ "radsort",
+ "serde",
+ "smallvec",
+]
+
 [[package]]
 name = "bevy_derive"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fbfc33a4c6b80760bb8bf850a2cc65a1e031da62fd3ca8b552189104dc98514"
 dependencies = [
- "bevy_macro_utils",
+ "bevy_macro_utils 0.14.2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "bevy_derive"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e141b7eda52a23bb88740b37a291e26394524cb9ee3b034c7014669671fc2bb5"
+dependencies = [
+ "bevy_macro_utils 0.15.0",
+ "quote",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -646,12 +869,27 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bebb154e0cc78e3bbfbfdb42fb502b14c1cd47e72f16e6d4228dfe6233ba6cbd"
 dependencies = [
- "bevy_app",
- "bevy_core",
- "bevy_ecs",
- "bevy_tasks",
- "bevy_time",
- "bevy_utils",
+ "bevy_app 0.14.2",
+ "bevy_core 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_tasks 0.14.2",
+ "bevy_time 0.14.2",
+ "bevy_utils 0.14.2",
+ "const-fnv1a-hash",
+]
+
+[[package]]
+name = "bevy_diagnostic"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa97748337405089edfb2857f7608f21bcc648a7ad272c9209808aad252ed542"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_core 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_tasks 0.15.0",
+ "bevy_time 0.15.0",
+ "bevy_utils 0.15.0",
  "const-fnv1a-hash",
  "sysinfo",
 ]
@@ -662,12 +900,11 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ee4222406637f3c8e3991a99788cfcde76097bf997c311f1b6297364057483f"
 dependencies = [
- "arrayvec",
- "bevy_ecs_macros",
- "bevy_ptr",
- "bevy_reflect",
- "bevy_tasks",
- "bevy_utils",
+ "bevy_ecs_macros 0.14.2",
+ "bevy_ptr 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_tasks 0.14.2",
+ "bevy_utils 0.14.2",
  "bitflags 2.6.0",
  "concurrent-queue",
  "fixedbitset 0.5.7",
@@ -677,16 +914,51 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "bevy_ecs"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4c4b60d2a712c6d5cbe610bac7ecf0838fc56a095fd5b15f30230873e84f15"
+dependencies = [
+ "arrayvec",
+ "bevy_ecs_macros 0.15.0",
+ "bevy_ptr 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_tasks 0.15.0",
+ "bevy_utils 0.15.0",
+ "bitflags 2.6.0",
+ "concurrent-queue",
+ "derive_more",
+ "disqualified",
+ "fixedbitset 0.5.7",
+ "nonmax",
+ "petgraph",
+ "serde",
+ "smallvec",
+]
+
 [[package]]
 name = "bevy_ecs_macros"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "36b573430b67aff7bde8292257494f39343401379bfbda64035ba4918bba7b20"
 dependencies = [
- "bevy_macro_utils",
+ "bevy_macro_utils 0.14.2",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "bevy_ecs_macros"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4296b3254b8bd29769f6a4512731b2e6c4b163343ca18b72316927315b6096"
+dependencies = [
+ "bevy_macro_utils 0.15.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -695,8 +967,18 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d06c9693847a2a6ea61d6b86288dd4d8b6a79f05d4bf6e27b96d4f5c8d552fe4"
 dependencies = [
- "bevy_macro_utils",
- "encase_derive_impl",
+ "bevy_macro_utils 0.14.2",
+ "encase_derive_impl 0.8.0",
+]
+
+[[package]]
+name = "bevy_encase_derive"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfe562b883fb652acde84cb6bb01cbc9f23c377e411f1484467ecfdd3a3d234e"
+dependencies = [
+ "bevy_macro_utils 0.15.0",
+ "encase_derive_impl 0.10.0",
 ]
 
 [[package]]
@@ -705,88 +987,90 @@ version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "833cedc8bce1f2ad204e9cce9c0b11d6ecaf1c2cc9cf6935adb87bd08df0f376"
 dependencies = [
- "bevy",
+ "bevy 0.14.2",
 ]
 
 [[package]]
 name = "bevy_gilrs"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0422ccb3ce0f79b264100cf064fdc5ef65cef5c7d51bf6378058f9b96fea4183"
+checksum = "adc3a5f9e872133d7f5c2fab82e17781c19ed0b98f371362a23ed972bb538d20"
 dependencies = [
- "bevy_app",
- "bevy_ecs",
- "bevy_input",
- "bevy_time",
- "bevy_utils",
+ "bevy_app 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_input 0.15.0",
+ "bevy_time 0.15.0",
+ "bevy_utils 0.15.0",
+ "derive_more",
  "gilrs",
- "thiserror",
 ]
 
 [[package]]
 name = "bevy_gizmos"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfe32af0666d8d8a7fd6eb6b5e41eceefdc6f2e5441c74b812e8f0902a9d7f52"
+checksum = "e1c82341f6a3517efeeeef2fe68135ac3a91b11b6e369fc1a07f6e9a4b462b57"
 dependencies = [
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_core_pipeline",
- "bevy_ecs",
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_core_pipeline 0.15.0",
+ "bevy_ecs 0.15.0",
  "bevy_gizmos_macros",
- "bevy_math",
+ "bevy_image",
+ "bevy_math 0.15.0",
  "bevy_pbr",
- "bevy_reflect",
- "bevy_render",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
  "bevy_sprite",
- "bevy_time",
- "bevy_transform",
- "bevy_utils",
+ "bevy_time 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
  "bytemuck",
 ]
 
 [[package]]
 name = "bevy_gizmos_macros"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "906b052f8cf3f3983f0f6df625fb10cbd9b27d44e362a327dc1ed51300d362bc"
+checksum = "9454ac9f0a2141900ef9f3482af9333e490d5546bbea3cab63a777447d35beed"
 dependencies = [
- "bevy_macro_utils",
+ "bevy_macro_utils 0.15.0",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
 name = "bevy_gltf"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6adbd325b90e3c700d0966b5404e226c7deec1b8bda8f36832788d7b435b9b8"
+checksum = "b21ed694796a001a5cf63de9ddc62fc017302b0e2998a361ef1126880ec93555"
 dependencies = [
  "base64 0.22.1",
  "bevy_animation",
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_core",
- "bevy_core_pipeline",
- "bevy_ecs",
- "bevy_hierarchy",
- "bevy_math",
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_core 0.15.0",
+ "bevy_core_pipeline 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_image",
+ "bevy_math 0.15.0",
  "bevy_pbr",
- "bevy_reflect",
- "bevy_render",
- "bevy_scene",
- "bevy_tasks",
- "bevy_transform",
- "bevy_utils",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
+ "bevy_scene 0.15.0",
+ "bevy_tasks 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "derive_more",
  "gltf",
  "percent-encoding",
  "serde",
  "serde_json",
  "smallvec",
- "thiserror",
 ]
 
 [[package]]
@@ -795,68 +1079,153 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a88b912b37e1bc4dbb2aa40723199f74c8b06c4fbb6da0bb4585131df28ef66e"
 dependencies = [
- "bevy_app",
- "bevy_core",
- "bevy_ecs",
- "bevy_reflect",
- "bevy_utils",
+ "bevy_app 0.14.2",
+ "bevy_core 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_utils 0.14.2",
+ "smallvec",
+]
+
+[[package]]
+name = "bevy_hierarchy"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fe0b538beea7edbf30a6062242b99e67ff3bfa716566aacf91d5b5e027f02a2"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_core 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_utils 0.15.0",
+ "disqualified",
  "smallvec",
 ]
 
+[[package]]
+name = "bevy_image"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db46fa6a2f9e20435f3231710abbb136d2cc0a376f3f8e6ecfe071e286f5a246"
+dependencies = [
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_utils 0.15.0",
+ "bitflags 2.6.0",
+ "bytemuck",
+ "derive_more",
+ "futures-lite",
+ "image 0.25.5",
+ "ktx2",
+ "ruzstd",
+ "serde",
+ "wgpu 23.0.1",
+]
+
 [[package]]
 name = "bevy_input"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8dd3a54e67cc3ba17971de7b1a7e64eda84493c1e7bb6bfa11c6cf8ac124377b"
 dependencies = [
- "bevy_app",
- "bevy_ecs",
- "bevy_math",
- "bevy_reflect",
- "bevy_utils",
+ "bevy_app 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_math 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_utils 0.14.2",
  "smol_str",
  "thiserror",
 ]
 
+[[package]]
+name = "bevy_input"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46b4ea60095d1a1851e40cb12481ad3d5d234e14376d6b73142a85586c266b74"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_core 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_utils 0.15.0",
+ "derive_more",
+ "smol_str",
+]
+
 [[package]]
 name = "bevy_internal"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "45d435cac77c568f3aef65f786a5fee0e53c81950c5258182dd2c1d6cd6c4fec"
 dependencies = [
- "bevy_a11y",
+ "bevy_a11y 0.14.2",
+ "bevy_app 0.14.2",
+ "bevy_asset 0.14.2",
+ "bevy_color 0.14.3",
+ "bevy_core 0.14.2",
+ "bevy_core_pipeline 0.14.2",
+ "bevy_derive 0.14.2",
+ "bevy_diagnostic 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_hierarchy 0.14.2",
+ "bevy_input 0.14.2",
+ "bevy_log 0.14.2",
+ "bevy_math 0.14.2",
+ "bevy_ptr 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_render 0.14.2",
+ "bevy_scene 0.14.2",
+ "bevy_tasks 0.14.2",
+ "bevy_time 0.14.2",
+ "bevy_transform 0.14.2",
+ "bevy_utils 0.14.2",
+ "bevy_window 0.14.2",
+]
+
+[[package]]
+name = "bevy_internal"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4237e6e9b03902321032f00f931f18a4a211093bd9a7cf81276a0228a2a4417"
+dependencies = [
+ "bevy_a11y 0.15.0",
  "bevy_animation",
- "bevy_app",
- "bevy_asset",
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
  "bevy_audio",
- "bevy_color",
- "bevy_core",
- "bevy_core_pipeline",
- "bevy_derive",
- "bevy_diagnostic",
- "bevy_ecs",
+ "bevy_color 0.15.1",
+ "bevy_core 0.15.0",
+ "bevy_core_pipeline 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_diagnostic 0.15.0",
+ "bevy_ecs 0.15.0",
  "bevy_gilrs",
  "bevy_gizmos",
  "bevy_gltf",
- "bevy_hierarchy",
- "bevy_input",
- "bevy_log",
- "bevy_math",
+ "bevy_hierarchy 0.15.0",
+ "bevy_image",
+ "bevy_input 0.15.0",
+ "bevy_log 0.15.0",
+ "bevy_math 0.15.0",
  "bevy_pbr",
- "bevy_ptr",
- "bevy_reflect",
- "bevy_render",
- "bevy_scene",
+ "bevy_picking",
+ "bevy_ptr 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
+ "bevy_scene 0.15.0",
  "bevy_sprite",
  "bevy_state",
- "bevy_tasks",
+ "bevy_tasks 0.15.0",
  "bevy_text",
- "bevy_time",
- "bevy_transform",
+ "bevy_time 0.15.0",
+ "bevy_transform 0.15.0",
  "bevy_ui",
- "bevy_utils",
- "bevy_window",
- "bevy_winit",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
+ "bevy_winit 0.15.0",
 ]
 
 [[package]]
@@ -866,10 +1235,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "67240c7596c8f0653e50fce35a60196516817449235193246599facba9002e02"
 dependencies = [
  "android_log-sys",
- "bevy_app",
- "bevy_ecs",
- "bevy_utils",
+ "bevy_app 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_utils 0.14.2",
+ "tracing-log",
+ "tracing-subscriber",
+ "tracing-wasm",
+]
+
+[[package]]
+name = "bevy_log"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a0bdb42b00ac3752f0d6f531fbda8abf313603157a7b3163da8529412119a0a"
+dependencies = [
+ "android_log-sys",
+ "bevy_app 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_utils 0.15.0",
  "tracing-log",
+ "tracing-oslog",
  "tracing-subscriber",
  "tracing-wasm",
 ]
@@ -882,7 +1267,19 @@ checksum = "bfc65e570012e64a21f3546df68591aaede8349e6174fb500071677f54f06630"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
+ "toml_edit",
+]
+
+[[package]]
+name = "bevy_macro_utils"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3954dbb56a66a6c09c783e767f6ceca0dc0492c22e536e2aeaefb5545eac33c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
  "toml_edit",
 ]
 
@@ -892,43 +1289,93 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5421792749dda753ab3718e77d27bfce38443daf1850b836b97530b6245a4581"
 dependencies = [
- "bevy_reflect",
- "glam",
+ "bevy_reflect 0.14.2",
+ "glam 0.27.0",
  "rand",
  "serde",
  "smallvec",
  "thiserror",
 ]
 
+[[package]]
+name = "bevy_math"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ae26f952598e293acac783d947b21af1809673cbeba25d76b969a56f287160b"
+dependencies = [
+ "bevy_reflect 0.15.0",
+ "derive_more",
+ "glam 0.29.2",
+ "itertools 0.13.0",
+ "rand",
+ "rand_distr",
+ "serde",
+ "smallvec",
+]
+
+[[package]]
+name = "bevy_mesh"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c324d45ca0043a4696d7324b569de65be17066ed3a97dd42205bc28693d20b5"
+dependencies = [
+ "bevy_asset 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_image",
+ "bevy_math 0.15.0",
+ "bevy_mikktspace 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "bitflags 2.6.0",
+ "bytemuck",
+ "derive_more",
+ "hexasphere 15.0.0",
+ "serde",
+ "wgpu 23.0.1",
+]
+
 [[package]]
 name = "bevy_mikktspace"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "66cf695a264b043f2c4edb92dd5c742e6892180d2b30dac870012d153f8557ea"
 dependencies = [
- "glam",
+ "glam 0.27.0",
+]
+
+[[package]]
+name = "bevy_mikktspace"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da5ea3ad25d74ea36ea45418ad799f135d046db35c322b9704c4a8934eb65ce9"
+dependencies = [
+ "glam 0.29.2",
 ]
 
 [[package]]
 name = "bevy_pbr"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dccaa3c945f19834dcf7cd8eb358236dbf0fc4000dacbc7710564e7856714db"
-dependencies = [
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_core_pipeline",
- "bevy_derive",
- "bevy_ecs",
- "bevy_math",
- "bevy_reflect",
- "bevy_render",
- "bevy_transform",
- "bevy_utils",
- "bevy_window",
+checksum = "01b3bd8e646ddd3f27743b712957d2990d7361eb21044accc47c4f66711bf2cb"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_core_pipeline 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_image",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
  "bitflags 2.6.0",
  "bytemuck",
+ "derive_more",
  "fixedbitset 0.5.7",
  "nonmax",
  "radsort",
@@ -936,25 +1383,54 @@ dependencies = [
  "static_assertions",
 ]
 
+[[package]]
+name = "bevy_picking"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a137ed706574dc4a01cac527eb2c44a0b0e477d5bce3afc892a9ee95ee0078"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_input 0.15.0",
+ "bevy_math 0.15.0",
+ "bevy_mesh",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
+ "bevy_time 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
+ "crossbeam-channel",
+ "uuid",
+]
+
 [[package]]
 name = "bevy_ptr"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "61baa1bdc1f4a7ac2c18217570a7cc04e1cd54d38456e91782f0371c79afe0a8"
 
+[[package]]
+name = "bevy_ptr"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af9e30b40fb3f0a80a658419f670f2de1e743efcaca1952c43cdcc923287944"
+
 [[package]]
 name = "bevy_reflect"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2508785a4a5809f25a237eec4fee2c91a4dbcf81324b2bbc2d6c52629e603781"
 dependencies = [
- "bevy_ptr",
- "bevy_reflect_derive",
- "bevy_utils",
+ "bevy_ptr 0.14.2",
+ "bevy_reflect_derive 0.14.2",
+ "bevy_utils 0.14.2",
  "downcast-rs",
  "erased-serde",
- "glam",
- "petgraph",
+ "glam 0.27.0",
  "serde",
  "smallvec",
  "smol_str",
@@ -962,16 +1438,51 @@ dependencies = [
  "uuid",
 ]
 
+[[package]]
+name = "bevy_reflect"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52a37e2ae5ed62df4a0e3f958076effe280b39bc81fe878587350897a89332a2"
+dependencies = [
+ "assert_type_match",
+ "bevy_ptr 0.15.0",
+ "bevy_reflect_derive 0.15.0",
+ "bevy_utils 0.15.0",
+ "derive_more",
+ "disqualified",
+ "downcast-rs",
+ "erased-serde",
+ "glam 0.29.2",
+ "petgraph",
+ "serde",
+ "smallvec",
+ "smol_str",
+ "uuid",
+]
+
 [[package]]
 name = "bevy_reflect_derive"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "967d5da1882ec3bb3675353915d3da909cafac033cbf31e58727824a1ad2a288"
 dependencies = [
- "bevy_macro_utils",
+ "bevy_macro_utils 0.14.2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+ "uuid",
+]
+
+[[package]]
+name = "bevy_reflect_derive"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94c683fc68c75fc26f90bb1e529590095380d7cec66f6610dbe6b93d9fd26f94"
+dependencies = [
+ "bevy_macro_utils 0.15.0",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
  "uuid",
 ]
 
@@ -979,48 +1490,93 @@ dependencies = [
 name = "bevy_render"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "836cf8a513db013cbe7d55a331060088efd407e49fd5b05c8404700cd82e7619"
+checksum = "836cf8a513db013cbe7d55a331060088efd407e49fd5b05c8404700cd82e7619"
+dependencies = [
+ "async-channel",
+ "bevy_app 0.14.2",
+ "bevy_asset 0.14.2",
+ "bevy_color 0.14.3",
+ "bevy_core 0.14.2",
+ "bevy_derive 0.14.2",
+ "bevy_diagnostic 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_encase_derive 0.14.2",
+ "bevy_hierarchy 0.14.2",
+ "bevy_math 0.14.2",
+ "bevy_mikktspace 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_render_macros 0.14.2",
+ "bevy_tasks 0.14.2",
+ "bevy_time 0.14.2",
+ "bevy_transform 0.14.2",
+ "bevy_utils 0.14.2",
+ "bevy_window 0.14.2",
+ "bitflags 2.6.0",
+ "bytemuck",
+ "codespan-reporting",
+ "downcast-rs",
+ "encase 0.8.0",
+ "futures-lite",
+ "hexasphere 12.0.0",
+ "image 0.25.5",
+ "js-sys",
+ "naga 0.20.0",
+ "naga_oil 0.14.0",
+ "nonmax",
+ "send_wrapper",
+ "serde",
+ "smallvec",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu 0.20.1",
+]
+
+[[package]]
+name = "bevy_render"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d188f392edf4edcae53dfda07f3ec618a7a704183ec3f2e8504657a9fb940c8a"
 dependencies = [
  "async-channel",
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_core",
- "bevy_derive",
- "bevy_diagnostic",
- "bevy_ecs",
- "bevy_encase_derive",
- "bevy_hierarchy",
- "bevy_math",
- "bevy_mikktspace",
- "bevy_reflect",
- "bevy_render_macros",
- "bevy_tasks",
- "bevy_time",
- "bevy_transform",
- "bevy_utils",
- "bevy_window",
- "bitflags 2.6.0",
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_core 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_diagnostic 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_encase_derive 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_image",
+ "bevy_math 0.15.0",
+ "bevy_mesh",
+ "bevy_reflect 0.15.0",
+ "bevy_render_macros 0.15.0",
+ "bevy_tasks 0.15.0",
+ "bevy_time 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
  "bytemuck",
  "codespan-reporting",
+ "derive_more",
  "downcast-rs",
- "encase",
+ "encase 0.10.0",
  "futures-lite",
- "hexasphere",
- "image 0.25.4",
+ "image 0.25.5",
  "js-sys",
  "ktx2",
- "naga",
- "naga_oil",
+ "naga 23.0.0",
+ "naga_oil 0.16.0",
  "nonmax",
- "ruzstd",
+ "offset-allocator",
  "send_wrapper",
  "serde",
  "smallvec",
- "thiserror",
  "wasm-bindgen",
  "web-sys",
- "wgpu",
+ "wgpu 23.0.1",
 ]
 
 [[package]]
@@ -1029,10 +1585,22 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cbc24e0e95061a38a7744218b9c7e52e4c08b53f1499f33480e2b749f3864432"
 dependencies = [
- "bevy_macro_utils",
+ "bevy_macro_utils 0.14.2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "bevy_render_macros"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ab37ee2945f93e9ba8daf91cd968b4cba9c677ac51d349dd8512a107a9a5d92"
+dependencies = [
+ "bevy_macro_utils 0.15.0",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1041,70 +1609,94 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ec57a72d75273bdbb6154390688fd07ba79ae9f6f99476d1937f799c736c2da"
 dependencies = [
- "bevy_app",
- "bevy_asset",
- "bevy_derive",
- "bevy_ecs",
- "bevy_hierarchy",
- "bevy_reflect",
- "bevy_render",
- "bevy_transform",
- "bevy_utils",
+ "bevy_app 0.14.2",
+ "bevy_asset 0.14.2",
+ "bevy_derive 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_hierarchy 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_render 0.14.2",
+ "bevy_transform 0.14.2",
+ "bevy_utils 0.14.2",
  "serde",
  "thiserror",
  "uuid",
 ]
 
+[[package]]
+name = "bevy_scene"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e883fd3c6d6e7761f1fe662e79bc7bdc7e917e73e7bfc434b1d16d2a5852119"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "derive_more",
+ "serde",
+ "uuid",
+]
+
 [[package]]
 name = "bevy_sprite"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e045b4d8cc8e7422a4c29b1eadbe224f5cc42f170b88d43e7535892fcede3840"
-dependencies = [
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_core_pipeline",
- "bevy_derive",
- "bevy_ecs",
- "bevy_math",
- "bevy_reflect",
- "bevy_render",
- "bevy_transform",
- "bevy_utils",
+checksum = "e975abc3f3f3432d6ad86ae32de804e96d7faf59d27f32b065b5ddc1e73ed7e1"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_core_pipeline 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_image",
+ "bevy_math 0.15.0",
+ "bevy_picking",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
  "bitflags 2.6.0",
  "bytemuck",
+ "derive_more",
  "fixedbitset 0.5.7",
  "guillotiere",
+ "nonmax",
  "radsort",
  "rectangle-pack",
- "thiserror",
 ]
 
 [[package]]
 name = "bevy_state"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25335bfa58cc22371182335c3b133017293bc9b6d3308402fd4d1f978b83f937"
+checksum = "036ec832197eae51b8a842220d2df03591dff75b4566dcf0f81153bbcb2b593b"
 dependencies = [
- "bevy_app",
- "bevy_ecs",
- "bevy_hierarchy",
- "bevy_reflect",
+ "bevy_app 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_reflect 0.15.0",
  "bevy_state_macros",
- "bevy_utils",
+ "bevy_utils 0.15.0",
 ]
 
 [[package]]
 name = "bevy_state_macros"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee600b659c739f1911f997a81611fec0a1832cf731727956e5fa4e7532b4dd5"
+checksum = "2828eb6762af9eccfebb5e4a0e56dbc4bd07bf3192083fa3e8525cfdb3e95add"
 dependencies = [
- "bevy_macro_utils",
+ "bevy_macro_utils 0.15.0",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1112,35 +1704,53 @@ name = "bevy_tasks"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77865f310b1fc48fb05b7c4adbe76607ec01d0c14f8ab4caba4d714c86439946"
+dependencies = [
+ "async-executor",
+ "futures-lite",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "bevy_tasks"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5171c605b462b4e3249e01986505e62e3933aa27642a9f793c841814fcbbfb4f"
 dependencies = [
  "async-channel",
  "async-executor",
  "concurrent-queue",
+ "futures-channel",
  "futures-lite",
+ "pin-project",
  "wasm-bindgen-futures",
 ]
 
 [[package]]
 name = "bevy_text"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b661db828fd423fc41a4ccf43aa4d1b8e50e75057ec40453317d0d761e8ad62d"
-dependencies = [
- "ab_glyph",
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_ecs",
- "bevy_math",
- "bevy_reflect",
- "bevy_render",
+checksum = "4fb000b2abad9f82f7a137fac7e0e3d2c6488cbf8dd9ddbb68f9a6b7e7af8d84"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_image",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
  "bevy_sprite",
- "bevy_transform",
- "bevy_utils",
- "bevy_window",
- "glyph_brush_layout",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
+ "cosmic-text",
+ "derive_more",
  "serde",
- "thiserror",
+ "smallvec",
+ "sys-locale",
+ "unicode-bidi",
 ]
 
 [[package]]
@@ -1149,56 +1759,86 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f4e4d53ec32a1b16492396951d04de0d2d90e924bf9adcb8d1adacab5ab6c17c"
 dependencies = [
- "bevy_app",
- "bevy_ecs",
- "bevy_reflect",
- "bevy_utils",
+ "bevy_app 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_utils 0.14.2",
  "crossbeam-channel",
  "thiserror",
 ]
 
+[[package]]
+name = "bevy_time"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291b6993b899c04554fc034ebb9e0d7fde9cb9b2fb58dcd912bfa6247abdedbb"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_utils 0.15.0",
+ "crossbeam-channel",
+]
+
 [[package]]
 name = "bevy_transform"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d5493dce84427d00a9266e8e4386d738a72ee8640423b62dfcecb6dfccbfe0d2"
 dependencies = [
- "bevy_app",
- "bevy_ecs",
- "bevy_hierarchy",
- "bevy_math",
- "bevy_reflect",
+ "bevy_app 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_hierarchy 0.14.2",
+ "bevy_math 0.14.2",
+ "bevy_reflect 0.14.2",
  "thiserror",
 ]
 
+[[package]]
+name = "bevy_transform"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35665624d0c728107ab0920d5ad2d352362b906a8c376eaf375ec9c751faf4"
+dependencies = [
+ "bevy_app 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "derive_more",
+]
+
 [[package]]
 name = "bevy_ui"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56d2cba6603b39a3765f043212ae530e25550af168a7eec6b23b9b93c19bc5f7"
-dependencies = [
- "bevy_a11y",
- "bevy_app",
- "bevy_asset",
- "bevy_color",
- "bevy_core_pipeline",
- "bevy_derive",
- "bevy_ecs",
- "bevy_hierarchy",
- "bevy_input",
- "bevy_math",
- "bevy_reflect",
- "bevy_render",
+checksum = "43da3326aa592d6f6326e31893901bf17cd6957ded4e0ea02bc54652e5624b7f"
+dependencies = [
+ "accesskit 0.17.1",
+ "bevy_a11y 0.15.0",
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_color 0.15.1",
+ "bevy_core_pipeline 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_image",
+ "bevy_input 0.15.0",
+ "bevy_math 0.15.0",
+ "bevy_picking",
+ "bevy_reflect 0.15.0",
+ "bevy_render 0.15.0",
  "bevy_sprite",
  "bevy_text",
- "bevy_transform",
- "bevy_utils",
- "bevy_window",
+ "bevy_transform 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
  "bytemuck",
+ "derive_more",
  "nonmax",
  "smallvec",
  "taffy",
- "thiserror",
 ]
 
 [[package]]
@@ -1208,7 +1848,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffb0ec333b5965771153bd746f92ffd8aeeb9d008a8620ffd9ed474859381a5e"
 dependencies = [
  "ahash",
- "bevy_utils_proc_macros",
+ "bevy_utils_proc_macros 0.14.2",
+ "getrandom",
+ "hashbrown 0.14.5",
+ "thread_local",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "bevy_utils"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0a48bad33c385a7818b7683a16c8b5c6930eded05cd3f176264fc1f5acea473"
+dependencies = [
+ "ahash",
+ "bevy_utils_proc_macros 0.15.0",
  "getrandom",
  "hashbrown 0.14.5",
  "thread_local",
@@ -1224,7 +1879,18 @@ checksum = "38f1ab8f2f6f58439d260081d89a42b02690e5fdd64f814edc9417d33fcf2857"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "bevy_utils_proc_macros"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfd8d4a525b8f04f85863e45ccad3e922d4c11ed4a8d54f7f62a40bf83fb90f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1233,12 +1899,30 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c89e88a20db64ea8204540afb4699295947c454738fd50293f7b32ab8be857a6"
 dependencies = [
- "bevy_a11y",
- "bevy_app",
- "bevy_ecs",
- "bevy_math",
- "bevy_reflect",
- "bevy_utils",
+ "bevy_a11y 0.14.2",
+ "bevy_app 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_math 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_utils 0.14.2",
+ "raw-window-handle",
+ "smol_str",
+]
+
+[[package]]
+name = "bevy_window"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f3520279aae65935d6a84443202c154ead3abebf8dae906d095665162de358"
+dependencies = [
+ "android-activity",
+ "bevy_a11y 0.15.0",
+ "bevy_app 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_input 0.15.0",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_utils 0.15.0",
  "raw-window-handle",
  "smol_str",
 ]
@@ -1249,20 +1933,20 @@ version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d0bef8ec3e4b45db943ad4d1c0bf59b09e382ce0651a706e2f33a70fa955303c"
 dependencies = [
- "accesskit_winit",
+ "accesskit_winit 0.20.4",
  "approx",
- "bevy_a11y",
- "bevy_app",
- "bevy_derive",
- "bevy_ecs",
- "bevy_hierarchy",
- "bevy_input",
- "bevy_log",
- "bevy_math",
- "bevy_reflect",
- "bevy_tasks",
- "bevy_utils",
- "bevy_window",
+ "bevy_a11y 0.14.2",
+ "bevy_app 0.14.2",
+ "bevy_derive 0.14.2",
+ "bevy_ecs 0.14.2",
+ "bevy_hierarchy 0.14.2",
+ "bevy_input 0.14.2",
+ "bevy_log 0.14.2",
+ "bevy_math 0.14.2",
+ "bevy_reflect 0.14.2",
+ "bevy_tasks 0.14.2",
+ "bevy_utils 0.14.2",
+ "bevy_window 0.14.2",
  "cfg-if",
  "crossbeam-channel",
  "raw-window-handle",
@@ -1271,6 +1955,39 @@ dependencies = [
  "winit",
 ]
 
+[[package]]
+name = "bevy_winit"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581bb2249a82285707e0977a9a1c79a2248ede587fcb289708faa03a82ebfa7f"
+dependencies = [
+ "accesskit 0.17.1",
+ "accesskit_winit 0.23.1",
+ "approx",
+ "bevy_a11y 0.15.0",
+ "bevy_app 0.15.0",
+ "bevy_asset 0.15.0",
+ "bevy_derive 0.15.0",
+ "bevy_ecs 0.15.0",
+ "bevy_hierarchy 0.15.0",
+ "bevy_image",
+ "bevy_input 0.15.0",
+ "bevy_log 0.15.0",
+ "bevy_math 0.15.0",
+ "bevy_reflect 0.15.0",
+ "bevy_tasks 0.15.0",
+ "bevy_utils 0.15.0",
+ "bevy_window 0.15.0",
+ "bytemuck",
+ "cfg-if",
+ "crossbeam-channel",
+ "raw-window-handle",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types 23.0.0",
+ "winit",
+]
+
 [[package]]
 name = "bindgen"
 version = "0.70.1"
@@ -1281,12 +1998,14 @@ dependencies = [
  "cexpr",
  "clang-sys",
  "itertools 0.13.0",
+ "log",
+ "prettyplease",
  "proc-macro2",
  "quote",
  "regex",
  "rustc-hash",
  "shlex",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1295,7 +2014,16 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
 dependencies = [
- "bit-vec",
+ "bit-vec 0.6.3",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
+dependencies = [
+ "bit-vec 0.8.0",
 ]
 
 [[package]]
@@ -1304,6 +2032,12 @@ version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
 
+[[package]]
+name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
 [[package]]
 name = "bit_field"
 version = "0.10.2"
@@ -1327,9 +2061,9 @@ dependencies = [
 
 [[package]]
 name = "bitstream-io"
-version = "2.5.3"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452"
+checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
 
 [[package]]
 name = "blake3"
@@ -1386,9 +2120,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
 name = "bytemuck"
-version = "1.19.0"
+version = "1.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
+checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
 dependencies = [
  "bytemuck_derive",
 ]
@@ -1401,7 +2135,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1444,9 +2178,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
 
 [[package]]
 name = "cc"
-version = "1.1.31"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
+checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
 dependencies = [
  "jobserver",
  "libc",
@@ -1566,9 +2300,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.20"
+version = "4.5.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
+checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -1576,13 +2310,13 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.20"
+version = "4.5.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
+checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
 dependencies = [
  "anstream",
  "anstyle",
- "clap_lex 0.7.2",
+ "clap_lex 0.7.3",
  "strsim 0.11.1",
 ]
 
@@ -1595,7 +2329,7 @@ dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1609,9 +2343,9 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.7.2"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
 
 [[package]]
 name = "codespan-reporting"
@@ -1720,6 +2454,26 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca"
 
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "tiny-keccak",
+]
+
 [[package]]
 name = "const_panic"
 version = "0.2.10"
@@ -1757,6 +2511,16 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "core-foundation"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.7"
@@ -1770,7 +2534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
 dependencies = [
  "bitflags 1.3.2",
- "core-foundation",
+ "core-foundation 0.9.4",
  "core-graphics-types",
  "foreign-types",
  "libc",
@@ -1783,7 +2547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
 dependencies = [
  "bitflags 1.3.2",
- "core-foundation",
+ "core-foundation 0.9.4",
  "libc",
 ]
 
@@ -1793,7 +2557,7 @@ version = "20.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5"
 dependencies = [
- "core-foundation",
+ "core-foundation 0.9.4",
  "core-graphics",
  "foreign-types",
  "libc",
@@ -1819,6 +2583,29 @@ dependencies = [
  "bindgen",
 ]
 
+[[package]]
+name = "cosmic-text"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2"
+dependencies = [
+ "bitflags 2.6.0",
+ "fontdb",
+ "log",
+ "rangemap",
+ "rayon",
+ "rustc-hash",
+ "rustybuzz",
+ "self_cell",
+ "swash",
+ "sys-locale",
+ "ttf-parser 0.21.1",
+ "unicode-bidi",
+ "unicode-linebreak",
+ "unicode-script",
+ "unicode-segmentation",
+]
+
 [[package]]
 name = "cpal"
 version = "0.15.3"
@@ -1860,7 +2647,7 @@ dependencies = [
  "anes",
  "cast",
  "ciborium",
- "clap 4.5.20",
+ "clap 4.5.21",
  "criterion-plot",
  "is-terminal",
  "itertools 0.10.5",
@@ -1929,9 +2716,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
 
 [[package]]
 name = "csv"
-version = "1.3.0"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
+checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
 dependencies = [
  "csv-core",
  "itoa",
@@ -1948,6 +2735,16 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "ctrlc"
+version = "3.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
+dependencies = [
+ "nix",
+ "windows-sys 0.59.0",
+]
+
 [[package]]
 name = "cursor-icon"
 version = "1.1.0"
@@ -2010,7 +2807,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim 0.11.1",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2032,7 +2829,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
  "darling_core 0.20.10",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2104,7 +2901,7 @@ dependencies = [
  "darling 0.20.10",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2124,7 +2921,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
 dependencies = [
  "derive_builder_core 0.20.2",
- "syn 2.0.85",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "derive_more"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+ "unicode-xid",
 ]
 
 [[package]]
@@ -2166,6 +2984,23 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
 
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "disqualified"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd"
+
 [[package]]
 name = "dlib"
 version = "0.5.2"
@@ -2255,8 +3090,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a9299a95fa5671ddf29ecc22b00e121843a65cb9ff24911e394b4ae556baf36"
 dependencies = [
  "const_panic",
- "encase_derive",
- "glam",
+ "encase_derive 0.8.0",
+ "glam 0.27.0",
+ "thiserror",
+]
+
+[[package]]
+name = "encase"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0a05902cf601ed11d564128448097b98ebe3c6574bd7b6a653a3d56d54aa020"
+dependencies = [
+ "const_panic",
+ "encase_derive 0.10.0",
+ "glam 0.29.2",
  "thiserror",
 ]
 
@@ -2266,7 +3113,16 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "07e09decb3beb1fe2db6940f598957b2e1f7df6206a804d438ff6cb2a9cddc10"
 dependencies = [
- "encase_derive_impl",
+ "encase_derive_impl 0.8.0",
+]
+
+[[package]]
+name = "encase_derive"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "181d475b694e2dd56ae919ce7699d344d1fd259292d590c723a50d1189a2ea85"
+dependencies = [
+ "encase_derive_impl 0.10.0",
 ]
 
 [[package]]
@@ -2277,7 +3133,18 @@ checksum = "fd31dbbd9743684d339f907a87fe212cb7b51d75b9e8e74181fe363199ee9b47"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "encase_derive_impl"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f97b51c5cc57ef7c5f7a0c57c250251c49ee4c28f819f87ac32f4aceabc36792"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2373,15 +3240,14 @@ dependencies = [
 
 [[package]]
 name = "exr"
-version = "1.72.0"
+version = "1.73.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
+checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
 dependencies = [
  "bit_field",
- "flume",
  "half",
  "lebe",
- "miniz_oxide 0.7.4",
+ "miniz_oxide",
  "rayon-core",
  "smallvec",
  "zune-inflate",
@@ -2389,9 +3255,9 @@ dependencies = [
 
 [[package]]
 name = "fastrand"
-version = "2.1.1"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
+checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
 
 [[package]]
 name = "fdeflate"
@@ -2416,12 +3282,12 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
 
 [[package]]
 name = "flate2"
-version = "1.0.34"
+version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
+checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
 dependencies = [
  "crc32fast",
- "miniz_oxide 0.8.0",
+ "miniz_oxide",
 ]
 
 [[package]]
@@ -2430,21 +3296,18 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
 
-[[package]]
-name = "flume"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
-dependencies = [
- "spin",
-]
-
 [[package]]
 name = "fnv"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
+[[package]]
+name = "foldhash"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
+
 [[package]]
 name = "font-kit"
 version = "0.14.2"
@@ -2453,7 +3316,7 @@ checksum = "b64b34f4efd515f905952d91bc185039863705592c0c53ae6d979805dd154520"
 dependencies = [
  "bitflags 2.6.0",
  "byteorder",
- "core-foundation",
+ "core-foundation 0.9.4",
  "core-graphics",
  "core-text",
  "dirs",
@@ -2463,11 +3326,43 @@ dependencies = [
  "lazy_static",
  "libc",
  "log",
- "pathfinder_geometry",
- "pathfinder_simd",
- "walkdir",
- "winapi",
- "yeslogic-fontconfig-sys",
+ "pathfinder_geometry",
+ "pathfinder_simd",
+ "walkdir",
+ "winapi",
+ "yeslogic-fontconfig-sys",
+]
+
+[[package]]
+name = "font-types"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "fontconfig-parser"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7"
+dependencies = [
+ "roxmltree",
+]
+
+[[package]]
+name = "fontdb"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3"
+dependencies = [
+ "fontconfig-parser",
+ "log",
+ "memmap2",
+ "slotmap",
+ "tinyvec",
+ "ttf-parser 0.20.0",
 ]
 
 [[package]]
@@ -2488,7 +3383,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2523,6 +3418,15 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
 
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+]
+
 [[package]]
 name = "futures-core"
 version = "0.3.31"
@@ -2537,9 +3441,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
 
 [[package]]
 name = "futures-lite"
-version = "2.3.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
+checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1"
 dependencies = [
  "fastrand",
  "futures-core",
@@ -2593,9 +3497,9 @@ dependencies = [
 
 [[package]]
 name = "gilrs"
-version = "0.10.10"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a556964c6d62458084356ce9770676f5104bd667e12e9a795691076e8a17c5cf"
+checksum = "bbb2c998745a3c1ac90f64f4f7b3a54219fd3612d7705e7798212935641ed18f"
 dependencies = [
  "fnv",
  "gilrs-core",
@@ -2606,11 +3510,11 @@ dependencies = [
 
 [[package]]
 name = "gilrs-core"
-version = "0.5.15"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "732dadc05170599ddec9a89653f10d7a2af54da9181b3fa6e2bd49907ec8f7e4"
+checksum = "495af945e45efd6386227613cd9fb7bd7c43d3c095040e30c5304c489e6abed5"
 dependencies = [
- "core-foundation",
+ "core-foundation 0.10.0",
  "inotify",
  "io-kit-sys",
  "js-sys",
@@ -2660,6 +3564,17 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "glam"
+version = "0.29.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
+dependencies = [
+ "bytemuck",
+ "rand",
+ "serde",
+]
+
 [[package]]
 name = "glob"
 version = "0.3.1"
@@ -2678,6 +3593,18 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "glow"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 name = "gltf"
 version = "1.4.1"
@@ -2699,7 +3626,7 @@ dependencies = [
  "inflections",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2724,14 +3651,12 @@ dependencies = [
 ]
 
 [[package]]
-name = "glyph_brush_layout"
-version = "0.2.4"
+name = "glutin_wgl_sys"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1e288bfd2f6c0313f78bf5aa538356ad481a3bb97e9b7f93220ab0066c5992"
+checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c"
 dependencies = [
- "ab_glyph",
- "approx",
- "xi-unicode",
+ "gl_generator",
 ]
 
 [[package]]
@@ -2766,6 +3691,18 @@ dependencies = [
  "windows 0.52.0",
 ]
 
+[[package]]
+name = "gpu-allocator"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
+dependencies = [
+ "log",
+ "presser",
+ "thiserror",
+ "windows 0.58.0",
+]
+
 [[package]]
 name = "gpu-descriptor"
 version = "0.3.0"
@@ -2831,9 +3768,12 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.15.0"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+dependencies = [
+ "foldhash",
+]
 
 [[package]]
 name = "hassle-rs"
@@ -2878,7 +3818,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "edd6b038160f086b0a7496edae34169ae22f328793cbe2b627a5a3d8373748ec"
 dependencies = [
  "constgebra",
- "glam",
+ "glam 0.27.0",
+]
+
+[[package]]
+name = "hexasphere"
+version = "15.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "741ab88b8cc670443da777c3daab02cebf5a3caccfc04e3c052f55c94d1643fe"
+dependencies = [
+ "constgebra",
+ "glam 0.29.2",
 ]
 
 [[package]]
@@ -2916,6 +3866,124 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "icu_collections"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+]
+
 [[package]]
 name = "ident_case"
 version = "1.0.1"
@@ -2924,12 +3992,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
 [[package]]
 name = "idna"
-version = "0.5.0"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
 dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
 ]
 
 [[package]]
@@ -2948,9 +4027,9 @@ dependencies = [
 
 [[package]]
 name = "image"
-version = "0.25.4"
+version = "0.25.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae"
+checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
 dependencies = [
  "bytemuck",
  "byteorder-lite",
@@ -3011,7 +4090,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
 dependencies = [
  "equivalent",
- "hashbrown 0.15.0",
+ "hashbrown 0.15.2",
 ]
 
 [[package]]
@@ -3022,11 +4101,11 @@ checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
 
 [[package]]
 name = "inotify"
-version = "0.10.2"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc"
+checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
  "inotify-sys",
  "libc",
 ]
@@ -3048,7 +4127,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -3116,9 +4195,9 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.11"
+version = "1.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2"
 
 [[package]]
 name = "jni"
@@ -3226,9 +4305,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.161"
+version = "0.2.167"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
+checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
 
 [[package]]
 name = "libflate"
@@ -3252,13 +4331,12 @@ dependencies = [
 
 [[package]]
 name = "libfuzzer-sys"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
+checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa"
 dependencies = [
  "arbitrary",
  "cc",
- "once_cell",
 ]
 
 [[package]]
@@ -3293,6 +4371,12 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "libm"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
+
 [[package]]
 name = "libredox"
 version = "0.1.3"
@@ -3332,6 +4416,12 @@ version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
+[[package]]
+name = "litemap"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+
 [[package]]
 name = "litrs"
 version = "0.4.1"
@@ -3407,6 +4497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
 dependencies = [
  "cfg-if",
+ "rayon",
 ]
 
 [[package]]
@@ -3424,6 +4515,15 @@ version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
+[[package]]
+name = "memmap2"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "metal"
 version = "0.28.0"
@@ -3440,19 +4540,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "minimal-lexical"
-version = "0.2.1"
+name = "metal"
+version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21"
+dependencies = [
+ "bitflags 2.6.0",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
 
 [[package]]
-name = "miniz_oxide"
-version = "0.7.4"
+name = "minimal-lexical"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
-dependencies = [
- "adler",
-]
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
@@ -3471,7 +4577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231"
 dependencies = [
  "arrayvec",
- "bit-set",
+ "bit-set 0.5.3",
  "bitflags 2.6.0",
  "codespan-reporting",
  "hexf-parse",
@@ -3486,17 +4592,59 @@ dependencies = [
  "unicode-xid",
 ]
 
+[[package]]
+name = "naga"
+version = "23.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d5941e45a15b53aad4375eedf02033adb7a28931eedc31117faffa52e6a857e"
+dependencies = [
+ "arrayvec",
+ "bit-set 0.8.0",
+ "bitflags 2.6.0",
+ "cfg_aliases 0.1.1",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap 2.6.0",
+ "log",
+ "pp-rs",
+ "rustc-hash",
+ "spirv",
+ "termcolor",
+ "thiserror",
+ "unicode-xid",
+]
+
 [[package]]
 name = "naga_oil"
 version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "275d9720a7338eedac966141089232514c84d76a246a58ef501af88c5edf402f"
 dependencies = [
- "bit-set",
+ "bit-set 0.5.3",
+ "codespan-reporting",
+ "data-encoding",
+ "indexmap 2.6.0",
+ "naga 0.20.0",
+ "once_cell",
+ "regex",
+ "regex-syntax 0.8.5",
+ "rustc-hash",
+ "thiserror",
+ "tracing",
+ "unicode-ident",
+]
+
+[[package]]
+name = "naga_oil"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31ea1f080bb359927cd5404d0af1e5e6758f4f2d82ecfbebb0a0c434764e40f1"
+dependencies = [
+ "bit-set 0.5.3",
  "codespan-reporting",
  "data-encoding",
  "indexmap 2.6.0",
- "naga",
+ "naga 23.0.0",
  "once_cell",
  "regex",
  "regex-syntax 0.8.5",
@@ -3532,7 +4680,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -3696,7 +4844,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -3738,6 +4886,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
+ "libm",
 ]
 
 [[package]]
@@ -3758,7 +4907,7 @@ dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -4005,6 +5154,16 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "offset-allocator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e234d535da3521eb95106f40f0b73483d80bfb3aacf27c40d7e2b72f1a3e00a2"
+dependencies = [
+ "log",
+ "nonmax",
+]
+
 [[package]]
 name = "ogg"
 version = "0.8.0"
@@ -4032,10 +5191,10 @@ version = "0.5.0"
 dependencies = [
  "approx",
  "assert_matches",
- "bevy",
+ "bevy 0.15.0",
  "bevy_flycam",
  "chrono",
- "clap 4.5.20",
+ "clap 4.5.21",
  "colorous",
  "criterion",
  "csv",
@@ -4043,7 +5202,7 @@ dependencies = [
  "earcutr",
  "embed-doc-image",
  "env_logger",
- "image 0.25.4",
+ "image 0.25.5",
  "itertools 0.13.0",
  "kahan",
  "log",
@@ -4060,7 +5219,6 @@ dependencies = [
  "serde_yaml",
  "sobol",
  "spade",
- "splines",
  "strum",
  "strum_macros",
  "tempfile",
@@ -4109,15 +5267,6 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
 
-[[package]]
-name = "owned_ttf_parser"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4"
-dependencies = [
- "ttf-parser 0.25.0",
-]
-
 [[package]]
 name = "parking"
 version = "2.2.1"
@@ -4207,7 +5356,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -4289,14 +5438,14 @@ dependencies = [
  "crc32fast",
  "fdeflate",
  "flate2",
- "miniz_oxide 0.8.0",
+ "miniz_oxide",
 ]
 
 [[package]]
 name = "polling"
-version = "3.7.3"
+version = "3.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511"
+checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
 dependencies = [
  "cfg-if",
  "concurrent-queue",
@@ -4337,6 +5486,16 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
 
+[[package]]
+name = "prettyplease"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.89",
+]
+
 [[package]]
 name = "proc-macro-crate"
 version = "3.2.0"
@@ -4348,9 +5507,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.89"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
 dependencies = [
  "unicode-ident",
 ]
@@ -4371,7 +5530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
 dependencies = [
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -4434,12 +5593,28 @@ dependencies = [
  "getrandom",
 ]
 
+[[package]]
+name = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
 [[package]]
 name = "range-alloc"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
 
+[[package]]
+name = "rangemap"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684"
+
 [[package]]
 name = "rav1e"
 version = "0.7.1"
@@ -4486,6 +5661,7 @@ dependencies = [
  "loop9",
  "quick-error",
  "rav1e",
+ "rayon",
  "rgb",
 ]
 
@@ -4521,6 +5697,16 @@ dependencies = [
  "crossbeam-utils",
 ]
 
+[[package]]
+name = "read-fonts"
+version = "0.22.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a04b892cb6f91951f144c33321843790c8574c825aafdb16d815fd7183b5229"
+dependencies = [
+ "bytemuck",
+ "font-types",
+]
+
 [[package]]
 name = "rectangle-pack"
 version = "0.4.2"
@@ -4564,7 +5750,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-automata 0.4.8",
+ "regex-automata 0.4.9",
  "regex-syntax 0.8.5",
 ]
 
@@ -4579,9 +5765,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.8"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -4632,9 +5818,9 @@ checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30"
 
 [[package]]
 name = "rodio"
-version = "0.18.1"
+version = "0.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1fceb9d127d515af1586d8d0cc601e1245bdb0af38e75c865a156290184f5b3"
+checksum = "6006a627c1a38d37f3d3a85c6575418cfe34a5392d60a686d0071e1c8d427acb"
 dependencies = [
  "cpal",
  "lewton",
@@ -4657,7 +5843,13 @@ dependencies = [
 name = "roots"
 version = "0.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "082f11ffa03bbef6c2c6ea6bea1acafaade2fd9050ae0234ab44a2153742b058"
+checksum = "082f11ffa03bbef6c2c6ea6bea1acafaade2fd9050ae0234ab44a2153742b058"
+
+[[package]]
+name = "roxmltree"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
 
 [[package]]
 name = "rprompt"
@@ -4696,9 +5888,9 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.38"
+version = "0.38.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
+checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
 dependencies = [
  "bitflags 2.6.0",
  "errno",
@@ -4713,6 +5905,23 @@ version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
 
+[[package]]
+name = "rustybuzz"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
+dependencies = [
+ "bitflags 2.6.0",
+ "bytemuck",
+ "libm",
+ "smallvec",
+ "ttf-parser 0.21.1",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-properties",
+ "unicode-script",
+]
+
 [[package]]
 name = "ruzstd"
 version = "0.7.2"
@@ -4752,6 +5961,12 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
+[[package]]
+name = "self_cell"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
+
 [[package]]
 name = "semver"
 version = "1.0.23"
@@ -4766,29 +5981,29 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
 
 [[package]]
 name = "serde"
-version = "1.0.214"
+version = "1.0.215"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
+checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.214"
+version = "1.0.215"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
+checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.132"
+version = "1.0.133"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
+checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
 dependencies = [
  "itoa",
  "memchr",
@@ -4877,6 +6092,16 @@ dependencies = [
  "quote",
 ]
 
+[[package]]
+name = "skrifa"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe"
+dependencies = [
+ "bytemuck",
+ "read-fonts",
+]
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -4932,15 +6157,6 @@ dependencies = [
  "smallvec",
 ]
 
-[[package]]
-name = "spin"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-dependencies = [
- "lock_api",
-]
-
 [[package]]
 name = "spirv"
 version = "0.3.0+sdk-1.3.268.0"
@@ -4951,10 +6167,16 @@ dependencies = [
 ]
 
 [[package]]
-name = "splines"
-version = "4.4.0"
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "stackfuture"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35abfe290b05ab81e1773eb1849e23d024b2571640444d5024308871308ae805"
+checksum = "6eae92052b72ef70dafa16eddbabffc77e5ca3574be2f7bc1127b36f0a7ad7f2"
 
 [[package]]
 name = "static_assertions"
@@ -4993,14 +6215,25 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
 name = "svg_fmt"
-version = "0.4.3"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa"
+
+[[package]]
+name = "swash"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20e16a0f46cf5fd675563ef54f26e83e20f2366bcf027bcb3cc3ed2b98aaf2ca"
+checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2"
+dependencies = [
+ "skrifa",
+ "yazi",
+ "zeno",
+]
 
 [[package]]
 name = "syn"
@@ -5015,27 +6248,46 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.85"
+version = "2.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
+checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
 dependencies = [
  "proc-macro2",
  "quote",
  "unicode-ident",
 ]
 
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "sys-locale"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "sysinfo"
-version = "0.30.13"
+version = "0.32.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3"
+checksum = "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af"
 dependencies = [
- "cfg-if",
  "core-foundation-sys",
  "libc",
+ "memchr",
  "ntapi",
- "once_cell",
- "windows 0.52.0",
+ "windows 0.54.0",
 ]
 
 [[package]]
@@ -5072,9 +6324,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
 
 [[package]]
 name = "tempfile"
-version = "3.13.0"
+version = "3.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
 dependencies = [
  "cfg-if",
  "fastrand",
@@ -5109,22 +6361,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
 
 [[package]]
 name = "thiserror"
-version = "1.0.65"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.65"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -5181,6 +6433,25 @@ dependencies = [
  "time-core",
 ]
 
+[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
 [[package]]
 name = "tinytemplate"
 version = "1.2.1"
@@ -5259,7 +6530,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -5283,6 +6554,21 @@ dependencies = [
  "tracing-core",
 ]
 
+[[package]]
+name = "tracing-oslog"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528bdd1f0e27b5dd9a4ededf154e824b0532731e4af73bb531de46276e0aab1e"
+dependencies = [
+ "bindgen",
+ "cc",
+ "cfg-if",
+ "once_cell",
+ "parking_lot",
+ "tracing-core",
+ "tracing-subscriber",
+]
+
 [[package]]
 name = "tracing-subscriber"
 version = "0.3.18"
@@ -5320,9 +6606,9 @@ checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
 
 [[package]]
 name = "ttf-parser"
-version = "0.25.0"
+version = "0.21.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e"
+checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
 
 [[package]]
 name = "twox-hash"
@@ -5352,20 +6638,41 @@ version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
 
+[[package]]
+name = "unicode-bidi-mirroring"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
+
+[[package]]
+name = "unicode-ccc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
+
 [[package]]
 name = "unicode-ident"
-version = "1.0.13"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
 
 [[package]]
-name = "unicode-normalization"
-version = "0.1.24"
+name = "unicode-linebreak"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
-dependencies = [
- "tinyvec",
-]
+checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
+
+[[package]]
+name = "unicode-properties"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
+
+[[package]]
+name = "unicode-script"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f"
 
 [[package]]
 name = "unicode-segmentation"
@@ -5407,15 +6714,27 @@ dependencies = [
 
 [[package]]
 name = "url"
-version = "2.5.2"
+version = "2.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
 dependencies = [
  "form_urlencoded",
  "idna",
  "percent-encoding",
 ]
 
+[[package]]
+name = "utf16_iter"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
 [[package]]
 name = "utf8parse"
 version = "0.2.2"
@@ -5559,7 +6878,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
  "wasm-bindgen-shared",
 ]
 
@@ -5593,7 +6912,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -5642,7 +6961,32 @@ dependencies = [
  "document-features",
  "js-sys",
  "log",
- "naga",
+ "naga 0.20.0",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "smallvec",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core 0.21.1",
+ "wgpu-hal 0.21.1",
+ "wgpu-types 0.20.0",
+]
+
+[[package]]
+name = "wgpu"
+version = "23.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80f70000db37c469ea9d67defdc13024ddf9a5f1b89cb2941b812ad7cde1735a"
+dependencies = [
+ "arrayvec",
+ "cfg_aliases 0.1.1",
+ "document-features",
+ "js-sys",
+ "log",
+ "naga 23.0.0",
  "parking_lot",
  "profiling",
  "raw-window-handle",
@@ -5651,9 +6995,9 @@ dependencies = [
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
- "wgpu-core",
- "wgpu-hal",
- "wgpu-types",
+ "wgpu-core 23.0.1",
+ "wgpu-hal 23.0.1",
+ "wgpu-types 23.0.0",
 ]
 
 [[package]]
@@ -5663,14 +7007,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39"
 dependencies = [
  "arrayvec",
- "bit-vec",
+ "bit-vec 0.6.3",
  "bitflags 2.6.0",
  "cfg_aliases 0.1.1",
  "codespan-reporting",
  "document-features",
  "indexmap 2.6.0",
  "log",
- "naga",
+ "naga 0.20.0",
  "once_cell",
  "parking_lot",
  "profiling",
@@ -5679,8 +7023,33 @@ dependencies = [
  "smallvec",
  "thiserror",
  "web-sys",
- "wgpu-hal",
- "wgpu-types",
+ "wgpu-hal 0.21.1",
+ "wgpu-types 0.20.0",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "23.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a"
+dependencies = [
+ "arrayvec",
+ "bit-vec 0.8.0",
+ "bitflags 2.6.0",
+ "cfg_aliases 0.1.1",
+ "document-features",
+ "indexmap 2.6.0",
+ "log",
+ "naga 23.0.0",
+ "once_cell",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "wgpu-hal 23.0.1",
+ "wgpu-types 23.0.0",
 ]
 
 [[package]]
@@ -5691,17 +7060,17 @@ checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222"
 dependencies = [
  "android_system_properties",
  "arrayvec",
- "ash",
- "bit-set",
+ "ash 0.37.3+1.3.251",
+ "bit-set 0.5.3",
  "bitflags 2.6.0",
  "block",
  "cfg_aliases 0.1.1",
  "core-graphics-types",
  "d3d12",
- "glow",
- "glutin_wgl_sys",
+ "glow 0.13.1",
+ "glutin_wgl_sys 0.5.0",
  "gpu-alloc",
- "gpu-allocator",
+ "gpu-allocator 0.25.0",
  "gpu-descriptor",
  "hassle-rs",
  "js-sys",
@@ -5709,8 +7078,8 @@ dependencies = [
  "libc",
  "libloading 0.8.5",
  "log",
- "metal",
- "naga",
+ "metal 0.28.0",
+ "naga 0.20.0",
  "ndk-sys 0.5.0+25.2.9519653",
  "objc",
  "once_cell",
@@ -5724,10 +7093,55 @@ dependencies = [
  "thiserror",
  "wasm-bindgen",
  "web-sys",
- "wgpu-types",
+ "wgpu-types 0.20.0",
  "winapi",
 ]
 
+[[package]]
+name = "wgpu-hal"
+version = "23.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash 0.38.0+1.3.281",
+ "bit-set 0.8.0",
+ "bitflags 2.6.0",
+ "block",
+ "bytemuck",
+ "cfg_aliases 0.1.1",
+ "core-graphics-types",
+ "glow 0.14.2",
+ "glutin_wgl_sys 0.6.0",
+ "gpu-alloc",
+ "gpu-allocator 0.27.0",
+ "gpu-descriptor",
+ "js-sys",
+ "khronos-egl",
+ "libc",
+ "libloading 0.8.5",
+ "log",
+ "metal 0.29.0",
+ "naga 23.0.0",
+ "ndk-sys 0.5.0+25.2.9519653",
+ "objc",
+ "once_cell",
+ "parking_lot",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types 23.0.0",
+ "windows 0.58.0",
+ "windows-core 0.58.0",
+]
+
 [[package]]
 name = "wgpu-types"
 version = "0.20.0"
@@ -5739,11 +7153,22 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "wgpu-types"
+version = "23.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068"
+dependencies = [
+ "bitflags 2.6.0",
+ "js-sys",
+ "web-sys",
+]
+
 [[package]]
 name = "wide"
-version = "0.7.28"
+version = "0.7.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690"
+checksum = "58e6db2670d2be78525979e9a5f9c69d296fd7d670549fe9ebf70f8708cb5019"
 dependencies = [
  "bytemuck",
  "safe_arch",
@@ -5858,7 +7283,7 @@ checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -5869,7 +7294,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -5880,7 +7305,7 @@ checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -5891,7 +7316,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -6150,7 +7575,7 @@ dependencies = [
  "calloop",
  "cfg_aliases 0.2.1",
  "concurrent-queue",
- "core-foundation",
+ "core-foundation 0.9.4",
  "core-graphics",
  "cursor-icon",
  "dpi",
@@ -6198,6 +7623,18 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "write16"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+
 [[package]]
 name = "x11-dl"
 version = "2.21.0"
@@ -6230,12 +7667,6 @@ version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
 
-[[package]]
-name = "xi-unicode"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a"
-
 [[package]]
 name = "xkbcommon-dl"
 version = "0.4.2"
@@ -6257,9 +7688,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
 
 [[package]]
 name = "xml-rs"
-version = "0.8.22"
+version = "0.8.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26"
+checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f"
 
 [[package]]
 name = "xtask"
@@ -6284,6 +7715,12 @@ dependencies = [
  "glob",
 ]
 
+[[package]]
+name = "yazi"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1"
+
 [[package]]
 name = "yeslogic-fontconfig-sys"
 version = "6.0.0"
@@ -6295,6 +7732,36 @@ dependencies = [
  "pkg-config",
 ]
 
+[[package]]
+name = "yoke"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+ "synstructure",
+]
+
+[[package]]
+name = "zeno"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
+
 [[package]]
 name = "zerocopy"
 version = "0.7.35"
@@ -6313,7 +7780,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.85",
+ "syn 2.0.89",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+ "synstructure",
 ]
 
 [[package]]
@@ -6322,6 +7810,28 @@ version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
 
+[[package]]
+name = "zerovec"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.89",
+]
+
 [[package]]
 name = "zune-core"
 version = "0.4.12"
diff --git a/Cargo.toml b/Cargo.toml
index a38a652e2136aab49d025281daa818cb9255608f..398392c260eeedf6a3a8f6404e4ada9bc730e372 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,15 +5,6 @@ members = [
 default-members = ["opossum"]
 resolver = "2"
 
-# Config for 'cargo dist'
-[workspace.metadata.dist]
-# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
-cargo-dist-version = "0.11.1"
-# The installers to generate for each app
-installers = ["msi"]
-# Target platforms to build apps for (Rust target-triple syntax)
-targets = ["x86_64-pc-windows-msvc"]
-
 # The profile that 'cargo dist' will build with
 [profile.dist]
 inherits = "release"
@@ -26,4 +17,4 @@ strip = true
 # Disable debug symbols an link time optimization due to limitations of the Linux pipeline VM.
 [profile.test]
 debug = 0
-lto = "off"
\ No newline at end of file
+lto = "off"
diff --git a/dist-workspace.toml b/dist-workspace.toml
new file mode 100644
index 0000000000000000000000000000000000000000..975cfea89b0f14e146ff0f26e648b4c48fe9edd5
--- /dev/null
+++ b/dist-workspace.toml
@@ -0,0 +1,11 @@
+[workspace]
+members = ["cargo:."]
+
+# Config for 'dist'
+[dist]
+# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
+cargo-dist-version = "0.25.1"
+# The installers to generate for each app
+installers = ["msi"]
+# Target platforms to build apps for (Rust target-triple syntax)
+targets = ["x86_64-pc-windows-msvc"]
diff --git a/opossum/Cargo.toml b/opossum/Cargo.toml
index b0ddb1eb66bee2c606fb5ea0b48478958ed5e19a..6ac1f3b7cc1304588ceb2e9102ba4de8fc3a243b 100644
--- a/opossum/Cargo.toml
+++ b/opossum/Cargo.toml
@@ -65,11 +65,10 @@ rayon = "1.10"
 
 tinytemplate = "1"
 
-bevy={ version="0.14", optional=true }
+bevy={ version="0.15", optional=true }
 bevy_flycam = { version= "0.14", optional =true }
 spade = "2.12"
 earcutr = "0.4.3"
-splines = "4.4.0"
 
 [lints.rust]
 # missing_docs = "warn"
diff --git a/opossum/benches/fluence_estimator.rs b/opossum/benches/fluence_estimator.rs
index 67eeed47b84aaed443e9a0b061cce41a2231005f..f2ecb21ad86524f0b99a8c6fec55410a70182092 100644
--- a/opossum/benches/fluence_estimator.rs
+++ b/opossum/benches/fluence_estimator.rs
@@ -3,8 +3,8 @@ use opossum::{
     joule, millimeter,
     position_distributions::{Hexapolar, PositionDistribution, SobolDist},
     surface::hit_map::{
+        fluence_estimator::FluenceEstimator,
         rays_hit_map::{HitPoint, RaysHitMap},
-        FluenceEstimator,
     },
 };
 use uom::si::f64::Ratio;
diff --git a/opossum/examples/fluence_test.rs b/opossum/examples/fluence_test.rs
index d2d001bc9c9d9c275fda1315c2bdc0e1ded0781f..80a87097ffff703645a8337612241c079d8d45ce 100644
--- a/opossum/examples/fluence_test.rs
+++ b/opossum/examples/fluence_test.rs
@@ -12,7 +12,7 @@ use opossum::{
     optic_node::OpticNode,
     position_distributions::SobolDist,
     rays::Rays,
-    surface::hit_map::FluenceEstimator,
+    surface::hit_map::fluence_estimator::FluenceEstimator,
     utils::geom_transformation::Isometry,
     OpmDocument,
 };
diff --git a/opossum/examples/kde.rs b/opossum/examples/kde.rs
index b85d8cc3f7a9514e0bb27dea744f16e3c5192935..c797ab6841204623c265592471aa658c8c01e83b 100644
--- a/opossum/examples/kde.rs
+++ b/opossum/examples/kde.rs
@@ -4,8 +4,8 @@ use opossum::{
     plottable::Plottable,
     position_distributions::{Hexapolar, PositionDistribution},
     surface::hit_map::{
+        fluence_estimator::FluenceEstimator,
         rays_hit_map::{HitPoint, RaysHitMap},
-        FluenceEstimator,
     },
 };
 use std::path::Path;
diff --git a/opossum/src/analyzers/analyzable.rs b/opossum/src/analyzers/analyzable.rs
index b8ab385d168389db8578e64a7d32eedf22bc18c8..012f2203660e221f89f352e344d34560fcde9dd1 100644
--- a/opossum/src/analyzers/analyzable.rs
+++ b/opossum/src/analyzers/analyzable.rs
@@ -4,48 +4,13 @@ use crate::{
     analyzers::{
         energy::AnalysisEnergy, ghostfocus::AnalysisGhostFocus, raytrace::AnalysisRayTrace,
     },
-    error::{OpmResult, OpossumError},
     optic_node::OpticNode,
-    utils::geom_transformation::Isometry,
 };
 use core::fmt::Debug;
 use std::fmt::Display;
 
 /// Marker trait for an optical node that can be analyzed
-pub trait Analyzable: OpticNode + AnalysisEnergy + AnalysisRayTrace + AnalysisGhostFocus {
-    ///Sets the coating and isometry of this surface
-    /// # Errors
-    /// This function errors if the coating cannot be accessed
-    // fn set_surface_iso_and_coating(
-    //     &mut self,
-    //     port_str: &str,
-    //     iso: &Isometry,
-    //     port_type: &PortType,
-    // ) -> OpmResult<()> {
-    //     let node_attr = self.node_attr().clone();
-
-    //     let input_surf = self.get_optic_surface_mut(port_str);
-    //     input_surf.set_isometry(iso);
-    //     input_surf.set_coating(
-    //         node_attr
-    //             .ports()
-    //             .coating(port_type, port_str)
-    //             .ok_or_else(|| OpossumError::Other("cannot access coating!".into()))?
-    //             .clone(),
-    //     );
-
-    //     Ok(())
-    // }
-
-    fn set_anchor_point_iso(&mut self, port_str: &str, iso: Isometry) -> OpmResult<()> {
-        if let Some(input_surf) = self.get_optic_surface_mut(port_str) {
-            input_surf.set_anchor_point_iso(iso);
-        } else {
-            return Err(OpossumError::OpticPort("No surface found.".into()));
-        }
-        Ok(())
-    }
-}
+pub trait Analyzable: OpticNode + AnalysisEnergy + AnalysisRayTrace + AnalysisGhostFocus {}
 impl Debug for dyn Analyzable {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         Display::fmt(&self, f)
@@ -56,3 +21,22 @@ impl Display for dyn Analyzable {
         write!(f, "'{}' ({})", self.name(), self.node_type())
     }
 }
+
+#[cfg(test)]
+mod test {
+    use crate::{analyzers::Analyzable, nodes::Dummy};
+    #[test]
+    fn fmt() {
+        assert_eq!(
+            format!("{}", &Dummy::new("test") as &dyn Analyzable),
+            "'test' (dummy)"
+        );
+    }
+    #[test]
+    fn debug() {
+        assert_eq!(
+            format!("{:?}", &Dummy::new("test") as &dyn Analyzable),
+            "'test' (dummy)"
+        );
+    }
+}
diff --git a/opossum/src/analyzers/ghostfocus.rs b/opossum/src/analyzers/ghostfocus.rs
index 52ad79e951ce4dea7f245dd80ac2b7c092b322a5..0dc949855c5ad1ae39253574633d013bf76800c1 100644
--- a/opossum/src/analyzers/ghostfocus.rs
+++ b/opossum/src/analyzers/ghostfocus.rs
@@ -22,7 +22,7 @@ use crate::{
     },
     rays::Rays,
     reporting::{analysis_report::AnalysisReport, node_report::NodeReport},
-    surface::hit_map::FluenceEstimator,
+    surface::hit_map::fluence_estimator::FluenceEstimator,
 };
 
 use super::{raytrace::AnalysisRayTrace, Analyzer, AnalyzerType, RayTraceConfig};
diff --git a/opossum/src/analyzers/raytrace.rs b/opossum/src/analyzers/raytrace.rs
index 4fef43586d737c3c41fb67de451545f1c3d8797b..e5b0ece5e40be0576fc8b719d7e5c870e7b982c1 100644
--- a/opossum/src/analyzers/raytrace.rs
+++ b/opossum/src/analyzers/raytrace.rs
@@ -362,17 +362,17 @@ mod test {
     use crate::{
         joule, millimeter,
         nodes::{round_collimated_ray_source, ParaxialSurface},
+        utils::test_helper::test_helper::check_logs,
     };
     #[test]
-    fn ray_tracing_config_default() {
+    fn config_default() {
         let rt_conf = RayTraceConfig::default();
-        // assert!(matches!(rt_conf.mode(), RayTracingMode::Sequential));
         assert_eq!(rt_conf.max_number_of_bounces(), 1000);
         assert_eq!(rt_conf.max_number_of_refractions(), 1000);
         assert_eq!(rt_conf.min_energy_per_ray(), picojoule!(1.0));
     }
     #[test]
-    fn ray_tracing_config_set_min_energy() {
+    fn config_set_min_energy() {
         let mut rt_conf = RayTraceConfig::default();
         assert!(rt_conf.set_min_energy_per_ray(picojoule!(-0.1)).is_err());
         assert!(rt_conf
@@ -386,7 +386,7 @@ mod test {
         assert_eq!(rt_conf.min_energy_per_ray, picojoule!(20.0));
     }
     #[test]
-    fn ray_tracing_config_setters() {
+    fn config_setters() {
         let mut rt_conf = RayTraceConfig::default();
         rt_conf.set_max_number_of_bounces(123);
         rt_conf.set_max_number_of_refractions(456);
@@ -394,14 +394,52 @@ mod test {
         assert_eq!(rt_conf.max_number_of_refractions, 456);
     }
     #[test]
-    fn ray_tracing_config_debug() {
+    fn config_debug() {
         assert_eq!(
             format!("{:?}", RayTraceConfig::default()),
             "RayTraceConfig { min_energy_per_ray: 1e-12 m^2 kg^1 s^-2, max_number_of_bounces: 1000, max_number_of_refractions: 1000 }"
         );
     }
     #[test]
-    fn ray_tracing_integration_test() {
+    fn new() {
+        let mut config = RayTraceConfig::default();
+        config.set_max_number_of_bounces(123);
+        let analyzer = RayTracingAnalyzer::new(config);
+        assert_eq!(analyzer.config.max_number_of_bounces(), 123);
+    }
+    #[test]
+    fn analyze_info() {
+        let mut scenery = NodeGroup::new("test");
+        let analyzer = RayTracingAnalyzer::default();
+        testing_logger::setup();
+        analyzer.analyze(&mut scenery).unwrap();
+        check_logs(
+            log::Level::Info,
+            vec![
+                "Calculate node positions of scenery 'test'.",
+                "Performing ray tracing analysis of scenery 'test'.",
+            ],
+        );
+        let mut scenery = NodeGroup::new("");
+        let analyzer = RayTracingAnalyzer::default();
+        testing_logger::setup();
+        analyzer.analyze(&mut scenery).unwrap();
+        check_logs(
+            log::Level::Info,
+            vec![
+                "Calculate node positions of scenery.",
+                "Performing ray tracing analysis of scenery.",
+            ],
+        );
+    }
+    #[test]
+    fn report() {
+        let analyzer = RayTracingAnalyzer::default();
+        let scenery = NodeGroup::new("");
+        analyzer.report(&scenery).unwrap();
+    }
+    #[test]
+    fn integration_test() {
         // simulate simple system for integration test
         let mut group = NodeGroup::default();
         let i_src = group
diff --git a/opossum/src/kde/mod.rs b/opossum/src/kde/mod.rs
index cc3fd98f10e93dc9132f3652b336078096d20784..851859013e6784fd5f0e746e879c812c6852aaf9 100644
--- a/opossum/src/kde/mod.rs
+++ b/opossum/src/kde/mod.rs
@@ -1,7 +1,12 @@
 //! Kernel density estimator
 
 mod gaussian;
-use crate::{millimeter, nodes::fluence_detector::Fluence, utils::usize_to_f64};
+use crate::{
+    error::OpmResult,
+    millimeter,
+    nodes::fluence_detector::Fluence,
+    utils::{f64_to_usize, math_utils::distance_2d_point, usize_to_f64},
+};
 use gaussian::Gaussian2D;
 use nalgebra::{point, DMatrix, Point2};
 use num::Zero;
@@ -25,20 +30,26 @@ impl Kde {
     pub fn set_hit_map(&mut self, hit_map: Vec<(Point2<Length>, Energy)>) {
         self.hit_map = hit_map;
     }
-    pub fn set_band_width(&mut self, band_width: Length) {
+    /// Sets the band width of this [`Kde`].
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if the given bandwidth is zero or not finite.
+    pub fn set_band_width(&mut self, band_width: Length) -> OpmResult<()> {
+        if !band_width.is_normal() {
+            return Err(crate::error::OpossumError::Other(
+                "bandwidth must be != 0.0 and finite".into(),
+            ));
+        }
         self.band_width = band_width;
-    }
-    fn distance(point1: &Point2<Length>, point2: &Point2<Length>) -> Length {
-        ((point1.x - point2.x) * (point1.x - point2.x)
-            + (point1.y - point2.y) * (point1.y - point2.y))
-            .sqrt()
+        Ok(())
     }
     fn point_distances_std_dev(&self) -> (Vec<Length>, Length) {
         let mut sum = Length::zero();
         let mut distances = Vec::default();
         for point1 in self.hit_map.iter().enumerate() {
             for point2 in &self.hit_map[point1.0 + 1..] {
-                let distance = Self::distance(&point1.1 .0, &point2.0);
+                let distance = distance_2d_point(&point1.1 .0, &point2.0);
                 distances.push(distance);
                 sum += distance;
             }
@@ -53,24 +64,44 @@ impl Kde {
     }
     fn distances_iqr(values: &[Length]) -> Length {
         if values.is_empty() {
-            millimeter!(0.0)
+            millimeter!(f64::NAN)
         } else {
             let mut sorted_values = values.to_owned();
             sorted_values.sort_by(|a, b| a.partial_cmp(b).unwrap());
-            let mid = sorted_values.len() / 2;
-            let lower_mid = sorted_values[..mid].len() / 2;
-            let upper_mid = sorted_values[mid..].len() / 2;
-            let lower_median = sorted_values[..mid][lower_mid];
-            let upper_median = sorted_values[mid..][upper_mid];
-            upper_median - lower_median
+            let quart_index = 0.75 * usize_to_f64(sorted_values.len());
+            // check if quart_index effectively an integer
+            if quart_index.fract().is_zero() {
+                0.5 * (sorted_values[f64_to_usize(quart_index) - 1]
+                    + sorted_values[f64_to_usize(quart_index)])
+            } else {
+                sorted_values[f64_to_usize(f64::floor(quart_index))]
+            }
         }
     }
     #[must_use]
     pub fn bandwidth_estimate(&self) -> Length {
-        let (distances, std_dev) = self.point_distances_std_dev();
-        let iqr = Self::distances_iqr(&distances);
-        // Silverman's rule of thumb
-        0.9 * Length::min(std_dev, iqr / 1.34) * (usize_to_f64(self.hit_map.len())).powf(-0.2)
+        match self.hit_map.len() {
+            0 | 1 => {
+                millimeter!(f64::NAN)
+            }
+            2 => {
+                let p1 = self.hit_map[0].0;
+                let p2 = self.hit_map[1].0;
+                let bw = 0.5 * distance_2d_point(&p1, &p2);
+                if bw.is_zero() {
+                    millimeter!(f64::NAN)
+                } else {
+                    bw
+                }
+            }
+            _ => {
+                let (distances, std_dev) = self.point_distances_std_dev();
+                let iqr = Self::distances_iqr(&distances);
+                // Silverman's rule of thumb
+                0.9 * Length::min(std_dev, iqr / 1.34)
+                    * (usize_to_f64(self.hit_map.len())).powf(-0.2)
+            }
+        }
     }
     #[must_use]
     pub fn kde_value(&self, point: Point2<Length>) -> Fluence {
@@ -101,40 +132,122 @@ impl Kde {
             });
         matrix
     }
+}
+
+#[cfg(test)]
+mod test {
+    use approx::assert_abs_diff_eq;
 
-    // pub fn kde_2d_2(&self,
-    //     ranges: &(Range<Length>, Range<Length>),
-    //     dimensions: (usize, usize),) -> DMatrix<Fluence>{
-    //     let dx = (ranges.0.end - ranges.0.start) / (dimensions.0 as f64);
-    //     let dy = (ranges.1.end - ranges.1.start) / (dimensions.1 as f64);
-    //     let num_interp = 150;
-    //     let mut spline_vec = Vec::<Key<f64, f64>>::with_capacity(num_interp);
-    //     let bin_size = 10.*self.band_width.value/(num_interp-1).to_f64().unwrap();
-    //     let norm_fac = 1./ (2.*std::f64::consts::PI*self.band_width.value * self.band_width.value);
-    //     for i in 0..num_interp{
-    //         let r = bin_size*i.to_f64().unwrap();
-    //         let r_squared = r*r;
-    //         let g = (-0.5*r_squared/(self.band_width.value*self.band_width.value)).exp()*norm_fac;
-    //         spline_vec.push(Key::new(r_squared, g, Interpolation::Linear))
-    //     }
-    //     let spline = Spline::from_vec(spline_vec);
-    //     let mut matrix = DMatrix::<Fluence>::zeros(dimensions.1, dimensions.0);
-    //     matrix.as_mut_slice()
-    //     .par_chunks_mut(dimensions.0)
-    //     .enumerate()
-    //     .for_each(|(y_i, tile)| {
-    //         let y = ranges.1.start + y_i.to_f64().unwrap()*dy;
-    //         for x_i in 0..dimensions.0{
-    //             let x = ranges.0.start + x_i.to_f64().unwrap()*dx;
-    //             for (hitp, e) in &self.hit_map {
-    //                 let dist_squared = (x.value-hitp.x.value)*(x.value-hitp.x.value) + (y.value-hitp.y.value)*(y.value-hitp.y.value);
-    //                 if let Some(val) = spline.sample(dist_squared){
-    //                     tile[x_i] += ArealNumberDensity::new::<per_square_meter>(val)**e;
-    //                 }
-    //             }
-    //         }
-    //     }
-    //     );
-    //     matrix
-    // }
+    use super::Kde;
+    use crate::{joule, meter, millimeter};
+    use core::f64;
+    #[test]
+    fn default() {
+        let kde = Kde::default();
+        assert_eq!(kde.hit_map.len(), 0);
+        assert_eq!(kde.band_width, millimeter!(1.0));
+    }
+    #[test]
+    fn set_hit_map() {
+        let mut kde = Kde::default();
+        let hit_map = vec![(millimeter!(1.0, 2.0), joule!(3.0))];
+        kde.set_hit_map(hit_map);
+        assert_eq!(kde.hit_map.len(), 1);
+        assert_eq!(kde.hit_map[0].0.x, millimeter!(1.0));
+        assert_eq!(kde.hit_map[0].0.y, millimeter!(2.0));
+        assert_eq!(kde.hit_map[0].1, joule!(3.0));
+    }
+    #[test]
+    fn set_bandwidth() {
+        let mut kde = Kde::default();
+        assert!(kde.set_band_width(millimeter!(0.0)).is_err());
+        assert!(kde.set_band_width(millimeter!(f64::NAN)).is_err());
+        assert!(kde.set_band_width(millimeter!(f64::INFINITY)).is_err());
+        assert!(kde.set_band_width(millimeter!(f64::NEG_INFINITY)).is_err());
+        kde.set_band_width(millimeter!(2.0)).unwrap();
+        assert_eq!(kde.band_width, millimeter!(2.0));
+    }
+    #[test]
+    fn point_distances_std_dev() {
+        let mut kde = Kde::default();
+        assert_eq!(kde.point_distances_std_dev().0.len(), 0);
+        assert!(kde.point_distances_std_dev().1.value.is_nan());
+        let hit_map = vec![(millimeter!(0.0, 0.0), joule!(0.0))];
+        kde.set_hit_map(hit_map);
+        assert_eq!(kde.point_distances_std_dev().0.len(), 0);
+        assert!(kde.point_distances_std_dev().1.value.is_nan());
+        let hit_map = vec![
+            (millimeter!(0.0, 0.0), joule!(0.0)),
+            (millimeter!(1.0, 0.0), joule!(0.0)),
+        ];
+        kde.set_hit_map(hit_map);
+        assert_eq!(kde.point_distances_std_dev().0, vec![millimeter!(1.0)]);
+        assert_eq!(kde.point_distances_std_dev().1, millimeter!(0.0));
+        let hit_map = vec![
+            (meter!(0.0, 0.0), joule!(0.0)),
+            (meter!(1.0, 0.0), joule!(0.0)),
+            (meter!(-1.0, 0.0), joule!(0.0)),
+        ];
+        kde.set_hit_map(hit_map);
+        assert_eq!(
+            kde.point_distances_std_dev().0,
+            vec![meter!(1.0), meter!(1.0), meter!(2.0)]
+        );
+        assert_eq!(
+            kde.point_distances_std_dev().1,
+            meter!(f64::sqrt(2.0 / 9.0))
+        );
+    }
+    #[test]
+    fn distances_iqr() {
+        assert!(Kde::distances_iqr(&vec![]).is_nan());
+        assert_eq!(Kde::distances_iqr(&vec![meter!(1.0)]), meter!(1.0));
+        assert_eq!(
+            Kde::distances_iqr(&vec![meter!(0.0), meter!(1.0)]),
+            meter!(1.0)
+        );
+        assert_eq!(
+            Kde::distances_iqr(&vec![meter!(0.0), meter!(1.0), meter!(2.0)]),
+            meter!(2.0)
+        );
+        assert_eq!(
+            Kde::distances_iqr(&vec![meter!(0.0), meter!(1.0), meter!(2.0), meter!(3.0)]),
+            meter!(2.5)
+        );
+        // Example from Wikipedia
+        let lengths = vec![
+            meter!(25.0),
+            meter!(28.0),
+            meter!(4.0),
+            meter!(28.0),
+            meter!(19.0),
+            meter!(3.0),
+            meter!(9.0),
+            meter!(17.0),
+            meter!(29.0),
+            meter!(29.0),
+        ];
+        assert_eq!(Kde::distances_iqr(&lengths), meter!(28.0));
+    }
+    #[test]
+    fn bandwidth_estimate() {
+        let mut kde = Kde::default();
+        assert!(kde.bandwidth_estimate().is_nan());
+        let hit_map = vec![(millimeter!(0.0, 0.0), joule!(0.0))];
+        kde.set_hit_map(hit_map);
+        assert!(kde.bandwidth_estimate().is_nan());
+        let hit_map = vec![
+            (millimeter!(0.0, 0.0), joule!(0.0)),
+            (millimeter!(1.0, 0.0), joule!(0.0)),
+        ];
+        kde.set_hit_map(hit_map);
+        assert_eq!(kde.bandwidth_estimate(), millimeter!(0.5));
+        let hit_map = vec![
+            (millimeter!(0.0, 0.0), joule!(0.0)),
+            (millimeter!(1.0, 0.0), joule!(0.0)),
+            (millimeter!(-1.0, 0.0), joule!(0.0)),
+        ];
+        kde.set_hit_map(hit_map);
+        assert_abs_diff_eq!(kde.bandwidth_estimate().value, 0.00034057440111656337);
+    }
 }
diff --git a/opossum/src/nodes/fluence_detector/mod.rs b/opossum/src/nodes/fluence_detector/mod.rs
index cf44f1c2fd985d625bbb0e61b427525965ec5aa6..8a231a2589c77f6102811f5f562aa1cdeae6c643 100644
--- a/opossum/src/nodes/fluence_detector/mod.rs
+++ b/opossum/src/nodes/fluence_detector/mod.rs
@@ -17,7 +17,7 @@ use crate::{
     properties::{Properties, Proptype},
     rays::Rays,
     reporting::node_report::NodeReport,
-    surface::hit_map::FluenceEstimator,
+    surface::hit_map::fluence_estimator::FluenceEstimator,
 };
 use log::warn;
 
diff --git a/opossum/src/nodes/mod.rs b/opossum/src/nodes/mod.rs
index 2fd87dca9ecbf5e8105baad9d9ede5bd10aee111..07ff0e273c7216080403b2a3a2e437189852797b 100644
--- a/opossum/src/nodes/mod.rs
+++ b/opossum/src/nodes/mod.rs
@@ -143,4 +143,31 @@ mod test {
     fn create_node_ref_error() {
         assert!(create_node_ref("test").is_err());
     }
+    #[test]
+    fn create_node_ref_ok() {
+        let node_types = vec![
+            "dummy",
+            "beam splitter",
+            "energy meter",
+            "group",
+            "ideal filter",
+            "reflective grating",
+            "reference",
+            "lens",
+            "cylindric lens",
+            "source",
+            "spectrometer",
+            "spot diagram",
+            "wavefront monitor",
+            "paraxial surface",
+            "ray propagation",
+            "fluence detector",
+            "wedge",
+            "mirror",
+            "parabolic mirror",
+        ];
+        for node_type in node_types {
+            assert!(create_node_ref(node_type).is_ok());
+        }
+    }
 }
diff --git a/opossum/src/nodes/node_group/optic_graph.rs b/opossum/src/nodes/node_group/optic_graph.rs
index bfb67d5df30e0332cec87a455f2aa7e0d3a9e92c..f08b325427a4b6a17e6f69e71361f5fd6ba04e51 100644
--- a/opossum/src/nodes/node_group/optic_graph.rs
+++ b/opossum/src/nodes/node_group/optic_graph.rs
@@ -938,7 +938,7 @@ mod test {
         nodes::{BeamSplitter, Dummy, Source},
         ray::SplittingConfig,
         spectrum_helper::create_he_ne_spec,
-        utils::{geom_transformation::Isometry, test_helper::test_helper::check_warnings},
+        utils::{geom_transformation::Isometry, test_helper::test_helper::check_logs},
     };
     use approx::assert_abs_diff_eq;
     use num::Zero;
@@ -1280,9 +1280,10 @@ mod test {
         let input = LightResult::default();
         testing_logger::setup();
         graph.analyze_energy(&input).unwrap();
-        check_warnings(vec![
-            "group contains unconnected sub-trees. Analysis might not be complete.",
-        ]);
+        check_logs(
+            log::Level::Warn,
+            vec!["group contains unconnected sub-trees. Analysis might not be complete."],
+        );
     }
     #[test]
     fn analyze_stale_node() {
@@ -1298,10 +1299,13 @@ mod test {
         input.insert("input_1".into(), LightData::Fourier);
         testing_logger::setup();
         assert!(graph.analyze_energy(&input).is_ok());
-        check_warnings(vec![
+        check_logs(
+            log::Level::Warn,
+            vec![
             "group contains unconnected sub-trees. Analysis might not be complete.",
             "graph contains stale (completely unconnected) node 'stale node' (dummy). Skipping.",
-        ]);
+        ],
+        );
     }
     fn prepare_group() -> OpticGraph {
         let mut graph = OpticGraph::default();
diff --git a/opossum/src/nodes/source.rs b/opossum/src/nodes/source.rs
index 5366b88109ddb28c95d43bf0556dad39ec290506..e63e380c5a5516b6218ae86057b2d7dd1ec90411 100644
--- a/opossum/src/nodes/source.rs
+++ b/opossum/src/nodes/source.rs
@@ -30,12 +30,11 @@ use std::fmt::Debug;
 ///
 /// ## Optical Ports
 ///   - Inputs
-///     - `in1` (input discarded, used to make the node invertable)
+///     - `input_1` (input discarded, used to make the node invertable)
 ///   - Outputs
-///     - `out1`
+///     - `output_1`
 ///
 /// ## Properties
-///   - `name`
 ///   - `light data`
 ///
 /// **Note**: If a [`Source`] is configured as `inverted` the initial output port becomes an input port and further data is discarded.
@@ -111,6 +110,11 @@ impl Source {
     /// # Errors
     /// This function only propagates the errors of the contained functions
     pub fn set_alignment_wavelength(&mut self, wvl: Length) -> OpmResult<()> {
+        if wvl.is_sign_negative() || !wvl.is_normal() {
+            return Err(OpossumError::Other(
+                "wavelength must be positive and finite".into(),
+            ));
+        }
         self.node_attr
             .set_property("alignment wavelength", Proptype::LengthOption(Some(wvl)))
     }
@@ -315,8 +319,12 @@ impl AnalysisGhostFocus for Source {
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::{lightdata::DataEnergy, optic_ports::PortType, spectrum_helper::create_he_ne_spec};
+    use crate::{
+        lightdata::DataEnergy, nanometer, optic_ports::PortType, position_distributions::Hexapolar,
+        spectrum_helper::create_he_ne_spec, utils::geom_transformation::Isometry,
+    };
     use assert_matches::assert_matches;
+    use core::f64;
 
     #[test]
     fn default() {
@@ -338,7 +346,42 @@ mod test {
         assert_eq!(source.name(), "test");
     }
     #[test]
-    fn not_invertable() {
+    fn set_alignment_wavelength() {
+        let mut node = Source::default();
+        assert!(node.set_alignment_wavelength(nanometer!(0.0)).is_err());
+        assert!(node.set_alignment_wavelength(nanometer!(f64::NAN)).is_err());
+        assert!(node
+            .set_alignment_wavelength(nanometer!(f64::INFINITY))
+            .is_err());
+        assert!(node
+            .set_alignment_wavelength(nanometer!(f64::NEG_INFINITY))
+            .is_err());
+        assert!(node.set_alignment_wavelength(nanometer!(-0.1)).is_err());
+        assert!(node.set_alignment_wavelength(nanometer!(600.0)).is_ok());
+        let Proptype::LengthOption(wavelength) =
+            node.node_attr.get_property("alignment wavelength").unwrap()
+        else {
+            panic!("wrong proptype")
+        };
+        assert_eq!(wavelength, &Some(nanometer!(600.0)));
+    }
+    #[test]
+    fn set_property() {
+        let mut node = Source::default();
+        node.set_property(
+            "alignment wavelength",
+            Proptype::LengthOption(Some(nanometer!(600.0))),
+        )
+        .unwrap();
+        let Proptype::LengthOption(wavelength) =
+            node.node_attr.get_property("alignment wavelength").unwrap()
+        else {
+            panic!("wrong proptype")
+        };
+        assert_eq!(wavelength, &Some(nanometer!(600.0)));
+    }
+    #[test]
+    fn is_invertable() {
         let mut node = Source::default();
         assert!(node.set_inverted(false).is_ok());
         assert!(node.set_inverted(true).is_ok());
@@ -361,13 +404,13 @@ mod test {
         }
     }
     #[test]
-    fn analyze_no_light_defined() {
+    fn analyze_energy_no_light_defined() {
         let mut node = Source::default();
         let output = AnalysisEnergy::analyze(&mut node, LightResult::default());
         assert!(output.is_err());
     }
     #[test]
-    fn analyze_ok() {
+    fn analyze_energy_ok() {
         let light = LightData::Energy(DataEnergy {
             spectrum: create_he_ne_spec(1.0).unwrap(),
         });
@@ -381,6 +424,99 @@ mod test {
         assert_eq!(*output, light);
     }
     #[test]
+    fn analyze_raytrace_no_light_defined() {
+        let mut node = Source::default();
+        node.set_isometry(Isometry::identity()).unwrap();
+        let output = AnalysisRayTrace::analyze(
+            &mut node,
+            LightResult::default(),
+            &RayTraceConfig::default(),
+        );
+        assert_eq!(
+            output.unwrap_err(),
+            OpossumError::Analysis("source has empty light data defined".into())
+        );
+    }
+    #[test]
+    fn analyze_raytrace_ok() {
+        let mut node = Source::default();
+        node.set_isometry(Isometry::identity()).unwrap();
+        let rays = Rays::new_uniform_collimated(
+            nanometer!(1000.0),
+            joule!(1.0),
+            &Hexapolar::new(millimeter!(1.0), 1).unwrap(),
+        )
+        .unwrap();
+        node.set_light_data(&LightData::Geometric(rays)).unwrap();
+        let output = AnalysisRayTrace::analyze(
+            &mut node,
+            LightResult::default(),
+            &RayTraceConfig::default(),
+        );
+        assert!(output.is_ok());
+    }
+    #[test]
+    fn calc_node_position_ok_alignement_wavelength_set() {
+        let mut node = Source::default();
+        node.set_isometry(Isometry::identity()).unwrap();
+        node.set_alignment_wavelength(nanometer!(630.0)).unwrap();
+        node.set_light_data(&LightData::Geometric(Rays::default()))
+            .unwrap();
+        let output = AnalysisRayTrace::calc_node_position(
+            &mut node,
+            LightResult::default(),
+            &RayTraceConfig::default(),
+        )
+        .unwrap();
+        let light_data = output.get("output_1").unwrap();
+        if let LightData::Geometric(rays) = light_data {
+            assert_eq!(rays.nr_of_rays(true), 1);
+            let ray = rays.iter().next().unwrap();
+            assert_eq!(ray.wavelength(), nanometer!(630.0));
+        } else {
+            panic!("no geometric light data found")
+        }
+    }
+    #[test]
+    fn analyze_ghost_focus_no_light_defined() {
+        let mut node = Source::default();
+        node.set_isometry(Isometry::identity()).unwrap();
+        let output = AnalysisGhostFocus::analyze(
+            &mut node,
+            LightRays::default(),
+            &GhostFocusConfig::default(),
+            &mut vec![],
+            0,
+        );
+        assert_eq!(
+            output.unwrap_err(),
+            OpossumError::Analysis("source has empty light data defined".into())
+        );
+    }
+    #[test]
+    fn analyze_ghost_focus_ok() {
+        let mut node = Source::default();
+        node.set_isometry(Isometry::identity()).unwrap();
+        let rays = Rays::new_uniform_collimated(
+            nanometer!(1000.0),
+            joule!(1.0),
+            &Hexapolar::new(millimeter!(1.0), 1).unwrap(),
+        )
+        .unwrap();
+        node.set_light_data(&LightData::Geometric(rays.clone()))
+            .unwrap();
+        let mut light_rays = LightRays::new();
+        light_rays.insert("input_1".into(), vec![rays]);
+        let output = AnalysisGhostFocus::analyze(
+            &mut node,
+            light_rays,
+            &GhostFocusConfig::default(),
+            &mut vec![],
+            0,
+        );
+        assert!(output.is_ok());
+    }
+    #[test]
     fn debug() {
         assert_eq!(format!("{:?}", Source::default()), "Source: no data");
         assert_eq!(
diff --git a/opossum/src/nodes/spot_diagram.rs b/opossum/src/nodes/spot_diagram.rs
index 570005ca446a1e6b7bcbe79cf6cb3c4c188e26dc..7389a84d7cab7eab2f6093e448847ca26fc83277 100644
--- a/opossum/src/nodes/spot_diagram.rs
+++ b/opossum/src/nodes/spot_diagram.rs
@@ -425,11 +425,18 @@ mod test {
         test_inverted::<SpotDiagram>()
     }
     #[test]
-    fn analyze_empty() {
+    fn reset_data() {
+        let mut spot = SpotDiagram::default();
+        spot.light_data = Some(LightData::Geometric(Rays::default()));
+        spot.reset_data();
+        assert!(spot.light_data.is_none());
+    }
+    #[test]
+    fn analyze_energy_empty() {
         test_analyze_empty::<SpotDiagram>()
     }
     #[test]
-    fn analyze_wrong() {
+    fn analyze_energy_wrong() {
         let mut node = SpotDiagram::default();
         let mut input = LightResult::default();
         let input_light = LightData::Geometric(Rays::default());
@@ -438,7 +445,7 @@ mod test {
         assert!(output.is_empty());
     }
     #[test]
-    fn analyze_ok() {
+    fn analyze_energy_ok() {
         let mut node = SpotDiagram::default();
         let mut input = LightResult::default();
         let input_light = LightData::Energy(DataEnergy {
@@ -458,7 +465,7 @@ mod test {
         test_analyze_apodization_warning::<SpotDiagram>()
     }
     #[test]
-    fn analyze_inverse() {
+    fn analyze_energy_inverse() {
         let mut node = SpotDiagram::default();
         node.set_inverted(true).unwrap();
         let mut input = LightResult::default();
@@ -475,27 +482,28 @@ mod test {
         let output = output.clone().unwrap();
         assert_eq!(*output, input_light);
     }
-    // #[test]
-    // fn export_data() {
-    //     testing_logger::setup();
-    //     let mut sd = SpotDiagram::default();
-    //     assert!(sd.export_data(Path::new(""), "").is_ok());
-    //     check_warnings(vec![
-    //         "spot diagram: no light data for export available. Cannot create plot!",
-    //     ]);
-    //     sd.light_data = Some(Rays::default());
-    //     let path = NamedTempFile::new().unwrap();
-    //     assert!(sd.export_data(path.path().parent().unwrap(), "").is_err());
-    //     sd.light_data = Some(
-    //         Rays::new_uniform_collimated(
-    //             nanometer!(1053.0),
-    //             joule!(1.0),
-    //             &Hexapolar::new(Length::zero(), 1).unwrap(),
-    //         )
-    //         .unwrap(),
-    //     );
-    //     assert!(sd.export_data(path.path().parent().unwrap(), "").is_ok());
-    // }
+    #[test]
+    fn analyze_ghostfocus_ok() {
+        let mut node = SpotDiagram::default();
+        node.set_isometry(Isometry::identity()).unwrap();
+        let mut input = LightRays::default();
+        let light_rays = Rays::default();
+        input.insert("input_1".into(), vec![light_rays.clone()]);
+        let output = AnalysisGhostFocus::analyze(
+            &mut node,
+            input,
+            &GhostFocusConfig::default(),
+            &mut vec![],
+            0,
+        )
+        .unwrap();
+        assert!(output.contains_key("output_1"));
+        assert_eq!(output.len(), 1);
+        let output = output.get("output_1");
+        assert!(output.is_some());
+        let output = output.clone().unwrap();
+        assert_eq!(output[0], light_rays);
+    }
     #[test]
     fn report() {
         let mut sd = SpotDiagram::default();
diff --git a/opossum/src/nodes/test_helper.rs b/opossum/src/nodes/test_helper.rs
index 76915aa0d6a084f20daf61aa48c1a322fa427fd7..0cf342f4169830a52c9811ac992fef2995597e78 100644
--- a/opossum/src/nodes/test_helper.rs
+++ b/opossum/src/nodes/test_helper.rs
@@ -12,7 +12,7 @@ pub mod test_helper {
         position_distributions::Hexapolar,
         rays::Rays,
         spectrum_helper::create_he_ne_spec,
-        utils::{geom_transformation::Isometry, test_helper::test_helper::check_warnings},
+        utils::{geom_transformation::Isometry, test_helper::test_helper::check_logs},
     };
     pub fn test_inverted<T: Default + OpticNode>() {
         let mut node = T::default();
@@ -89,7 +89,7 @@ pub mod test_helper {
         let msg=format!("Rays have been apodized at input aperture of '{}' ({}). Results might not be accurate.", 
             node.node_attr().name(),
             node.node_attr().node_type());
-        check_warnings(vec![&msg]);
+        check_logs(log::Level::Warn, vec![&msg]);
     }
     pub fn test_analyze_geometric_no_isometry<T: Default + AnalysisRayTrace>(
         input_port_name: &str,
diff --git a/opossum/src/plottable.rs b/opossum/src/plottable.rs
index 0b93d579414aa38f8a466739a85f3363c0905ee6..c0db8a3d189e42954d5accb72ba8bcb59175319c 100644
--- a/opossum/src/plottable.rs
+++ b/opossum/src/plottable.rs
@@ -2583,7 +2583,7 @@ pub enum PlotArgs {
 
 #[cfg(test)]
 mod test {
-    use crate::utils::test_helper::test_helper::check_warnings;
+    use crate::utils::test_helper::test_helper::check_logs;
 
     use super::*;
     use approx::{assert_relative_eq, relative_eq};
@@ -3475,7 +3475,10 @@ mod test {
         let mut plot = Plot::try_from(&PlotParameters::default()).unwrap();
         testing_logger::setup();
         plot.define_axes_bounds();
-        check_warnings(vec!["No plot series defined! Cannot define axes bounds!"]);
+        check_logs(
+            log::Level::Warn,
+            vec!["No plot series defined! Cannot define axes bounds!"],
+        );
         let mut plot = Plot::new(&vec![plt_series_dim2], &PlotParameters::default());
         let _ = plot.define_axes_bounds();
         assert_relative_eq!(plot.bounds.x.unwrap().min, -0.1);
@@ -3581,12 +3584,15 @@ mod test {
         axlim.expand_lim_range_by_factor(f64::INFINITY);
         axlim.expand_lim_range_by_factor(0.);
 
-        check_warnings(vec![
-            "Cannot expand ax limits! Expansion factor must be normal and positive!",
-            "Cannot expand ax limits! Expansion factor must be normal and positive!",
-            "Cannot expand ax limits! Expansion factor must be normal and positive!",
-            "Cannot expand ax limits! Expansion factor must be normal and positive!",
-        ]);
+        check_logs(
+            log::Level::Warn,
+            vec![
+                "Cannot expand ax limits! Expansion factor must be normal and positive!",
+                "Cannot expand ax limits! Expansion factor must be normal and positive!",
+                "Cannot expand ax limits! Expansion factor must be normal and positive!",
+                "Cannot expand ax limits! Expansion factor must be normal and positive!",
+            ],
+        );
     }
     #[test]
     fn create_useful_axlims_test() {
diff --git a/opossum/src/properties/proptype.rs b/opossum/src/properties/proptype.rs
index f43654cbfc1ee550dfc37bfa607a7cbd54bdf3cc..7d01f2be95687f59ccac929a112de14a23a1d8f3 100644
--- a/opossum/src/properties/proptype.rs
+++ b/opossum/src/properties/proptype.rs
@@ -14,7 +14,7 @@ use crate::{
     ray::SplittingConfig,
     refractive_index::RefractiveIndexType,
     reporting::{html_report::HtmlNodeReport, node_report::NodeReport},
-    surface::hit_map::{FluenceEstimator, HitMap},
+    surface::hit_map::{fluence_estimator::FluenceEstimator, HitMap},
     utils::{
         geom_transformation::Isometry,
         unit_format::{get_exponent_for_base_unit_in_e3_steps, get_prefix_for_base_unit},
diff --git a/opossum/src/rays.rs b/opossum/src/rays.rs
index 020d8dd7fc84a045194560e9faf1d84a9249adff..0f5feff9bad39b4ebee3b8d878be04bf8b824536 100644
--- a/opossum/src/rays.rs
+++ b/opossum/src/rays.rs
@@ -203,7 +203,7 @@ impl Rays {
         pos_strategy: &dyn PositionDistribution,
     ) -> OpmResult<Self> {
         let ray_pos = pos_strategy.generate();
-        let (spec_amp, wvls) = spectral_distribution.generate()?;
+        let dist = spectral_distribution.generate()?;
 
         //currently the energy distribution only works in the x-y plane. therefore, all points are projected to this plane
         let ray_pos_plane = ray_pos
@@ -218,8 +218,8 @@ impl Rays {
         let nr_of_rays = ray_pos.len();
         let mut rays: Vec<Ray> = Vec::<Ray>::with_capacity(nr_of_rays);
         for (pos, energy) in izip!(ray_pos.iter(), ray_energies.iter()) {
-            for (spec_amp, wvl) in izip!(spec_amp.iter(), wvls.iter()) {
-                let ray = Ray::new_collimated(*pos, *wvl, *energy * *spec_amp)?;
+            for value in &dist {
+                let ray = Ray::new_collimated(*pos, value.0, *energy * value.1)?;
                 rays.push(ray);
             }
         }
@@ -1299,7 +1299,7 @@ mod test {
         ray::SplittingConfig,
         refractive_index::{refr_index_vaccuum, RefrIndexConst},
         surface::optic_surface::OpticSurface,
-        utils::test_helper::test_helper::check_warnings,
+        utils::test_helper::test_helper::check_logs,
     };
     use approx::{assert_abs_diff_eq, assert_relative_eq};
     use itertools::izip;
@@ -1675,9 +1675,10 @@ mod test {
             RefrIndexConst::new(1.5).unwrap(),
         ))
         .unwrap();
-        check_warnings(vec![
-            "ray bundle contains no valid rays for setting the refractive index",
-        ]);
+        check_logs(
+            log::Level::Warn,
+            vec!["ray bundle contains no valid rays for setting the refractive index"],
+        );
         let ray = Ray::new_collimated(Point3::origin(), nanometer!(1053.0), joule!(1.0)).unwrap();
         rays.add_ray(ray);
         let ray = Ray::new_collimated(Point3::origin(), nanometer!(1053.0), joule!(1.0)).unwrap();
@@ -1822,7 +1823,10 @@ mod test {
                 true,
             )
             .unwrap();
-        check_warnings(vec!["ray bundle contains no valid rays - not propagating"]);
+        check_logs(
+            log::Level::Warn,
+            vec!["ray bundle contains no valid rays - not propagating"],
+        );
         assert_eq!(reflected.nr_of_rays(false), 0);
     }
     #[test]
@@ -1867,7 +1871,10 @@ mod test {
                 true,
             )
             .unwrap();
-        check_warnings(vec!["rays totally reflected or missed a surface"]);
+        check_logs(
+            log::Level::Warn,
+            vec!["rays totally reflected or missed a surface"],
+        );
         assert_eq!(reflected.nr_of_rays(false), 0);
     }
     #[test]
@@ -1915,9 +1922,10 @@ mod test {
             .invalidate_by_threshold_energy(joule!(f64::INFINITY))
             .is_err());
         assert!(rays.invalidate_by_threshold_energy(joule!(-0.1)).is_ok());
-        check_warnings(vec![
-            "negative threshold energy given. Ray bundle unmodified.",
-        ]);
+        check_logs(
+            log::Level::Warn,
+            vec!["negative threshold energy given. Ray bundle unmodified."],
+        );
         assert!(rays.invalidate_by_threshold_energy(joule!(0.0)).is_ok());
         let ray =
             Ray::new_collimated(millimeter!(0., 0., 0.), nanometer!(1053.0), joule!(1.0)).unwrap();
@@ -2324,9 +2332,10 @@ mod test {
         );
         testing_logger::setup();
         assert!(rays.define_up_direction().is_ok());
-        check_warnings(vec![
-            "Ray bundle not used for alignment, use first ray for up-direction calculation",
-        ]);
+        check_logs(
+            log::Level::Warn,
+            vec!["Ray bundle not used for alignment, use first ray for up-direction calculation"],
+        );
     }
     #[test]
     fn calc_new_up_direction_test() {
@@ -2353,8 +2362,9 @@ mod test {
         testing_logger::setup();
         //error because underlying function return error
         assert!(rays.calc_new_up_direction(&mut Vector3::y()).is_err());
-        check_warnings(vec![
-            "Ray bundle not used for alignment, use first ray for up-direction calculation",
-        ]);
+        check_logs(
+            log::Level::Warn,
+            vec!["Ray bundle not used for alignment, use first ray for up-direction calculation"],
+        );
     }
 }
diff --git a/opossum/src/spectral_distribution/gaussian.rs b/opossum/src/spectral_distribution/gaussian.rs
index 763d72dc8ebc450cac8806b275361ab312dc5443..7305e816b576b1afdcca450fbc521ddde1950412 100644
--- a/opossum/src/spectral_distribution/gaussian.rs
+++ b/opossum/src/spectral_distribution/gaussian.rs
@@ -18,11 +18,15 @@ pub struct Gaussian {
 
 impl Gaussian {
     /// Create a new Gaussian distribution generator
+    ///
     /// # Attributes
-    /// - `mx`: the mean value  -> Shifts the distribution n to be centered at `mu`
+    ///
+    /// - `mu`: the mean value  -> Shifts the distribution n to be centered at `mu`
     /// - `fwhm`: the full-with at half maximum of the gaussian
     /// - `power`: the power of the distribution. A standard Gaussian distribution has a power of 1. Larger powers are so called super-Gaussians
+
     /// # Errors
+
     /// This function will return an error if
     ///   - the mean value are non-finite
     ///   - the fwhm are non-finite, zero or below zero
@@ -34,17 +38,29 @@ impl Gaussian {
         fwhm: Length,
         power: f64,
     ) -> OpmResult<Self> {
-        if !mu.is_finite() {
-            return Err(OpossumError::Other("Mean value must be finite!".into()));
+        if !wvl_range.0.is_normal() || wvl_range.0.is_sign_negative() {
+            return Err(OpossumError::Other(
+                "range start must be positive and finite".into(),
+            ));
+        };
+        if !wvl_range.1.is_normal() || wvl_range.1.is_sign_negative() {
+            return Err(OpossumError::Other(
+                "range end must be positive and finite".into(),
+            ));
+        }
+        if !mu.is_normal() || mu.is_sign_negative() {
+            return Err(OpossumError::Other(
+                "mean value must be positive and finite!".into(),
+            ));
         };
         if !fwhm.is_normal() || fwhm.is_sign_negative() {
             return Err(OpossumError::Other(
                 "fwhm must be greater than zero and finite!".into(),
             ));
         };
-        if !power.is_finite() {
+        if !power.is_normal() || power.is_sign_negative() {
             return Err(OpossumError::Other(
-                "Power of the distribution must be positive and finite!".into(),
+                "power of the distribution must be positive and finite!".into(),
             ));
         };
 
@@ -58,7 +74,7 @@ impl Gaussian {
     }
 }
 impl SpectralDistribution for Gaussian {
-    fn generate(&self) -> OpmResult<(Vec<f64>, Vec<Length>)> {
+    fn generate(&self) -> OpmResult<Vec<(Length, f64)>> {
         let wvls = linspace(
             self.wvl_range.0.value,
             self.wvl_range.1.value,
@@ -71,10 +87,93 @@ impl SpectralDistribution for Gaussian {
             self.power,
         );
         let sum: f64 = spectral_distribution.iter().kahan_sum().sum();
-
-        Ok((
-            spectral_distribution.iter().map(|x| *x / sum).collect_vec(),
-            wvls.iter().map(|w| meter!(*w)).collect_vec(),
-        ))
+        Ok(spectral_distribution
+            .iter()
+            .zip(wvls.iter())
+            .map(|v| (meter!(*v.1), *v.0 / sum))
+            .collect_vec())
+    }
+}
+#[cfg(test)]
+mod test {
+    use crate::{
+        nanometer,
+        spectral_distribution::{Gaussian, SpectralDistribution},
+    };
+    use approx::assert_abs_diff_eq;
+    use core::f64;
+    use uom::si::f64::Length;
+    #[test]
+    fn new() {
+        assert!(Gaussian::new(
+            (nanometer!(1000.0), nanometer!(2000.0)),
+            10,
+            nanometer!(1500.0),
+            nanometer!(100.0),
+            1.0
+        )
+        .is_ok());
+        let test_values = vec![0.0, -0.1, f64::INFINITY, f64::NAN, f64::NEG_INFINITY];
+        for value in &test_values {
+            assert!(Gaussian::new(
+                (nanometer!(1000.0), nanometer!(2000.0)),
+                10,
+                nanometer!(1500.0),
+                nanometer!(100.0),
+                *value
+            )
+            .is_err());
+        }
+        let wvl_values: Vec<Length> = test_values.iter().map(|v| nanometer!(*v)).collect();
+        for value in &wvl_values {
+            assert!(Gaussian::new(
+                (*value, nanometer!(2000.0)),
+                10,
+                nanometer!(1500.0),
+                nanometer!(100.0),
+                1.0
+            )
+            .is_err());
+            assert!(Gaussian::new(
+                (nanometer!(2000.0), *value),
+                10,
+                nanometer!(1500.0),
+                nanometer!(100.0),
+                1.0
+            )
+            .is_err());
+            assert!(Gaussian::new(
+                (nanometer!(1000.0), nanometer!(2000.0)),
+                10,
+                *value,
+                nanometer!(100.0),
+                1.0
+            )
+            .is_err());
+            assert!(Gaussian::new(
+                (nanometer!(1000.0), nanometer!(2000.0)),
+                10,
+                nanometer!(1500.0),
+                *value,
+                1.0
+            )
+            .is_err());
+        }
+    }
+    #[test]
+    fn generate() {
+        let gauss = Gaussian::new(
+            (nanometer!(1000.0), nanometer!(2000.0)),
+            11,
+            nanometer!(1500.0),
+            nanometer!(500.0),
+            1.0,
+        )
+        .unwrap();
+        let values = gauss.generate().unwrap();
+        assert_eq!(values.len(), 11);
+        assert_abs_diff_eq!(values[5].0.value, nanometer!(1500.0).value);
+        let v_sum: f64 = values.iter().map(|v| v.1).sum();
+        assert_abs_diff_eq!(v_sum, 1.0);
     }
 }
diff --git a/opossum/src/spectral_distribution/mod.rs b/opossum/src/spectral_distribution/mod.rs
index c2705f70b45a3ca925ecfbf72dc87add2c252e01..1b85595049428e4f4a900e05739c521ddb296bde 100644
--- a/opossum/src/spectral_distribution/mod.rs
+++ b/opossum/src/spectral_distribution/mod.rs
@@ -9,5 +9,5 @@ pub trait SpectralDistribution {
     /// Creates a Gaussian spectral distribution
     /// # Errors
     /// This function only propagates errors of the contained functions
-    fn generate(&self) -> OpmResult<(Vec<f64>, Vec<Length>)>;
+    fn generate(&self) -> OpmResult<Vec<(Length, f64)>>;
 }
diff --git a/opossum/src/spectrum.rs b/opossum/src/spectrum.rs
index 7c5fedfb7c1556be7cad5880e5a3fa983c685968..a63858e5e53ee59d2c060d62c8915e145d1d831a 100644
--- a/opossum/src/spectrum.rs
+++ b/opossum/src/spectrum.rs
@@ -606,7 +606,7 @@ mod test {
         spectrum_helper::{
             create_he_ne_spec, create_nd_glass_spec, create_nir_spec, create_visible_spec,
         },
-        utils::test_helper::test_helper::check_warnings,
+        utils::test_helper::test_helper::check_logs,
     };
     use approx::{assert_abs_diff_eq, AbsDiffEq};
     use testing_logger;
@@ -733,13 +733,15 @@ mod test {
         testing_logger::setup();
         let mut s = prep();
         assert!(s.add_single_peak(micrometer!(0.5), 1.0).is_ok());
-        check_warnings(vec![
-            "peak wavelength is not in spectrum range. Spectrum unmodified.",
-        ]);
+        check_logs(
+            log::Level::Warn,
+            vec!["peak wavelength is not in spectrum range. Spectrum unmodified."],
+        );
         assert!(s.add_single_peak(micrometer!(4.0), 1.0).is_ok());
-        check_warnings(vec![
-            "peak wavelength is not in spectrum range. Spectrum unmodified.",
-        ]);
+        check_logs(
+            log::Level::Warn,
+            vec!["peak wavelength is not in spectrum range. Spectrum unmodified."],
+        );
         assert!(s.add_single_peak(micrometer!(1.5), -1.0).is_err());
     }
     #[test]
diff --git a/opossum/src/spectrum_helper.rs b/opossum/src/spectrum_helper.rs
index 1cee0c4a3ddad8822fdc97fa8bf67d57572743df..8c2ec7218b17a4e835ba72849590b4b911d5d641 100644
--- a/opossum/src/spectrum_helper.rs
+++ b/opossum/src/spectrum_helper.rs
@@ -196,7 +196,7 @@ pub fn generate_filter_spectrum(
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::{micrometer, utils::test_helper::test_helper::check_warnings};
+    use crate::{micrometer, utils::test_helper::test_helper::check_logs};
     use num::Zero;
     use testing_logger;
 
@@ -211,7 +211,10 @@ mod test {
             }
         )
         .is_ok());
-        check_warnings(vec!["cut-off wavelength must be inside the spectrum range"]);
+        check_logs(
+            log::Level::Warn,
+            vec!["cut-off wavelength must be inside the spectrum range"],
+        );
         let s = generate_filter_spectrum(
             micrometer!(1.0)..micrometer!(5.0),
             micrometer!(1.0),
@@ -237,7 +240,10 @@ mod test {
             }
         )
         .is_ok());
-        check_warnings(vec!["cut-off wavelength must be inside the spectrum range"]);
+        check_logs(
+            log::Level::Warn,
+            vec!["cut-off wavelength must be inside the spectrum range"],
+        );
         let s = generate_filter_spectrum(
             micrometer!(1.0)..micrometer!(5.0),
             micrometer!(1.0),
diff --git a/opossum/src/surface/hit_map/fluence_estimator.rs b/opossum/src/surface/hit_map/fluence_estimator.rs
new file mode 100644
index 0000000000000000000000000000000000000000..62516343dc939b7b2bbd453c820ad48066316226
--- /dev/null
+++ b/opossum/src/surface/hit_map/fluence_estimator.rs
@@ -0,0 +1,50 @@
+//! Strategies for fluence estimation
+
+use crate::properties::Proptype;
+use serde::{Deserialize, Serialize};
+use std::fmt::Display;
+
+/// Strategy for fluence estimation
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[non_exhaustive]
+pub enum FluenceEstimator {
+    /// Calculate Voronoi cells of the hit points and use the cell area for calculation of the fluence.
+    Voronoi,
+    /// Calculate the fluence at given point using a Kernel Density Estimator
+    KDE,
+    /// Simply perform binning of the hit points on a given matrix (not implemented yet)
+    Binning,
+}
+impl Display for FluenceEstimator {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Voronoi => write!(f, "Voronoi"),
+            Self::KDE => write!(f, "KDE"),
+            Self::Binning => write!(f, "Binning"),
+        }
+    }
+}
+impl From<FluenceEstimator> for Proptype {
+    fn from(value: FluenceEstimator) -> Self {
+        Self::FluenceEstimator(value)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{properties::Proptype, surface::hit_map::fluence_estimator::FluenceEstimator};
+
+    #[test]
+    fn fmt() {
+        assert_eq!(format!("{}", FluenceEstimator::Voronoi), "Voronoi");
+        assert_eq!(format!("{}", FluenceEstimator::KDE), "KDE");
+        assert_eq!(format!("{}", FluenceEstimator::Binning), "Binning");
+    }
+    #[test]
+    fn from() {
+        assert!(matches!(
+            FluenceEstimator::Voronoi.into(),
+            Proptype::FluenceEstimator(_)
+        ));
+    }
+}
diff --git a/opossum/src/surface/hit_map/mod.rs b/opossum/src/surface/hit_map/mod.rs
index 5230c9178ab00fa22de12f192ca5c20ba1f23a3e..42d47687a66c6851e408b53f6efed2f88781ad1b 100644
--- a/opossum/src/surface/hit_map/mod.rs
+++ b/opossum/src/surface/hit_map/mod.rs
@@ -15,6 +15,7 @@
 //!    first entry contains all [`BouncedHitMap`]s caused by rays wih zero bounces, the second entry all [`BouncedHitMap`]s
 //!    caused by rays wih one bounce, ...
 
+pub mod fluence_estimator;
 pub mod rays_hit_map;
 
 use crate::{
@@ -31,35 +32,10 @@ use nalgebra::{DVector, MatrixXx2, Point2};
 use plotters::style::RGBAColor;
 use rays_hit_map::{HitPoint, RaysHitMap};
 use serde::{Deserialize, Serialize};
-use std::{collections::HashMap, fmt::Display};
+use std::collections::HashMap;
 use uom::si::f64::Length;
 use uuid::Uuid;
 
-/// Strategy for fluence estimation
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[non_exhaustive]
-pub enum FluenceEstimator {
-    /// Calculate Voronoi cells of the hit points and use the cell area for calculation of the fluence.
-    Voronoi,
-    /// Calculate the fluence at given point using a Kernel Density Estimator
-    KDE,
-    /// Simply perform binning of the hit points on a given matrix (not implemented yet)
-    Binning,
-}
-impl Display for FluenceEstimator {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Self::Voronoi => write!(f, "Voronoi"),
-            Self::KDE => write!(f, "KDE"),
-            Self::Binning => write!(f, "Binning"),
-        }
-    }
-}
-impl From<FluenceEstimator> for Proptype {
-    fn from(value: FluenceEstimator) -> Self {
-        Self::FluenceEstimator(value)
-    }
-}
 #[derive(Default, Debug, Clone, Serialize, Deserialize)]
 /// Storage struct for `RaysHitMap` on a surface from a single bounce
 pub struct BouncedHitMap {
diff --git a/opossum/src/surface/hit_map/rays_hit_map.rs b/opossum/src/surface/hit_map/rays_hit_map.rs
index 3eff082007ffe0e101947c6aa6adcc0c93db2eef..cfc5a13d9024d58bc9fca4ce5d89d08099e2b3ab 100644
--- a/opossum/src/surface/hit_map/rays_hit_map.rs
+++ b/opossum/src/surface/hit_map/rays_hit_map.rs
@@ -28,7 +28,7 @@ use crate::{
     J_per_cm2,
 };
 
-use super::FluenceEstimator;
+use super::fluence_estimator::FluenceEstimator;
 
 /// A hit point as part of a [`RaysHitMap`].
 ///
@@ -258,7 +258,7 @@ impl RaysHitMap {
             .collect();
         kde.set_hit_map(hitmap_2d);
         let est_bandwidth = kde.bandwidth_estimate();
-        kde.set_band_width(est_bandwidth);
+        kde.set_band_width(est_bandwidth)?;
         let (left, right, top, bottom) = self.calc_2d_bounding_box(3. * est_bandwidth)?;
         let fluence_matrix = kde.kde_2d(&(left..right, bottom..top), nr_of_points);
         let fluence_data = FluenceData::new(fluence_matrix, left..right, bottom..top);
diff --git a/opossum/src/surface/optic_surface.rs b/opossum/src/surface/optic_surface.rs
index f8954e89e010d802f0d7ee5c32106daab169ee5f..c5d20c055a69518927e8850a9d679dc7f6e0bf8c 100644
--- a/opossum/src/surface/optic_surface.rs
+++ b/opossum/src/surface/optic_surface.rs
@@ -16,8 +16,8 @@ use crate::{
 use super::{
     geo_surface::GeoSurfaceRef,
     hit_map::{
+        fluence_estimator::FluenceEstimator,
         rays_hit_map::{HitPoint, RaysHitMap},
-        FluenceEstimator,
     },
 };
 use core::fmt::Debug;
diff --git a/opossum/src/utils/griddata.rs b/opossum/src/utils/griddata.rs
index 04fad84a7840d5172a36bf215be24da78a329771..9bfb847a21d0eacd0119ede58ab8a40ce6403554 100644
--- a/opossum/src/utils/griddata.rs
+++ b/opossum/src/utils/griddata.rs
@@ -204,7 +204,7 @@ pub fn meshgrid(x: &DVector<f64>, y: &DVector<f64>) -> OpmResult<(DMatrix<f64>,
 /// # Errors
 /// This function will return an error if
 ///  - `start` or `end` are not finite
-///  - `num` cannot be casted to usize.
+///  - `num` cannot be casted to float.
 ///
 /// # Panics
 /// This function panics if step cannot be casted from usize to T
diff --git a/opossum/src/utils/math_utils.rs b/opossum/src/utils/math_utils.rs
index dba9150623ce5be619901512bcfc2c092eca15d9..81496997f1e3c458a5b3a212060ffe441fda99c1 100644
--- a/opossum/src/utils/math_utils.rs
+++ b/opossum/src/utils/math_utils.rs
@@ -1,5 +1,8 @@
 //! various simple helper functions (e.g. number format conversion)
 
+use nalgebra::Point2;
+use uom::si::f64::Length;
+
 /// Convert a `usize` value to a `f64`.
 ///
 /// This function is used to avoid linter warnings (precision loss) which
@@ -31,3 +34,36 @@ pub const fn f64_to_usize(value: f64) -> usize {
     let newval = value as usize;
     newval
 }
+#[must_use]
+pub fn distance_2d_point(point1: &Point2<Length>, point2: &Point2<Length>) -> Length {
+    ((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y))
+        .sqrt()
+}
+
+#[cfg(test)]
+mod test {
+    use approx::assert_abs_diff_eq;
+
+    use crate::{millimeter, utils::math_utils::distance_2d_point};
+
+    #[test]
+    fn distance() {
+        let p1 = millimeter!(0.0, 0.0);
+        assert_eq!(
+            distance_2d_point(&p1, &millimeter!(0.0, 0.0)),
+            millimeter!(0.0)
+        );
+        assert_eq!(
+            distance_2d_point(&p1, &millimeter!(1.0, 0.0)),
+            millimeter!(1.0)
+        );
+        assert_eq!(
+            distance_2d_point(&p1, &millimeter!(-1.0, 0.0)),
+            millimeter!(1.0)
+        );
+        assert_abs_diff_eq!(
+            distance_2d_point(&p1, &millimeter!(1.0, 1.0)).value,
+            millimeter!(f64::sqrt(2.0)).value
+        );
+    }
+}
diff --git a/opossum/src/utils/test_helper.rs b/opossum/src/utils/test_helper.rs
index 85fde7d0abd11191eaa593116ffa9d4f1382058b..b66cdab5dbc15d9e271615b7d74cd093653660a3 100644
--- a/opossum/src/utils/test_helper.rs
+++ b/opossum/src/utils/test_helper.rs
@@ -1,18 +1,14 @@
 //! various helper functions used to simplify unit tests.
 //!
-//! **Note**: This module is only compiled and used during testing. Hence, ther might be no
+//! **Note**: This module is only compiled and used during testing. Hence, there might be no
 //! further documentation show up.
 
 #[cfg(test)]
 pub mod test_helper {
-    use log::Level;
 
-    pub fn check_warnings(expected_warnings: Vec<&str>) {
+    pub fn check_logs(level: log::Level, expected_warnings: Vec<&str>) {
         testing_logger::validate(|captured_logs| {
-            let captured_logs: Vec<_> = captured_logs
-                .iter()
-                .filter(|l| l.level == Level::Warn)
-                .collect();
+            let captured_logs: Vec<_> = captured_logs.iter().filter(|l| l.level == level).collect();
             assert_eq!(captured_logs.len(), expected_warnings.len());
             for log in captured_logs.iter().zip(expected_warnings.clone()) {
                 assert_eq!(log.0.body, log.1);