diff --git a/Cargo.lock b/Cargo.lock index 116149cf..0d059ea5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.28" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79faae4620f45232f599d9bc7b290f88247a0834162c4495ab2f02d60004adfb" +checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -20,9 +20,9 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.12.3" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" +checksum = "6cb10ed32c63247e4e39a8f42e8e30fb9442fbf7878c8e4a9849e7e381619bea" [[package]] name = "accesskit_consumer" @@ -74,9 +74,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -87,12 +87,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - [[package]] name = "ahash" version = "0.8.11" @@ -108,28 +102,27 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alsa" -version = "0.9.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +checksum = "37fe60779335388a88c01ac6c3be40304d1e349de3ada3b15f7808bb90fa9dce" dependencies = [ "alsa-sys", - "bitflags 2.6.0", - "cfg-if", + "bitflags 2.4.2", "libc", ] @@ -150,7 +143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" dependencies = [ "android-properties", - "bitflags 2.6.0", + "bitflags 2.4.2", "cc", "cesu8", "jni", @@ -193,48 +186,47 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -242,9 +234,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "approx" @@ -257,33 +249,34 @@ dependencies = [ [[package]] name = "arboard" -version = "3.4.1" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +checksum = "a2041f1943049c7978768d84e6d0fd95de98b76d6c4727b09e78ec253d29fa58" dependencies = [ "clipboard-win", "core-graphics", - "image 0.25.2", + "image", "log", - "objc2 0.5.2", - "objc2-app-kit", - "objc2-foundation", + "objc", + "objc-foundation", + "objc_id", "parking_lot", + "thiserror", "windows-sys 0.48.0", "x11rb", ] [[package]] name = "arrayref" -version = "0.3.9" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "as-raw-xcb-connection" @@ -312,26 +305,28 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener-strategy", + "event-listener 5.2.0", + "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.13.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ + "async-lock", "async-task", "concurrent-queue", - "fastrand 2.1.1", - "futures-lite 2.3.0", + "fastrand 2.0.1", + "futures-lite 2.2.0", "slab", ] @@ -343,17 +338,17 @@ checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" dependencies = [ "async-lock", "blocking", - "futures-lite 2.3.0", + "futures-lite 2.2.0", ] [[package]] name = "async-lock" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "event-listener 5.3.1", - "event-listener-strategy", + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -392,23 +387,23 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", + "cc", "cfg-if", "libc", - "miniz_oxide 0.8.0", + "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", ] [[package]] @@ -417,12 +412,6 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - [[package]] name = "bevy" version = "0.13.2" @@ -500,7 +489,7 @@ dependencies = [ "crossbeam-channel", "downcast-rs", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.2.0", "js-sys", "parking_lot", "ron", @@ -520,7 +509,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -573,7 +562,7 @@ dependencies = [ "bevy_render", "bevy_transform", "bevy_utils", - "bitflags 2.6.0", + "bitflags 2.4.2", "radsort", "serde", ] @@ -586,7 +575,7 @@ checksum = "f0e01f8343f391e2d6a63b368b82fb5b252ed43c8713fc87f9a8f2d59407dd00" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -634,7 +623,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -715,7 +704,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -724,7 +713,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21ecf404295055deb7fe037495891bc135ca10d46bc5b6c55f9ab7b7ebc61d31" dependencies = [ - "base64 0.21.7", + "base64", "bevy_animation", "bevy_app", "bevy_asset", @@ -759,7 +748,7 @@ dependencies = [ "bevy_transform", "gltf", "gltf-json", - "image 0.24.9", + "image", "serde", "serde_json", "thiserror", @@ -812,7 +801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bc0cd7c08a0188545e1126d07f83efcd23d7ab55f5df1ba0fb93e850838c3d2" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -903,7 +892,7 @@ dependencies = [ "proc-macro2", "quote", "rustc-hash", - "syn 2.0.77", + "syn 2.0.52", "toml_edit 0.21.1", ] @@ -933,7 +922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5822bcdc76304f6c9090c6377c279978feaf346e1d67ab7d42302557b8634fe" dependencies = [ "bevy", - "bitfield 0.14.0", + "bitfield", "interpolation", "thiserror", "wgpu-types", @@ -995,7 +984,7 @@ dependencies = [ "bevy_transform", "bevy_utils", "bevy_window", - "bitflags 2.6.0", + "bitflags 2.4.2", "bytemuck", "fixedbitset", "radsort", @@ -1009,7 +998,7 @@ version = "0.9.0" source = "git+https://github.com/luca-della-vedova/bevy_polyline?branch=luca/bevy_0.13_panic#4853b1c51a6d083fbba7bf2acb8e3fe15aa341d9" dependencies = [ "bevy", - "bitflags 2.6.0", + "bitflags 2.4.2", ] [[package]] @@ -1045,7 +1034,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", "uuid", ] @@ -1073,14 +1062,14 @@ dependencies = [ "bevy_transform", "bevy_utils", "bevy_window", - "bitflags 2.6.0", + "bitflags 2.4.2", "bytemuck", "codespan-reporting", "downcast-rs", "encase", - "futures-lite 2.3.0", + "futures-lite 2.2.0", "hexasphere", - "image 0.24.9", + "image", "js-sys", "ktx2", "naga", @@ -1103,7 +1092,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -1143,7 +1132,7 @@ dependencies = [ "bevy_render", "bevy_transform", "bevy_utils", - "bitflags 2.6.0", + "bitflags 2.4.2", "bytemuck", "fixedbitset", "guillotiere", @@ -1160,7 +1149,7 @@ checksum = "261f9c2b71604c560149a181044b049a5564c5c4f79f1d72abde608e8d57fc98" dependencies = [ "anyhow", "bevy", - "futures-lite 2.3.0", + "futures-lite 2.2.0", "stl_io", "thiserror", ] @@ -1175,7 +1164,7 @@ dependencies = [ "async-executor", "async-task", "concurrent-queue", - "futures-lite 2.3.0", + "futures-lite 2.2.0", "wasm-bindgen-futures", ] @@ -1266,7 +1255,7 @@ dependencies = [ "ahash", "bevy_utils_proc_macros", "getrandom", - "hashbrown 0.14.5", + "hashbrown 0.14.3", "nonmax", "petgraph", "smallvec", @@ -1284,7 +1273,7 @@ checksum = "bef158627f30503d5c18c20c60b444829f698d343516eeaf6eeee078c9a45163" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -1331,20 +1320,22 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.70.1" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.11.0", + "lazy_static", + "lazycell", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -1374,12 +1365,6 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" -[[package]] -name = "bitfield" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0" - [[package]] name = "bitflags" version = "1.3.2" @@ -1388,18 +1373,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" dependencies = [ "serde", ] [[package]] name = "blake3" -version = "1.5.4" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" dependencies = [ "arrayref", "arrayvec", @@ -1461,33 +1446,27 @@ dependencies = [ "objc2 0.4.1", ] -[[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2 0.5.2", -] - [[package]] name = "blocking" -version = "1.6.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ "async-channel", + "async-lock", "async-task", + "fastrand 2.0.1", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.2.0", "piper", + "tracing", ] [[package]] name = "bstr" -version = "1.10.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -1495,28 +1474,28 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.7.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -1525,17 +1504,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" -version = "1.7.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cairo-sys-rs" @@ -1553,7 +1526,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "log", "polling", "rustix", @@ -1575,13 +1548,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.20" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bcde016d64c21da4be18b655631e5ab6d3107607e71a73a9f53eb48aae23fb" +checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" dependencies = [ "jobserver", "libc", - "shlex", ] [[package]] @@ -1601,9 +1573,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.8" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" dependencies = [ "smallvec", "target-lexicon", @@ -1621,29 +1593,23 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] name = "chrono-tz" -version = "0.9.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" dependencies = [ "chrono", "chrono-tz-build", @@ -1652,9 +1618,9 @@ dependencies = [ [[package]] name = "chrono-tz-build" -version = "0.3.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" dependencies = [ "parse-zoneinfo", "phf", @@ -1663,20 +1629,20 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", - "libloading 0.8.5", + "libloading 0.8.3", ] [[package]] name = "clap" -version = "4.5.17" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -1684,9 +1650,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", @@ -1696,27 +1662,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ - "heck 0.5.0", + "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clipboard-win" -version = "5.4.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297" dependencies = [ "error-code", ] @@ -1739,9 +1705,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "com" @@ -1776,9 +1742,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.7" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ "bytes", "memchr", @@ -1786,9 +1752,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.5.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] @@ -1821,9 +1787,9 @@ checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" [[package]] name = "const_panic" -version = "0.2.9" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7782af8f90fe69a4bb41e460abe1727d493403d8b2cc43201a3a3e906b24379f" +checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" [[package]] name = "const_soft_float" @@ -1833,15 +1799,15 @@ checksum = "87ca1caa64ef4ed453e68bb3db612e51cf1b2f5b871337f0fcab1c8f87cc3dff" [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "constgebra" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1aaf9b65849a68662ac6c0810c8893a765c960b907dd7cfab9c4a50bf764fbc" +checksum = "edd23e864550e6dafc1e41ac78ce4f1ccddc8672b40c403524a04ff3f0518420" dependencies = [ "const_soft_float", ] @@ -1867,15 +1833,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-graphics" -version = "0.23.2" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -1908,9 +1874,9 @@ dependencies = [ [[package]] name = "coreaudio-sys" -version = "0.2.16" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce857aa0b77d77287acc1ac3e37a05a8c95a2af3647d23b15f263bdaeb7562b" +checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9" dependencies = [ "bindgen", ] @@ -1940,33 +1906,33 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "critical-section" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -1992,9 +1958,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -2024,8 +1990,8 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" dependencies = [ - "bitflags 2.6.0", - "libloading 0.8.5", + "bitflags 2.4.2", + "libloading 0.8.3", "winapi", ] @@ -2037,9 +2003,9 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "derive_more" @@ -2049,14 +2015,14 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] name = "deunicode" -version = "1.6.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" +checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94" [[package]] name = "digest" @@ -2101,23 +2067,23 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.5", + "libloading 0.8.3", ] [[package]] name = "document-features" -version = "0.2.10" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" dependencies = [ "litrs", ] [[package]] name = "downcast-rs" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "earcutr" @@ -2166,9 +2132,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "emath" @@ -2208,7 +2174,7 @@ checksum = "92959a9e8d13eaa13b8ae8c7b583c3bf1669ca7a8e7708a088d12587ba86effc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -2244,9 +2210,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2254,15 +2220,15 @@ dependencies = [ [[package]] name = "error-code" -version = "3.3.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" [[package]] name = "euclid" -version = "0.22.11" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" dependencies = [ "num-traits", ] @@ -2275,9 +2241,20 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.3.1" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" dependencies = [ "concurrent-queue", "parking", @@ -2286,11 +2263,21 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 5.3.1", + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.2.0", "pin-project-lite", ] @@ -2304,7 +2291,7 @@ dependencies = [ "flume", "half", "lebe", - "miniz_oxide 0.7.4", + "miniz_oxide", "rayon-core", "smallvec", "zune-inflate", @@ -2321,9 +2308,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fdeflate" @@ -2342,12 +2329,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -2404,7 +2391,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -2487,11 +2474,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" dependencies = [ - "fastrand 2.1.1", + "fastrand 2.0.1", "futures-core", "futures-io", "parking", @@ -2506,7 +2493,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -2629,9 +2616,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "js-sys", @@ -2652,9 +2639,9 @@ dependencies = [ [[package]] name = "gilrs" -version = "0.10.10" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a556964c6d62458084356ce9770676f5104bd667e12e9a795691076e8a17c5cf" +checksum = "d8b2e57a9cb946b5d04ae8638c5f554abb5a9f82c4c950fd5b1fee6d119592fb" dependencies = [ "fnv", "gilrs-core", @@ -2665,9 +2652,9 @@ dependencies = [ [[package]] name = "gilrs-core" -version = "0.5.15" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732dadc05170599ddec9a89653f10d7a2af54da9181b3fa6e2bd49907ec8f7e4" +checksum = "0af1827b7dd2f36d740ae804c1b3ea0d64c12533fb61ff91883005143a0e8c5a" dependencies = [ "core-foundation", "inotify", @@ -2681,14 +2668,14 @@ dependencies = [ "vec_map", "wasm-bindgen", "web-sys", - "windows 0.58.0", + "windows 0.52.0", ] [[package]] name = "gimli" -version = "0.31.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gio-sys" @@ -2742,24 +2729,24 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.15" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.6", + "regex-syntax 0.8.2", ] [[package]] name = "globwalk" -version = "0.9.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" dependencies = [ - "bitflags 2.6.0", + "bitflags 1.3.2", "ignore", "walkdir", ] @@ -2778,9 +2765,9 @@ dependencies = [ [[package]] name = "gltf" -version = "1.4.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ce1918195723ce6ac74e80542c5a96a40c2b26162c1957a5cd70799b8cacf7" +checksum = "3b78f069cf941075835822953c345b9e1edd67ae347b81ace3aea9de38c2ef33" dependencies = [ "byteorder", "gltf-json", @@ -2790,21 +2777,21 @@ dependencies = [ [[package]] name = "gltf-derive" -version = "1.4.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14070e711538afba5d6c807edb74bcb84e5dbb9211a3bf5dea0dfab5b24f4c51" +checksum = "438ffe1a5540d75403feaf23636b164e816e93f6f03131674722b3886ce32a57" dependencies = [ "inflections", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] name = "gltf-json" -version = "1.4.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6176f9d60a7eab0a877e8e96548605dedbde9190a7ae1e80bbcc1c9af03ab14" +checksum = "655951ba557f2bc69ea4b0799446bae281fa78efae6319968bdd2c3e9a06d8e1" dependencies = [ "gltf-derive", "serde", @@ -2823,9 +2810,9 @@ dependencies = [ [[package]] name = "glyph_brush_layout" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1e288bfd2f6c0313f78bf5aa538356ad481a3bb97e9b7f93220ab0066c5992" +checksum = "cc32c2334f00ca5ac3695c5009ae35da21da8c62d255b5b96d56e2597a637a38" dependencies = [ "ab_glyph", "approx", @@ -2849,7 +2836,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "gpu-alloc-types", ] @@ -2859,7 +2846,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", ] [[package]] @@ -2881,9 +2868,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "gpu-descriptor-types", - "hashbrown 0.14.5", + "hashbrown 0.14.3", ] [[package]] @@ -2892,7 +2879,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", ] [[package]] @@ -2937,8 +2924,8 @@ dependencies = [ "crossbeam-channel", "dirs", "ehttp", - "futures-lite 2.3.0", - "itertools 0.13.0", + "futures-lite 2.2.0", + "itertools 0.11.0", "serde", "serde_json", ] @@ -2970,9 +2957,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -2985,10 +2972,10 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "com", "libc", - "libloading 0.8.5", + "libloading 0.8.3", "thiserror", "widestring", "winapi", @@ -3018,9 +3005,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.5.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -3064,9 +3051,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -3108,15 +3095,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.23" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.6", "same-file", "walkdir", "winapi-util", @@ -3140,19 +3127,6 @@ dependencies = [ "tiff", ] -[[package]] -name = "image" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" -dependencies = [ - "bytemuck", - "byteorder-lite", - "num-traits", - "png", - "tiff", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -3165,12 +3139,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.14.3", ] [[package]] @@ -3201,9 +3175,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.13" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] @@ -3216,20 +3190,14 @@ checksum = "e9c13ae9d91148fcb4aab6654c4c2a7d02a15395ea9e23f65170f175f8b269ce" [[package]] name = "io-kit-sys" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +checksum = "4769cb30e5dcf1710fc6730d3e94f78c47723a014a567de385e113c737394640" dependencies = [ "core-foundation-sys", "mach2", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itertools" version = "0.11.0" @@ -3250,9 +3218,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jni" @@ -3278,9 +3246,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] @@ -3296,9 +3264,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -3310,7 +3278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.5", + "libloading 0.8.3", "pkg-config", ] @@ -3331,9 +3299,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "lebe" @@ -3354,9 +3328,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" @@ -3370,12 +3344,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -3386,23 +3360,24 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" -version = "0.0.2" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "libc", "redox_syscall 0.4.1", ] [[package]] name = "libredox" -version = "0.1.3" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "libc", + "redox_syscall 0.4.1", ] [[package]] @@ -3423,9 +3398,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "litrs" @@ -3435,9 +3410,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -3445,9 +3420,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "mach2" @@ -3478,9 +3453,9 @@ dependencies = [ [[package]] name = "matrixmultiply" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" dependencies = [ "autocfg", "rawpointer", @@ -3488,9 +3463,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" @@ -3507,7 +3482,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "block", "core-graphics-types", "foreign-types", @@ -3524,23 +3499,14 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", ] -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - [[package]] name = "naga" version = "0.19.2" @@ -3548,10 +3514,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" dependencies = [ "bit-set", - "bitflags 2.6.0", + "bitflags 2.4.2", "codespan-reporting", "hexf-parse", - "indexmap 2.5.0", + "indexmap 2.2.5", "log", "num-traits", "pp-rs", @@ -3571,11 +3537,11 @@ dependencies = [ "bit-set", "codespan-reporting", "data-encoding", - "indexmap 2.5.0", + "indexmap 2.2.5", "naga", "once_cell", "regex", - "regex-syntax 0.8.4", + "regex-syntax 0.8.2", "rustc-hash", "thiserror", "tracing", @@ -3584,9 +3550,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.6" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" +checksum = "3ea4908d4f23254adda3daa60ffef0f1ac7b8c3e9a864cf3cc154b251908a2ef" dependencies = [ "approx", "matrixmultiply", @@ -3600,13 +3566,13 @@ dependencies = [ [[package]] name = "nalgebra-macros" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 1.0.109", ] [[package]] @@ -3615,7 +3581,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "jni-sys", "log", "ndk-sys", @@ -3641,13 +3607,12 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "cfg-if", - "cfg_aliases 0.2.1", "libc", ] @@ -3694,9 +3659,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] @@ -3709,7 +3674,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -3723,19 +3688,20 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ + "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.19" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -3743,23 +3709,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -3816,56 +3782,6 @@ dependencies = [ "objc2-encode 3.0.0", ] -[[package]] -name = "objc2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" -dependencies = [ - "objc-sys 0.3.5", - "objc2-encode 4.0.3", -] - -[[package]] -name = "objc2-app-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" -dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "libc", - "objc2 0.5.2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-image" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation", - "objc2-metal", -] - [[package]] name = "objc2-encode" version = "2.0.0-pre.2" @@ -3881,49 +3797,6 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" -[[package]] -name = "objc2-encode" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" - -[[package]] -name = "objc2-foundation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" -dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "libc", - "objc2 0.5.2", -] - -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" -dependencies = [ - "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation", - "objc2-metal", -] - [[package]] name = "objc_exception" version = "0.1.2" @@ -3944,9 +3817,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -4012,9 +3885,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owned_ttf_parser" -version = "0.24.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490d3a563d3122bf7c911a59b0add9389e5ec0f5f0c3ac6b91ff235a0e6a7f90" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" dependencies = [ "ttf-parser", ] @@ -4033,15 +3906,15 @@ dependencies = [ [[package]] name = "parking" -version = "2.2.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", @@ -4049,31 +3922,31 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.4", + "redox_syscall 0.4.1", "smallvec", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] name = "parse-zoneinfo" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" dependencies = [ "regex", ] [[package]] name = "paste" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathdiff" @@ -4089,9 +3962,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.12" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" dependencies = [ "memchr", "thiserror", @@ -4100,9 +3973,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.12" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" dependencies = [ "pest", "pest_generator", @@ -4110,22 +3983,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.12" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] name = "pest_meta" -version = "2.7.12" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" dependencies = [ "once_cell", "pest", @@ -4134,12 +4007,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.5" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.2.5", ] [[package]] @@ -4182,9 +4055,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -4194,12 +4067,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", - "fastrand 2.1.1", + "fastrand 2.0.1", "futures-io", ] @@ -4219,14 +4092,14 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.4", + "miniz_oxide", ] [[package]] name = "polling" -version = "3.7.3" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" dependencies = [ "cfg-if", "concurrent-queue", @@ -4234,7 +4107,7 @@ dependencies = [ "pin-project-lite", "rustix", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4248,12 +4121,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "presser" @@ -4263,18 +4133,18 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.22.21", + "toml_edit 0.21.1", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -4305,18 +4175,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "radsort" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "019b4b213425016d7d84a153c4c73afb0946fbb4840e4eece7ba8848b9d6da22" +checksum = "17fd96390ed3feda12e1dfe2645ed587e0bea749e319333f104a33ff62f77a0b" [[package]] name = "rand" @@ -4416,36 +4286,27 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" -dependencies = [ - "bitflags 2.6.0", -] - [[package]] name = "redox_users" -version = "0.4.6" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "libredox 0.1.3", + "libredox 0.0.1", "thiserror", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.6", + "regex-syntax 0.8.2", ] [[package]] @@ -4459,13 +4320,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.2", ] [[package]] @@ -4476,9 +4337,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "renderdoc-sys" @@ -4528,6 +4389,7 @@ dependencies = [ name = "rmf_site_editor" version = "0.0.1" dependencies = [ + "anyhow", "bevy", "bevy_egui", "bevy_gltf_export", @@ -4538,7 +4400,7 @@ dependencies = [ "bevy_obj", "bevy_polyline", "bevy_stl", - "bitfield 0.17.0", + "bitfield", "clap", "console_error_panic_hook", "crossbeam-channel", @@ -4547,7 +4409,7 @@ dependencies = [ "futures-lite 1.13.0", "geo", "gz-fuel", - "itertools 0.13.0", + "itertools 0.11.0", "nalgebra", "pathdiff", "rfd", @@ -4610,8 +4472,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64 0.21.7", - "bitflags 2.6.0", + "base64", + "bitflags 2.4.2", "serde", "serde_derive", ] @@ -4629,9 +4491,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -4641,20 +4503,20 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -4663,12 +4525,11 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" dependencies = [ "log", - "once_cell", "ring", "rustls-pki-types", "rustls-webpki", @@ -4678,15 +4539,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", "rustls-pki-types", @@ -4706,15 +4567,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "safe_arch" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" dependencies = [ "bytemuck", ] @@ -4767,47 +4628,46 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", - "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -4895,9 +4755,9 @@ dependencies = [ [[package]] name = "slug" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" +checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" dependencies = [ "deunicode", "wasm-bindgen", @@ -4905,9 +4765,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" dependencies = [ "serde", ] @@ -4918,7 +4778,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "calloop", "calloop-wayland-source", "cursor-icon", @@ -4939,20 +4799,20 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49" dependencies = [ "serde", ] [[package]] name = "spade" -version = "2.12.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f5ef1f863aca7d1d7dda7ccfc36a0a4279bd6d3c375176e5e0712e25cb4889" +checksum = "61addf9117b11d1f5b4bf6fe94242ba25f59d2d4b2080544b771bd647024fd00" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.14.3", "num-traits", "robust", "smallvec", @@ -4973,7 +4833,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", ] [[package]] @@ -5006,21 +4866,21 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "strsim" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "subtle" -version = "2.6.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svg_fmt" -version = "0.4.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e16a0f46cf5fd675563ef54f26e83e20f2366bcf027bcb3cc3ed2b98aaf2ca" +checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" [[package]] name = "syn" @@ -5035,9 +4895,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -5060,12 +4920,12 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.2.2" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" dependencies = [ "cfg-expr", - "heck 0.5.0", + "heck 0.4.1", "pkg-config", "toml", "version-compare", @@ -5073,9 +4933,9 @@ dependencies = [ [[package]] name = "taffy" -version = "0.3.19" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1315457ccd9c3def787a18fae91914e623e4dcff019b64ce39f5268ded53d3d" +checksum = "3c2287b6d7f721ada4cddf61ade5e760b2c6207df041cac9bfaa192897362fd3" dependencies = [ "arrayvec", "grid", @@ -5085,15 +4945,15 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tera" -version = "1.20.0" +version = "1.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee" +checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" dependencies = [ "chrono", "chrono-tz", @@ -5122,22 +4982,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -5188,9 +5048,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -5203,9 +5063,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tobj" -version = "4.0.2" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd4ba05f29e4c65b6c0c11a58b6465ffa820bac890d76ad407b4e81d8372e8" +checksum = "5f7ca3ec0405b0f2f95e0dbcced28882190dc79b0dac63ff82533d256d770223" dependencies = [ "ahash", "log", @@ -5213,9 +5073,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.40.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "pin-project-lite", @@ -5223,21 +5083,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.21", + "toml_edit 0.22.6", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -5248,22 +5108,22 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.2.5", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.2.5", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.18", + "winnow 0.6.5", ] [[package]] @@ -5285,7 +5145,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] @@ -5351,9 +5211,9 @@ dependencies = [ [[package]] name = "ttf-parser" -version = "0.24.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" [[package]] name = "twox-hash" @@ -5441,36 +5301,36 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "untrusted" @@ -5493,25 +5353,26 @@ dependencies = [ [[package]] name = "ureq" -version = "2.10.1" +version = "2.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" dependencies = [ - "base64 0.22.1", + "base64", "flate2", "log", "once_cell", "rustls", "rustls-pki-types", + "rustls-webpki", "url", "webpki-roots", ] [[package]] name = "url" -version = "2.5.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -5520,9 +5381,9 @@ dependencies = [ [[package]] name = "utf8parse" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "utm" @@ -5532,9 +5393,9 @@ checksum = "95b09e3b3a0abd1ccb77673a6b7b8875d9d1c80626154add451cf18392dc4c3c" [[package]] name = "uuid" -version = "1.10.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "getrandom", "serde", @@ -5554,21 +5415,21 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version-compare" -version = "0.2.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" [[package]] name = "version_check" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "waker-fn" -version = "1.2.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" @@ -5608,15 +5469,15 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -5642,7 +5503,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5673,7 +5534,7 @@ version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "rustix", "wayland-backend", "wayland-scanner", @@ -5685,7 +5546,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "cursor-icon", "wayland-backend", ] @@ -5707,7 +5568,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5719,7 +5580,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5732,7 +5593,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5764,9 +5625,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -5784,9 +5645,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.15" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" +checksum = "d1b04c569c83a9bb971dd47ec6fd48753315f4bf989b9b04a2e7ca4d7f0dc950" dependencies = [ "core-foundation", "home", @@ -5801,9 +5662,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.5" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" dependencies = [ "rustls-pki-types", ] @@ -5822,7 +5683,7 @@ checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01" dependencies = [ "arrayvec", "cfg-if", - "cfg_aliases 0.1.1", + "cfg_aliases", "js-sys", "log", "naga", @@ -5847,10 +5708,10 @@ checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.6.0", - "cfg_aliases 0.1.1", + "bitflags 2.4.2", + "cfg_aliases", "codespan-reporting", - "indexmap 2.5.0", + "indexmap 2.2.5", "log", "naga", "once_cell", @@ -5875,9 +5736,9 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.6.0", + "bitflags 2.4.2", "block", - "cfg_aliases 0.1.1", + "cfg_aliases", "core-graphics-types", "d3d12", "glow", @@ -5889,7 +5750,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.5", + "libloading 0.8.3", "log", "metal", "naga", @@ -5916,16 +5777,16 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "js-sys", "web-sys", ] [[package]] name = "wide" -version = "0.7.28" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" +checksum = "89beec544f246e679fc25490e3f8e08003bc4bf612068f325120dad4cea02c1c" dependencies = [ "bytemuck", "safe_arch", @@ -5933,9 +5794,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.1.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" [[package]] name = "winapi" @@ -5955,11 +5816,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ - "windows-sys 0.59.0", + "winapi", ] [[package]] @@ -5974,8 +5835,8 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-implement 0.48.0", - "windows-interface 0.48.0", + "windows-implement", + "windows-interface", "windows-targets 0.48.5", ] @@ -5986,7 +5847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core 0.52.0", - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -5996,17 +5857,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ "windows-core 0.54.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -6015,7 +5866,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -6024,21 +5875,8 @@ version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" dependencies = [ - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings", - "windows-targets 0.52.6", + "windows-result", + "windows-targets 0.52.4", ] [[package]] @@ -6052,17 +5890,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - [[package]] name = "windows-interface" version = "0.48.0" @@ -6074,43 +5901,13 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -6137,16 +5934,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -6181,18 +5969,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -6209,9 +5996,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -6227,9 +6014,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -6245,15 +6032,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -6269,9 +6050,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -6287,9 +6068,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -6305,9 +6086,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -6323,9 +6104,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winit" @@ -6336,10 +6117,10 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.6.0", + "bitflags 2.4.2", "bytemuck", "calloop", - "cfg_aliases 0.1.1", + "cfg_aliases", "core-foundation", "core-graphics", "cursor-icon", @@ -6386,9 +6167,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] @@ -6406,14 +6187,14 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading 0.8.5", + "libloading 0.8.3", "once_cell", "rustix", "x11rb-protocol", @@ -6421,9 +6202,9 @@ dependencies = [ [[package]] name = "x11rb-protocol" -version = "0.13.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" [[package]] name = "xcursor" @@ -6443,7 +6224,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.4.2", "dlib", "log", "once_cell", @@ -6458,9 +6239,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.22" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" [[package]] name = "xmltree" @@ -6506,30 +6287,29 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.52", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" [[package]] name = "zune-inflate" diff --git a/rmf_site_editor/Cargo.toml b/rmf_site_editor/Cargo.toml index ca4d1601..44271f17 100644 --- a/rmf_site_editor/Cargo.toml +++ b/rmf_site_editor/Cargo.toml @@ -50,6 +50,7 @@ pathdiff = "*" tera = "1.19.1" ehttp = { version = "0.4", features = ["native-async"] } nalgebra = "0.32.5" +anyhow = "*" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] clap = { version = "4.0.10", features = ["color", "derive", "help", "usage", "suggestions"] } diff --git a/rmf_site_editor/src/interaction/anchor.rs b/rmf_site_editor/src/interaction/anchor.rs index 5a9f2ca0..beef344b 100644 --- a/rmf_site_editor/src/interaction/anchor.rs +++ b/rmf_site_editor/src/interaction/anchor.rs @@ -23,6 +23,12 @@ use crate::{ }; use bevy::prelude::*; +/// Use this resource to indicate whether anchors should be constantly highlighted. +/// This is used during anchor selection modes to make it easier for users to know +/// where selectable anchors are. +#[derive(Clone, Copy, Debug, Resource)] +pub struct HighlightAnchors(pub bool); + #[derive(Component, Debug, Clone, Copy)] pub struct AnchorVisualization { pub body: Entity, @@ -37,6 +43,7 @@ pub fn add_anchor_visual_cues( >, categories: Query<&Category>, site_assets: Res, + highlight: Res, ) { for (e, parent, subordinate, anchor) in &new_anchors { let body_mesh = match categories.get(parent.get()).unwrap() { @@ -65,12 +72,29 @@ pub fn add_anchor_visual_cues( .insert(OutlineVisualization::Anchor { body }) .add_child(body); - // 3D anchors should always be visible with arrow cue meshes - if anchor.is_3D() { - entity_commands.insert(VisualCue::outline()); + let cue = if anchor.is_3D() { + // 3D anchors should always be visible with arrow cue meshes + VisualCue::outline() } else { - entity_commands.insert(VisualCue::outline().irregular()); - } + let mut cue = VisualCue::outline().irregular(); + cue.xray.set_always(highlight.0); + cue + }; + + entity_commands.insert(cue); + } +} + +pub fn on_highlight_anchors_change( + highlight: Res, + mut anchor_visual_cues: Query<&mut VisualCue, With>, +) { + if !highlight.is_changed() { + return; + } + + for mut cue in &mut anchor_visual_cues { + cue.xray.set_always(highlight.0); } } @@ -110,12 +134,12 @@ pub fn update_anchor_proximity_xray( } let p_c = match intersect_ground_params.ground_plane_intersection() { - Some(p) => p, + Some(p) => p.translation, None => return, }; for (anchor_tf, mut cue) in &mut anchors { - // TODO(MXG): Make the proximity range configurable + // TODO(@mxgrey): Make the proximity range configurable let proximity = { // We make the xray effect a little "sticky" so that there isn't an // ugly flicker for anchors that are right at the edge of the @@ -156,22 +180,6 @@ pub fn update_unassigned_anchor_cues( } } -pub fn update_anchor_cues_for_mode( - mode: Res, - mut anchors: Query<&mut VisualCue, With>, -) { - if !mode.is_changed() { - return; - } - - let anchor_always_visible = mode.is_selecting_anchor(); - for mut cue in &mut anchors { - if cue.xray.always() != anchor_always_visible { - cue.xray.set_always(anchor_always_visible); - } - } -} - pub fn update_anchor_visual_cues( mut commands: Commands, mut anchors: Query< @@ -191,10 +199,11 @@ pub fn update_anchor_visual_cues( mut visibility: Query<&mut Visibility>, mut materials: Query<&mut Handle>, deps: Query<&Dependents>, - cursor: Res, + mut cursor: ResMut, site_assets: Res, interaction_assets: Res, debug_mode: Option>, + gizmo_blockers: Res, ) { for ( a, @@ -235,8 +244,10 @@ pub fn update_anchor_visual_cues( .set_support_hovered(!hovered.support_hovering.is_empty()); } - if hovered.is_hovered { - set_visibility(cursor.frame, &mut visibility, false); + if hovered.is_hovered && !gizmo_blockers.blocking() { + cursor.add_blocker(a, &mut visibility); + } else { + cursor.remove_blocker(a, &mut visibility); } if hovered.cue() && selected.cue() { @@ -303,7 +314,7 @@ pub fn update_anchor_visual_cues( } // NOTE(MXG): Currently only anchors ever have support cues, so we filter down -// to entities with AnchorVisualCues. We will need to broaden that if any other +// to entities with AnchorVisualization. We will need to broaden that if any other // visual cue types ever have a supporting role. pub fn remove_deleted_supports_from_visual_cues( mut hovered: Query<&mut Hovered, With>, diff --git a/rmf_site_editor/src/interaction/assets.rs b/rmf_site_editor/src/interaction/assets.rs index 36b29400..daf17cd5 100644 --- a/rmf_site_editor/src/interaction/assets.rs +++ b/rmf_site_editor/src/interaction/assets.rs @@ -202,11 +202,14 @@ impl InteractionAssets { commands.entity(drag_parent).with_children(|parent| { for (polyline, material) in &self.centimeter_finite_grid { - parent.spawn(PolylineBundle { - polyline: polyline.clone(), - material: material.clone(), - ..default() - }); + parent.spawn(( + PolylineBundle { + polyline: polyline.clone(), + material: material.clone(), + ..default() + }, + DisableXray, + )); } }); diff --git a/rmf_site_editor/src/interaction/camera_controls/cursor.rs b/rmf_site_editor/src/interaction/camera_controls/cursor.rs index c26f0584..da96fb0a 100644 --- a/rmf_site_editor/src/interaction/camera_controls/cursor.rs +++ b/rmf_site_editor/src/interaction/camera_controls/cursor.rs @@ -20,7 +20,7 @@ use super::{ CameraCommandType, CameraControls, ProjectionMode, MAX_FOV, MAX_SCALE, MIN_FOV, MIN_SCALE, }; use crate::interaction::SiteRaycastSet; -use bevy::input::mouse::MouseWheel; +use bevy::input::mouse::{MouseScrollUnit, MouseWheel}; use bevy::prelude::*; use bevy::window::PrimaryWindow; use bevy_mod_raycast::deferred::RaycastSource; @@ -92,15 +92,10 @@ pub fn update_cursor_command( // Scroll input let mut scroll_motion = 0.0; for ev in mouse_wheel.read() { - #[cfg(not(target_arch = "wasm32"))] - { - scroll_motion += ev.y; - } - #[cfg(target_arch = "wasm32")] - { - // scrolling in wasm is a different beast - scroll_motion += 0.4 * ev.y / ev.y.abs(); - } + scroll_motion += match ev.unit { + MouseScrollUnit::Line => ev.y, + MouseScrollUnit::Pixel => ev.y / 100.0, + }; } // Command type, return if inactive diff --git a/rmf_site_editor/src/interaction/camera_controls/keyboard.rs b/rmf_site_editor/src/interaction/camera_controls/keyboard.rs index 6dba913d..32bdacd0 100644 --- a/rmf_site_editor/src/interaction/camera_controls/keyboard.rs +++ b/rmf_site_editor/src/interaction/camera_controls/keyboard.rs @@ -187,7 +187,7 @@ pub fn update_keyboard_command( // Set camera selection as orbit center, discard once orbit operation complete let camera_selection = match keyboard_command.camera_selection { - Some(camera_selection) => camera_selection, + Some(camera_selection) => Some(camera_selection), None => get_camera_selected_point( &camera, &camera_global_transform, @@ -195,6 +195,12 @@ pub fn update_keyboard_command( immediate_raycast, ), }; + + let Some(camera_selection) = camera_selection else { + warn!("Point could not be calculated for camera"); + return; + }; + if command_type == CameraCommandType::Orbit { camera_controls.orbit_center = Some(camera_selection); } diff --git a/rmf_site_editor/src/interaction/camera_controls/utils.rs b/rmf_site_editor/src/interaction/camera_controls/utils.rs index bd0885f7..f09e8fa9 100644 --- a/rmf_site_editor/src/interaction/camera_controls/utils.rs +++ b/rmf_site_editor/src/interaction/camera_controls/utils.rs @@ -71,12 +71,11 @@ pub fn get_camera_selected_point( camera_global_transform: &GlobalTransform, user_camera_display: Res, mut immediate_raycast: Raycast, -) -> Vec3 { +) -> Option { // Assume that the camera spans the full window, covered by egui panels let available_viewport_center = user_camera_display.region.center(); - let camera_ray = camera - .viewport_to_world(camera_global_transform, available_viewport_center) - .expect("Active camera does not have a valid ray from center of its viewport"); + let camera_ray = + camera.viewport_to_world(camera_global_transform, available_viewport_center)?; let camera_ray = Ray3d::new(camera_ray.origin, *camera_ray.direction); let raycast_setting = RaycastSettings::default() .always_early_exit() @@ -86,13 +85,13 @@ pub fn get_camera_selected_point( let intersections = immediate_raycast.cast_ray(camera_ray, &raycast_setting); if intersections.len() > 0 { let (_, intersection_data) = &intersections[0]; - return intersection_data.position(); + return Some(intersection_data.position()); } else { - return get_groundplane_else_default_selection( + return Some(get_groundplane_else_default_selection( camera_ray.origin, *camera_ray.direction, *camera_ray.direction, - ); + )); } } diff --git a/rmf_site_editor/src/interaction/cursor.rs b/rmf_site_editor/src/interaction/cursor.rs index 891a7bb5..d4590134 100644 --- a/rmf_site_editor/src/interaction/cursor.rs +++ b/rmf_site_editor/src/interaction/cursor.rs @@ -21,12 +21,12 @@ use crate::{ site::{AnchorBundle, Pending, SiteAssets, Trashcan}, }; use bevy::{ecs::system::SystemParam, prelude::*, window::PrimaryWindow}; -use bevy_mod_raycast::{ - deferred::RaycastMesh, - deferred::RaycastSource, - primitives::rays::{ray_from_screenspace, to_aligned_transform}, +use bevy_mod_raycast::primitives::{ + rays::{intersects_primitive, ray_from_screenspace}, + Primitive3d, }; -use rmf_site_format::{FloorMarker, Model, ModelMarker, PrimitiveShape, WallMarker, WorkcellModel}; + +use rmf_site_format::{FloorMarker, Model, WallMarker, WorkcellModel}; use std::collections::HashSet; /// A resource that keeps track of the unique entities that play a role in @@ -97,6 +97,14 @@ impl Cursor { } } + pub fn clear_blockers(&mut self, visibility: &mut Query<&mut Visibility>) { + let had_blockers = !self.blockers.is_empty(); + self.blockers.clear(); + if had_blockers { + self.toggle_visibility(visibility); + } + } + fn toggle_visibility(&mut self, visibility: &mut Query<&mut Visibility>) { if let Ok(mut v) = visibility.get_mut(self.frame) { let new_visible = if self.should_be_visible() { @@ -110,9 +118,12 @@ impl Cursor { } } - fn remove_preview(&mut self, commands: &mut Commands) { + pub fn remove_preview(&mut self, commands: &mut Commands) { if let Some(current_preview) = self.preview_model { - commands.entity(current_preview).set_parent(self.trashcan); + commands.get_entity(current_preview).map(|mut e_mut| { + e_mut.set_parent(self.trashcan); + }); + self.preview_model = None; } } @@ -287,7 +298,23 @@ pub struct IntersectGroundPlaneParams<'w, 's> { } impl<'w, 's> IntersectGroundPlaneParams<'w, 's> { - pub fn ground_plane_intersection(&self) -> Option { + pub fn ground_plane_intersection(&self) -> Option { + let ground_plane = Primitive3d::Plane { + point: Vec3::ZERO, + normal: Vec3::Z, + }; + self.primitive_intersection(ground_plane) + } + + pub fn frame_plane_intersection(&self, frame: Entity) -> Option { + let tf = self.global_transforms.get(frame).ok()?; + let affine = tf.affine(); + let point = affine.translation.into(); + let normal = affine.matrix3.col(2).into(); + self.primitive_intersection(Primitive3d::Plane { point, normal }) + } + + pub fn primitive_intersection(&self, primitive: Primitive3d) -> Option { let window = self.primary_windows.get_single().ok()?; let cursor_position = window.cursor_position()?; let e_active_camera = self.camera_controls.active_camera(); @@ -295,145 +322,17 @@ impl<'w, 's> IntersectGroundPlaneParams<'w, 's> { let camera_tf = self.global_transforms.get(e_active_camera).ok()?; let primary_window = self.primary_window.get_single().ok()?; let ray = ray_from_screenspace(cursor_position, active_camera, camera_tf, primary_window)?; - let n_p = Vec3::Z; - let n_r = ray.direction; - let denom = n_p.dot(*n_r); - if denom.abs() < 1e-3 { - // Too close to parallel - return None; - } - - Some(ray.origin - n_r * ray.origin.dot(n_p) / denom) - } -} - -pub fn update_cursor_transform( - mode: Res, - cursor: Res, - raycast_sources: Query<&RaycastSource>, - models: Query<(), Or<(With, With)>>, - mut transforms: Query<&mut Transform>, - hovering: Res, - intersect_ground_params: IntersectGroundPlaneParams, - mut visibility: Query<&mut Visibility>, -) { - match &*mode { - InteractionMode::Inspect => { - // TODO(luca) this will not work if more than one raycast source exist - let Ok(source) = raycast_sources.get_single() else { - return; - }; - let intersection = match source.get_nearest_intersection() { - Some((_, intersection)) => intersection, - None => { - return; - } - }; - - let mut transform = match transforms.get_mut(cursor.frame) { - Ok(transform) => transform, - Err(_) => { - return; - } - }; - - let ray = Ray3d::new(intersection.position(), intersection.normal()); - *transform = Transform::from_matrix(to_aligned_transform(ray, [0., 0., 1.].into())); - } - InteractionMode::SelectAnchor(_) => { - let intersection = match intersect_ground_params.ground_plane_intersection() { - Some(intersection) => intersection, - None => { - return; - } - }; - - let mut transform = match transforms.get_mut(cursor.frame) { - Ok(transform) => transform, - Err(_) => { - return; - } - }; - - *transform = Transform::from_translation(intersection); - } - // TODO(luca) snap to features of meshes - InteractionMode::SelectAnchor3D(_mode) => { - let mut transform = match transforms.get_mut(cursor.frame) { - Ok(transform) => transform, - Err(_) => { - error!("No cursor transform found"); - return; - } - }; - - let Ok(source) = raycast_sources.get_single() else { - return; - }; - - // Check if there is an intersection to a mesh, if there isn't fallback to ground plane - if let Some((_, intersection)) = source.get_nearest_intersection() { - let Some(triangle) = intersection.triangle() else { - return; - }; - // Make sure we are hovering over a model and not anything else (i.e. anchor) - match cursor.preview_model { - None => { - if hovering.0.and_then(|e| models.get(e).ok()).is_some() { - // Find the closest triangle vertex - // TODO(luca) Also snap to edges of triangles or just disable altogether and snap - // to area, then populate a MeshConstraint component to be used by downstream - // spawning methods - // TODO(luca) there must be a better way to find a minimum given predicate in Rust - let triangle_vecs = vec![triangle[1], triangle[2]]; - let position = intersection.position(); - let mut closest_vertex = triangle[0]; - let mut closest_dist = position.distance(triangle[0].into()); - for v in triangle_vecs { - let dist = position.distance(v.into()); - if dist < closest_dist { - closest_dist = dist; - closest_vertex = v; - } - } - //closest_vertex = *triangle_vecs.iter().min_by(|position, ver| position.distance(**ver).cmp(closest_dist)).unwrap(); - let ray = Ray3d::new(closest_vertex.into(), intersection.normal()); - *transform = Transform::from_matrix(to_aligned_transform( - ray, - [0., 0., 1.].into(), - )); - set_visibility(cursor.frame, &mut visibility, true); - } else { - // Hide the cursor - set_visibility(cursor.frame, &mut visibility, false); - } - } - Some(_) => { - // If we are placing a model avoid snapping to faced and just project to - // ground plane - let intersection = match intersect_ground_params.ground_plane_intersection() - { - Some(intersection) => intersection, - None => { - return; - } - }; - set_visibility(cursor.frame, &mut visibility, true); - *transform = Transform::from_translation(intersection); - } - } - } else { - let intersection = match intersect_ground_params.ground_plane_intersection() { - Some(intersection) => intersection, - None => { - return; - } - }; - set_visibility(cursor.frame, &mut visibility, true); - *transform = Transform::from_translation(intersection); + let n = *match &primitive { + Primitive3d::Plane { normal, .. } => normal, + _ => { + warn!("Unsupported primitive type found"); + return None; } - } + }; + let p = intersects_primitive(ray, primitive).map(|intersection| intersection.position())?; + + Some(Transform::from_translation(p).with_rotation(aligned_z_axis(n))) } } @@ -475,16 +374,19 @@ pub fn update_cursor_hover_visualization( } } -// This system makes sure model previews are not picked up by raycasting -pub fn make_model_previews_not_selectable( - mut commands: Commands, - new_models: Query, Added)>, - cursor: Res, -) { - if let Some(e) = cursor.preview_model.and_then(|m| new_models.get(m).ok()) { - commands - .entity(e) - .remove::() - .remove::>(); +pub fn aligned_z_axis(z: Vec3) -> Quat { + let z_length = z.length(); + if z_length < 1e-8 { + // The given direction is too close to singular + return Quat::IDENTITY; + } + + let axis = Vec3::Z.cross(z); + let axis_length = axis.length(); + if axis_length < 1e-8 { + // The change in angle is too close to zero + return Quat::IDENTITY; } + let angle = f32::asin(axis_length / z_length); + Quat::from_axis_angle(axis / axis_length, angle) } diff --git a/rmf_site_editor/src/interaction/gizmo.rs b/rmf_site_editor/src/interaction/gizmo.rs index 56261ee8..5093b061 100644 --- a/rmf_site_editor/src/interaction/gizmo.rs +++ b/rmf_site_editor/src/interaction/gizmo.rs @@ -36,6 +36,23 @@ pub struct GizmoMaterialSet { pub drag: Handle, } +#[derive(Resource)] +pub struct GizmoBlockers { + pub selecting: bool, +} + +impl GizmoBlockers { + pub fn blocking(&self) -> bool { + self.selecting + } +} + +impl Default for GizmoBlockers { + fn default() -> Self { + Self { selecting: false } + } +} + impl GizmoMaterialSet { pub fn make_x_axis(materials: &mut Mut>) -> Self { Self { @@ -136,6 +153,7 @@ pub struct DragAxisBundle { pub gizmo: Gizmo, pub draggable: Draggable, pub axis: DragAxis, + pub selectable: Selectable, } impl DragAxisBundle { @@ -147,6 +165,10 @@ impl DragAxisBundle { along, frame: FrameOfReference::Local, }, + selectable: Selectable { + is_selectable: true, + element: for_entity, + }, } } @@ -174,6 +196,7 @@ pub struct DragPlaneBundle { pub gizmo: Gizmo, pub draggable: Draggable, pub plane: DragPlane, + pub selectable: Selectable, } impl DragPlaneBundle { @@ -185,6 +208,10 @@ impl DragPlaneBundle { in_plane, frame: FrameOfReference::Local, }, + selectable: Selectable { + is_selectable: true, + element: for_entity, + }, } } @@ -242,6 +269,7 @@ pub fn update_gizmo_click_start( &mut Handle, )>, mut selection_blocker: ResMut, + gizmo_blocker: Res, mut visibility: Query<&mut Visibility>, mouse_button_input: Res>, transforms: Query<(&Transform, &GlobalTransform)>, @@ -252,6 +280,16 @@ pub fn update_gizmo_click_start( mut click: EventWriter, mut removed_gizmos: RemovedComponents, ) { + if gizmo_blocker.blocking() { + if gizmo_blocker.is_changed() { + // This has started being blocked since the last cycle + cursor.clear_blockers(&mut visibility); + } + + // Don't start any gizmos + return; + } + for e in removed_gizmos.read() { cursor.remove_blocker(e, &mut visibility); } @@ -327,12 +365,14 @@ pub fn update_gizmo_click_start( pub fn update_gizmo_release( mut draggables: Query<(&Gizmo, &mut Draggable, &mut Handle)>, mut selection_blockers: ResMut, + gizmo_blockers: Res, mut gizmo_state: ResMut, mouse_button_input: Res>, - picked: Res, - mut change_pick: EventWriter, + mut picked: ResMut, ) { - if mouse_button_input.just_released(MouseButton::Left) { + let mouse_released = mouse_button_input.just_released(MouseButton::Left); + let gizmos_blocked = gizmo_blockers.blocking(); + if mouse_released || gizmos_blocked { if let GizmoState::Dragging(e) = *gizmo_state { if let Ok((gizmo, mut draggable, mut material)) = draggables.get_mut(e) { draggable.drag = None; @@ -348,10 +388,7 @@ pub fn update_gizmo_release( // to move the cursor off of whatever object it happens to be // hovering over after the drag is finished before interactions like // selecting or dragging can resume. - change_pick.send(ChangePick { - from: None, - to: picked.0, - }); + picked.refresh = true; } } } diff --git a/rmf_site_editor/src/interaction/lift.rs b/rmf_site_editor/src/interaction/lift.rs index efbf9457..0262ccc9 100644 --- a/rmf_site_editor/src/interaction/lift.rs +++ b/rmf_site_editor/src/interaction/lift.rs @@ -57,7 +57,7 @@ pub fn handle_lift_doormat_clicks( for click in clicks.read() { if let Ok(doormat) = doormats.get(click.0) { toggle.send(doormat.toggle_availability()); - select.send(Select(Some(doormat.for_lift))); + select.send(Select::new(Some(doormat.for_lift))); } } } diff --git a/rmf_site_editor/src/interaction/light.rs b/rmf_site_editor/src/interaction/light.rs index 0caf3fc1..3fa79985 100644 --- a/rmf_site_editor/src/interaction/light.rs +++ b/rmf_site_editor/src/interaction/light.rs @@ -16,7 +16,7 @@ */ use crate::{ - interaction::{DragPlaneBundle, HeadlightToggle, InteractionAssets, Selectable}, + interaction::{DragPlaneBundle, HeadlightToggle, InteractionAssets}, site::LightKind, }; use bevy::prelude::*; @@ -94,7 +94,6 @@ pub fn add_physical_light_visual_cues( material: assets.physical_light_cover_material.clone(), ..default() }) - .insert(Selectable::new(e)) .insert(DragPlaneBundle::new(e, Vec3::Z).globally()); point @@ -103,7 +102,6 @@ pub fn add_physical_light_visual_cues( material: light_material.clone(), ..default() }) - .insert(Selectable::new(e)) .insert(DragPlaneBundle::new(e, Vec3::Z).globally()); }) .id(); @@ -123,7 +121,6 @@ pub fn add_physical_light_visual_cues( material: assets.physical_light_cover_material.clone(), ..default() }) - .insert(Selectable::new(e)) .insert(DragPlaneBundle::new(e, Vec3::Z).globally()); spot.spawn(PbrBundle { @@ -131,7 +128,6 @@ pub fn add_physical_light_visual_cues( material: light_material.clone(), ..default() }) - .insert(Selectable::new(e)) .insert(DragPlaneBundle::new(e, Vec3::Z).globally()); }) .id(); @@ -151,7 +147,6 @@ pub fn add_physical_light_visual_cues( material: assets.direction_light_cover_material.clone(), ..default() }) - .insert(Selectable::new(e)) .insert(DragPlaneBundle::new(e, Vec3::Z).globally()); dir.spawn(PbrBundle { @@ -159,7 +154,6 @@ pub fn add_physical_light_visual_cues( material: light_material.clone(), ..default() }) - .insert(Selectable::new(e)) .insert(DragPlaneBundle::new(e, Vec3::Z).globally()); }) .id(); diff --git a/rmf_site_editor/src/interaction/mod.rs b/rmf_site_editor/src/interaction/mod.rs index bc7b6a1f..a977f693 100644 --- a/rmf_site_editor/src/interaction/mod.rs +++ b/rmf_site_editor/src/interaction/mod.rs @@ -55,9 +55,6 @@ pub use lift::*; pub mod light; pub use light::*; -pub mod mode; -pub use mode::*; - pub mod model_preview; pub use model_preview::*; @@ -82,9 +79,6 @@ pub use preview::*; pub mod select; pub use select::*; -pub mod select_anchor; -pub use select_anchor::*; - pub mod visual_cue; pub use visual_cue::*; @@ -134,8 +128,9 @@ pub enum InteractionUpdateSet { impl Plugin for InteractionPlugin { fn build(&self, app: &mut App) { app.init_state::() + .init_resource::() .configure_sets( - Update, + PostUpdate, ( SiteUpdateSet::AssignOrphansFlush, InteractionUpdateSet::AddVisuals, @@ -155,17 +150,10 @@ impl Plugin for InteractionPlugin { .init_resource::() .init_resource::() .init_resource::() - .init_resource::() - .init_resource::() - .init_resource::() .init_resource::() - .init_resource::() - .init_resource::() + .insert_resource(HighlightAnchors(false)) .add_event::() - .add_event::() + .add_event::() + .add_event::() + .add_systems( + Update, + ( + (apply_deferred, flush_impulses()) + .chain() + .in_set(SelectionServiceStages::PickFlush), + (apply_deferred, flush_impulses()) + .chain() + .in_set(SelectionServiceStages::HoverFlush), + (apply_deferred, flush_impulses()) + .chain() + .in_set(SelectionServiceStages::SelectFlush), + ), + ) + .add_plugins(( + InspectorServicePlugin::default(), + AnchorSelectionPlugin::default(), + ObjectPlacementPlugin::default(), + )); + + let inspector_service = app.world.resource::().inspector_service; + let new_selector_service = app.spawn_event_streaming_service::(Update); + let selection_workflow = app.world.spawn_io_workflow(build_selection_workflow( + inspector_service, + new_selector_service, + )); + + // Get the selection workflow running + app.world.command(|commands| { + commands.request((), selection_workflow).detach(); + }); + } +} + +/// This builder function creates the high-level workflow that manages "selection" +/// behavior, which is largely driven by mouse interactions. "Selection" behaviors +/// determine how the application responds to the mouse cursor hovering over +/// objects in the scene and what happens when the mouse is clicked. +/// +/// The default selection behavior is the "inspector" service which allows the +/// user to select objects in the scene so that their properties get displayed +/// in the inspector panel. The inspector service will automatically be run by +/// this workflow at startup. +/// +/// When the user asks for some other type of mouse interaction to begin, such as +/// drawing walls and floors, or placing models in the scene, this workflow will +/// "trim" (stop) the inspector workflow and inject the new requested interaction +/// mode into the workflow. The requested interaction mode is represented by a +/// service specified by the `selector` field of [`RunSelector`]. When that +/// service terminates, this workflow will resume running the inspector service +/// until the user requests some other mouse interaction service to run. +/// +/// In most cases downstream users will not need to call this function since the +/// [`SelectionPlugin`] will use this to build and run the default selection +/// workflow. If you are not using the [`SelectionPlugin`] that we provide and +/// want to customize the inspector service, then you could use this to build a +/// customized selection workflow by passingin a custom inspector service. +pub fn build_selection_workflow( + inspector_service: Service<(), ()>, + new_selector_service: Service<(), (), StreamOf>, +) -> impl FnOnce(Scope<(), ()>, &mut Builder) -> DeliverySettings { + move |scope, builder| { + // This creates a service that will listen to run_service_buffer. + // The job of this service is to atomically pull the most recent item + // out of the buffer and also close the buffer gate to ensure that we + // never have multiple selector services racing to be injected. If we + // don't bother to close the gate after pulling exactly one selection + // service, then it's theoretically possible for multiple selection + // services to get simultaneously injected after the trim operation + // finishes. + let process_new_selector_service = builder + .commands() + .spawn_service(process_new_selector.into_blocking_service()); + + // The run_service_buffer queues up the most recent RunSelector request + // sent in by the user. That request will be held in this buffer while + // we wait for any ongoing mouse interaction services to cleanly exit + // after we trigger the trim operation. + let run_service_buffer = builder.create_buffer::(BufferSettings::keep_last(1)); + let input = scope.input.fork_clone(builder); + // Run the default inspector service + let inspector = input.clone_chain(builder).then_node(inspector_service); + + // Create a node that reads RunSelector events from the world and streams + // them into the workflow. + let new_selector_node = input.clone_chain(builder).then_node(new_selector_service); + builder.connect(new_selector_node.output, scope.terminate); + new_selector_node + .streams + .chain(builder) + .inner() + .connect(run_service_buffer.input_slot()); + + let open_gate = builder.create_gate_open(run_service_buffer); + // Create an operation that trims the gate opening operation, the injected + // selector service, and the default inspector service. + let trim = builder.create_trim([TrimBranch::between(open_gate.input, inspector.input)]); + builder.connect(trim.output, open_gate.input); + + // Create a sequence where we listen for updates in the run service buffer, + // then pull an item out of the buffer (if available), then begin the + // trim of all ongoing selection services, then opening the gate of the + // buffer to allow new selection services to be started. + builder + .listen(run_service_buffer) + .then(process_new_selector_service) + .dispose_on_none() + .connect(trim.input); + + // After we open the gate it is safe to inject the user-requested selecion + // service. Once that service finishes, we will trigger the inspector to + // resume. + open_gate + .output + .chain(builder) + .map_block(|r: RunSelector| (r.input, r.selector)) + .then_injection() + .trigger() + .connect(inspector.input); + + // This workflow only makes sense to run in serial. + DeliverySettings::Serial + } +} + +fn process_new_selector( + In(key): In>, + mut access: BufferAccessMut, +) -> Option { + let Ok(mut buffer) = access.get_mut(&key) else { + return None; + }; + + let output = buffer.pull(); + if output.is_some() { + // We should lock the gate while the trim is going on so we can't have + // multiple new selectors trying to start at the same time + buffer.close_gate(); + } + + output +} + +#[derive(Debug, Clone, Copy, Event)] +pub struct RunSelector { + /// The select workflow will run this service until it terminates and then + /// revert back to the inspector selector. + selector: Service, ()>, + /// If there is input for the selector, it will be stored in a [`SelectorInput`] + /// component in this entity. The entity will be despawned as soon as the + /// input is extracted. + input: Option, +} + +#[derive(Component)] +pub struct SelectorInput(T); /// This component is put on entities with meshes to mark them as items that can /// be interacted with to @@ -97,11 +326,46 @@ pub struct Selection(pub Option); pub struct Hovering(pub Option); /// Used as an event to command a change in the selected entity. -#[derive(Default, Debug, Clone, Copy, Deref, DerefMut, Event)] -pub struct Select(pub Option); +#[derive(Default, Debug, Clone, Copy, Deref, DerefMut, Event, Stream)] +pub struct Select(pub Option); + +impl Select { + pub fn new(candidate: Option) -> Select { + Select(candidate.map(|c| SelectionCandidate::new(c))) + } + + pub fn provisional(candidate: Entity) -> Select { + Select(Some(SelectionCandidate::provisional(candidate))) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct SelectionCandidate { + /// The entity that's being requested as a selection + pub candidate: Entity, + /// The entity was created specifically to be selected, so if it ends up + /// going unused by the workflow then it should be despawned. + pub provisional: bool, +} + +impl SelectionCandidate { + pub fn new(candidate: Entity) -> SelectionCandidate { + SelectionCandidate { + candidate, + provisional: false, + } + } + + pub fn provisional(candidate: Entity) -> SelectionCandidate { + SelectionCandidate { + candidate, + provisional: true, + } + } +} /// Used as an event to command a change in the hovered entity. -#[derive(Default, Debug, Clone, Copy, Deref, DerefMut, Event)] +#[derive(Default, Debug, Clone, Copy, Deref, DerefMut, Event, Stream)] pub struct Hover(pub Option); /// A resource to track what kind of blockers are preventing the selection @@ -110,22 +374,17 @@ pub struct Hover(pub Option); pub struct SelectionBlockers { /// An entity is being dragged pub dragging: bool, - /// An entity is being placed - pub placing: bool, } impl SelectionBlockers { pub fn blocking(&self) -> bool { - self.dragging || self.placing + self.dragging } } impl Default for SelectionBlockers { fn default() -> Self { - SelectionBlockers { - dragging: false, - placing: false, - } + SelectionBlockers { dragging: false } } } @@ -155,75 +414,372 @@ pub fn make_selectable_entities_pickable( } } -pub fn handle_selection_picking( - blockers: Option>, - mode: Res, - selectables: Query<&Selectable>, - anchors: Query<(), With>, +/// This allows an [`App`] to spawn a service that can stream Hover and +/// Select events that are managed by a filter. This can only be used with +/// [`App`] because some of the internal services are continuous, so they need +/// to be added to the schedule. +pub trait SpawnSelectionServiceExt { + fn spawn_selection_service( + &mut self, + ) -> Service<(), (), (Hover, Select)> + where + for<'w, 's> F::Item<'w, 's>: SelectionFilter; +} + +impl SpawnSelectionServiceExt for App { + fn spawn_selection_service( + &mut self, + ) -> Service<(), (), (Hover, Select)> + where + for<'w, 's> F::Item<'w, 's>: SelectionFilter, + { + let picking_service = self.spawn_continuous_service( + Update, + picking_service:: + .configure(|config: SystemConfigs| config.in_set(SelectionServiceStages::Pick)), + ); + + let hover_service = self.spawn_continuous_service( + Update, + hover_service:: + .configure(|config: SystemConfigs| config.in_set(SelectionServiceStages::Hover)), + ); + + let select_service = self.spawn_continuous_service( + Update, + select_service:: + .configure(|config: SystemConfigs| config.in_set(SelectionServiceStages::Select)), + ); + + self.world + .spawn_workflow::<_, _, (Hover, Select), _>(|scope, builder| { + let hover = builder.create_node(hover_service); + builder.connect(hover.streams, scope.streams.0); + builder.connect(hover.output, scope.terminate); + + let select = builder.create_node(select_service); + builder.connect(select.streams, scope.streams.1); + builder.connect(select.output, scope.terminate); + + // Activate all the services at the start + scope.input.chain(builder).fork_clone(( + |chain: Chain<_>| { + chain + .then(refresh_picked.into_blocking_callback()) + .then(picking_service) + .connect(scope.terminate) + }, + |chain: Chain<_>| chain.connect(hover.input), + |chain: Chain<_>| chain.connect(select.input), + )); + + // This is just a dummy buffer to let us have a cleanup workflow + let buffer = builder.create_buffer::<()>(BufferSettings::keep_all()); + builder.on_cleanup(buffer, |scope, builder| { + scope + .input + .chain(builder) + .trigger() + .then(clear_hover_select.into_blocking_callback()) + .connect(scope.terminate); + }); + }) + } +} + +// TODO(@mxgrey): Remove flush stages when we move to bevy 0.13 which can infer +// when to flush +#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] +pub enum SelectionServiceStages { + Pick, + PickFlush, + Hover, + HoverFlush, + Select, + SelectFlush, +} + +#[derive(Resource)] +pub struct InspectorService { + /// Workflow that updates the [`Selection`] as well as [`Hovered`] and + /// [`Selected`] states in the application. + pub inspector_service: Service<(), ()>, + /// Workflow that outputs hover and select streams that are compatible with + /// a general inspector. This service never terminates. + pub inspector_select_service: Service<(), (), (Hover, Select)>, + pub inspector_cursor_transform: Service<(), ()>, + pub selection_update: Service, +} + +#[derive(Default)] +pub struct InspectorServicePlugin {} + +impl Plugin for InspectorServicePlugin { + fn build(&self, app: &mut App) { + let inspector_select_service = app.spawn_selection_service::(); + let inspector_cursor_transform = app.spawn_continuous_service( + Update, + inspector_cursor_transform + .configure(|config: SystemConfigs| config.in_set(SelectionServiceStages::Pick)), + ); + let selection_update = app.spawn_service(selection_update); + let keyboard_just_pressed = app + .world + .resource::() + .keyboard_just_pressed; + + let inspector_service = app.world.spawn_workflow(|scope, builder| { + let fork_input = scope.input.fork_clone(builder); + fork_input + .clone_chain(builder) + .then(inspector_cursor_transform) + .unused(); + fork_input + .clone_chain(builder) + .then_node(keyboard_just_pressed) + .streams + .chain(builder) + .inner() + .then(deselect_on_esc.into_blocking_callback()) + .unused(); + let selection = fork_input + .clone_chain(builder) + .then_node(inspector_select_service); + selection + .streams + .1 + .chain(builder) + .then(selection_update) + .unused(); + builder.connect(selection.output, scope.terminate); + }); + + app.world.insert_resource(InspectorService { + inspector_service, + inspector_select_service, + inspector_cursor_transform, + selection_update, + }); + } +} + +pub fn deselect_on_esc(In(code): In, mut select: EventWriter; +} + +#[derive(SystemParam)] +pub struct InspectorFilter<'w, 's> { + selectables: Query<'w, 's, &'static Selectable, (Without, Without)>, +} + +impl<'w, 's> SelectionFilter for InspectorFilter<'w, 's> { + fn filter_pick(&mut self, select: Entity) -> Option { + self.selectables + .get(select) + .ok() + .map(|selectable| selectable.element) + } + fn filter_select(&mut self, target: Entity) -> Option { + Some(target) + } + fn on_click(&mut self, hovered: Hover) -> Option, - mode: Res, blockers: Option>, -) { + filter: StaticSystemParam, + selection_blockers: Res, +) where + for<'w, 's> Filter::Item<'w, 's>: SelectionFilter, +{ + let Some(mut orders) = orders.get_mut(&key) else { + return; + }; + + if orders.is_empty() { + // Nothing is asking for this service to run + return; + } + + if selection_blockers.blocking() { + return; + } + + let mut filter = filter.into_inner(); + if let Some(new_hovered) = hover.read().last() { - if hovering.0 != new_hovered.0 { + let new_hovered = new_hovered.0.and_then(|e| filter.filter_select(e)); + if hovering.0 != new_hovered { if let Some(previous_hovered) = hovering.0 { if let Ok(mut hovering) = hovered.get_mut(previous_hovered) { hovering.is_hovered = false; } } - if let Some(new_hovered) = new_hovered.0 { + if let Some(new_hovered) = new_hovered { if let Ok(mut hovering) = hovered.get_mut(new_hovered) { hovering.is_hovered = true; } } - hovering.0 = new_hovered.0; + hovering.0 = new_hovered; + orders.for_each(|order| order.streams().send(Hover(new_hovered))); } } @@ -232,46 +788,148 @@ pub fn maintain_hovered_entities( let blocked = blockers.filter(|x| x.blocking()).is_some(); if clicked && !blocked { - if let Some(current_hovered) = hovering.0 { - // TODO(luca) refactor to remove this hack - // Skip if we are in SelectAnchor3D mode - if let InteractionMode::SelectAnchor3D(_) = &*mode { - return; - } - select.send(Select(Some(current_hovered))); + if let Some(new_select) = filter.on_click(Hover(hovering.0)) { + select.send(new_select); } } } -pub fn maintain_selected_entities( - mode: Res, - mut selected: Query<&mut Selected>, - mut selection: ResMut, +/// A continuous service that filters [`Select`] events and issues out a +/// [`Hover`] stream. +/// +/// This complements [`hover_service`] and [`hover_picking`] +/// and is the final piece of the [`SelectionService`] workflow. +pub fn select_service( + In(ContinuousService { key }): ContinuousServiceInput<(), (), Select>, + mut orders: ContinuousQuery<(), (), Select>, mut select: EventReader, + mut selected: Query<&mut Selected>, + mut selection: ResMut, +) { + if selection.0 != new_selection.map(|s| s.candidate) { + if let Some(previous_selection) = selection.0 { + if let Ok(mut selected) = selected.get_mut(previous_selection) { + selected.is_selected = false; } + } - selection.0 = new_selection.0; + if let Some(new_selection) = new_selection { + if let Ok(mut selected) = selected.get_mut(new_selection.candidate) { + selected.is_selected = true; + } } + + selection.0 = new_selection.map(|s| s.candidate); } } + +/// This is used to clear out the currently picked item at the start of a new +/// selection workflow to make sure the Hover events don't get lost during the +/// workflow switch. +pub fn refresh_picked(In(_): In<()>, mut picked: ResMut) { + picked.refresh = true; +} + +/// This is used to clear out hoverings and selections from a workflow that is +/// cleaning up so that these properties don't spill over into other workflows. +pub fn clear_hover_select( + In(_): In<()>, + mut hovered: Query<&mut Hovered>, + mut hovering: ResMut, + mut selected: Query<&mut Selected>, + mut selection: ResMut, +) { + if let Some(previous_hovering) = hovering.0.take() { + if let Ok(mut hovered) = hovered.get_mut(previous_hovering) { + hovered.is_hovered = false; + } + } + + if let Some(previous_selection) = selection.0.take() { + if let Ok(mut selected) = selected.get_mut(previous_selection) { + selected.is_selected = false; + } + } +} + +/// Update the virtual cursor (dagger and circle) transform while in inspector mode +pub fn inspector_cursor_transform( + In(ContinuousService { key }): ContinuousServiceInput<(), ()>, + orders: ContinuousQuery<(), ()>, + cursor: Res, + raycast_sources: Query<&RaycastSource>, + mut transforms: Query<&mut Transform>, +) { + let Some(orders) = orders.view(&key) else { + return; + }; + + if orders.is_empty() { + return; + } + + let Ok(source) = raycast_sources.get_single() else { + return; + }; + let intersection = match source.get_nearest_intersection() { + Some((_, intersection)) => intersection, + None => { + return; + } + }; + + let mut transform = match transforms.get_mut(cursor.frame) { + Ok(transform) => transform, + Err(_) => { + return; + } + }; + + let ray = Ray3d::new(intersection.position(), intersection.normal()); + *transform = Transform::from_matrix(to_aligned_transform(ray, [0., 0., 1.].into())); +} diff --git a/rmf_site_editor/src/interaction/select/create_edges.rs b/rmf_site_editor/src/interaction/select/create_edges.rs new file mode 100644 index 00000000..aa75a250 --- /dev/null +++ b/rmf_site_editor/src/interaction/select/create_edges.rs @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use crate::{ + interaction::*, + site::{ChangeDependent, Pending, TextureNeedsAssignment}, +}; +use bevy::prelude::*; +use bevy_impulse::*; +use rmf_site_format::{Edge, Side}; +use std::borrow::Borrow; + +pub fn spawn_create_edges_service( + helpers: &AnchorSelectionHelpers, + app: &mut App, +) -> Service, ()> { + let anchor_setup = + app.spawn_service(anchor_selection_setup::.into_blocking_service()); + let state_setup = app.spawn_service(create_edges_setup.into_blocking_service()); + let update_preview = app.spawn_service(on_hover_for_create_edges.into_blocking_service()); + let update_current = app.spawn_service(on_select_for_create_edges.into_blocking_service()); + let handle_key_code = app.spawn_service(on_keyboard_for_create_edges.into_blocking_service()); + let cleanup_state = app.spawn_service(cleanup_create_edges.into_blocking_service()); + + helpers.spawn_anchor_selection_workflow( + anchor_setup, + state_setup, + update_preview, + update_current, + handle_key_code, + cleanup_state, + &mut app.world, + ) +} + +pub struct CreateEdges { + pub spawn_edge: fn(Edge, &mut Commands) -> Entity, + pub preview_edge: Option, + pub continuity: EdgeContinuity, + pub scope: AnchorScope, +} + +impl CreateEdges { + pub fn new>>( + continuity: EdgeContinuity, + scope: AnchorScope, + ) -> Self { + Self { + spawn_edge: create_edge::, + preview_edge: None, + continuity, + scope, + } + } + + pub fn new_with_texture>>( + continuity: EdgeContinuity, + scope: AnchorScope, + ) -> Self { + Self { + spawn_edge: create_edge_with_texture::, + preview_edge: None, + continuity, + scope, + } + } + + pub fn initialize_preview(&mut self, anchor: Entity, commands: &mut Commands) { + let edge = Edge::new(anchor, anchor); + let edge = (self.spawn_edge)(edge, commands); + self.preview_edge = Some(PreviewEdge { + edge, + side: Side::start(), + provisional_start: false, + }); + + commands.add(ChangeDependent::add(anchor, edge)); + } +} + +impl Borrow for CreateEdges { + fn borrow(&self) -> &AnchorScope { + &self.scope + } +} + +fn create_edge>>( + edge: Edge, + commands: &mut Commands, +) -> Entity { + let new_bundle: T = edge.into(); + commands.spawn((new_bundle, Pending)).id() +} + +fn create_edge_with_texture>>( + edge: Edge, + commands: &mut Commands, +) -> Entity { + let new_bundle: T = edge.into(); + commands + .spawn((new_bundle, TextureNeedsAssignment, Pending)) + .id() +} + +#[derive(Clone, Copy)] +pub struct PreviewEdge { + pub edge: Entity, + pub side: Side, + /// True if the start anchor of the edge was created specifically to build + /// this edge. If this true, we will despawn the anchor during cleanup if + /// the edge does not get completed. + pub provisional_start: bool, +} + +impl PreviewEdge { + pub fn cleanup( + &self, + edges: &Query<&'static Edge>, + commands: &mut Commands, + ) -> SelectionNodeResult { + let edge = edges.get(self.edge).or_broken_query()?; + for anchor in edge.array() { + commands.add(ChangeDependent::remove(anchor, self.edge)); + } + + if self.provisional_start { + // The start anchor was created specifically for this preview edge + // which we are about to despawn. Let's despawn both so we aren't + // littering the scene with unintended anchors. + commands + .get_entity(edge.start()) + .or_broken_query()? + .despawn_recursive(); + } + + commands + .get_entity(self.edge) + .or_broken_query()? + .despawn_recursive(); + Ok(()) + } +} + +pub enum EdgeContinuity { + /// Create just a single edge + Single, + /// Create a sequence of separate edges + Separate, + /// Create edges continuously, i.e. the beginning of the next edge will + /// automatically be the end of the previous edge. + Continuous, +} + +pub fn create_edges_setup( + In(key): In>, + mut access: BufferAccessMut, + cursor: Res, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + if state.preview_edge.is_none() { + state.initialize_preview(cursor.level_anchor_placement, &mut commands); + } + Ok(()) +} + +pub fn on_hover_for_create_edges( + In((hover, key)): In<(Hover, BufferKey)>, + mut access: BufferAccessMut, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut edges: Query<&mut Edge>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + // TODO(@mxgrey): Consider moving this logic into AnchorFilter since it gets + // used by all the different anchor selection modes. + let anchor = match hover.0 { + Some(anchor) => { + cursor.remove_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + anchor + } + None => { + cursor.add_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + cursor.level_anchor_placement + } + }; + + if let Some(preview) = &mut state.preview_edge { + // If we already have an active preview, then use the new anchor for the + // side that we currently need to select for. + let index = preview.side.index(); + let mut edge = edges.get_mut(preview.edge).or_broken_query()?; + + let old_anchor = edge.array()[index]; + if old_anchor != anchor { + let opposite_anchor = edge.array()[preview.side.opposite().index()]; + if opposite_anchor != old_anchor { + commands.add(ChangeDependent::remove(old_anchor, preview.edge)); + } + + edge.array_mut()[index] = anchor; + commands.add(ChangeDependent::add(anchor, preview.edge)); + } + } else { + // There is currently no active preview, so we need to create one. + let edge = Edge::new(anchor, anchor); + let edge = (state.spawn_edge)(edge, &mut commands); + state.preview_edge = Some(PreviewEdge { + edge, + side: Side::start(), + provisional_start: false, + }); + commands.add(ChangeDependent::add(anchor, edge)); + } + + Ok(()) +} + +pub fn on_select_for_create_edges( + In((selection, key)): In<(SelectionCandidate, BufferKey)>, + mut access: BufferAccessMut, + mut edges: Query<&mut Edge>, + mut commands: Commands, + cursor: Res, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + let anchor = selection.candidate; + if let Some(preview) = &mut state.preview_edge { + match preview.side { + Side::Left => { + // We are pinning down the first anchor of the edge + let mut edge = edges.get_mut(preview.edge).or_broken_query()?; + commands.add(ChangeDependent::remove(edge.left(), preview.edge)); + *edge.left_mut() = anchor; + commands.add(ChangeDependent::add(anchor, preview.edge)); + + if edge.right() != anchor { + commands.add(ChangeDependent::remove(edge.right(), preview.edge)); + } + + *edge.right_mut() = cursor.level_anchor_placement; + commands.add(ChangeDependent::add( + cursor.level_anchor_placement, + preview.edge, + )); + + preview.side = Side::Right; + preview.provisional_start = selection.provisional; + } + Side::Right => { + // We are finishing the edge + let mut edge = edges.get_mut(preview.edge).or_broken_query()?; + if edge.left() == anchor { + // The user is trying to use the same point for the start + // and end of an edge. Issue a warning and exit early. + warn!( + "You are trying to select an anchor {:?} for both the \ + start and end points of an edge, which is not allowed.", + anchor, + ); + return Ok(()); + } + *edge.right_mut() = anchor; + commands.add(ChangeDependent::add(anchor, preview.edge)); + commands + .get_entity(preview.edge) + .or_broken_query()? + .remove::(); + + match state.continuity { + EdgeContinuity::Single => { + state.preview_edge = None; + // This simply means we are terminating the workflow now + // because we have finished drawing the single edge + return Err(None); + } + EdgeContinuity::Separate => { + // Start drawing a new edge from a blank slate with the + // next selection + state.initialize_preview(cursor.level_anchor_placement, &mut commands); + } + EdgeContinuity::Continuous => { + // Start drawing a new edge, picking up from the end + // point of the previous edge + let edge = Edge::new(anchor, cursor.level_anchor_placement); + let edge = (state.spawn_edge)(edge, &mut commands); + state.preview_edge = Some(PreviewEdge { + edge, + side: Side::end(), + provisional_start: false, + }); + commands.add(ChangeDependent::add(anchor, edge)); + commands.add(ChangeDependent::add(cursor.level_anchor_placement, edge)); + } + } + } + } + } else { + // We have no preview at all yet somehow, so we'll need to create a + // fresh new edge to insert the selected anchor into + let edge = Edge::new(anchor, anchor); + let edge = (state.spawn_edge)(edge, &mut commands); + state.preview_edge = Some(PreviewEdge { + edge, + side: Side::start(), + provisional_start: selection.provisional, + }); + } + + Ok(()) +} + +pub fn on_keyboard_for_create_edges( + In((button, key)): In<(KeyCode, BufferKey)>, + mut access: BufferAccessMut, + mut edges: Query<&'static mut Edge>, + cursor: Res, + mut commands: Commands, +) -> SelectionNodeResult { + if !matches!(button, KeyCode::Escape) { + // The button was not the escape key, so there's nothing for us to do + // here. + return Ok(()); + } + + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + if let Some(preview) = &mut state.preview_edge { + if preview.side == Side::end() { + // We currently have an active preview edge and are selecting for + // the second point in the edge. Esc means we should back out of the + // current edge without exiting the edge creation workflow so the + // user can choose a different start point. + let mut edge = edges.get_mut(preview.edge).or_broken_query()?; + for anchor in edge.array() { + commands.add(ChangeDependent::remove(anchor, preview.edge)); + } + if preview.provisional_start { + commands + .get_entity(edge.start()) + .or_broken_query()? + .despawn_recursive(); + } + + *edge.left_mut() = cursor.level_anchor_placement; + *edge.right_mut() = cursor.level_anchor_placement; + preview.side = Side::start(); + preview.provisional_start = false; + commands.add(ChangeDependent::add( + cursor.level_anchor_placement, + preview.edge, + )); + } else { + // We are selecting for the first point in the edge. If the user has + // pressed Esc then that means they want to stop creating edges + // altogether. Return Err(None) to indicate that the workflow should + // exit cleaning. + return Err(None); + } + } else { + // We currently have no preview active at all. If the user hits Esc then + // they want to exit the workflow altogether. + return Err(None); + } + + Ok(()) +} + +pub fn cleanup_create_edges( + In(key): In>, + mut access: BufferAccessMut, + edges: Query<&'static Edge>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.pull().or_broken_state()?; + + if let Some(preview) = state.preview_edge { + // We created a preview, so we should despawn it while cleaning up + preview.cleanup(&edges, &mut commands)?; + } + Ok(()) +} diff --git a/rmf_site_editor/src/interaction/select/create_path.rs b/rmf_site_editor/src/interaction/select/create_path.rs new file mode 100644 index 00000000..3df425f2 --- /dev/null +++ b/rmf_site_editor/src/interaction/select/create_path.rs @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use crate::{ + interaction::*, + site::{ChangeDependent, Pending, TextureNeedsAssignment}, +}; +use bevy::prelude::*; +use bevy_impulse::*; +use rmf_site_format::Path; +use std::borrow::Borrow; + +use std::collections::HashSet; + +pub fn spawn_create_path_service( + helpers: &AnchorSelectionHelpers, + app: &mut App, +) -> Service, ()> { + let anchor_setup = + app.spawn_service(anchor_selection_setup::.into_blocking_service()); + let state_setup = app.spawn_service(create_path_setup.into_blocking_service()); + let update_preview = app.spawn_service(on_hover_for_create_path.into_blocking_service()); + let update_current = app.spawn_service(on_select_for_create_path.into_blocking_service()); + let handle_key_code = app.spawn_service(exit_on_esc::.into_blocking_service()); + let cleanup_state = app.spawn_service(cleanup_create_path.into_blocking_service()); + + helpers.spawn_anchor_selection_workflow( + anchor_setup, + state_setup, + update_preview, + update_current, + handle_key_code, + cleanup_state, + &mut app.world, + ) +} + +pub struct CreatePath { + /// Function pointer for spawning an initial path. + pub spawn_path: fn(Path, &mut Commands) -> Entity, + /// The path which is being built. This will initially be [`None`] until setup + /// happens, then `spawn_path` will be used to create this. For all the + /// services in the `create_path` workflow besides setup, this should + /// contain [`Some`]. + /// + /// If points are being added to an existing path, this could be initialized + /// as [`Some`] before the state is passed into the workflow. + pub path: Option, + /// A minimum for how many points need to be selected for the path to be + /// considered valid. Use 0 if there is no minimum. + pub minimum_points: usize, + /// Whether the path is allowed to have an inner loop. E.g. + /// `A -> B -> C -> D -> B` would be an inner loop. + pub allow_inner_loops: bool, + /// The path is implied to always be a complete loop. This has two consequences: + /// 1. If the first point gets re-selected later in the path then we automatically + /// consider the path to be finished. + /// 2. When (1) occurs, the first point does not get re-added to the path. + pub implied_complete_loop: bool, + /// A list of all anchors being used in the path which are provisional, + /// meaning they should be despawned if the path creation ends before + /// reaching the minimum number of points. + pub provisional_anchors: HashSet, + pub scope: AnchorScope, +} + +impl CreatePath { + pub fn new( + spawn_path: fn(Path, &mut Commands) -> Entity, + minimum_points: usize, + allow_inner_loops: bool, + implied_complete_loop: bool, + scope: AnchorScope, + ) -> Self { + Self { + spawn_path, + path: None, + allow_inner_loops, + minimum_points, + implied_complete_loop, + scope, + provisional_anchors: Default::default(), + } + } + + pub fn set_last( + &self, + chosen: Entity, + path_mut: &mut Path, + commands: &mut Commands, + ) -> SelectionNodeResult { + let path = self.path.or_broken_state()?; + let last = path_mut.0.last_mut().or_broken_state()?; + if chosen == *last { + // Nothing to change + return Ok(()); + } + + let previous = *last; + *last = chosen; + if !path_mut.0.contains(&previous) { + commands.add(ChangeDependent::remove(previous, path)); + } + + commands.add(ChangeDependent::add(chosen, path)); + Ok(()) + } +} + +impl Borrow for CreatePath { + fn borrow(&self) -> &AnchorScope { + &self.scope + } +} + +pub fn create_path_with_texture>>( + path: Path, + commands: &mut Commands, +) -> Entity { + let new_bundle: T = path.into(); + commands + .spawn((new_bundle, TextureNeedsAssignment, Pending)) + .id() +} + +pub fn create_path_setup( + In(key): In>, + mut access: BufferAccessMut, + cursor: Res, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + if state.path.is_none() { + let path = Path(vec![cursor.level_anchor_placement]); + let path = (state.spawn_path)(path, &mut commands); + commands.add(ChangeDependent::add(cursor.level_anchor_placement, path)); + state.path = Some(path); + } + + Ok(()) +} + +pub fn on_hover_for_create_path( + In((hover, key)): In<(Hover, BufferKey)>, + mut access: BufferAccessMut, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut paths: Query<&mut Path>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + let chosen = match hover.0 { + Some(anchor) => { + cursor.remove_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + anchor + } + None => { + cursor.add_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + cursor.level_anchor_placement + } + }; + + let path = state.path.or_broken_state()?; + let mut path_mut = paths.get_mut(path).or_broken_query()?; + state.set_last(chosen, path_mut.as_mut(), &mut commands) +} + +pub fn on_select_for_create_path( + In((selection, key)): In<(SelectionCandidate, BufferKey)>, + mut access: BufferAccessMut, + mut paths: Query<&mut Path>, + mut commands: Commands, + cursor: Res, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + let chosen = selection.candidate; + let provisional = selection.provisional; + let path = state.path.or_broken_state()?; + let mut path_mut = paths.get_mut(path).or_broken_query()?; + + if state.implied_complete_loop { + let first = path_mut.0.first().or_broken_state()?; + if chosen == *first && path_mut.0.len() >= state.minimum_points { + // The user has re-selected the first point and there are enough + // points in the path to meet the minimum requirement, so we can + // just end the workflow. + return Err(None); + } + } + + if !state.allow_inner_loops { + for a in &path_mut.0[..path_mut.0.len() - 1] { + if *a == chosen { + warn!( + "Attempting to create an inner loop in a type of path \ + which does not allow inner loops." + ); + return Ok(()); + } + } + } + + if path_mut.0.len() >= 2 { + if let Some(second_to_last) = path_mut.0.get(path_mut.0.len() - 2) { + if *second_to_last == chosen { + // Even if inner loops are allowed, we should never allow the same + // anchor to be chosen twice in a row. + warn!("Trying to select the same anchor for a path twice in a row"); + return Ok(()); + } + } + } + + state.set_last(chosen, path_mut.as_mut(), &mut commands)?; + if provisional { + state.provisional_anchors.insert(chosen); + } + + path_mut.0.push(cursor.level_anchor_placement); + commands.add(ChangeDependent::add(cursor.level_anchor_placement, path)); + + Ok(()) +} + +pub fn cleanup_create_path( + In(key): In>, + mut access: BufferAccessMut, + mut paths: Query<&'static mut Path>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.pull().or_broken_state()?; + + let Some(path) = state.path else { + // If there is no path then there is nothing to cleanup. This might + // happen if the setup needed to bail out for some reason. + return Ok(()); + }; + commands + .get_entity(path) + .or_broken_query()? + .remove::(); + let mut path_mut = paths.get_mut(path).or_broken_query()?; + + // First check if the len-1 meets the minimum point requirement. If not we + // should despawn the path as well as any provisional anchors that it used. + if path_mut.0.len() - 1 < state.minimum_points { + // We did not collect enough points for the path so we should despawn it + // as well as any provisional points it contains. + for a in &path_mut.0 { + commands.add(ChangeDependent::remove(*a, path)); + } + + for a in state.provisional_anchors { + if let Some(a_mut) = commands.get_entity(a) { + a_mut.despawn_recursive(); + } + } + + commands + .get_entity(path) + .or_broken_query()? + .despawn_recursive(); + } else { + if let Some(a) = path_mut.0.last() { + // The last point in the path is always a preview point so we need + // to pop it. + let a = *a; + path_mut.0.pop(); + if !path_mut.contains(&a) { + // Remove the dependency on the last point since it no longer + // exists in the path + commands.add(ChangeDependent::remove(a, path)); + } + } + + if path_mut.0.is_empty() { + // The path is empty... we shouldn't keep an empty path so let's + // just despawn it. + commands + .get_entity(path) + .or_broken_query()? + .despawn_recursive(); + } + } + + Ok(()) +} diff --git a/rmf_site_editor/src/interaction/select/create_point.rs b/rmf_site_editor/src/interaction/select/create_point.rs new file mode 100644 index 00000000..645d5dc1 --- /dev/null +++ b/rmf_site_editor/src/interaction/select/create_point.rs @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use crate::{ + interaction::*, + site::{ChangeDependent, Pending}, +}; +use bevy::prelude::*; +use bevy_impulse::*; +use rmf_site_format::Point; +use std::borrow::Borrow; + +pub fn spawn_create_point_service( + helpers: &AnchorSelectionHelpers, + app: &mut App, +) -> Service, ()> { + let anchor_setup = + app.spawn_service(anchor_selection_setup::.into_blocking_service()); + let state_setup = app.spawn_service(create_point_setup.into_blocking_service()); + let update_preview = app.spawn_service(on_hover_for_create_point.into_blocking_service()); + let update_current = app.spawn_service(on_select_for_create_point.into_blocking_service()); + let handle_key_code = app.spawn_service(exit_on_esc::.into_blocking_service()); + let cleanup_state = app.spawn_service(cleanup_create_point.into_blocking_service()); + + helpers.spawn_anchor_selection_workflow( + anchor_setup, + state_setup, + update_preview, + update_current, + handle_key_code, + cleanup_state, + &mut app.world, + ) +} + +pub struct CreatePoint { + /// Function pointer for spawning a point. + pub spawn_point: fn(Point, &mut Commands) -> Entity, + /// The point which is being created. This will initially be [`None`] until + /// setup happens, then `spawn_point` will be used to create this. For all + /// the services in the `create_point` workflow besides setup, this should + /// contain [`Some`]. + pub point: Option, + /// True if we should keep creating new points until the user presses Esc, + /// False if we should only create one point. + pub repeating: bool, + pub scope: AnchorScope, +} + +impl CreatePoint { + pub fn new>>(repeating: bool, scope: AnchorScope) -> Self { + Self { + spawn_point: create_point::, + point: None, + repeating, + scope, + } + } + + pub fn create_new_point(&mut self, anchor: Entity, commands: &mut Commands) { + let point = Point(anchor); + let point = (self.spawn_point)(point, commands); + commands.add(ChangeDependent::add(anchor, point)); + self.point = Some(point); + } +} + +impl Borrow for CreatePoint { + fn borrow(&self) -> &AnchorScope { + &self.scope + } +} + +fn create_point>>( + point: Point, + commands: &mut Commands, +) -> Entity { + let new_bundle: T = point.into(); + commands.spawn((new_bundle, Pending)).id() +} + +pub fn create_point_setup( + In(key): In>, + mut access: BufferAccessMut, + cursor: Res, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + if state.point.is_none() { + state.create_new_point(cursor.level_anchor_placement, &mut commands); + } + + Ok(()) +} + +fn change_point( + chosen: Entity, + point: Entity, + points: &mut Query<&mut Point>, + commands: &mut Commands, +) -> SelectionNodeResult { + let mut point_mut = points.get_mut(point).or_broken_query()?; + if point_mut.0 == chosen { + return Ok(()); + } + + commands.add(ChangeDependent::remove(point_mut.0, point)); + commands.add(ChangeDependent::add(chosen, point)); + point_mut.0 = chosen; + Ok(()) +} + +pub fn on_hover_for_create_point( + In((hover, key)): In<(Hover, BufferKey)>, + mut access: BufferAccessMut, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut points: Query<&mut Point>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + let chosen = match hover.0 { + Some(anchor) => { + cursor.remove_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + anchor + } + None => { + cursor.add_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + cursor.level_anchor_placement + } + }; + + let point = state.point.or_broken_state()?; + change_point(chosen, point, &mut points, &mut commands) +} + +pub fn on_select_for_create_point( + In((selection, key)): In<(SelectionCandidate, BufferKey)>, + mut access: BufferAccessMut, + cursor: Res, + mut points: Query<&mut Point>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + let point = state.point.or_broken_state()?; + change_point(selection.candidate, point, &mut points, &mut commands)?; + commands + .get_entity(point) + .or_broken_query()? + .remove::(); + if state.repeating { + state.create_new_point(cursor.level_anchor_placement, &mut commands); + return Ok(()); + } else { + state.point = None; + return Err(None); + } +} + +pub fn cleanup_create_point( + In(key): In>, + mut access: BufferAccessMut, + points: Query<&'static Point>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.pull().or_broken_state()?; + + let Some(point) = state.point else { + // If there is no point then there is nothing to cleanup. + return Ok(()); + }; + + let point_ref = points.get(point).or_broken_query()?; + commands.add(ChangeDependent::remove(point_ref.0, point)); + commands + .get_entity(point) + .or_broken_query()? + .despawn_recursive(); + + Ok(()) +} diff --git a/rmf_site_editor/src/interaction/select/place_object.rs b/rmf_site_editor/src/interaction/select/place_object.rs new file mode 100644 index 00000000..96a9f346 --- /dev/null +++ b/rmf_site_editor/src/interaction/select/place_object.rs @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use crate::{interaction::select::*, site::Model}; +use bevy::ecs::system::SystemParam; + +#[derive(Default)] +pub struct ObjectPlacementPlugin {} + +impl Plugin for ObjectPlacementPlugin { + fn build(&self, app: &mut App) { + let services = ObjectPlacementServices::from_app(app); + app.insert_resource(services); + } +} + +#[derive(Resource, Clone, Copy)] +pub struct ObjectPlacementServices { + pub place_object_2d: Service, ()>, + pub place_object_3d: Service, ()>, + pub replace_parent_3d: Service, ()>, + pub hover_service_object_3d: Service<(), (), Hover>, +} + +impl ObjectPlacementServices { + pub fn from_app(app: &mut App) -> Self { + let hover_service_object_3d = app.spawn_continuous_service( + Update, + hover_service:: + .configure(|config: SystemConfigs| config.in_set(SelectionServiceStages::Hover)), + ); + let place_object_2d = spawn_place_object_2d_workflow(app); + let place_object_3d = spawn_place_object_3d_workflow(hover_service_object_3d, app); + let replace_parent_3d = spawn_replace_parent_3d_workflow(hover_service_object_3d, app); + Self { + place_object_2d, + place_object_3d, + replace_parent_3d, + hover_service_object_3d, + } + } +} + +#[derive(SystemParam)] +pub struct ObjectPlacement<'w, 's> { + pub services: Res<'w, ObjectPlacementServices>, + pub commands: Commands<'w, 's>, +} + +impl<'w, 's> ObjectPlacement<'w, 's> { + pub fn place_object_2d(&mut self, object: Model, level: Entity) { + let state = self + .commands + .spawn(SelectorInput(PlaceObject2d { object, level })) + .id(); + self.send(RunSelector { + selector: self.services.place_object_2d, + input: Some(state), + }); + } + + pub fn place_object_3d( + &mut self, + object: PlaceableObject, + parent: Option, + workspace: Entity, + ) { + let state = self + .commands + .spawn(SelectorInput(PlaceObject3d { + object, + parent, + workspace, + })) + .id(); + self.send(RunSelector { + selector: self.services.place_object_3d, + input: Some(state), + }); + } + + pub fn replace_parent_3d(&mut self, object: Entity, workspace: Entity) { + let state = self + .commands + .spawn(SelectorInput(ReplaceParent3d { object, workspace })) + .id(); + self.send(RunSelector { + selector: self.services.replace_parent_3d, + input: Some(state), + }); + } + + fn send(&mut self, run: RunSelector) { + self.commands.add(move |world: &mut World| { + world.send_event(run); + }); + } +} diff --git a/rmf_site_editor/src/interaction/select/place_object_2d.rs b/rmf_site_editor/src/interaction/select/place_object_2d.rs new file mode 100644 index 00000000..58c053bb --- /dev/null +++ b/rmf_site_editor/src/interaction/select/place_object_2d.rs @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use crate::{interaction::select::*, site::Model}; +use bevy::prelude::ButtonInput; + +pub const PLACE_OBJECT_2D_MODE_LABEL: &'static str = "place_object_2d"; + +pub fn spawn_place_object_2d_workflow(app: &mut App) -> Service, ()> { + let setup = app.spawn_service(place_object_2d_setup.into_blocking_service()); + let find_position = app.spawn_continuous_service(Update, place_object_2d_find_placement); + let placement_chosen = app.spawn_service(on_placement_chosen_2d.into_blocking_service()); + let handle_key_code = + app.spawn_service(on_keyboard_for_place_object_2d.into_blocking_service()); + let cleanup = app.spawn_service(place_object_2d_cleanup.into_blocking_service()); + + let keyboard_just_pressed = app + .world + .resource::() + .keyboard_just_pressed; + + app.world.spawn_io_workflow(build_place_object_2d_workflow( + setup, + find_position, + placement_chosen, + handle_key_code, + cleanup, + keyboard_just_pressed, + )) +} + +pub fn build_place_object_2d_workflow( + setup: Service, SelectionNodeResult>, + find_placement: Service<(), Transform>, + placement_chosen: Service<(Transform, BufferKey), SelectionNodeResult>, + handle_key_code: Service, + cleanup: Service, ()>, + keyboard_just_pressed: Service<(), (), StreamOf>, +) -> impl FnOnce(Scope, ()>, &mut Builder) { + move |scope, builder| { + let buffer = builder.create_buffer::(BufferSettings::keep_last(1)); + + let setup_finished = scope + .input + .chain(builder) + .then(extract_selector_input::.into_blocking_callback()) + .branch_for_err(|err| err.connect(scope.terminate)) + .cancel_on_none() + .then_push(buffer) + .then_access(buffer) + .then(setup) + .branch_for_err(|err| err.map_block(print_if_err).connect(scope.terminate)) + .output() + .fork_clone(builder); + + setup_finished + .clone_chain(builder) + .then(find_placement) + .with_access(buffer) + .then(placement_chosen) + .fork_result( + |ok| ok.connect(scope.terminate), + |err| err.map_block(print_if_err).connect(scope.terminate), + ); + + let keyboard_node = setup_finished + .clone_chain(builder) + .then_node(keyboard_just_pressed); + keyboard_node + .streams + .chain(builder) + .inner() + .then(handle_key_code) + .fork_result( + |ok| ok.connect(scope.terminate), + |err| err.map_block(print_if_err).connect(scope.terminate), + ); + + builder.on_cleanup(buffer, move |scope, builder| { + scope + .input + .chain(builder) + .then(cleanup) + .connect(scope.terminate); + }); + } +} + +pub struct PlaceObject2d { + pub object: Model, + pub level: Entity, +} + +pub fn place_object_2d_setup( + In(key): In>, + mut access: BufferAccessMut, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut commands: Commands, + mut gizmo_blockers: ResMut, + mut highlight: ResMut, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_buffer()?; + + cursor.set_model_preview(&mut commands, Some(state.object.clone())); + set_visibility(cursor.dagger, &mut visibility, false); + set_visibility(cursor.halo, &mut visibility, false); + + highlight.0 = false; + gizmo_blockers.selecting = true; + + cursor.add_mode(PLACE_OBJECT_2D_MODE_LABEL, &mut visibility); + + Ok(()) +} + +pub fn place_object_2d_cleanup( + In(_): In>, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut commands: Commands, + mut gizmo_blockers: ResMut, +) { + cursor.remove_preview(&mut commands); + cursor.remove_mode(PLACE_OBJECT_2D_MODE_LABEL, &mut visibility); + gizmo_blockers.selecting = false; +} + +pub fn place_object_2d_find_placement( + In(ContinuousService { key }): ContinuousServiceInput<(), Transform>, + mut orders: ContinuousQuery<(), Transform>, + cursor: Res, + mut transforms: Query<&mut Transform>, + intersect_ground_params: IntersectGroundPlaneParams, + mouse_button_input: Res>, + blockers: Option>, +) { + let Some(mut orders) = orders.get_mut(&key) else { + return; + }; + + let Some(order) = orders.get_mut(0) else { + return; + }; + + // TODO(@mxgrey): Consider allowing models to be snapped to existing objects + // similar to how they can for the 3D object placement workflow. Either we + // need to introduce parent frames to the 2D sites or just don't bother with + // parenting. + if let Some(intersection) = intersect_ground_params.ground_plane_intersection() { + match transforms.get_mut(cursor.frame) { + Ok(mut transform) => { + *transform = intersection; + } + Err(err) => { + error!("No cursor transform found: {err}"); + } + } + + let clicked = mouse_button_input.just_pressed(MouseButton::Left); + let blocked = blockers.filter(|x| x.blocking()).is_some(); + if clicked && !blocked { + order.respond(intersection); + } + } else { + warn!("Unable to find a placement position. Try adjusting your camera angle."); + } +} + +pub fn on_keyboard_for_place_object_2d(In(key): In) -> SelectionNodeResult { + if matches!(key, KeyCode::Escape) { + // Simply end the workflow if the escape key was pressed + info!("Exiting 2D object placement"); + return Err(None); + } + + Ok(()) +} + +pub fn on_placement_chosen_2d( + In((placement, key)): In<(Transform, BufferKey)>, + mut access: BufferAccessMut, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let mut state = access.pull().or_broken_state()?; + + state.object.pose = placement.into(); + commands.spawn(state.object).set_parent(state.level); + + Ok(()) +} diff --git a/rmf_site_editor/src/interaction/select/place_object_3d.rs b/rmf_site_editor/src/interaction/select/place_object_3d.rs new file mode 100644 index 00000000..54910168 --- /dev/null +++ b/rmf_site_editor/src/interaction/select/place_object_3d.rs @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use crate::{ + interaction::select::*, + site::{ + Anchor, AnchorBundle, Dependents, FrameMarker, Model, NameInSite, NameInWorkcell, Pending, + SiteID, WorkcellModel, + }, + widgets::canvas_tooltips::CanvasTooltips, +}; +use bevy::{ecs::system::SystemParam, prelude::ButtonInput}; +use bevy_mod_raycast::deferred::RaycastSource; +use std::borrow::Cow; + +pub const PLACE_OBJECT_3D_MODE_LABEL: &'static str = "place_object_3d"; + +pub fn spawn_place_object_3d_workflow( + hover_service: Service<(), (), Hover>, + app: &mut App, +) -> Service, ()> { + let setup = app.spawn_service(place_object_3d_setup); + let find_position = app.spawn_continuous_service(Update, place_object_3d_find_placement); + let placement_chosen = app.spawn_service(on_placement_chosen_3d.into_blocking_service()); + let handle_key_code = app.spawn_service(on_keyboard_for_place_object_3d); + let cleanup = app.spawn_service(place_object_3d_cleanup.into_blocking_service()); + let selection_update = app.world.resource::().selection_update; + let keyboard_just_pressed = app + .world + .resource::() + .keyboard_just_pressed; + + app.world.spawn_io_workflow(build_place_object_3d_workflow( + setup, + find_position, + placement_chosen, + handle_key_code, + cleanup, + hover_service.optional_stream_cast(), + selection_update, + keyboard_just_pressed, + )) +} + +pub fn build_place_object_3d_workflow( + setup: Service, SelectionNodeResult, Select>, + find_placement: Service, Transform, Select>, + placement_chosen: Service<(Transform, BufferKey), SelectionNodeResult>, + handle_key_code: Service<(KeyCode, BufferKey), SelectionNodeResult, Select>, + cleanup: Service, SelectionNodeResult>, + // Used to manage highlighting prospective parent frames + hover_service: Service<(), ()>, + // Used to manage highlighting the current parent frame + selection_update: Service, + keyboard_just_pressed: Service<(), (), StreamOf>, +) -> impl FnOnce(Scope, ()>, &mut Builder) { + move |scope, builder| { + let buffer = builder.create_buffer::(BufferSettings::keep_last(1)); + let selection_update_node = builder.create_node(selection_update); + let setup_node = scope + .input + .chain(builder) + .then(extract_selector_input::.into_blocking_callback()) + .branch_for_err(|err| err.connect(scope.terminate)) + .cancel_on_none() + .then_push(buffer) + .then_access(buffer) + .then_node(setup); + + builder.connect(setup_node.streams, selection_update_node.input); + + let begin_input_services = setup_node + .output + .chain(builder) + .branch_for_err(|err| err.map_block(print_if_err).connect(scope.terminate)) + .output() + .fork_clone(builder); + + let find_placement_node = begin_input_services + .clone_chain(builder) + .then_access(buffer) + .then_node(find_placement); + + find_placement_node + .output + .chain(builder) + .with_access(buffer) + .then(placement_chosen) + .fork_result( + |ok| ok.connect(scope.terminate), + |err| err.map_block(print_if_err).connect(scope.terminate), + ); + + builder.connect(find_placement_node.streams, selection_update_node.input); + + begin_input_services + .clone_chain(builder) + .then(hover_service) + .connect(scope.terminate); + + let keyboard = begin_input_services + .clone_chain(builder) + .then_node(keyboard_just_pressed); + let handle_key_node = keyboard + .streams + .chain(builder) + .inner() + .with_access(buffer) + .then_node(handle_key_code); + + handle_key_node + .output + .chain(builder) + .dispose_on_ok() + .map_block(print_if_err) + .connect(scope.terminate); + + builder.connect(handle_key_node.streams, selection_update_node.input); + + builder.on_cleanup(buffer, move |scope, builder| { + scope.input.chain(builder).then(cleanup).fork_result( + |ok| ok.connect(scope.terminate), + |err| err.map_block(print_if_err).connect(scope.terminate), + ); + }); + } +} + +pub struct PlaceObject3d { + pub object: PlaceableObject, + pub parent: Option, + pub workspace: Entity, +} + +#[derive(Clone, Debug)] +pub enum PlaceableObject { + Model(Model), + Anchor, + VisualMesh(WorkcellModel), + CollisionMesh(WorkcellModel), +} + +pub fn place_object_3d_setup( + In(srv): BlockingServiceInput, Select>, + mut access: BufferAccessMut, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut commands: Commands, + mut highlight: ResMut, + mut filter: PlaceObject3dFilter, + mut gizmo_blockers: ResMut, +) -> SelectionNodeResult { + let mut access = access.get_mut(&srv.request).or_broken_buffer()?; + let state = access.newest_mut().or_broken_buffer()?; + + match &state.object { + PlaceableObject::Anchor => { + // Make the anchor placement component of the cursor visible + set_visibility(cursor.frame_placement, &mut visibility, true); + set_visibility(cursor.dagger, &mut visibility, true); + set_visibility(cursor.halo, &mut visibility, true); + } + PlaceableObject::Model(m) => { + // Spawn the model as a child of the cursor + cursor.set_model_preview(&mut commands, Some(m.clone())); + set_visibility(cursor.dagger, &mut visibility, false); + set_visibility(cursor.halo, &mut visibility, false); + } + PlaceableObject::VisualMesh(m) | PlaceableObject::CollisionMesh(m) => { + // Spawn the model as a child of the cursor + cursor.set_workcell_model_preview(&mut commands, Some(m.clone())); + set_visibility(cursor.dagger, &mut visibility, false); + set_visibility(cursor.halo, &mut visibility, false); + } + } + + if let Some(parent) = state.parent { + let parent = filter.filter_select(parent); + state.parent = parent; + } + srv.streams.send(Select::new(state.parent)); + + highlight.0 = true; + gizmo_blockers.selecting = true; + + cursor.add_mode(PLACE_OBJECT_3D_MODE_LABEL, &mut visibility); + + Ok(()) +} + +pub fn place_object_3d_cleanup( + In(_): In>, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut commands: Commands, + mut highlight: ResMut, + mut gizmo_blockers: ResMut, +) -> SelectionNodeResult { + cursor.remove_preview(&mut commands); + cursor.remove_mode(PLACE_OBJECT_3D_MODE_LABEL, &mut visibility); + set_visibility(cursor.frame_placement, &mut visibility, false); + highlight.0 = false; + gizmo_blockers.selecting = false; + + Ok(()) +} + +pub fn place_object_3d_find_placement( + In(ContinuousService { key: srv_key }): ContinuousServiceInput< + BufferKey, + Transform, + Select, + >, + mut orders: ContinuousQuery, Transform, Select>, + mut buffer: BufferAccessMut, + mut cursor: ResMut, + raycast_sources: Query<&RaycastSource>, + mut transforms: Query<&mut Transform>, + intersect_ground_params: IntersectGroundPlaneParams, + mut visibility: Query<&mut Visibility>, + mut tooltips: ResMut, + keyboard_input: Res>, + mut hover: EventWriter, + hovering: Res, + mouse_button_input: Res>, + blockers: Option>, + meta: Query<(Option<&'static NameInSite>, Option<&'static SiteID>)>, + mut filter: PlaceObject3dFilter, +) { + let Some(mut orders) = orders.get_mut(&srv_key) else { + return; + }; + + let Some(order) = orders.get_mut(0) else { + return; + }; + + let key = order.request(); + let Ok(mut buffer) = buffer.get_mut(key) else { + error!("Unable to retrieve buffer in place_object_3d_cursor_transform"); + return; + }; + let Some(state) = buffer.newest_mut() else { + error!("Missing state in place_object_3d_cursor_transform"); + return; + }; + + if state.parent.is_some() { + tooltips.add(Cow::Borrowed("Esc: deselect current parent")); + } + + let project_to_plane = keyboard_input.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]); + + let mut transform = match transforms.get_mut(cursor.frame) { + Ok(transform) => transform, + Err(err) => { + error!("No cursor transform found: {err}"); + return; + } + }; + + let Ok(source) = raycast_sources.get_single() else { + return; + }; + + // Check if there is an intersection with a mesh + let mut intersection: Option = None; + let mut new_hover = None; + let mut select_new_parent = false; + if !project_to_plane { + for (e, i) in source.intersections() { + let Some(e) = filter.filter_pick(*e) else { + continue; + }; + + if let Some(parent) = state.parent { + if e == parent { + new_hover = Some(parent); + cursor.add_mode(PLACE_OBJECT_3D_MODE_LABEL, &mut visibility); + tooltips.add(Cow::Borrowed("Click to place")); + tooltips.add(Cow::Borrowed("+Shift: Project to parent frame")); + + // Don't use the intersection with the parent if the parent + // is an anchor because that results in silly orientations + // which the user probably does not want. + if !filter.anchors.contains(e) { + intersection = Some( + Transform::from_translation(i.position()) + .with_rotation(aligned_z_axis(i.normal())), + ); + } + break; + } + } else { + new_hover = Some(e); + select_new_parent = true; + cursor.remove_mode(PLACE_OBJECT_3D_MODE_LABEL, &mut visibility); + tooltips.add(Cow::Borrowed("Click to set as parent")); + tooltips.add(Cow::Borrowed("+Shift: Project to ground plane")); + break; + } + } + } else { + cursor.add_mode(PLACE_OBJECT_3D_MODE_LABEL, &mut visibility); + } + + if new_hover != hovering.0 { + hover.send(Hover(new_hover)); + } + + if !select_new_parent { + intersection = intersection.or_else(|| { + if let Some(parent) = state.parent { + tooltips.add(Cow::Borrowed("Click to place")); + cursor.add_mode(PLACE_OBJECT_3D_MODE_LABEL, &mut visibility); + intersect_ground_params.frame_plane_intersection(parent) + } else { + tooltips.add(Cow::Borrowed("Click to place")); + cursor.add_mode(PLACE_OBJECT_3D_MODE_LABEL, &mut visibility); + intersect_ground_params.ground_plane_intersection() + } + }); + + if let Some(intersection) = intersection { + *transform = intersection; + } + } + + let clicked = mouse_button_input.just_pressed(MouseButton::Left); + let blocked = blockers.filter(|x| x.blocking()).is_some(); + if clicked && !blocked { + if select_new_parent { + if let Some(new_parent) = new_hover { + state.parent = Some(new_parent); + order.streams().send(Select::new(Some(new_parent))); + if let Ok((name, id)) = meta.get(new_parent) { + let id = id.map(|id| id.0.to_string()); + info!( + "Placing object in the frame of [{}], id: {}", + name.map(|name| name.0.as_str()).unwrap_or(""), + id.as_ref().map(|id| id.as_str()).unwrap_or("*"), + ); + } + } + } else { + if let Some(intersection) = intersection { + // The user is choosing a location to place the object. + order.respond(intersection); + } else { + warn!("Unable to find a placement position. Try adjusting your camera angle."); + } + } + } +} + +#[derive(SystemParam)] +pub struct PlaceObject3dFilter<'w, 's> { + inspect: InspectorFilter<'w, 's>, + ignore: Query<'w, 's, (), Or<(With, With)>>, + // We aren't using this in the filter functions, we're sneaking this query + // into this system param to skirt around the 16-parameter limit for + // place_object_3d_find_placement + anchors: Query<'w, 's, (), With>, +} + +impl<'w, 's> SelectionFilter for PlaceObject3dFilter<'w, 's> { + fn filter_pick(&mut self, target: Entity) -> Option { + let e = self.inspect.filter_pick(target); + + if let Some(e) = e { + if self.ignore.contains(e) { + return None; + } + } + e + } + + fn filter_select(&mut self, target: Entity) -> Option { + self.inspect.filter_select(target) + } + + fn on_click(&mut self, _: Hover) -> Option, +) { + let Some(mut orders) = orders.get_mut(&key) else { + selected.clear(); + return; + }; + + let Some(order) = orders.get_mut(0) else { + // Clear the selected reader so we don't mistake an earlier signal as + // being intended for this workflow. + selected.clear(); + return; + }; + + let object = *order.request(); + for s in selected.read() { + // Allow users to signal the choice of parent by means other than clicking + match s.0 { + Some(s) => { + if let Some(e) = filter.filter_pick(s.candidate) { + order.respond(Some(e)); + return; + } + + info!( + "Received parent replacement selection signal for an invalid parent candidate" + ); + } + None => { + // The user has sent a signal to remove the object from its parent + order.respond(None); + return; + } + } + } + + let Ok(source) = raycast_sources.get_single() else { + return; + }; + + let mut hovered: Option = None; + let mut ignore_click = false; + for (e, _) in source.intersections() { + let Some(e) = filter.filter_pick(*e) else { + continue; + }; + + if AncestorIter::new(&parents, e) + .filter(|e| *e == object) + .next() + .is_some() + { + ignore_click = true; + tooltips.add(Cow::Borrowed( + "Cannot select a child of the object to be its parent", + )); + break; + } + + if e == object { + ignore_click = true; + tooltips.add(Cow::Borrowed( + "Cannot select an object to be its own parent", + )); + break; + } + + hovered = Some(e); + } + + if hovered.is_some() { + tooltips.add(Cow::Borrowed("Click to select this as the parent")); + } else if !ignore_click { + tooltips.add(Cow::Borrowed("Click to remove parent")); + } + + if hovered != hovering.0 { + hover.send(Hover(hovered)); + } + + let clicked = mouse_button_input.just_pressed(MouseButton::Left); + let blocked = blockers.filter(|x| x.blocking()).is_some(); + if clicked && !blocked && !ignore_click { + order.respond(hovered); + } +} + +pub fn replace_parent_3d_parent_chosen( + In((parent, key)): In<(Option, BufferKey)>, + access: BufferAccess, + mut dependents: Query<&mut Dependents>, + mut poses: Query<&mut Pose>, + global_tfs: Query<&GlobalTransform>, + parents: Query<&Parent>, + frames: Query<(), With>, + mut commands: Commands, + mut anchors: Query<&mut Anchor>, +) -> SelectionNodeResult { + let access = access.get(&key).or_broken_buffer()?; + let state = access.newest().or_broken_state()?; + + let parent = parent + .and_then(|p| { + if frames.contains(p) { + Some(p) + } else { + // The selected parent is not a frame, so find its first ancestor + // that contains a FrameMarker + AncestorIter::new(&parents, p).find(|e| frames.contains(*e)) + } + }) + .unwrap_or(state.workspace); + + let previous_parent = parents.get(state.object).or_broken_query()?.get(); + if parent == previous_parent { + info!("Object's parent remains the same"); + return Ok(()); + } + + let object_tf = global_tfs.get(state.object).or_broken_query()?.affine(); + let inv_parent_tf = global_tfs.get(parent).or_broken_query()?.affine().inverse(); + let relative_pose: Pose = Transform::from_matrix((inv_parent_tf * object_tf).into()).into(); + + let [mut previous_deps, mut new_deps] = dependents + .get_many_mut([previous_parent, parent]) + .or_broken_query()?; + + if let Ok(mut pose_mut) = poses.get_mut(state.object) { + *pose_mut = relative_pose; + } else { + let mut anchor = anchors.get_mut(state.object).or_broken_query()?; + *anchor = Anchor::Pose3D(relative_pose); + } + + // Do all mutations after everything is successfully queried so we don't + // risk an inconsistent/broken world due to a query failing. + commands + .get_entity(state.object) + .or_broken_query()? + .set_parent(parent); + previous_deps.remove(&state.object); + new_deps.insert(state.object); + + Ok(()) +} + +pub fn on_keyboard_for_replace_parent_3d(In(code): In) -> SelectionNodeResult { + if matches!(code, KeyCode::Escape) { + // Simply exit the workflow if the user presses esc + return Err(None); + } + + Ok(()) +} diff --git a/rmf_site_editor/src/interaction/select/replace_point.rs b/rmf_site_editor/src/interaction/select/replace_point.rs new file mode 100644 index 00000000..54339714 --- /dev/null +++ b/rmf_site_editor/src/interaction/select/replace_point.rs @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use crate::{ + interaction::*, + site::{ChangeDependent, Original}, +}; +use bevy::prelude::*; +use bevy_impulse::*; +use rmf_site_format::Point; +use std::borrow::Borrow; + +pub fn spawn_replace_point_service( + helpers: &AnchorSelectionHelpers, + app: &mut App, +) -> Service, ()> { + let anchor_setup = + app.spawn_service(anchor_selection_setup::.into_blocking_service()); + let state_setup = app.spawn_service(replace_point_setup.into_blocking_service()); + let update_preview = app.spawn_service(on_hover_for_replace_point.into_blocking_service()); + let update_current = app.spawn_service(on_select_for_replace_point.into_blocking_service()); + let handle_key_code = app.spawn_service(exit_on_esc::.into_blocking_service()); + let cleanup_state = app.spawn_service(cleanup_replace_point.into_blocking_service()); + + helpers.spawn_anchor_selection_workflow( + anchor_setup, + state_setup, + update_preview, + update_current, + handle_key_code, + cleanup_state, + &mut app.world, + ) +} + +pub struct ReplacePoint { + /// The point whose anchor is being replaced + pub point: Entity, + /// The original value of the point. This is None until setup occurs, then + /// its value will be available. + pub original: Option>, + /// The scope that the point exists in + pub scope: AnchorScope, + /// Keeps track of whether the replacement really happened. If false, the + /// cleanup will revert the point to its original state. If true, the cleanup + /// will not need to do anything. + pub replaced: bool, +} + +impl ReplacePoint { + pub fn new(point: Entity, scope: AnchorScope) -> Self { + Self { + point, + original: None, + scope, + replaced: false, + } + } + + pub fn set_chosen( + &mut self, + chosen: Entity, + points: &mut Query<&mut Point>, + commands: &mut Commands, + ) -> SelectionNodeResult { + let mut point_mut = points.get_mut(self.point).or_broken_query()?; + commands.add(ChangeDependent::remove(point_mut.0, self.point)); + point_mut.0 = chosen; + commands.add(ChangeDependent::add(chosen, self.point)); + Ok(()) + } +} + +impl Borrow for ReplacePoint { + fn borrow(&self) -> &AnchorScope { + &self.scope + } +} + +pub fn replace_point_setup( + In(key): In>, + mut access: BufferAccessMut, + mut points: Query<&'static mut Point>, + cursor: Res, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + let original = *points.get(state.point).or_broken_query()?; + state.original = Some(original); + commands.entity(state.point).insert(Original(original)); + state.set_chosen(cursor.level_anchor_placement, &mut points, &mut commands)?; + + Ok(()) +} + +pub fn on_hover_for_replace_point( + In((hover, key)): In<(Hover, BufferKey)>, + mut access: BufferAccessMut, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut points: Query<&mut Point>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + let chosen = match hover.0 { + Some(anchor) => { + cursor.remove_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + anchor + } + None => { + cursor.add_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + cursor.level_anchor_placement + } + }; + + state.set_chosen(chosen, &mut points, &mut commands) +} + +pub fn on_select_for_replace_point( + In((selection, key)): In<(SelectionCandidate, BufferKey)>, + mut access: BufferAccessMut, + mut points: Query<&mut Point>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + state.set_chosen(selection.candidate, &mut points, &mut commands)?; + state.replaced = true; + // Since the selection has been made, we should exit the workflow now + Err(None) +} + +pub fn cleanup_replace_point( + In(key): In>, + mut access: BufferAccessMut, + mut points: Query<&'static mut Point>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let mut state = access.pull().or_broken_state()?; + + commands + .get_entity(state.point) + .or_broken_query()? + .remove::>>(); + + if state.replaced { + // The anchor was fully replaced, so nothing furtehr to do + return Ok(()); + } + + let Some(original) = state.original else { + return Ok(()); + }; + + state.set_chosen(original.0, &mut points, &mut commands)?; + + Ok(()) +} diff --git a/rmf_site_editor/src/interaction/select/replace_side.rs b/rmf_site_editor/src/interaction/select/replace_side.rs new file mode 100644 index 00000000..fa0608ab --- /dev/null +++ b/rmf_site_editor/src/interaction/select/replace_side.rs @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use crate::{ + interaction::*, + site::{ChangeDependent, Original}, +}; +use bevy::prelude::*; +use bevy_impulse::*; +use rmf_site_format::{Edge, Side}; +use std::borrow::Borrow; + +pub fn spawn_replace_side_service( + helpers: &AnchorSelectionHelpers, + app: &mut App, +) -> Service, ()> { + let anchor_setup = + app.spawn_service(anchor_selection_setup::.into_blocking_service()); + let state_setup = app.spawn_service(replace_side_setup.into_blocking_service()); + let update_preview = app.spawn_service(on_hover_for_replace_side.into_blocking_service()); + let update_current = app.spawn_service(on_select_for_replace_side.into_blocking_service()); + let handle_key_code = app.spawn_service(exit_on_esc::.into_blocking_service()); + let cleanup_state = app.spawn_service(cleanup_replace_side.into_blocking_service()); + + helpers.spawn_anchor_selection_workflow( + anchor_setup, + state_setup, + update_preview, + update_current, + handle_key_code, + cleanup_state, + &mut app.world, + ) +} + +pub struct ReplaceSide { + /// The edge whose anchor is being replaced + pub edge: Entity, + /// The side of the edge which is being replaced + pub side: Side, + /// The original values for the edge. This is None until setup occurs, then + /// its value will be available. + pub original: Option>, + /// The scope that the edge exists in + pub scope: AnchorScope, + /// Keeps track of whether the replacement really happened. If false, the + /// cleanup will revert the edge to its original state. If true, the cleanup + /// will not need to do anything. + pub replaced: bool, +} + +impl ReplaceSide { + pub fn new(edge: Entity, side: Side, scope: AnchorScope) -> Self { + Self { + edge, + side, + scope, + original: None, + replaced: false, + } + } + + pub fn set_chosen( + &mut self, + chosen: Entity, + edges: &mut Query<&mut Edge>, + commands: &mut Commands, + ) -> SelectionNodeResult { + let original = self.original.or_broken_buffer()?; + let mut edge_mut = edges.get_mut(self.edge).or_broken_query()?; + + for a in edge_mut.array() { + // Remove both current dependencies in case both of them change. + // If either dependency doesn't change then they'll be added back + // later anyway. + commands.add(ChangeDependent::remove(a, self.edge)); + } + + if chosen == original.array()[self.side.opposite().index()] { + // The user is choosing the anchor on the opposite side of the edge as + // the replacement anchor. We take this to mean that the user wants to + // flip the edge. + *edge_mut.left_mut() = original.right(); + *edge_mut.right_mut() = original.left(); + } else { + edge_mut.array_mut()[self.side.index()] = chosen; + let opp = self.side.opposite().index(); + edge_mut.array_mut()[opp] = original.array()[opp]; + } + + for a in edge_mut.array() { + commands.add(ChangeDependent::add(a, self.edge)); + } + + Ok(()) + } +} + +impl Borrow for ReplaceSide { + fn borrow(&self) -> &AnchorScope { + &self.scope + } +} + +pub fn replace_side_setup( + In(key): In>, + mut access: BufferAccessMut, + mut edges: Query<&'static mut Edge>, + cursor: Res, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + let edge_ref = edges.get(state.edge).or_broken_query()?; + let original_edge: Edge = *edge_ref; + state.original = Some(original_edge); + commands.entity(state.edge).insert(Original(original_edge)); + state.set_chosen(cursor.level_anchor_placement, &mut edges, &mut commands)?; + + Ok(()) +} + +pub fn on_hover_for_replace_side( + In((hover, key)): In<(Hover, BufferKey)>, + mut access: BufferAccessMut, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut edges: Query<&mut Edge>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + + let chosen = match hover.0 { + Some(anchor) => { + cursor.remove_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + anchor + } + None => { + cursor.add_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + cursor.level_anchor_placement + } + }; + + state.set_chosen(chosen, &mut edges, &mut commands) +} + +pub fn on_select_for_replace_side( + In((selection, key)): In<(SelectionCandidate, BufferKey)>, + mut access: BufferAccessMut, + mut edges: Query<&mut Edge>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let state = access.newest_mut().or_broken_state()?; + state.set_chosen(selection.candidate, &mut edges, &mut commands)?; + state.replaced = true; + // Since the selection has been made, we should exit the workflow now + Err(None) +} + +pub fn cleanup_replace_side( + In(key): In>, + mut access: BufferAccessMut, + mut edges: Query<&'static mut Edge>, + mut commands: Commands, +) -> SelectionNodeResult { + let mut access = access.get_mut(&key).or_broken_buffer()?; + let mut state = access.pull().or_broken_state()?; + + commands + .get_entity(state.edge) + .or_broken_query()? + .remove::>>(); + + if state.replaced { + // The anchor was fully replaced, so nothing further to do + return Ok(()); + } + + // The anchor was not replaced so we need to revert to the original setup + let Some(original) = state.original else { + return Ok(()); + }; + + let revert = original.array()[state.side.index()]; + state.set_chosen(revert, &mut edges, &mut commands)?; + + Ok(()) +} diff --git a/rmf_site_editor/src/interaction/select/select_anchor.rs b/rmf_site_editor/src/interaction/select/select_anchor.rs new file mode 100644 index 00000000..399a63d4 --- /dev/null +++ b/rmf_site_editor/src/interaction/select/select_anchor.rs @@ -0,0 +1,765 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +use bevy::prelude::*; +use bevy_impulse::*; + +use crate::{interaction::select::*, site::CurrentLevel}; +use rmf_site_format::{Fiducial, Floor, LevelElevation, Location, Path, Point}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Resource)] +pub enum AnchorScope { + Drawing, + General, + Site, +} + +impl AnchorScope { + pub fn is_site(&self) -> bool { + match self { + AnchorScope::Site => true, + _ => false, + } + } +} + +#[derive(Default)] +pub struct AnchorSelectionPlugin {} + +impl Plugin for AnchorSelectionPlugin { + fn build(&self, app: &mut App) { + let helpers = AnchorSelectionHelpers::from_app(app); + let services = AnchorSelectionServices::from_app(&helpers, app); + app.init_resource::() + .insert_resource(AnchorScope::General) + .insert_resource(helpers) + .insert_resource(services); + } +} + +#[derive(Resource, Clone, Copy)] +pub struct AnchorSelectionHelpers { + pub anchor_select_stream: Service<(), (), (Hover, Select)>, + pub anchor_cursor_transform: Service<(), ()>, + pub keyboard_just_pressed: Service<(), (), StreamOf>, + pub cleanup_anchor_selection: Service<(), ()>, +} + +impl AnchorSelectionHelpers { + pub fn from_app(app: &mut App) -> Self { + let anchor_select_stream = app.spawn_selection_service::(); + let anchor_cursor_transform = app.spawn_continuous_service( + Update, + select_anchor_cursor_transform + .configure(|config: SystemConfigs| config.in_set(SelectionServiceStages::Pick)), + ); + let cleanup_anchor_selection = app + .world + .spawn_service(cleanup_anchor_selection.into_blocking_service()); + + let keyboard_just_pressed = app + .world + .resource::() + .keyboard_just_pressed; + + Self { + anchor_select_stream, + anchor_cursor_transform, + keyboard_just_pressed, + cleanup_anchor_selection, + } + } + + pub fn spawn_anchor_selection_workflow( + &self, + anchor_setup: Service, SelectionNodeResult>, + state_setup: Service, SelectionNodeResult>, + update_preview: Service<(Hover, BufferKey), SelectionNodeResult>, + update_current: Service<(SelectionCandidate, BufferKey), SelectionNodeResult>, + handle_key_code: Service<(KeyCode, BufferKey), SelectionNodeResult>, + cleanup_state: Service, SelectionNodeResult>, + world: &mut World, + ) -> Service, ()> { + world.spawn_io_workflow(build_anchor_selection_workflow( + anchor_setup, + state_setup, + update_preview, + update_current, + handle_key_code, + cleanup_state, + self.anchor_cursor_transform, + self.anchor_select_stream, + self.keyboard_just_pressed, + self.cleanup_anchor_selection, + )) + } +} + +#[derive(Resource, Clone, Copy)] +pub struct AnchorSelectionServices { + pub create_edges: Service, ()>, + pub replace_side: Service, ()>, + pub create_path: Service, ()>, + pub create_point: Service, ()>, + pub replace_point: Service, ()>, +} + +impl AnchorSelectionServices { + pub fn from_app(helpers: &AnchorSelectionHelpers, app: &mut App) -> Self { + let create_edges = spawn_create_edges_service(helpers, app); + let replace_side = spawn_replace_side_service(helpers, app); + let create_path = spawn_create_path_service(helpers, app); + let create_point = spawn_create_point_service(helpers, app); + let replace_point = spawn_replace_point_service(helpers, app); + Self { + create_edges, + replace_side, + create_path, + create_point, + replace_point, + } + } +} + +#[derive(SystemParam)] +pub struct AnchorSelection<'w, 's> { + pub services: Res<'w, AnchorSelectionServices>, + pub commands: Commands<'w, 's>, +} + +impl<'w, 's> AnchorSelection<'w, 's> { + pub fn create_lanes(&mut self) { + self.create_edges::>(EdgeContinuity::Continuous, AnchorScope::General); + } + + pub fn create_measurements(&mut self) { + self.create_edges::>(EdgeContinuity::Separate, AnchorScope::Drawing) + } + + pub fn create_walls(&mut self) { + self.create_edges_with_texture::>( + EdgeContinuity::Continuous, + AnchorScope::General, + ); + } + + pub fn create_door(&mut self) { + self.create_edges::>(EdgeContinuity::Single, AnchorScope::General) + } + + pub fn create_lift(&mut self) { + self.create_edges::>(EdgeContinuity::Single, AnchorScope::Site) + } + + pub fn create_floor(&mut self) { + self.create_path::>( + create_path_with_texture::>, + 3, + false, + true, + AnchorScope::General, + ); + } + + pub fn create_location(&mut self) { + self.create_point::>(false, AnchorScope::General); + } + + pub fn create_site_fiducial(&mut self) { + self.create_point::>(false, AnchorScope::Site); + } + + pub fn create_drawing_fiducial(&mut self) { + self.create_point::>(false, AnchorScope::Drawing); + } + + pub fn create_edges>>( + &mut self, + continuity: EdgeContinuity, + scope: AnchorScope, + ) { + let state = self + .commands + .spawn(SelectorInput(CreateEdges::new::(continuity, scope))) + .id(); + + self.send(RunSelector { + selector: self.services.create_edges, + input: Some(state), + }); + } + + pub fn create_edges_with_texture>>( + &mut self, + continuity: EdgeContinuity, + scope: AnchorScope, + ) { + let state = self + .commands + .spawn(SelectorInput(CreateEdges::new_with_texture::( + continuity, scope, + ))) + .id(); + + self.send(RunSelector { + selector: self.services.create_edges, + input: Some(state), + }); + } + + pub fn replace_side(&mut self, edge: Entity, side: Side, category: Category) -> bool { + let scope = match category { + Category::Lane | Category::Wall | Category::Door => AnchorScope::General, + Category::Measurement => AnchorScope::Drawing, + Category::Lift => AnchorScope::Site, + _ => return false, + }; + let state = self + .commands + .spawn(SelectorInput(ReplaceSide::new(edge, side, scope))) + .id(); + + self.send(RunSelector { + selector: self.services.replace_side, + input: Some(state), + }); + + true + } + + pub fn create_path>>( + &mut self, + spawn_path: fn(Path, &mut Commands) -> Entity, + minimum_points: usize, + allow_inner_loops: bool, + implied_complete_loop: bool, + scope: AnchorScope, + ) { + let state = self + .commands + .spawn(SelectorInput(CreatePath::new( + spawn_path, + minimum_points, + allow_inner_loops, + implied_complete_loop, + scope, + ))) + .id(); + + self.send(RunSelector { + selector: self.services.create_path, + input: Some(state), + }); + } + + pub fn create_point>>( + &mut self, + repeating: bool, + scope: AnchorScope, + ) { + let state = self + .commands + .spawn(SelectorInput(CreatePoint::new::(repeating, scope))) + .id(); + + self.send(RunSelector { + selector: self.services.create_point, + input: Some(state), + }); + } + + pub fn replace_point(&mut self, point: Entity, scope: AnchorScope) { + let state = self + .commands + .spawn(SelectorInput(ReplacePoint::new(point, scope))) + .id(); + + self.send(RunSelector { + selector: self.services.replace_point, + input: Some(state), + }); + } + + fn send(&mut self, run: RunSelector) { + self.commands.add(move |world: &mut World| { + world.send_event(run); + }); + } +} + +#[derive(Resource, Default)] +pub struct HiddenSelectAnchorEntities { + /// All drawing anchors, hidden when users draw level entities such as walls, lanes, floors to + /// make sure they don't connect to drawing anchors + pub drawing_anchors: HashSet, +} + +/// The first five services should be customized for the State data. The services +/// that return [`SelectionNodeResult`] should return `Ok(())` if it is okay for the +/// workflow to continue as normal, and they should return `Err(None)` if it's +/// time for the workflow to terminate as normal. If the workflow needs to +/// terminate because of an error, return `Err(Some(_))`. +/// +/// In most cases you should use [`AnchorSelectionHelpers::spawn_anchor_selection_workflow`] +/// instead of running this function yourself directly, unless you know that you +/// need to customize the last four services. +/// +/// * `anchor_setup`: This is run once at the start of the workflow to prepare the +/// world to select anchors from the right kind of scope for the request. This +/// is usually just [`anchor_selection_setup`] instantiated for the right type +/// of state. +/// * `state_setup`: This is for any additional custom setup that is relevant to +/// the state information for your selection workflow. This gets run exactly once +/// immediately after `anchor_setup` +/// * `update_preview`: This is run each time a [`Hover`] signal arrives. This +/// is where you should put the logic to update the preview that's being displayed +/// for users. +/// * `update_current`: This is run each time a [`Select`] signal containing `Some` +/// value is sent. This is where you should put the logic to make a persistent +/// (rather than just a preview) modification to the world. +/// * `handle_key_code`: This is where you should put the logic for how your +/// workflow responds to various key codes. For example, should the workflow +/// exit? +/// * `cleanup_state`: This is where you should run anything that's needed to +/// clean up the state of the world after your workflow is finished running. +/// This will be run no matter whether your workflow terminates with a success, +/// terminates with a failure, or cancels prematurely. +/// +/// ### The remaining parameters can all be provided by [`AnchorSelectionHelpers`] in most cases: +/// +/// * `anchor_cursor_transform`: This service should update the 3D cursor transform. +/// A suitable service for this is available from [`AnchorSelectionHelpers`]. +/// * `anchor_select_stream`: This service should produce the [`Hover`] and [`Select`] +/// streams that hook into `update_preview` and `update_current` respectively. +/// A suitable service for this is provided by [`AnchorSelectionHelpers`]. +/// * `keyobard_just_pressed`: This service should produce [`KeyCode`] streams +/// when the keyboard gets pressed. A suitable service for this is provided by +/// [`AnchorSelectionHelpers`]. +/// * `cleanup_anchor_selection`: This service will run during the cleanup phase +/// and should cleanup any anchor-related modifications to the world. A suitable +/// service for this is provided by [`AnchorSelectionHelpers`]. +pub fn build_anchor_selection_workflow( + anchor_setup: Service, SelectionNodeResult>, + state_setup: Service, SelectionNodeResult>, + update_preview: Service<(Hover, BufferKey), SelectionNodeResult>, + update_current: Service<(SelectionCandidate, BufferKey), SelectionNodeResult>, + handle_key_code: Service<(KeyCode, BufferKey), SelectionNodeResult>, + cleanup_state: Service, SelectionNodeResult>, + anchor_cursor_transform: Service<(), ()>, + anchor_select_stream: Service<(), (), (Hover, Select)>, + keyboard_just_pressed: Service<(), (), StreamOf>, + cleanup_anchor_selection: Service<(), ()>, +) -> impl FnOnce(Scope, ()>, &mut Builder) { + move |scope, builder| { + let buffer = builder.create_buffer::(BufferSettings::keep_last(1)); + + let setup_node = builder.create_buffer_access(buffer); + scope + .input + .chain(builder) + .then(extract_selector_input.into_blocking_callback()) + // If the setup failed, then terminate right away. + .branch_for_err(|chain: Chain<_>| chain.connect(scope.terminate)) + .fork_option( + |some: Chain<_>| some.then_push(buffer).connect(setup_node.input), + |none: Chain<_>| none.connect(setup_node.input), + ); + + let begin_input_services = setup_node + .output + .chain(builder) + .map_block(|(_, key)| key) + .then(anchor_setup) + .branch_for_err(|err| err.map_block(print_if_err).connect(scope.terminate)) + .with_access(buffer) + .map_block(|(_, key)| key) + .then(state_setup) + .branch_for_err(|err| err.map_block(print_if_err).connect(scope.terminate)) + .output() + .fork_clone(builder); + + begin_input_services + .clone_chain(builder) + .then(anchor_cursor_transform) + .unused(); + + let select = begin_input_services + .clone_chain(builder) + .then_node(anchor_select_stream); + select + .streams + .0 + .chain(builder) + .with_access(buffer) + .then(update_preview) + .dispose_on_ok() + .map_block(print_if_err) + .connect(scope.terminate); + + select + .streams + .1 + .chain(builder) + .map_block(|s| s.0) + .dispose_on_none() + .with_access(buffer) + .then(update_current) + .dispose_on_ok() + .map_block(print_if_err) + .connect(scope.terminate); + + let keyboard = begin_input_services + .clone_chain(builder) + .then_node(keyboard_just_pressed); + keyboard + .streams + .chain(builder) + .inner() + .with_access(buffer) + .then(handle_key_code) + .dispose_on_ok() + .map_block(print_if_err) + .connect(scope.terminate); + + builder.on_cleanup(buffer, move |scope, builder| { + let state_node = builder.create_node(cleanup_state); + let anchor_node = builder.create_node(cleanup_anchor_selection); + + builder.connect(scope.input, state_node.input); + state_node.output.chain(builder).fork_result( + |ok| ok.connect(anchor_node.input), + |err| err.map_block(print_if_err).connect(anchor_node.input), + ); + + builder.connect(anchor_node.output, scope.terminate); + }); + } +} + +pub fn print_if_err(err: Option) { + if let Some(err) = err { + error!("{err}"); + } +} + +pub fn anchor_selection_setup>( + In(key): In>, + access: BufferAccess, + anchors: Query>, + drawings: Query<(), With>, + parents: Query<&'static Parent>, + mut visibility: Query<&'static mut Visibility>, + mut hidden_anchors: ResMut, + mut current_anchor_scope: ResMut, + mut cursor: ResMut, + mut highlight: ResMut, + mut gizmo_blockers: ResMut, +) -> SelectionNodeResult +where + State: 'static + Send + Sync, +{ + let access = access.get(&key).or_broken_buffer()?; + let state = access.newest().or_broken_state()?; + let scope: &AnchorScope = (&*state).borrow(); + match scope { + AnchorScope::General | AnchorScope::Site => { + // If we are working with normal level or site requests, hide all drawing anchors + for e in anchors + .iter() + .filter(|e| parents.get(*e).is_ok_and(|p| drawings.get(p.get()).is_ok())) + { + set_visibility(e, &mut visibility, false); + hidden_anchors.drawing_anchors.insert(e); + } + } + // Nothing to hide, it's done by the drawing editor plugin + AnchorScope::Drawing => {} + } + + if scope.is_site() { + set_visibility(cursor.site_anchor_placement, &mut visibility, true); + } else { + set_visibility(cursor.level_anchor_placement, &mut visibility, true); + } + + highlight.0 = true; + gizmo_blockers.selecting = true; + + *current_anchor_scope = *scope; + + cursor.add_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + set_visibility(cursor.dagger, &mut visibility, true); + set_visibility(cursor.halo, &mut visibility, true); + + Ok(()) +} + +pub fn cleanup_anchor_selection( + In(_): In<()>, + mut cursor: ResMut, + mut visibility: Query<&mut Visibility>, + mut hidden_anchors: ResMut, + mut anchor_scope: ResMut, + mut highlight: ResMut, + mut gizmo_blockers: ResMut, +) { + cursor.remove_mode(SELECT_ANCHOR_MODE_LABEL, &mut visibility); + set_visibility(cursor.site_anchor_placement, &mut visibility, false); + set_visibility(cursor.level_anchor_placement, &mut visibility, false); + for e in hidden_anchors.drawing_anchors.drain() { + set_visibility(e, &mut visibility, true); + } + + highlight.0 = false; + gizmo_blockers.selecting = false; + + *anchor_scope = AnchorScope::General; +} + +pub fn extract_selector_input( + In(e): In>, + world: &mut World, +) -> Result, ()> { + let Some(e) = e else { + // There is no input to provide, so move ahead with the workflow + return Ok(None); + }; + + let Some(mut e_mut) = world.get_entity_mut(e) else { + error!( + "Could not begin selector service because the input entity {e:?} \ + does not exist.", + ); + return Err(()); + }; + + let Some(input) = e_mut.take::>() else { + error!( + "Could not begin selector service because the input entity {e:?} \ + did not contain a value {:?}. This is a bug, please report it.", + std::any::type_name::>(), + ); + return Err(()); + }; + + e_mut.despawn_recursive(); + + Ok(Some(input.0)) +} + +#[derive(SystemParam)] +pub struct AnchorFilter<'w, 's> { + inspect: InspectorFilter<'w, 's>, + anchors: Query<'w, 's, (), With>, + cursor: Res<'w, Cursor>, + anchor_scope: Res<'w, AnchorScope>, + workspace: Res<'w, CurrentWorkspace>, + open_sites: Query<'w, 's, Entity, With>, + transforms: Query<'w, 's, &'static GlobalTransform>, + commands: Commands<'w, 's>, + current_drawing: Res<'w, CurrentEditDrawing>, + drawings: Query<'w, 's, &'static PixelsPerMeter, With>, + parents: Query<'w, 's, &'static Parent>, + levels: Query<'w, 's, (), With>, + current_level: Res<'w, CurrentLevel>, +} + +impl<'w, 's> SelectionFilter for AnchorFilter<'w, 's> { + fn filter_pick(&mut self, select: Entity) -> Option { + self.inspect + .filter_pick(select) + .and_then(|e| self.filter_target(e)) + } + + fn filter_select(&mut self, target: Entity) -> Option { + self.filter_target(target) + } + + fn on_click(&mut self, hovered: Hover) -> Option, - mut hover: EventWriter, - blockers: Option>, - workspace: Res, - open_sites: Query>, - current_drawing: Res, -) { - let mut request = match &*mode { - InteractionMode::SelectAnchor(request) => request.clone(), - _ => { - return; - } - }; - - if mode.is_changed() { - // The mode was changed to this one on this update cycle. We should - // check if something besides an anchor is being hovered, and clear it - // out if it is. - if let Some(hovering) = hovering.0 { - if anchors.contains(hovering) { - params - .cursor - .remove_mode(SELECT_ANCHOR_MODE_LABEL, &mut params.visibility); - } else { - hover.send(Hover(None)); - params - .cursor - .add_mode(SELECT_ANCHOR_MODE_LABEL, &mut params.visibility); - } - } else { - params - .cursor - .add_mode(SELECT_ANCHOR_MODE_LABEL, &mut params.visibility); - } - - // Make the anchor placement component of the cursor visible - if request.site_scope() { - set_visibility( - params.cursor.site_anchor_placement, - &mut params.visibility, - true, - ); - } else { - set_visibility( - params.cursor.level_anchor_placement, - &mut params.visibility, - true, - ); - } - - match request.scope { - Scope::General | Scope::Site => { - // If we are working with normal level or site requests, hide all drawing anchors - for anchor in params.anchors.iter().filter(|(e, _)| { - params - .parents - .get(*e) - .is_ok_and(|p| params.drawings.get(**p).is_ok()) - }) { - set_visibility(anchor.0, &mut params.visibility, false); - params.hidden_entities.drawing_anchors.insert(anchor.0); - } - } - // Nothing to hide, it's done by the drawing editor plugin - Scope::Drawing => {} - } - - // If we are creating a new object, then we should deselect anything - // that might be currently selected. - if request.begin_creating() { - if let Some(previous_selection) = selection.0 { - if let Ok(mut selected) = selected.get_mut(previous_selection) { - selected.is_selected = false; - } - selection.0 = None; - } - } - - if request.continuity.needs_original() { - // Keep track of the original anchor that we intend to replace so - // that we can revert any previews. - let for_element = match request.target { - Some(for_element) => for_element, - None => { - error!( - "for_element must be Some for ReplaceAnchor. \ - Reverting to Inspect Mode." - ); - params.cleanup(); - *mode = InteractionMode::Inspect; - return; - } - }; - - let original = match request.placement.save_original(for_element, &mut params) { - Some(original) => original, - None => { - error!( - "cannot locate an original anchor for \ - entity {:?}. Reverting to Inspect Mode.", - for_element, - ); - params.cleanup(); - *mode = InteractionMode::Inspect; - return; - } - }; - - request.continuity = SelectAnchorContinuity::ReplaceAnchor { - original_anchor: Some(original), - }; - // Save the new mode here in case it doesn't get saved by any - // branches in the rest of this system function. - *mode = InteractionMode::SelectAnchor(request.clone()); - } - } - - if hovering.is_changed() { - if hovering.0.is_none() { - params - .cursor - .add_mode(SELECT_ANCHOR_MODE_LABEL, &mut params.visibility); - } else { - params - .cursor - .remove_mode(SELECT_ANCHOR_MODE_LABEL, &mut params.visibility); - } - } - - if select.is_empty() { - let clicked = mouse_button_input.just_pressed(MouseButton::Left) - || touch_input.iter_just_pressed().next().is_some(); - let blocked = blockers.filter(|x| x.blocking()).is_some(); - - if clicked && !blocked { - // Since the user clicked but there are no actual selections, the - // user is effectively asking to create a new anchor at the current - // cursor location. We will create that anchor and treat it as if it - // were selected. - let tf = match transforms.get(params.cursor.frame) { - Ok(tf) => tf, - Err(_) => { - error!( - "Could not get transform for cursor frame \ - {:?} in SelectAnchor mode.", - params.cursor.frame, - ); - // TODO(MXG): Put in backout behavior here. - return; - } - }; - - let new_anchor = match request.scope { - Scope::Site => { - let site = workspace.to_site(&open_sites).expect("No current site??"); - let new_anchor = params.commands.spawn(AnchorBundle::at_transform(tf)).id(); - params.commands.entity(site).add_child(new_anchor); - new_anchor - } - Scope::Drawing => { - let drawing_entity = current_drawing - .target() - .expect("No drawing while spawning drawing anchor") - .drawing; - let (parent, ppm) = params - .drawings - .get(drawing_entity) - .expect("Entity being edited is not a drawing"); - // We also need to have a transform such that the anchor will spawn in the - // right spot - let pose = compute_parent_inverse_pose(&tf, &transforms, parent); - let ppm = ppm.0; - let new_anchor = params - .commands - .spawn(AnchorBundle::new([pose.trans[0], pose.trans[1]].into())) - .insert(Transform::from_scale(Vec3::new(ppm, ppm, 1.0))) - .set_parent(parent) - .id(); - new_anchor - } - Scope::General => params.commands.spawn(AnchorBundle::at_transform(tf)).id(), - }; - - request = match request.next(AnchorSelection::new(new_anchor), &mut params) { - Some(next_mode) => next_mode, - None => { - params.cleanup(); - *mode = InteractionMode::Inspect; - return; - } - }; - - *mode = InteractionMode::SelectAnchor(request); - } else { - // Offer a preview based on the current hovering status - let hovered = hovering.0.unwrap_or(params.cursor.level_anchor_placement); - let current = request - .target - .map(|target| request.placement.current(target, ¶ms)) - .flatten(); - - if Some(hovered) != current { - // We should only call this function if the current hovered - // anchor is not the one currently assigned. Otherwise we - // are wasting query+command effort. - match request.preview(hovered, &mut params) { - PreviewResult::Updated(next) => { - *mode = InteractionMode::SelectAnchor(next); - } - PreviewResult::Updated3D(next) => { - *mode = InteractionMode::SelectAnchor3D(next); - } - PreviewResult::Unchanged => { - // Do nothing, the mode has not changed - } - PreviewResult::Invalid => { - // Something was invalid about the request, so we - // will exit back to Inspect mode. - params.cleanup(); - *mode = InteractionMode::Inspect; - } - }; - } - } - } else { - for new_selection in select - .read() - .filter_map(|s| s.0) - .filter(|s| anchors.contains(*s)) - { - request = match request.next(AnchorSelection::existing(new_selection), &mut params) { - Some(next_mode) => next_mode, - None => { - params.cleanup(); - *mode = InteractionMode::Inspect; - return; - } - }; - } - - *mode = InteractionMode::SelectAnchor(request); - } -} - -fn compute_parent_inverse_pose( - tf: &GlobalTransform, - transforms: &Query<&GlobalTransform>, - parent: Entity, -) -> Pose { - let parent_tf = transforms - .get(parent) - .expect("Failed in fetching parent transform"); - - let inv_tf = parent_tf.affine().inverse(); - let goal_tf = tf.affine(); - let mut pose = Pose::default(); - pose.rot = pose.rot.as_euler_extrinsic_xyz(); - pose.align_with(&Transform::from_matrix((inv_tf * goal_tf).into())) -} - -pub fn handle_select_anchor_3d_mode( - mut mode: ResMut, - anchors: Query<(), With>, - transforms: Query<&GlobalTransform>, - hovering: Res, - mouse_button_input: Res>, - touch_input: Res, - mut params: SelectAnchorPlacementParams, - selection: Res, - mut select: EventReader