diff --git a/Cargo.lock b/Cargo.lock index d61a1d96..30b19d57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1061f3ff92c2f65800df1f12fc7b4ff44ee14783104187dd04dfee6f11b0fd2" +checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -142,21 +142,22 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", - "getrandom 0.2.10", + "getrandom 0.2.11", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -213,6 +214,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_log-sys" version = "0.3.1" @@ -332,21 +339,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37875bd9915b7d67c2f117ea2c30a0989874d0b2cb694fe25403c85763c0c9e" +dependencies = [ + "concurrent-queue", + "event-listener 3.1.0", + "event-listener-strategy", "futures-core", + "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" dependencies = [ - "async-lock", + "async-lock 2.8.0", "async-task", "concurrent-queue", - "fastrand 1.9.0", - "futures-lite", + "fastrand 2.0.1", + "futures-lite 1.13.0", "slab", ] @@ -356,12 +376,12 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-executor", "async-io", - "async-lock", + "async-lock 2.8.0", "blocking", - "futures-lite", + "futures-lite 1.13.0", "once_cell", ] @@ -371,11 +391,11 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "cfg-if", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", "polling", @@ -391,7 +411,18 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb2ab2aa8a746e221ab826c73f48bc6ba41be6763f0855cb249eb6d154cf1d7" +dependencies = [ + "event-listener 3.1.0", + "event-listener-strategy", + "pin-project-lite", ] [[package]] @@ -400,15 +431,15 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-global-executor", "async-io", - "async-lock", + "async-lock 2.8.0", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite", + "futures-lite 1.13.0", "gloo-timers", "kv-log-macro", "log", @@ -422,19 +453,19 @@ dependencies = [ [[package]] name = "async-task" -version = "4.4.1" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -457,9 +488,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atomic_refcell" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f2bfe491d41d45507b8431da8274f7feeca64a49e86d980eed2937ec2ff020" +checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" [[package]] name = "autocfg" @@ -496,9 +527,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "bevy" @@ -564,7 +595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9714af523da4cdf58c42a317e5ed40349708ad954a18533991fd64c8ae0a6f68" dependencies = [ "anyhow", - "async-channel", + "async-channel 1.9.0", "bevy_app", "bevy_diagnostic", "bevy_ecs", @@ -637,7 +668,7 @@ dependencies = [ "bevy_render", "bevy_transform", "bevy_utils", - "bitflags 2.4.0", + "bitflags 2.4.1", "radsort", "serde", ] @@ -650,7 +681,7 @@ checksum = "a44e4e2784a81430199e4157e02903a987a32127c773985506f020e7d501b62e" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -683,14 +714,14 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266144b36df7e834d5198049e037ecdf2a2310a76ce39ed937d1b0a6a2c4e8c6" dependencies = [ - "async-channel", + "async-channel 1.9.0", "bevy_ecs_macros", "bevy_ptr", "bevy_reflect", "bevy_tasks", "bevy_utils", "downcast-rs", - "event-listener", + "event-listener 2.5.3", "fixedbitset", "rustc-hash", "serde", @@ -707,7 +738,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -888,7 +919,7 @@ dependencies = [ "bevy_ecs", "bevy_utils", "console_error_panic_hook", - "tracing-log", + "tracing-log 0.1.4", "tracing-subscriber", "tracing-wasm", ] @@ -901,7 +932,7 @@ checksum = "23ddc18d489b4e57832d4958cde7cd2f349f0ad91e5892ac9e2f2ee16546b981" dependencies = [ "quote", "rustc-hash", - "syn 2.0.37", + "syn 2.0.39", "toml_edit 0.19.15", ] @@ -926,9 +957,9 @@ dependencies = [ [[package]] name = "bevy_mod_outline" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479f08715afd391e7012c7d8c28f771fd1acdbf8a2bccea0ebacec0f81648cfe" +checksum = "6bc7f9d694a9b767def28e2a649bdc9b290bb9cd3c8f6d8ae90c3ad2234e3580" dependencies = [ "bevy", "bitfield", @@ -939,9 +970,9 @@ dependencies = [ [[package]] name = "bevy_mod_raycast" -version = "0.13.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9789907394dc92193e7a0123041de3bc302fd12ffc187c4fc2f365060aee941" +checksum = "b833e94bd71d2ef27445f3e146b07da4a7922fdb7b464f0f1221521f85a5725a" dependencies = [ "bevy", "crossbeam-channel", @@ -982,7 +1013,7 @@ dependencies = [ "bevy_transform", "bevy_utils", "bevy_window", - "bitflags 2.4.0", + "bitflags 2.4.1", "bytemuck", "naga_oil", "radsort", @@ -995,7 +1026,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b82c222b756d1d41ca808595934fc02edc5b05604151930bc7d00bb4fd8d1bd" dependencies = [ "bevy", - "bitflags 2.4.0", + "bitflags 2.4.1", "naga", ] @@ -1005,19 +1036,6 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c7586401a46f7d8e436028225c1df5288f2e0082d066b247a82466fea155c6" -[[package]] -name = "bevy_rapier3d" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12402872b857ba490f1040ab6212915bd9bf25f8584b31f2c43cef41b33f3be4" -dependencies = [ - "bevy", - "bitflags 1.3.2", - "log", - "nalgebra 0.32.3", - "rapier3d", -] - [[package]] name = "bevy_reflect" version = "0.11.3" @@ -1049,7 +1067,7 @@ dependencies = [ "bit-set", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", "uuid", ] @@ -1060,7 +1078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39df4824b760928c27afc7b00fb649c7a63c9d76661ab014ff5c86537ee906cb" dependencies = [ "anyhow", - "async-channel", + "async-channel 1.9.0", "bevy_app", "bevy_asset", "bevy_core", @@ -1078,12 +1096,12 @@ dependencies = [ "bevy_transform", "bevy_utils", "bevy_window", - "bitflags 2.4.0", + "bitflags 2.4.1", "bytemuck", "codespan-reporting", "downcast-rs", "encase", - "futures-lite", + "futures-lite 1.13.0", "hexasphere", "image", "js-sys", @@ -1112,7 +1130,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -1154,7 +1172,7 @@ dependencies = [ "bevy_render", "bevy_transform", "bevy_utils", - "bitflags 2.4.0", + "bitflags 2.4.1", "bytemuck", "fixedbitset", "guillotiere", @@ -1180,11 +1198,11 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c73bbb847c83990d3927005090df52f8ac49332e1643d2ad9aac3cd2974e66bf" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-executor", "async-task", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "wasm-bindgen-futures", ] @@ -1276,8 +1294,8 @@ checksum = "08d9484e32434ea84dc548cff246ce0c6f756c1336f5ea03f24ac120a48595c7" dependencies = [ "ahash", "bevy_utils_proc_macros", - "getrandom 0.2.10", - "hashbrown 0.14.1", + "getrandom 0.2.11", + "hashbrown 0.14.2", "instant", "petgraph", "thiserror", @@ -1293,7 +1311,7 @@ checksum = "5391b242c36f556db01d5891444730c83aa9dd648b6a8fd2b755d22cb3bddb57" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -1342,7 +1360,7 @@ version = "0.68.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cexpr", "clang-sys", "lazy_static", @@ -1353,7 +1371,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -1385,9 +1403,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" dependencies = [ "serde", ] @@ -1407,6 +1425,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block-sys" version = "0.1.0-beta.1" @@ -1428,20 +1455,30 @@ dependencies = [ [[package]] name = "blocking" -version = "1.4.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c4ef1f913d78636d78d538eec1f18de81e481f44b1be0a81060090530846e1" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel", - "async-lock", + "async-channel 2.1.0", + "async-lock 3.1.0", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite", + "futures-lite 2.0.1", "piper", "tracing", ] +[[package]] +name = "bstr" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -1465,14 +1502,14 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -1498,11 +1535,10 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856" dependencies = [ - "jobserver", "libc", ] @@ -1543,6 +1579,40 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.48.5", +] + +[[package]] +name = "chrono-tz" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + [[package]] name = "cipher" version = "0.2.5" @@ -1565,9 +1635,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.6" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", @@ -1575,9 +1645,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", @@ -1587,21 +1657,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clipboard-win" @@ -1719,7 +1789,7 @@ dependencies = [ "hmac", "percent-encoding", "rand 0.8.5", - "sha2", + "sha2 0.9.9", "time", "version_check", ] @@ -1730,16 +1800,10 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ - "core-foundation-sys 0.8.4", + "core-foundation-sys", "libc", ] -[[package]] -name = "core-foundation-sys" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" - [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -1772,12 +1836,12 @@ dependencies = [ [[package]] name = "coreaudio-rs" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb17e2d1795b1996419648915df94bc7103c28f7b48062d7acf4652fc371b2ff" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" dependencies = [ "bitflags 1.3.2", - "core-foundation-sys 0.6.2", + "core-foundation-sys", "coreaudio-sys", ] @@ -1797,7 +1861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" dependencies = [ "alsa", - "core-foundation-sys 0.8.4", + "core-foundation-sys", "coreaudio-rs", "dasp_sample", "jni 0.19.0", @@ -1817,9 +1881,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -1839,20 +1903,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" -dependencies = [ - "cfg-if", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -1864,46 +1914,22 @@ dependencies = [ ] [[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.8" +name = "crossbeam-utils" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", - "crossbeam-utils", ] [[package]] -name = "crossbeam-utils" -version = "0.8.16" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "cfg-if", + "generic-array", + "typenum", ] [[package]] @@ -1942,9 +1968,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.66+curl-8.3.0" +version = "0.4.68+curl-8.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70c44a72e830f0e40ad90dda8a6ab6ed6314d39776599a58a2e5e37fbc6db5b9" +checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f" dependencies = [ "cc", "libc", @@ -1979,6 +2005,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +[[package]] +name = "deunicode" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1abaf4d861455be59f64fd2b55606cb151fce304ede7165f410243ce96bde6" + [[package]] name = "digest" version = "0.9.0" @@ -1989,12 +2021,13 @@ dependencies = [ ] [[package]] -name = "dirs" -version = "4.0.0" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "dirs-sys 0.3.7", + "block-buffer 0.10.4", + "crypto-common", ] [[package]] @@ -2003,18 +2036,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", + "dirs-sys", ] [[package]] @@ -2111,7 +2133,7 @@ checksum = "3fe2568f851fd6144a45fa91cfed8fe5ca8fc0b56ba6797bfc1ed2771b90e37c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2156,25 +2178,14 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "2.3.1" @@ -2200,6 +2211,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" +dependencies = [ + "event-listener 3.1.0", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -2217,9 +2249,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" dependencies = [ "simd-adler32", ] @@ -2244,9 +2276,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -2261,14 +2293,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "float_eq" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a80e3145d8ad11ba0995949bbcf48b9df2be62772b3d351ef017dff6ecb853" + [[package]] name = "float_next_after" -version = "0.1.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc612c5837986b7104a87a0df74a5460931f1c5274be12f8d0f40aa2f30d632" -dependencies = [ - "num-traits", -] +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" [[package]] name = "flume" @@ -2322,9 +2357,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -2337,9 +2372,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -2347,15 +2382,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -2364,9 +2399,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-lite" @@ -2383,34 +2418,48 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +dependencies = [ + "fastrand 2.0.1", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -2487,9 +2536,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "js-sys", @@ -2510,9 +2559,9 @@ dependencies = [ [[package]] name = "gilrs" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62fd19844d0eb919aca41d3e4ea0e0b6bf60e1e827558b101c269015b8f5f27a" +checksum = "9e9eec02069fcbd7abe00a28adf216547774889129a777cb5e53fdfb75d59f09" dependencies = [ "fnv", "gilrs-core", @@ -2523,17 +2572,18 @@ dependencies = [ [[package]] name = "gilrs-core" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ccc99e9b8d63ffcaa334c4babfa31f46e156618a11f63efb6e8e6bcb37b830d" +checksum = "178769da179a47b187837d1ab2b5b9b684a21180166a77a4ca37e7e58ee3833d" dependencies = [ "core-foundation", + "inotify 0.10.2", "io-kit-sys", "js-sys", "libc", "libudev-sys", "log", - "nix 0.26.4", + "nix 0.27.1", "uuid", "vec_map", "wasm-bindgen", @@ -2586,6 +2636,30 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "globset" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "gloo-timers" version = "0.2.6" @@ -2630,7 +2704,7 @@ dependencies = [ "inflections", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2705,9 +2779,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "gpu-descriptor-types", - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -2716,7 +2790,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", ] [[package]] @@ -2758,8 +2832,8 @@ name = "gz-fuel" version = "0.1.0" source = "git+https://github.com/open-rmf/gz-fuel-rs?branch=first_implementation#44c2e1e074df4499d3c40eedaa386d9d612a9314" dependencies = [ - "dirs 5.0.1", - "futures-lite", + "dirs", + "futures-lite 2.0.1", "itertools", "serde", "serde_json", @@ -2774,9 +2848,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" dependencies = [ "ahash", "allocator-api2", @@ -2841,7 +2915,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" dependencies = [ - "digest", + "digest 0.9.0", "hmac", ] @@ -2852,7 +2926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ "crypto-mac", - "digest", + "digest 0.9.0", ] [[package]] @@ -2866,9 +2940,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes 1.5.0", "fnv", @@ -2901,11 +2975,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" dependencies = [ "anyhow", - "async-channel", + "async-channel 1.9.0", "async-std", "base64 0.13.1", "cookie", - "futures-lite", + "futures-lite 1.13.0", "infer", "pin-project-lite", "rand 0.7.3", @@ -2916,6 +2990,38 @@ dependencies = [ "url", ] +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.4.0" @@ -2926,6 +3032,23 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.24.7" @@ -2954,12 +3077,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -2986,12 +3109,23 @@ dependencies = [ ] [[package]] -name = "inotify-sys" -version = "0.1.5" +name = "inotify" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" dependencies = [ - "libc", + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", ] [[package]] @@ -3014,11 +3148,11 @@ checksum = "d3b7357d2bbc5ee92f8e899ab645233e43d21407573cceb37fed8bc3dede2c02" [[package]] name = "io-kit-sys" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2d4429acc1deff0fbdece0325b4997bdb02b2c245ab7023fd5deca0f6348de" +checksum = "4769cb30e5dcf1710fc6730d3e94f78c47723a014a567de385e113c737394640" dependencies = [ - "core-foundation-sys 0.8.4", + "core-foundation-sys", "mach2", ] @@ -3044,7 +3178,7 @@ dependencies = [ "curl", "curl-sys", "flume", - "futures-lite", + "futures-lite 1.13.0", "http", "log", "once_cell", @@ -3121,15 +3255,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -3219,9 +3344,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -3245,9 +3370,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libnghttp2-sys" @@ -3259,6 +3384,28 @@ dependencies = [ "libc", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + [[package]] name = "libudev-sys" version = "0.1.4" @@ -3295,9 +3442,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -3355,9 +3502,9 @@ dependencies = [ [[package]] name = "lyon_tessellation" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d2124218d5428149f9e09520b9acc024334a607e671f032d06567b61008977c" +checksum = "23bcac20d47825850fabf1e869bf7c2bbe2daefa0776c3cd2eb7cb74635f6e4a" dependencies = [ "float_next_after", "lyon_path", @@ -3416,15 +3563,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - [[package]] name = "metal" version = "0.24.0" @@ -3482,9 +3620,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "log", @@ -3533,22 +3671,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "nalgebra" -version = "0.31.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20bd243ab3dbb395b39ee730402d2e5405e448c75133ec49cc977762c4cba3d1" -dependencies = [ - "approx", - "matrixmultiply", - "nalgebra-macros 0.1.0", - "num-complex", - "num-rational", - "num-traits", - "simba 0.7.3", - "typenum", -] - [[package]] name = "nalgebra" version = "0.32.3" @@ -3556,27 +3678,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" dependencies = [ "approx", - "glam", "matrixmultiply", - "nalgebra-macros 0.2.1", + "nalgebra-macros", "num-complex", "num-rational", "num-traits", - "simba 0.8.1", + "simba", "typenum", ] -[[package]] -name = "nalgebra-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "nalgebra-macros" version = "0.2.1" @@ -3626,16 +3736,16 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset 0.6.5", + "memoffset", ] [[package]] name = "nix" -version = "0.26.4" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cfg-if", "libc", ] @@ -3662,11 +3772,11 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "crossbeam-channel", "filetime", "fsevent-sys", - "inotify", + "inotify 0.9.6", "kqueue", "libc", "log", @@ -3737,9 +3847,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -3784,7 +3894,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -3913,9 +4023,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" dependencies = [ "cc", "libc", @@ -3929,19 +4039,13 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "optional" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978aa494585d3ca4ad74929863093e87cac9790d81fe7aba2b3dc2890643a0fc" - [[package]] name = "orbclient" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8378ac0dfbd4e7895f2d2c1f1345cab3836910baf3a300b000d04250f0c8428f" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "redox_syscall 0.3.5", + "libredox 0.0.2", ] [[package]] @@ -3952,9 +4056,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owned_ttf_parser" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" dependencies = [ "ttf-parser", ] @@ -3973,9 +4077,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -3989,36 +4093,24 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] [[package]] -name = "parry3d" -version = "0.13.5" +name = "parse-zoneinfo" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55dc0e6db79bddbc5fd583569f7356cdcc63e1e9b2b93a9ab70dd8e717160e0" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" dependencies = [ - "approx", - "arrayvec", - "bitflags 1.3.2", - "downcast-rs", - "either", - "nalgebra 0.32.3", - "num-derive", - "num-traits", - "rustc-hash", - "simba 0.8.1", - "slab", - "smallvec", - "spade", + "regex", ] [[package]] @@ -4045,6 +4137,51 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pest" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "pest_meta" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.8", +] + [[package]] name = "petgraph" version = "0.6.4" @@ -4052,7 +4189,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.2", + "indexmap 2.1.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", ] [[package]] @@ -4072,7 +4247,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -4177,9 +4352,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -4264,7 +4439,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", ] [[package]] @@ -4282,26 +4457,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" -[[package]] -name = "rapier3d" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62a8a0bd9d3135f7b4eb45d0796540e7bab47b6b7c974f90567ccc5a0454f42b" -dependencies = [ - "approx", - "arrayvec", - "bit-vec", - "bitflags 1.3.2", - "crossbeam", - "downcast-rs", - "nalgebra 0.32.3", - "num-derive", - "num-traits", - "parry3d", - "rustc-hash", - "simba 0.8.1", -] - [[package]] name = "raw-window-handle" version = "0.5.2" @@ -4322,43 +4477,43 @@ checksum = "a0d463f2884048e7153449a55166f91028d5b0ea53c79377099ce4e8cf0cf9bb" [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", + "getrandom 0.2.11", + "libredox 0.0.1", "thiserror", ] [[package]] name = "regex" -version = "1.9.6" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.9", - "regex-syntax 0.7.5", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -4372,13 +4527,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -4389,9 +4544,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "renderdoc-sys" @@ -4433,15 +4588,14 @@ dependencies = [ "bevy_mod_raycast", "bevy_obj", "bevy_polyline", - "bevy_rapier3d", "bevy_stl", "bevy_utils", "bitfield", "clap", "console_error_panic_hook", "crossbeam-channel", - "dirs 4.0.0", - "futures-lite", + "dirs", + "futures-lite 1.13.0", "gz-fuel", "itertools", "lyon", @@ -4454,6 +4608,7 @@ dependencies = [ "serde_yaml", "smallvec", "surf", + "tera", "thiserror", "thread_local", "tracing", @@ -4468,6 +4623,7 @@ name = "rmf_site_format" version = "0.0.1" dependencies = [ "bevy", + "float_eq", "glam", "pathdiff", "ron", @@ -4479,17 +4635,11 @@ dependencies = [ "uuid", ] -[[package]] -name = "robust" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5864e7ef1a6b7bcf1d6ca3f655e65e724ed3b52546a0d0a663c991522f552ea" - [[package]] name = "rodio" -version = "0.17.1" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf1d4dea18dff2e9eb6dca123724f8b60ef44ad74a9ad283cdfe025df7e73fa" +checksum = "3b1bb7b48ee48471f55da122c0044fcc7600cfcc85db88240b89cb832935e611" dependencies = [ "cpal", "lewton", @@ -4501,8 +4651,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64 0.21.4", - "bitflags 2.4.0", + "base64 0.21.5", + "bitflags 2.4.1", "serde", "serde_derive", ] @@ -4530,9 +4680,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.24" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4279d76516df406a8bd37e7dff53fd37d1a093f997a3c34a5c21658c126db06d" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", "errno", @@ -4612,11 +4762,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdformat_rs" version = "0.1.0" -source = "git+https://github.com/open-rmf/sdf_rust_experimental?rev=f86344f#f86344f95adedf9c330aa92931015041135e6594" +source = "git+https://github.com/open-rmf/sdf_rust_experimental?rev=a5daef0#a5daef0c03380e55cfdaba507c89883a87127a05" dependencies = [ "convert_case", "minidom", - "nalgebra 0.31.4", + "nalgebra", "xmltree", "yaserde", "yaserde_derive", @@ -4639,29 +4789,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -4681,9 +4831,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -4733,13 +4883,24 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -4755,19 +4916,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" -[[package]] -name = "simba" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3fd720c48c53cace224ae62bef1bbff363a70c68c4802a78b5cc6159618176" -dependencies = [ - "approx", - "num-complex", - "num-traits", - "paste", - "wide", -] - [[package]] name = "simba" version = "0.8.1" @@ -4787,6 +4935,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -4805,22 +4959,32 @@ dependencies = [ "version_check", ] +[[package]] +name = "slug" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "sluice" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" dependencies = [ - "async-channel", + "async-channel 1.9.0", "futures-core", "futures-io", ] [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" dependencies = [ "serde", ] @@ -4847,26 +5011,14 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", ] -[[package]] -name = "spade" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88e65803986868d2372c582007c39ba89936a36ea5f236bf7a7728dc258f04f9" -dependencies = [ - "num-traits", - "optional", - "robust", - "smallvec", -] - [[package]] name = "spinning_top" version = "0.2.5" @@ -4989,7 +5141,7 @@ dependencies = [ "cfg-if", "encoding_rs", "futures-util", - "getrandom 0.2.10", + "getrandom 0.2.11", "http-client", "http-types", "log", @@ -5020,9 +5172,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -5036,7 +5188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a18d114d420ada3a891e6bc8e96a2023402203296a47cdd65083377dad18ba5" dependencies = [ "cfg-if", - "core-foundation-sys 0.8.4", + "core-foundation-sys", "libc", "ntapi", "once_cell", @@ -5045,9 +5197,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.1.2" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af52f9402f94aac4948a2518b43359be8d9ce6cd9efc1c4de3b2f7b7e897d6" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" dependencies = [ "cfg-expr", "heck 0.4.1", @@ -5058,9 +5210,9 @@ dependencies = [ [[package]] name = "taffy" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798053135b826949571726ab5e30bf9509a65b3bae6425d2e2b2dd391c50a101" +checksum = "3c2287b6d7f721ada4cddf61ade5e760b2c6207df041cac9bfaa192897362fd3" dependencies = [ "arrayvec", "grid", @@ -5070,24 +5222,46 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + +[[package]] +name = "tera" +version = "1.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand 0.8.5", + "regex", + "serde", + "serde_json", + "slug", + "unic-segment", +] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] @@ -5114,13 +5288,13 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -5209,21 +5383,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.21.0", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -5234,18 +5408,18 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.2", + "indexmap 2.1.0", "toml_datetime", "winnow", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap 2.0.2", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -5254,11 +5428,10 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -5267,20 +5440,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -5298,20 +5471,31 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -5322,7 +5506,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.2.0", ] [[package]] @@ -5338,9 +5522,9 @@ dependencies = [ [[package]] name = "ttf-parser" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" [[package]] name = "twox-hash" @@ -5358,6 +5542,62 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +dependencies = [ + "unic-ucd-segment", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicase" version = "2.7.0" @@ -5455,11 +5695,11 @@ checksum = "95b09e3b3a0abd1ccb77673a6b7b8875d9d1c80626154add451cf18392dc4c3c" [[package]] name = "uuid" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.11", "serde", ] @@ -5471,9 +5711,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" +checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" [[package]] name = "vcpkg" @@ -5616,9 +5856,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2c79b77f525a2d670cb40619d7d9c673d09e0666f72c591ebd7861f84a87e57" +checksum = "82b2391658b02c27719fc5a0a73d6e696285138e8b12fba9d4baa70451023c71" dependencies = [ "core-foundation", "home", @@ -5669,7 +5909,7 @@ checksum = "8f478237b4bf0d5b70a39898a66fa67ca3a007d79f2520485b8b0c3dfc46f8c2" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.4.0", + "bitflags 2.4.1", "codespan-reporting", "log", "naga", @@ -5694,7 +5934,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.4.0", + "bitflags 2.4.1", "block", "core-graphics-types", "d3d12", @@ -5732,16 +5972,16 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c153280bb108c2979eb5c7391cb18c56642dd3c072e55f52065e13e2a1252a" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "js-sys", "web-sys", ] [[package]] name = "wide" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebecebefc38ff1860b4bc47550bbfa63af5746061cf0d29fcd7fa63171602598" +checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242" dependencies = [ "bytemuck", "safe_arch", @@ -6027,9 +6267,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] @@ -6120,3 +6360,23 @@ dependencies = [ "syn 1.0.109", "xml-rs", ] + +[[package]] +name = "zerocopy" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] diff --git a/assets/demo_workcells/demo.workcell.json b/assets/demo_workcells/demo.workcell.json index fbbd9c84..91a8e549 100644 --- a/assets/demo_workcells/demo.workcell.json +++ b/assets/demo_workcells/demo.workcell.json @@ -75,5 +75,9 @@ "visuals": { }, "collisions": { + }, + "inertias": { + }, + "joints": { } } diff --git a/rmf_site_editor/Cargo.toml b/rmf_site_editor/Cargo.toml index 2be40461..66dad7fd 100644 --- a/rmf_site_editor/Cargo.toml +++ b/rmf_site_editor/Cargo.toml @@ -17,13 +17,12 @@ path = "examples/extending_menu.rs" [dependencies] bevy_egui = "0.21" -bevy_mod_raycast = "0.13" +bevy_mod_raycast = "0.15" bevy_mod_outline = "0.5" bevy_infinite_grid = "0.8" bevy_polyline = "0.7" bevy_stl = "0.11" bevy_obj = { version = "0.11", features = ["scene"] } -bevy_rapier3d = "0.22.0" smallvec = "*" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.8.23" @@ -33,7 +32,7 @@ wasm-bindgen = "=0.2.84" futures-lite = "1.12.0" bevy = { version = "0.11", features = ["pnm", "jpeg", "tga"] } bevy_utils = "0.11" -dirs = "4.0" +dirs = "5.0" thread_local = "*" lyon = "1" thiserror = "*" @@ -46,9 +45,10 @@ tracing-subscriber = "0.3.1" rfd = "0.12" urdf-rs = "0.7" utm = "0.1.6" -sdformat_rs = { git = "https://github.com/open-rmf/sdf_rust_experimental", rev = "f86344f"} +sdformat_rs = { git = "https://github.com/open-rmf/sdf_rust_experimental", rev = "a5daef0"} gz-fuel = { git = "https://github.com/open-rmf/gz-fuel-rs", branch = "first_implementation" } pathdiff = "*" +tera = "1.19.1" # only enable the 'dynamic_linking' feature if we're not building for web or windows # TODO(luca) it seems this can be enabled on windows, check diff --git a/rmf_site_editor/src/interaction/cursor.rs b/rmf_site_editor/src/interaction/cursor.rs index b57a6997..bc4b1bca 100644 --- a/rmf_site_editor/src/interaction/cursor.rs +++ b/rmf_site_editor/src/interaction/cursor.rs @@ -18,11 +18,11 @@ use crate::{ animate::*, interaction::*, - site::{AnchorBundle, Pending, SiteAssets}, + site::{AnchorBundle, Pending, SiteAssets, Trashcan}, }; use bevy::{ecs::system::SystemParam, prelude::*, window::PrimaryWindow}; -use bevy_mod_raycast::{Ray3d, RaycastMesh, RaycastSource}; -use rmf_site_format::{FloorMarker, Model, ModelMarker, WallMarker, WorkcellModel}; +use bevy_mod_raycast::{deferred::RaycastMesh, deferred::RaycastSource, primitives::rays::Ray3d}; +use rmf_site_format::{FloorMarker, Model, ModelMarker, PrimitiveShape, WallMarker, WorkcellModel}; use std::collections::HashSet; /// A resource that keeps track of the unique entities that play a role in @@ -36,6 +36,7 @@ pub struct Cursor { pub level_anchor_placement: Entity, pub site_anchor_placement: Entity, pub frame_placement: Entity, + pub trashcan: Entity, pub preview_model: Option, dependents: HashSet, /// Use a &str to label each mode that might want to turn the cursor on @@ -107,10 +108,7 @@ impl Cursor { fn remove_preview(&mut self, commands: &mut Commands) { if let Some(current_preview) = self.preview_model { - commands - .entity(self.frame) - .remove_children(&[current_preview]); - commands.entity(current_preview).despawn_recursive(); + commands.entity(current_preview).set_parent(self.trashcan); } } @@ -133,9 +131,9 @@ impl Cursor { ) { self.remove_preview(commands); self.preview_model = if let Some(model) = model { - let cmd = commands.spawn(Pending); + let mut cmd = commands.spawn(Pending); let e = cmd.id(); - model.add_bevy_components(cmd); + model.add_bevy_components(&mut cmd); commands.entity(self.frame).push_children(&[e]); Some(e) } else { @@ -251,6 +249,8 @@ impl FromWorld for Cursor { }) .id(); + let trashcan = world.spawn(Trashcan).id(); + Self { frame: cursor, halo, @@ -258,6 +258,7 @@ impl FromWorld for Cursor { level_anchor_placement, site_anchor_placement, frame_placement, + trashcan, preview_model: None, dependents: Default::default(), modes: Default::default(), @@ -278,6 +279,7 @@ pub struct IntersectGroundPlaneParams<'w, 's> { camera_controls: Res<'w, CameraControls>, cameras: Query<'w, 's, &'static Camera>, global_transforms: Query<'w, 's, &'static GlobalTransform>, + primary_window: Query<'w, 's, &'static Window, With>, } impl<'w, 's> IntersectGroundPlaneParams<'w, 's> { @@ -287,7 +289,9 @@ impl<'w, 's> IntersectGroundPlaneParams<'w, 's> { let e_active_camera = self.camera_controls.active_camera(); let active_camera = self.cameras.get(e_active_camera).ok()?; let camera_tf = self.global_transforms.get(e_active_camera).ok()?; - let ray = Ray3d::from_screenspace(cursor_position, active_camera, camera_tf)?; + let primary_window = self.primary_window.get_single().ok()?; + let ray = + Ray3d::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); @@ -304,7 +308,7 @@ pub fn update_cursor_transform( mode: Res, cursor: Res, raycast_sources: Query<&RaycastSource>, - models: Query<(), With>, + models: Query<(), Or<(With, With)>>, mut transforms: Query<&mut Transform>, hovering: Res, intersect_ground_params: IntersectGroundPlaneParams, diff --git a/rmf_site_editor/src/interaction/gizmo.rs b/rmf_site_editor/src/interaction/gizmo.rs index 734943bb..2c37a669 100644 --- a/rmf_site_editor/src/interaction/gizmo.rs +++ b/rmf_site_editor/src/interaction/gizmo.rs @@ -16,8 +16,8 @@ */ use crate::interaction::*; -use bevy::{math::Affine3A, prelude::*}; -use bevy_mod_raycast::{Ray3d, RaycastMesh, RaycastSource}; +use bevy::{math::Affine3A, prelude::*, window::PrimaryWindow}; +use bevy_mod_raycast::{deferred::RaycastMesh, deferred::RaycastSource, primitives::rays::Ray3d}; use rmf_site_format::Pose; #[derive(Debug, Clone, Copy)] @@ -363,6 +363,7 @@ pub fn update_drag_motions( drag_state: Res, mut cursor_motion: EventReader, mut move_to: EventWriter, + primary_window: Query<&Window, With>, ) { if let GizmoState::Dragging(dragging) = *drag_state { let cursor_position = match cursor_motion.iter().last() { @@ -381,7 +382,10 @@ pub fn update_drag_motions( } }; - match Ray3d::from_screenspace(cursor_position, camera, &camera_tf) { + let Ok(primary_window) = primary_window.get_single() else { + return; + }; + match Ray3d::from_screenspace(cursor_position, camera, &camera_tf, primary_window) { Some(ray) => ray, None => { return; diff --git a/rmf_site_editor/src/interaction/mod.rs b/rmf_site_editor/src/interaction/mod.rs index 8b4c1b2b..7debd4fd 100644 --- a/rmf_site_editor/src/interaction/mod.rs +++ b/rmf_site_editor/src/interaction/mod.rs @@ -90,7 +90,7 @@ pub use visual_cue::*; use bevy::prelude::*; use bevy_mod_outline::OutlinePlugin; -use bevy_mod_raycast::{DefaultRaycastingPlugin, RaycastSystem}; +use bevy_mod_raycast::deferred::DeferredRaycastingPlugin; use bevy_polyline::PolylinePlugin; #[derive(Reflect)] @@ -136,7 +136,7 @@ impl Plugin for InteractionPlugin { apply_deferred.in_set(InteractionUpdateSet::CommandFlush), ) .add_plugins(PolylinePlugin) - .add_plugins(DefaultRaycastingPlugin::::default()) + .add_plugins(DeferredRaycastingPlugin::::default()) .init_resource::() .init_resource::() .init_resource::() @@ -254,14 +254,7 @@ impl Plugin for InteractionPlugin { ) .run_if(in_state(InteractionState::Enable)), ) - .add_systems( - First, - ( - update_picked, - update_interaction_mode, - update_raycast_with_cursor.before(RaycastSystem::BuildRays::), - ), - ); + .add_systems(First, (update_picked, update_interaction_mode)); } } diff --git a/rmf_site_editor/src/interaction/outline.rs b/rmf_site_editor/src/interaction/outline.rs index cd1ba3e3..61dd4ab5 100644 --- a/rmf_site_editor/src/interaction/outline.rs +++ b/rmf_site_editor/src/interaction/outline.rs @@ -20,7 +20,7 @@ use bevy::render::view::RenderLayers; use bevy_mod_outline::{OutlineBundle, OutlineRenderLayers, OutlineVolume, SetOutlineDepth}; use rmf_site_format::{ ConstraintMarker, DoorType, FiducialMarker, FloorMarker, LiftCabin, LightKind, LocationTags, - MeasurementMarker, ModelMarker, PhysicalCameraProperties, WallMarker, + MeasurementMarker, ModelMarker, PhysicalCameraProperties, PrimitiveShape, WallMarker, }; use smallvec::SmallVec; @@ -118,6 +118,7 @@ pub fn add_outline_visualization( Added, Added, Added, + Added, )>, >, ) { diff --git a/rmf_site_editor/src/interaction/picking.rs b/rmf_site_editor/src/interaction/picking.rs index 4202791c..505d9c04 100644 --- a/rmf_site_editor/src/interaction/picking.rs +++ b/rmf_site_editor/src/interaction/picking.rs @@ -17,7 +17,9 @@ use crate::{interaction::*, site::Anchor, CurrentWorkspace}; use bevy::prelude::*; -use bevy_mod_raycast::{RaycastMethod, RaycastSource}; +use bevy_mod_raycast::{ + deferred::RaycastMethod, deferred::RaycastSource, immediate::RaycastVisibility, +}; /// A resource to track what kind of picking blockers are currently active #[derive(Resource)] @@ -72,9 +74,11 @@ pub fn update_picking_cam( .remove::>(); } - commands - .entity(camera_controls.active_camera()) - .insert(RaycastSource::::new().with_early_exit(false)); + commands.entity(camera_controls.active_camera()).insert( + RaycastSource::::new_cursor() + .with_early_exit(false) + .with_visibility(RaycastVisibility::MustBeVisible), + ); } } } @@ -115,20 +119,6 @@ fn pick_topmost( return None; } -// Update our `RaycastSource` with the current cursor position every frame. -pub fn update_raycast_with_cursor( - mut cursor: EventReader, - mut query: Query<&mut RaycastSource>, -) { - // Grab the most recent cursor event if it exists: - let Some(cursor_moved) = cursor.iter().last() else { - return; - }; - for mut pick_source in &mut query { - pick_source.cast_method = RaycastMethod::Screenspace(cursor_moved.position); - } -} - pub fn update_picked( mode: Res, selectable: Query<&Selectable>, diff --git a/rmf_site_editor/src/interaction/select.rs b/rmf_site_editor/src/interaction/select.rs index a0add4d7..80380e17 100644 --- a/rmf_site_editor/src/interaction/select.rs +++ b/rmf_site_editor/src/interaction/select.rs @@ -17,7 +17,7 @@ use crate::{interaction::*, site::Anchor}; use bevy::prelude::*; -use bevy_mod_raycast::RaycastMesh; +use bevy_mod_raycast::deferred::RaycastMesh; use std::collections::HashSet; /// This component is put on entities with meshes to mark them as items that can @@ -236,9 +236,7 @@ pub fn maintain_hovered_entities( // TODO(luca) refactor to remove this hack // Skip if we are in SelectAnchor3D mode if let InteractionMode::SelectAnchor3D(mode) = &*mode { - if mode.begin_creating() { - return; - } + return; } select.send(Select(Some(current_hovered))); } diff --git a/rmf_site_editor/src/interaction/select_anchor.rs b/rmf_site_editor/src/interaction/select_anchor.rs index e7b50d4b..288c36e9 100644 --- a/rmf_site_editor/src/interaction/select_anchor.rs +++ b/rmf_site_editor/src/interaction/select_anchor.rs @@ -22,13 +22,13 @@ use crate::{ Dependents, DrawingMarker, Original, PathBehavior, Pending, TextureNeedsAssignment, VisualMeshMarker, }, - CurrentWorkspace, + AppState, CurrentWorkspace, }; use bevy::{ecs::system::SystemParam, prelude::*}; use rmf_site_format::{ - Constraint, ConstraintDependents, Door, Edge, Fiducial, Floor, Lane, LiftProperties, Location, - Measurement, MeshConstraint, MeshElement, Model, ModelMarker, NameInWorkcell, NameOfSite, Path, - PixelsPerMeter, Point, Pose, Side, Wall, WorkcellModel, + Constraint, ConstraintDependents, Door, Edge, Fiducial, Floor, FrameMarker, Lane, + LiftProperties, Location, Measurement, MeshConstraint, MeshElement, Model, ModelMarker, + NameInWorkcell, NameOfSite, Path, PixelsPerMeter, Point, Pose, Side, Wall, WorkcellModel, }; use std::collections::HashSet; use std::sync::Arc; @@ -1755,19 +1755,6 @@ impl SelectAnchor3D { params: &mut SelectAnchorPlacementParams<'w, 's>, ) -> Result<(), ()> { if let Some(target) = self.target { - // Change the anchor that the location is attached to. - let (e, anchor) = match params.anchors.get_mut(target) { - Ok(l) => l, - Err(_) => { - error!( - "Unable to get anchor {:?} while \ - replacing 3D Anchor.", - target - ); - return Err(()); - } - }; - // Make sure the selected entity is an anchor // TODO(luca) Should this be at the caller level? match params.anchors.get(anchor_selection.entity()) { @@ -1790,27 +1777,6 @@ impl SelectAnchor3D { match self.parent { Some(new_parent) => { if anchor_selection.entity() != target { - // Delete parent and dependent - if let Ok(old_parent) = params.parents.get(target) { - params - .commands - .entity(**old_parent) - .remove_children(&[target]); - params.remove_dependent( - target, - **old_parent, - &mut Some(&mut anchor_selection), - )?; - } - params.add_dependent( - target, - anchor_selection.entity(), - &mut Some(&mut anchor_selection), - )?; - params - .commands - .entity(anchor_selection.entity()) - .push_children(&[target]); self.parent = Some(anchor_selection.entity()); } return Ok(()); @@ -1852,58 +1818,10 @@ impl SelectAnchor3D { return PreviewResult::Unchanged; } - /// Return Ok if we need to keep the entity, Err if we need to remove it - fn placement_backout<'w, 's>( - &self, - continuity: SelectAnchorContinuity, - target: Entity, - params: &mut SelectAnchorPlacementParams<'w, 's>, - ) -> Result<(), ()> { - match self.parent { - Some(parent) => { - if let Ok((e, _anchor)) = params.anchors.get_mut(parent) { - if let Ok(mut deps) = params.dependents.get_mut(e) { - deps.remove(&target); - params.commands.entity(e).remove_children(&[target]); - } - - if let Some(replacing) = continuity.replacing() { - // Restore the target to the original - if let Ok(mut deps) = params.dependents.get_mut(replacing) { - deps.insert(target); - params.commands.entity(replacing).push_children(&[target]); - } - - // point.0 = replacing; - params.commands.entity(target).remove::(); - return Ok(()); - } else { - // Delete the location entirely because there is no anchor to - // return it to. - params.commands.entity(target).despawn_recursive(); - return Err(()); - } - } else { - error!( - "Cannot find point for location {target:?} while \ - trying to back out of SelectAnchor mode" - ); - return Err(()); - } - } - None => { - return Ok(()); - } - } - } - pub fn backout<'w, 's>( &self, params: &mut SelectAnchorPlacementParams<'w, 's>, ) -> InteractionMode { - if let Some(target) = self.target { - self.placement_backout(self.continuity, target, params); - } params.cleanup(); return InteractionMode::Inspect; } @@ -2190,16 +2108,6 @@ fn compute_parent_inverse_pose( pose.align_with(&Transform::from_matrix((inv_tf * goal_tf).into())) } -fn find_mesh_element( - params: &SelectAnchorPlacementParams, - cursor_tf: &GlobalTransform, - hovered: Entity, -) -> MeshElement { - // TODO(luca) Assign a proper vertex id, will need mesh lookup based on - // hover status and (expensive) iteration on vertices - MeshElement::Vertex(0) -} - pub fn handle_select_anchor_3d_mode( mut mode: ResMut, anchors: Query<(), With>, @@ -2213,6 +2121,7 @@ pub fn handle_select_anchor_3d_mode( mut hover: EventWriter, blockers: Option>, workspace: Res, + app_state: Res>, ) { let mut request = match &*mode { InteractionMode::SelectAnchor3D(request) => request.clone(), @@ -2263,7 +2172,7 @@ pub fn handle_select_anchor_3d_mode( // Set the request parent to the currently selected element, to spawn new object as // children of selected frames - if request.begin_creating() { + if matches!(**app_state, AppState::WorkcellEditor) && request.begin_creating() { request.parent = selection.0; } } @@ -2283,82 +2192,84 @@ pub fn handle_select_anchor_3d_mode( .get(params.cursor.frame) .expect("Unable to get transform for cursor frame"); - let id = params - .commands - .spawn(NameInWorkcell("Unnamed".to_string())) - .id(); - let parent = match request.bundle { - PlaceableObject::Anchor => { - // If parent is a mesh this will be a mesh constraint, otherwise an anchor - let parent = if let Some(parent) = - hovering.0.and_then(|p| params.models.get(p).ok()) - { - let pose = compute_parent_inverse_pose(&cursor_tf, &transforms, parent); - let element = find_mesh_element(¶ms, &cursor_tf, parent); - params.commands.entity(id).insert(MeshConstraint { - entity: parent, - element: element, - relative_pose: pose, - }); - // Add constraint dependent - if let Ok(mut parent_deps) = - params.constraint_dependents.get_mut(parent) - { - parent_deps.0.insert(id); - } - // Parent to be assigned is the first frame parent of the currently - // hovered model - AncestorIter::new(¶ms.parents, parent) - .find(|&p| params.anchors.get(p).is_ok()) - .unwrap_or(workspace.root.expect("No workspace")) - } else { - let parent = request - .parent - .unwrap_or(workspace.root.expect("No workspace")); - let pose = compute_parent_inverse_pose(&cursor_tf, &transforms, parent); - params - .commands - .entity(id) - .insert(AnchorBundle::new(Anchor::Pose3D(pose))); - parent - }; - parent - } + let parent = request + .parent + .unwrap_or(workspace.root.expect("No workspace")); + let pose = compute_parent_inverse_pose(&cursor_tf, &transforms, parent); + let id = match request.bundle { + PlaceableObject::Anchor => params + .commands + .spawn(( + AnchorBundle::new(Anchor::Pose3D(pose)), + FrameMarker, + NameInWorkcell("Unnamed".to_string()), + )) + .id(), PlaceableObject::Model(ref a) => { let mut model = a.clone(); - let parent = workspace.root.expect("No workspace"); - model.pose = compute_parent_inverse_pose(&cursor_tf, &transforms, parent); - params.commands.entity(id).insert(model); - parent + // If we are in workcell mode, add a "base link" frame to the model + if matches!(**app_state, AppState::WorkcellEditor) { + let child_id = params.commands.spawn(model).id(); + params + .commands + .spawn(( + AnchorBundle::new(Anchor::Pose3D(pose)) + .dependents(Dependents::single(child_id)), + FrameMarker, + NameInWorkcell("model_root".to_string()), + )) + .add_child(child_id) + .id() + } else { + model.pose = pose; + params.commands.spawn(model).id() + } } PlaceableObject::VisualMesh(ref a) => { let mut model = a.clone(); - let parent = request - .parent - .unwrap_or(workspace.root.expect("No workspace")); - model.pose = compute_parent_inverse_pose(&cursor_tf, &transforms, parent); - let mut cmd = params.commands.entity(id); - cmd.insert(VisualMeshMarker); - model.add_bevy_components(cmd); - parent + model.pose = pose; + let mut cmd = params.commands.spawn(VisualMeshMarker); + model.add_bevy_components(&mut cmd); + cmd.id() } PlaceableObject::CollisionMesh(ref a) => { let mut model = a.clone(); - let parent = request - .parent - .unwrap_or(workspace.root.expect("No workspace")); - model.pose = compute_parent_inverse_pose(&cursor_tf, &transforms, parent); - let mut cmd = params.commands.entity(id); - cmd.insert(CollisionMeshMarker); - model.add_bevy_components(cmd); - parent + model.pose = pose; + let mut cmd = params.commands.spawn(CollisionMeshMarker); + model.add_bevy_components(&mut cmd); + cmd.id() } }; // Add child and dependent to parent - params.commands.entity(parent).add_child(id); + params.commands.entity(id).set_parent(parent); if let Ok(mut deps) = params.dependents.get_mut(parent) { deps.insert(id); } + } else { + // We are replacing an anchor, which in this mode refers to changing a parent + if let (Some(target), Some(parent)) = (request.target, request.parent) { + if let Ok(old_parent) = params.parents.get(target) { + if let Ok(mut deps) = params.dependents.get_mut(**old_parent) { + deps.remove(&target); + } + } + if let Ok(mut deps) = params.dependents.get_mut(parent) { + deps.insert(target); + } + let mut cmd = params.commands.entity(target); + cmd.set_parent(parent); + // Anchors store their pose in the Anchor component, other elements in Pose, + // set accordingly + let previous_tf = transforms + .get(target) + .expect("Transform not found for entity"); + let pose = compute_parent_inverse_pose(&previous_tf, &transforms, parent); + if anchors.get(target).is_ok() { + cmd.insert(AnchorBundle::new(Anchor::Pose3D(pose))); + } else { + cmd.insert(pose); + } + } } params.cleanup(); diff --git a/rmf_site_editor/src/lib.rs b/rmf_site_editor/src/lib.rs index 8e29696f..00717a9b 100644 --- a/rmf_site_editor/src/lib.rs +++ b/rmf_site_editor/src/lib.rs @@ -17,7 +17,6 @@ use keyboard::*; pub mod widgets; use widgets::{menu_bar::MenuPluginManager, *}; - pub mod occupancy; use occupancy::OccupancyPlugin; pub mod issue; diff --git a/rmf_site_editor/src/shapes.rs b/rmf_site_editor/src/shapes.rs index fc31f6b8..c9862ed3 100644 --- a/rmf_site_editor/src/shapes.rs +++ b/rmf_site_editor/src/shapes.rs @@ -660,10 +660,10 @@ pub(crate) fn make_cylinder(height: f32, radius: f32) -> MeshBuffer { make_smooth_wrap([top_circle, bottom_circle], resolution) .merge_with( make_bottom_circle(mid_circle, resolution) - .transform_by(Affine3A::from_translation([0.0, 0., -height].into())), + .transform_by(Affine3A::from_translation([0.0, 0., -height / 2.0].into())), ) .merge_with(make_bottom_circle(mid_circle, resolution).transform_by( - Affine3A::from_translation([0., 0., height].into()) + Affine3A::from_translation([0., 0., height / 2.0].into()) * Affine3A::from_rotation_x(180_f32.to_radians()), )) } diff --git a/rmf_site_editor/src/site/anchor.rs b/rmf_site_editor/src/site/anchor.rs index 4b804415..9e189cc9 100644 --- a/rmf_site_editor/src/site/anchor.rs +++ b/rmf_site_editor/src/site/anchor.rs @@ -100,7 +100,11 @@ pub fn update_anchor_transforms( mut changed_anchors: Query<(&Anchor, &mut Transform), Changed>, ) { for (anchor, mut tf) in &mut changed_anchors { - tf.translation = anchor.local_transform(Category::General).translation; + // Only update rotation and translation since scale, for drawing anchors, is managed by + // another system. + let new_tf = anchor.local_transform(Category::General); + tf.translation = new_tf.translation; + tf.rotation = new_tf.rotation; } } diff --git a/rmf_site_editor/src/site/drawing_editor/mod.rs b/rmf_site_editor/src/site/drawing_editor/mod.rs index a094541d..ee709d74 100644 --- a/rmf_site_editor/src/site/drawing_editor/mod.rs +++ b/rmf_site_editor/src/site/drawing_editor/mod.rs @@ -24,8 +24,8 @@ use crate::AppState; use crate::{ interaction::{ChangeProjectionMode, Selection, SuppressHighlight, SuppressOutline}, site::{ - Anchor, DrawingMarker, Edge, FiducialMarker, MeasurementMarker, NameOfSite, Pending, - PixelsPerMeter, Point, PreventDeletion, SiteProperties, WorkcellProperties, + Anchor, DrawingMarker, Edge, FiducialMarker, MeasurementMarker, NameOfSite, NameOfWorkcell, + Pending, PixelsPerMeter, Point, PreventDeletion, SiteProperties, }, CurrentWorkspace, WorkspaceMarker, }; @@ -88,7 +88,7 @@ fn switch_edit_drawing_mode( current_workspace: Res, parent: Query<&Parent, With>, is_site: Query<(), With>, - is_workcell: Query<(), With>, + is_workcell: Query<(), With>, ) { // TODO(@mxgrey): We can make this implementation much cleaner after we // update to the latest version of bevy that distinguishes between inherited diff --git a/rmf_site_editor/src/site/georeference.rs b/rmf_site_editor/src/site/georeference.rs index 608c1e3c..74280e27 100644 --- a/rmf_site_editor/src/site/georeference.rs +++ b/rmf_site_editor/src/site/georeference.rs @@ -1,9 +1,9 @@ -use bevy::{ecs::system::SystemParam, prelude::*}; +use bevy::{ecs::system::SystemParam, prelude::*, window::PrimaryWindow}; use bevy_egui::{ egui::{self, Slider}, EguiContexts, }; -use bevy_mod_raycast::Ray3d; +use bevy_mod_raycast::primitives::rays::Ray3d; use camera_controls::{CameraControls, ProjectionMode}; use rmf_site_format::{Anchor, AssetSource, GeographicComponent, GeographicOffset}; use std::collections::HashSet; @@ -386,6 +386,7 @@ pub fn render_map_tiles( mut commands: Commands, site_properties: Query<(Entity, &GeographicComponent)>, mut render_settings: Local, + primary_window: Query<&Window, With>, ) { if let Some((_, site_properties)) = site_properties .iter() @@ -429,14 +430,29 @@ pub fn render_map_tiles( if let Some(Rect { min, max }) = camera.logical_viewport_rect() { let viewport_size = max - min; - let top_left_ray = - Ray3d::from_screenspace(Vec2::new(0.0, 0.0), camera, transform); - let top_right_ray = - Ray3d::from_screenspace(Vec2::new(viewport_size.x, 0.0), camera, transform); - let bottom_left_ray = - Ray3d::from_screenspace(Vec2::new(0.0, viewport_size.y), camera, transform); + let Ok(primary_window) = primary_window.get_single() else { + return; + }; + let top_left_ray = Ray3d::from_screenspace( + Vec2::new(0.0, 0.0), + camera, + transform, + primary_window, + ); + let top_right_ray = Ray3d::from_screenspace( + Vec2::new(viewport_size.x, 0.0), + camera, + transform, + primary_window, + ); + let bottom_left_ray = Ray3d::from_screenspace( + Vec2::new(0.0, viewport_size.y), + camera, + transform, + primary_window, + ); let bottom_right_ray = - Ray3d::from_screenspace(viewport_size, camera, transform); + Ray3d::from_screenspace(viewport_size, camera, transform, primary_window); let top_left = ray_groundplane_intersection(&top_left_ray); let top_right = ray_groundplane_intersection(&top_right_ray); diff --git a/rmf_site_editor/src/site/mod.rs b/rmf_site_editor/src/site/mod.rs index c82e308e..fbd3c96c 100644 --- a/rmf_site_editor/src/site/mod.rs +++ b/rmf_site_editor/src/site/mod.rs @@ -209,37 +209,39 @@ impl Plugin for SitePlugin { ChangePlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), + ChangePlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), ChangePlugin::>::default(), ChangePlugin::::default(), ChangePlugin::::default(), - ChangePlugin::::default(), )) .add_plugins(( + ChangePlugin::::default(), RecallPlugin::::default(), ChangePlugin::::default(), ChangePlugin::>::default(), RecallPlugin::>::default(), ChangePlugin::::default(), RecallPlugin::::default(), - ChangePlugin::::default(), - RecallPlugin::::default(), + ChangePlugin::::default(), + RecallPlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), RecallPlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), - RecallPlugin::::default(), )) .add_plugins(( + RecallPlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), ChangePlugin::::default(), ChangePlugin::>::default(), + ChangePlugin::::default(), RecencyRankingPlugin::::default(), RecencyRankingPlugin::::default(), RecencyRankingPlugin::::default(), @@ -386,7 +388,7 @@ impl Plugin for SitePlugin { update_model_scales, make_models_selectable, propagate_model_render_layers, - handle_new_mesh_primitives, + handle_new_primitive_shapes, add_drawing_visuals, handle_loaded_drawing, update_drawing_rank, diff --git a/rmf_site_editor/src/site/model.rs b/rmf_site_editor/src/site/model.rs index ded431d8..e287eb85 100644 --- a/rmf_site_editor/src/site/model.rs +++ b/rmf_site_editor/src/site/model.rs @@ -362,7 +362,7 @@ pub struct Trashcan; /// trash can and waiting to despawn them during a later stage after any /// modifier commands have been flushed. #[derive(Resource)] -pub struct ModelTrashcan(Entity); +pub struct ModelTrashcan(pub Entity); impl FromWorld for ModelTrashcan { fn from_world(world: &mut World) -> Self { @@ -403,6 +403,7 @@ pub fn make_models_selectable( let Some((selectable, render_layers)) = AncestorIter::new(&parents, model_scene_root) .filter_map(|p| scene_roots.get(p).ok()) .last() + .or_else(|| scene_roots.get(model_scene_root).ok()) else { continue; }; @@ -442,14 +443,14 @@ pub fn make_models_selectable( pub fn propagate_model_render_layers( mut commands: Commands, new_scene_roots: Query>, - scene_roots: Query<&RenderLayers, With>, + render_layers: Query<&RenderLayers>, parents: Query<&Parent>, mesh_entities: Query>>, children: Query<&Children>, ) { for e in &new_scene_roots { let Some(render_layers) = AncestorIter::new(&parents, e) - .filter_map(|p| scene_roots.get(p).ok()) + .filter_map(|p| render_layers.get(p).ok()) .last() else { continue; diff --git a/rmf_site_editor/src/site/sdf.rs b/rmf_site_editor/src/site/sdf.rs index 944c8cee..af66681c 100644 --- a/rmf_site_editor/src/site/sdf.rs +++ b/rmf_site_editor/src/site/sdf.rs @@ -25,8 +25,8 @@ use crate::SdfRoot; use sdformat_rs::{SdfGeometry, SdfPose, Vector3d}; use rmf_site_format::{ - Angle, AssetSource, ConstraintDependents, Geometry, IsStatic, MeshPrimitive, Model, - ModelMarker, NameInSite, Pose, Rotation, Scale, + Angle, AssetSource, Category, ConstraintDependents, Geometry, IsStatic, Model, ModelMarker, + NameInSite, Pose, PrimitiveShape, Rotation, Scale, }; /// An empty component to mark this entity as a visual mesh @@ -67,7 +67,7 @@ fn compute_model_source(path: &str, uri: &str) -> AssetSource { "".into() }; } - AssetSource::Local(ref mut p) => { + AssetSource::Local(ref mut p) | AssetSource::Package(ref mut p) => { let binding = p.clone(); *p = if let Some(stripped) = uri.strip_prefix("model://") { // Search for a model with the requested name in the same folder as the sdf file @@ -93,7 +93,7 @@ fn compute_model_source(path: &str, uri: &str) -> AssetSource { "".into() }; } - AssetSource::Bundled(_) | AssetSource::Package(_) | AssetSource::OSMTile { .. } => { + AssetSource::Bundled(_) | AssetSource::OSMTile { .. } => { warn!("Requested asset source {:?} type not supported for SDFs, might behave unexpectedly", asset_source); } } @@ -153,40 +153,44 @@ fn spawn_geometry( let s = &b.size.0; Some( commands - .spawn(MeshPrimitive::Box { + .spawn(PrimitiveShape::Box { size: [s.x as f32, s.y as f32, s.z as f32], }) .insert(pose) + .insert(NameInSite(visual_name.to_owned())) .insert(SpatialBundle::INHERITED_IDENTITY) .id(), ) } SdfGeometry::Capsule(c) => Some( commands - .spawn(MeshPrimitive::Capsule { + .spawn(PrimitiveShape::Capsule { radius: c.radius as f32, length: c.length as f32, }) .insert(pose) + .insert(NameInSite(visual_name.to_owned())) .insert(SpatialBundle::INHERITED_IDENTITY) .id(), ), SdfGeometry::Cylinder(c) => Some( commands - .spawn(MeshPrimitive::Cylinder { + .spawn(PrimitiveShape::Cylinder { radius: c.radius as f32, length: c.length as f32, }) .insert(pose) + .insert(NameInSite(visual_name.to_owned())) .insert(SpatialBundle::INHERITED_IDENTITY) .id(), ), SdfGeometry::Sphere(s) => Some( commands - .spawn(MeshPrimitive::Sphere { + .spawn(PrimitiveShape::Sphere { radius: s.radius as f32, }) .insert(pose) + .insert(NameInSite(visual_name.to_owned())) .insert(SpatialBundle::INHERITED_IDENTITY) .id(), ), @@ -194,7 +198,7 @@ fn spawn_geometry( } } -// TODO(luca) reduce duplication between sdf -> MeshPrimitive and urdf -> MeshPrimitive +// TODO(luca) reduce duplication between sdf -> PrimitiveShape and urdf -> PrimitiveShape pub fn handle_new_sdf_roots(mut commands: Commands, new_sdfs: Query<(Entity, &SdfRoot)>) { for (e, sdf) in new_sdfs.iter() { for link in &sdf.model.link { @@ -214,8 +218,11 @@ pub fn handle_new_sdf_roots(mut commands: Commands, new_sdfs: Query<(Entity, &Sd ); match id { Some(id) => { - commands.entity(id).insert(VisualMeshMarker); - commands.entity(link_id).add_child(id); + commands + .entity(id) + .insert(VisualMeshMarker) + .insert(Category::Visual) + .set_parent(link_id); } None => warn!("Found unhandled geometry type {:?}", &visual.geometry), } @@ -231,8 +238,11 @@ pub fn handle_new_sdf_roots(mut commands: Commands, new_sdfs: Query<(Entity, &Sd ); match id { Some(id) => { - commands.entity(id).insert(CollisionMeshMarker); - commands.entity(link_id).add_child(id); + commands + .entity(id) + .insert(CollisionMeshMarker) + .insert(Category::Collision) + .set_parent(link_id); } None => warn!("Found unhandled geometry type {:?}", &collision.geometry), } @@ -242,9 +252,9 @@ pub fn handle_new_sdf_roots(mut commands: Commands, new_sdfs: Query<(Entity, &Sd } } -pub fn handle_new_mesh_primitives( +pub fn handle_new_primitive_shapes( mut commands: Commands, - primitives: Query<(Entity, &MeshPrimitive), Added>, + primitives: Query<(Entity, &PrimitiveShape), Added>, parents: Query<&Parent>, selectables: Query< &Selectable, @@ -259,35 +269,37 @@ pub fn handle_new_mesh_primitives( ) { for (e, primitive) in primitives.iter() { let mesh = match primitive { - MeshPrimitive::Box { size } => Mesh::from(shape::Box::new(size[0], size[1], size[2])), - MeshPrimitive::Cylinder { radius, length } => { + PrimitiveShape::Box { size } => Mesh::from(shape::Box::new(size[0], size[1], size[2])), + PrimitiveShape::Cylinder { radius, length } => { Mesh::from(make_cylinder(*length, *radius)) } - MeshPrimitive::Capsule { radius, length } => Mesh::from(Capsule { + PrimitiveShape::Capsule { radius, length } => Mesh::from(Capsule { radius: *radius, depth: *length, ..default() }), - MeshPrimitive::Sphere { radius } => Mesh::from(UVSphere { + PrimitiveShape::Sphere { radius } => Mesh::from(UVSphere { radius: *radius, ..default() }), }; - // Parent is the first of ModelMarker and / or WorkcellVisualMarker or - // WorkcelLCollisionMarker - let child_id = commands + // If there is a parent with a Selectable component, use it to make this primitive + // point to it. Otherwise set the Selectable to point to itself. + let selectable = if let Some(selectable) = AncestorIter::new(&parents, e) + .filter_map(|p| selectables.get(p).ok()) + .last() + { + selectable.clone() + } else { + Selectable::new(e) + }; + commands .spawn(PbrBundle { mesh: meshes.add(mesh), material: site_assets.default_mesh_grey_material.clone(), ..default() }) - .id(); - if let Some(selectable) = AncestorIter::new(&parents, e) - .filter_map(|p| selectables.get(p).ok()) - .last() - { - commands.entity(child_id).insert(selectable.clone()); - } - commands.entity(e).push_children(&[child_id]); + .insert(selectable) + .set_parent(e); } } diff --git a/rmf_site_editor/src/widgets/create.rs b/rmf_site_editor/src/widgets/create.rs index ba5692c0..ea124c48 100644 --- a/rmf_site_editor/src/widgets/create.rs +++ b/rmf_site_editor/src/widgets/create.rs @@ -218,12 +218,12 @@ impl<'a, 'w1, 'w2, 's1, 's2> CreateWidget<'a, 'w1, 'w2, 's1, 's2> { if ui.button("Spawn visual").clicked() { let workcell_model = WorkcellModel { geometry: Geometry::Mesh { - filename: (&self + source: self .events .display .pending_model - .source) - .into(), + .source + .clone(), scale: Some( *self.events.display.pending_model.scale, ), @@ -239,12 +239,12 @@ impl<'a, 'w1, 'w2, 's1, 's2> CreateWidget<'a, 'w1, 'w2, 's1, 's2> { if ui.button("Spawn collision").clicked() { let workcell_model = WorkcellModel { geometry: Geometry::Mesh { - filename: (&self + source: self .events .display .pending_model - .source) - .into(), + .source + .clone(), scale: Some( *self.events.display.pending_model.scale, ), diff --git a/rmf_site_editor/src/widgets/inspector/inspect_anchor.rs b/rmf_site_editor/src/widgets/inspector/inspect_anchor.rs index 7917cdc4..65dc05bf 100644 --- a/rmf_site_editor/src/widgets/inspector/inspect_anchor.rs +++ b/rmf_site_editor/src/widgets/inspector/inspect_anchor.rs @@ -19,9 +19,10 @@ use crate::{ interaction::{ChangeMode, Hover, MoveTo, SelectAnchor3D}, site::{ latlon_to_world, world_to_latlon, Anchor, AssociatedGraphs, Category, Change, Dependents, - GeographicComponent, LocationTags, MeshConstraint, SiteID, Subordinate, + GeographicComponent, JointProperties, LocationTags, MeshConstraint, SiteID, Subordinate, }, widgets::{inspector::InspectPose, inspector::SelectionWidget, AppEvents, Icons}, + workcell::CreateJoint, }; use bevy::{ecs::system::SystemParam, prelude::*}; use bevy_egui::egui::{DragValue, ImageButton, Ui}; @@ -42,6 +43,7 @@ pub struct InspectAnchorParams<'w, 's> { >, pub icons: Res<'w, Icons>, pub site_id: Query<'w, 's, &'static SiteID>, + pub joints: Query<'w, 's, Entity, With>, pub geographic_offset: Query<'w, 's, &'static GeographicComponent>, } @@ -183,8 +185,7 @@ impl<'a, 'w1, 'w2, 's1, 's2> InspectAnchorWidget<'a, 'w1, 'w2, 's1, 's2> { Anchor::Pose3D(pose) => { ui.vertical(|ui| { if let Some(c) = mesh_constraint { - // For mesh constraints we only allow rotation and inspection of - // parents + // For mesh constraints we only allow rotation editing if let Some(new_pose) = InspectPose::new(&c.relative_pose).for_rotation().show(ui) { @@ -202,23 +203,6 @@ impl<'a, 'w1, 'w2, 's1, 's2> InspectAnchorWidget<'a, 'w1, 'w2, 's1, 's2> { self.anchor, )); } - ui.label("Mesh Parent"); - SelectionWidget::new( - c.entity, - self.params.site_id.get(c.entity).ok().cloned(), - self.params.icons.as_ref(), - self.events, - ) - .show(ui); - - ui.label("Frame Parent"); - SelectionWidget::new( - parent.get(), - self.params.site_id.get(parent.get()).ok().cloned(), - self.params.icons.as_ref(), - self.events, - ) - .show(ui); } else { if let Some(new_pose) = InspectPose::new(pose).show(ui) { // TODO(luca) Using moveto doesn't allow switching between variants of @@ -228,37 +212,14 @@ impl<'a, 'w1, 'w2, 's1, 's2> InspectAnchorWidget<'a, 'w1, 'w2, 's1, 's2> { transform: new_pose.transform(), }); } - - // Parent reassigning widget - ui.label("Parent"); - SelectionWidget::new( - parent.get(), - self.params.site_id.get(parent.get()).ok().cloned(), - self.params.icons.as_ref(), - self.events, - ) - .show(ui); - - let assign_response = ui.add(ImageButton::new( - self.params.icons.edit.egui(), - [18., 18.], - )); - - if assign_response.hovered() { - self.events.request.hover.send(Hover(Some(self.anchor))); - } - - let parent_replace = assign_response.clicked(); - assign_response.on_hover_text("Reassign"); - - if parent_replace { - let request = - SelectAnchor3D::replace_point(self.anchor, parent.get()) - .for_anchor(Some(parent.get())); - self.events - .request - .change_mode - .send(ChangeMode::To(request.into())); + } + // If the parent is not a joint, add a joint creation widget + if self.params.joints.get(parent.get()).is_err() { + if ui.button("Create joint").on_hover_text("Create a fixed joint and place it between the parent frame and this frame").clicked() { + self.events.request.create_joint.send(CreateJoint { + parent: parent.get(), + child: self.anchor, + }); } } }); diff --git a/rmf_site_editor/src/widgets/inspector/inspect_joint.rs b/rmf_site_editor/src/widgets/inspector/inspect_joint.rs new file mode 100644 index 00000000..ce253715 --- /dev/null +++ b/rmf_site_editor/src/widgets/inspector/inspect_joint.rs @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 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::{ + site::{Change, Dependents, FrameMarker, JointProperties, SiteID}, + widgets::{inspector::SelectionWidget, AppEvents}, + Icons, +}; +use bevy::{ecs::system::SystemParam, prelude::*}; +use bevy_egui::egui::{ComboBox, Ui}; + +#[derive(SystemParam)] +pub struct InspectJointParams<'w, 's> { + pub joints: Query< + 'w, + 's, + ( + &'static Parent, + &'static Dependents, + &'static JointProperties, + ), + >, + pub icons: Res<'w, Icons>, + pub site_id: Query<'w, 's, &'static SiteID>, + pub frames: Query<'w, 's, (), With>, +} + +pub struct InspectJointWidget<'a, 'w1, 'w2, 's1, 's2> { + pub joint: Entity, + pub params: &'a InspectJointParams<'w1, 's1>, + pub events: &'a mut AppEvents<'w2, 's2>, +} + +impl<'a, 'w1, 'w2, 's1, 's2> InspectJointWidget<'a, 'w1, 'w2, 's1, 's2> { + pub fn new( + joint: Entity, + params: &'a InspectJointParams<'w1, 's1>, + events: &'a mut AppEvents<'w2, 's2>, + ) -> Self { + Self { + joint, + params, + events, + } + } + + pub fn show(self, ui: &mut Ui) { + let Ok((parent, deps, joint_properties)) = self.params.joints.get(self.joint) else { + return; + }; + + ui.label("Parent frame"); + SelectionWidget::new( + **parent, + self.params.site_id.get(**parent).ok().cloned(), + self.params.icons.as_ref(), + self.events, + ) + .show(ui); + + if let Some(frame_dep) = deps.iter().find(|d| self.params.frames.get(**d).is_ok()) { + ui.label("Child frame"); + SelectionWidget::new( + *frame_dep, + self.params.site_id.get(*frame_dep).ok().cloned(), + self.params.icons.as_ref(), + self.events, + ) + .show(ui); + } + + ui.horizontal(|ui| { + ui.label("Joint Type"); + // TODO(luca) Make this a ComboBox to edit joint value data + ui.label(joint_properties.label()); + }); + // TODO(luca) add joint limit and joint axis inspectors + } +} diff --git a/rmf_site_editor/src/widgets/inspector/inspect_mesh_primitive.rs b/rmf_site_editor/src/widgets/inspector/inspect_mesh_primitive.rs deleted file mode 100644 index 8ac38655..00000000 --- a/rmf_site_editor/src/widgets/inspector/inspect_mesh_primitive.rs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2023 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_egui::egui::{ComboBox, DragValue, Ui}; -use rmf_site_format::{MeshPrimitive, RecallMeshPrimitive}; - -pub struct InspectMeshPrimitive<'a> { - pub primitive: &'a MeshPrimitive, - pub recall: &'a RecallMeshPrimitive, -} - -impl<'a> InspectMeshPrimitive<'a> { - pub fn new(primitive: &'a MeshPrimitive, recall: &'a RecallMeshPrimitive) -> Self { - Self { primitive, recall } - } - - pub fn show(self, ui: &mut Ui) -> Option { - let mut new_primitive = self.primitive.clone(); - // TODO(luca) implement recall plugin - ui.horizontal(|ui| { - ui.label("Primitive"); - ComboBox::from_id_source("Mesh Primitive") - .selected_text(new_primitive.label()) - .show_ui(ui, |ui| { - for variant in &[ - self.recall.assume_box(self.primitive), - self.recall.assume_cylinder(self.primitive), - self.recall.assume_capsule(self.primitive), - self.recall.assume_sphere(self.primitive), - ] { - ui.selectable_value(&mut new_primitive, variant.clone(), variant.label()); - } - ui.end_row(); - }); - }); - match &mut new_primitive { - MeshPrimitive::Box { size } => { - ui.add(DragValue::new(&mut size[0]).clamp_range(0_f32..=std::f32::INFINITY)); - ui.add(DragValue::new(&mut size[1]).clamp_range(0_f32..=std::f32::INFINITY)); - ui.add(DragValue::new(&mut size[2]).clamp_range(0_f32..=std::f32::INFINITY)); - } - MeshPrimitive::Cylinder { radius, length } => {} - MeshPrimitive::Capsule { radius, length } => {} - MeshPrimitive::Sphere { radius } => {} - } - if &new_primitive != self.primitive { - Some(new_primitive) - } else { - None - } - } -} diff --git a/rmf_site_editor/src/widgets/inspector/inspect_name.rs b/rmf_site_editor/src/widgets/inspector/inspect_name.rs index 396d729d..9ce0457a 100644 --- a/rmf_site_editor/src/widgets/inspector/inspect_name.rs +++ b/rmf_site_editor/src/widgets/inspector/inspect_name.rs @@ -16,8 +16,9 @@ */ use bevy_egui::egui::Ui; -use rmf_site_format::{NameInSite, NameInWorkcell}; +use rmf_site_format::{NameInSite, NameInWorkcell, NameOfWorkcell}; +// TODO(luca) refactor all these into a generic name inspection widget pub struct InspectName<'a> { pub name: &'a NameInSite, } @@ -65,3 +66,27 @@ impl<'a> InspectNameInWorkcell<'a> { .inner } } + +pub struct InspectNameOfWorkcell<'a> { + pub name: &'a NameOfWorkcell, +} + +impl<'a> InspectNameOfWorkcell<'a> { + pub fn new(name: &'a NameOfWorkcell) -> Self { + Self { name } + } + + pub fn show(self, ui: &mut Ui) -> Option { + ui.horizontal(|ui| { + ui.label("Name"); + let mut new_name = self.name.clone(); + ui.text_edit_singleline(&mut new_name.0); + if new_name != *self.name { + Some(new_name) + } else { + None + } + }) + .inner + } +} diff --git a/rmf_site_editor/src/widgets/inspector/inspect_primitive_shape.rs b/rmf_site_editor/src/widgets/inspector/inspect_primitive_shape.rs new file mode 100644 index 00000000..83c11686 --- /dev/null +++ b/rmf_site_editor/src/widgets/inspector/inspect_primitive_shape.rs @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 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_egui::egui::{ComboBox, DragValue, Ui}; +use rmf_site_format::{PrimitiveShape, RecallPrimitiveShape}; + +pub struct InspectPrimitiveShape<'a> { + pub primitive: &'a PrimitiveShape, + pub recall: &'a RecallPrimitiveShape, +} + +impl<'a> InspectPrimitiveShape<'a> { + pub fn new(primitive: &'a PrimitiveShape, recall: &'a RecallPrimitiveShape) -> Self { + Self { primitive, recall } + } + + pub fn show(self, ui: &mut Ui) -> Option { + let mut new_primitive = self.primitive.clone(); + ui.horizontal(|ui| { + ui.label("Primitive:"); + // TODO(luca) make this a ComboBox again once we have a system to update primitives + ui.label(new_primitive.label()); + }); + // TODO(luca) Make these values editable and implement a system to parse changes + match &mut new_primitive { + PrimitiveShape::Box { size } => { + ui.label("Size"); + ui.horizontal(|ui| { + ui.label("X:"); + ui.label(size[0].to_string()); + ui.label("Y:"); + ui.label(size[1].to_string()); + ui.label("Z:"); + ui.label(size[2].to_string()); + }); + } + PrimitiveShape::Cylinder { radius, length } + | PrimitiveShape::Capsule { radius, length } => { + ui.horizontal(|ui| { + ui.label("Radius:"); + ui.label(radius.to_string()); + ui.label("Length:"); + ui.label(length.to_string()); + }); + } + PrimitiveShape::Sphere { radius } => { + ui.horizontal(|ui| { + ui.label("Radius:"); + ui.label(radius.to_string()); + }); + } + } + if &new_primitive != self.primitive { + Some(new_primitive) + } else { + None + } + } +} diff --git a/rmf_site_editor/src/widgets/inspector/inspect_workcell_parent.rs b/rmf_site_editor/src/widgets/inspector/inspect_workcell_parent.rs new file mode 100644 index 00000000..a3fcdd4d --- /dev/null +++ b/rmf_site_editor/src/widgets/inspector/inspect_workcell_parent.rs @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023 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::{ChangeMode, Hover, SelectAnchor3D}, + site::{FrameMarker, MeshConstraint, NameInWorkcell, NameOfWorkcell, SiteID}, + widgets::{inspector::SelectionWidget, AppEvents, Icons}, +}; +use bevy::{ecs::system::SystemParam, prelude::*}; +use bevy_egui::egui::{ImageButton, Ui}; + +#[derive(SystemParam)] +pub struct InspectWorkcellParentParams<'w, 's> { + pub parents: Query<'w, 's, &'static Parent>, + pub workcell_elements: Query< + 'w, + 's, + Entity, + Or<( + With, + With, + With, + )>, + >, + pub mesh_constraints: Query<'w, 's, &'static MeshConstraint>, + pub site_id: Query<'w, 's, &'static SiteID>, + pub icons: Res<'w, Icons>, +} + +pub struct InspectWorkcellParentWidget<'a, 'w1, 'w2, 's1, 's2> { + pub entity: Entity, + pub params: &'a InspectWorkcellParentParams<'w1, 's1>, + pub events: &'a mut AppEvents<'w2, 's2>, +} + +impl<'a, 'w1, 'w2, 's1, 's2> InspectWorkcellParentWidget<'a, 'w1, 'w2, 's1, 's2> { + pub fn new( + entity: Entity, + params: &'a InspectWorkcellParentParams<'w1, 's1>, + events: &'a mut AppEvents<'w2, 's2>, + ) -> Self { + Self { + entity, + params, + events, + } + } + + pub fn show(self, ui: &mut Ui) { + // If the parent is a frame it should be reassignable + if let Ok(parent) = self + .params + .parents + .get(self.entity) + .and_then(|p| self.params.workcell_elements.get(**p)) + { + ui.vertical(|ui| { + if let Ok(c) = self.params.mesh_constraints.get(self.entity) { + ui.label("Mesh Parent"); + SelectionWidget::new( + c.entity, + self.params.site_id.get(c.entity).ok().cloned(), + self.params.icons.as_ref(), + self.events, + ) + .show(ui); + } + ui.label("Parent Frame"); + SelectionWidget::new( + parent, + self.params.site_id.get(parent).ok().cloned(), + self.params.icons.as_ref(), + self.events, + ) + .show(ui); + + let assign_response = + ui.add(ImageButton::new(self.params.icons.edit.egui(), [18., 18.])); + + if assign_response.hovered() { + self.events.request.hover.send(Hover(Some(self.entity))); + } + + let parent_replace = assign_response.clicked(); + assign_response.on_hover_text("Reassign"); + + if parent_replace { + let request = + SelectAnchor3D::replace_point(self.entity, parent).for_anchor(Some(parent)); + self.events + .request + .change_mode + .send(ChangeMode::To(request.into())); + } + }); + } + } +} diff --git a/rmf_site_editor/src/widgets/inspector/mod.rs b/rmf_site_editor/src/widgets/inspector/mod.rs index 47593648..6f0026fc 100644 --- a/rmf_site_editor/src/widgets/inspector/mod.rs +++ b/rmf_site_editor/src/widgets/inspector/mod.rs @@ -39,6 +39,9 @@ pub use inspect_fiducial::*; pub mod inspect_group; pub use inspect_group::*; +pub mod inspect_joint; +pub use inspect_joint::*; + pub mod inspect_is_static; pub use inspect_is_static::*; @@ -60,8 +63,8 @@ pub use inspect_location::*; pub mod inspect_mesh_constraint; pub use inspect_mesh_constraint::*; -pub mod inspect_mesh_primitive; -pub use inspect_mesh_primitive::*; +pub mod inspect_primitive_shape; +pub use inspect_primitive_shape::*; pub mod inspect_motion; pub use inspect_motion::*; @@ -90,6 +93,9 @@ pub use inspect_texture::*; pub mod inspect_value; pub use inspect_value::*; +pub mod inspect_workcell_parent; +pub use inspect_workcell_parent::*; + pub mod selection_widget; pub use selection_widget::*; @@ -116,12 +122,11 @@ pub struct InspectorParams<'w, 's> { pub heading: Query<'w, 's, (Option<&'static Category>, Option<&'static SiteID>)>, pub anchor_params: InspectAnchorParams<'w, 's>, pub anchor_dependents_params: InspectAnchorDependentsParams<'w, 's>, - pub constraint_dependents_params: InspectModelDependentsParams<'w, 's>, + pub workcell_params: InspectorWorkcellParams<'w, 's>, pub component: InspectorComponentParams<'w, 's>, pub drawing: InspectDrawingParams<'w, 's>, // TODO(luca) move to new systemparam, reached 16 limit on main one - pub mesh_primitives: Query<'w, 's, (&'static MeshPrimitive, &'static RecallMeshPrimitive)>, - pub names_in_workcell: Query<'w, 's, &'static NameInWorkcell>, + pub primitive_shapes: Query<'w, 's, (&'static PrimitiveShape, &'static RecallPrimitiveShape)>, pub scales: Query<'w, 's, &'static Scale>, pub layer: InspectorLayerParams<'w, 's>, pub texture: InspectTextureAffiliationParams<'w, 's>, @@ -129,6 +134,15 @@ pub struct InspectorParams<'w, 's> { pub default_file: Query<'w, 's, &'static DefaultFile>, } +#[derive(SystemParam)] +pub struct InspectorWorkcellParams<'w, 's> { + pub joints: InspectJointParams<'w, 's>, + pub constraint_dependents_params: InspectModelDependentsParams<'w, 's>, + pub names_in_workcell: Query<'w, 's, &'static NameInWorkcell>, + pub workcell_names: Query<'w, 's, &'static NameOfWorkcell>, + pub parent_params: InspectWorkcellParentParams<'w, 's>, +} + // NOTE: We may need to split this struct into multiple structs if we ever need // it to have more than 16 fields. #[derive(SystemParam)] @@ -262,7 +276,7 @@ impl<'a, 'w1, 'w2, 's1, 's2> InspectorWidget<'a, 'w1, 'w2, 's1, 's2> { ui.add_space(10.0); } - if let Ok(name) = self.params.names_in_workcell.get(selection) { + if let Ok(name) = self.params.workcell_params.names_in_workcell.get(selection) { if let Some(new_name) = InspectNameInWorkcell::new(name).show(ui) { self.events .workcell_change @@ -272,6 +286,16 @@ impl<'a, 'w1, 'w2, 's1, 's2> InspectorWidget<'a, 'w1, 'w2, 's1, 's2> { ui.add_space(10.0); } + if let Ok(name) = self.params.workcell_params.workcell_names.get(selection) { + if let Some(new_name) = InspectNameOfWorkcell::new(name).show(ui) { + self.events + .workcell_change + .workcell_name + .send(Change::new(new_name, selection)); + } + ui.add_space(10.0); + } + if let Ok((floor_vis, alpha)) = self.params.layer.floors.get(selection) { ui.horizontal(|ui| { MoveLayer::new( @@ -487,13 +511,14 @@ impl<'a, 'w1, 'w2, 's1, 's2> InspectorWidget<'a, 'w1, 'w2, 's1, 's2> { ui.add_space(10.0); } - if let Ok((source, recall)) = self.params.mesh_primitives.get(selection) { - if let Some(new_mesh_primitive) = InspectMeshPrimitive::new(source, recall).show(ui) + if let Ok((source, recall)) = self.params.primitive_shapes.get(selection) { + if let Some(new_primitive_shape) = + InspectPrimitiveShape::new(source, recall).show(ui) { self.events .workcell_change - .mesh_primitives - .send(Change::new(new_mesh_primitive, selection)); + .primitive_shapes + .send(Change::new(new_primitive_shape, selection)); } ui.add_space(10.0); } @@ -507,13 +532,26 @@ impl<'a, 'w1, 'w2, 's1, 's2> InspectorWidget<'a, 'w1, 'w2, 's1, 's2> { { InspectModelDependentsWidget::new( selection, - &self.params.constraint_dependents_params, + &self.params.workcell_params.constraint_dependents_params, self.events, ) .show(ui); ui.add_space(10.0); } + InspectWorkcellParentWidget::new( + selection, + &self.params.workcell_params.parent_params, + &mut self.events, + ) + .show(ui); + InspectJointWidget::new( + selection, + &self.params.workcell_params.joints, + &mut self.events, + ) + .show(ui); + if let Ok(distance) = self.params.drawing.distance.get(selection) { if let Some(new_distance) = InspectOptionF32::new("Distance".to_string(), distance.0, 10.0) diff --git a/rmf_site_editor/src/widgets/mod.rs b/rmf_site_editor/src/widgets/mod.rs index 66dd8a08..20128c92 100644 --- a/rmf_site_editor/src/widgets/mod.rs +++ b/rmf_site_editor/src/widgets/mod.rs @@ -26,9 +26,10 @@ use crate::{ AlignSiteDrawings, AssociatedGraphs, BeginEditDrawing, Change, CollisionMeshMarker, ConsiderAssociatedGraph, ConsiderLocationTag, CurrentLevel, Delete, DrawingMarker, ExportLights, FinishEditDrawing, GlobalDrawingVisibility, GlobalFloorVisibility, - LayerVisibility, MergeGroups, PhysicalLightToggle, SaveNavGraphs, Texture, + JointProperties, LayerVisibility, MergeGroups, PhysicalLightToggle, SaveNavGraphs, Texture, ToggleLiftDoorAvailability, VisualMeshMarker, }, + workcell::CreateJoint, AppState, CreateNewWorkspace, CurrentWorkspace, LoadWorkspace, SaveWorkspace, ValidateWorkspace, }; @@ -162,6 +163,7 @@ pub struct ChangeEvents<'w> { pub search_for_texture: ResMut<'w, SearchForTexture>, pub distance: EventWriter<'w, Change>, pub texture: EventWriter<'w, Change>, + pub joint_properties: EventWriter<'w, Change>, pub merge_groups: EventWriter<'w, MergeGroups>, pub filtered_issues: EventWriter<'w, Change>>, pub filtered_issue_kinds: EventWriter<'w, Change>, @@ -170,9 +172,10 @@ pub struct ChangeEvents<'w> { #[derive(SystemParam)] pub struct WorkcellChangeEvents<'w> { pub mesh_constraints: EventWriter<'w, Change>>, - pub mesh_primitives: EventWriter<'w, Change>, pub name_in_workcell: EventWriter<'w, Change>, + pub workcell_name: EventWriter<'w, Change>, pub scale: EventWriter<'w, Change>, + pub primitive_shapes: EventWriter<'w, Change>, } #[derive(SystemParam)] @@ -214,6 +217,7 @@ pub struct Requests<'w> { pub consider_graph: EventWriter<'w, ConsiderAssociatedGraph>, pub align_site: EventWriter<'w, AlignSiteDrawings>, pub validate_workspace: EventWriter<'w, ValidateWorkspace>, + pub create_joint: EventWriter<'w, CreateJoint>, } #[derive(SystemParam)] diff --git a/rmf_site_editor/src/wireframe.rs b/rmf_site_editor/src/wireframe.rs index 22b46d94..90a7189b 100644 --- a/rmf_site_editor/src/wireframe.rs +++ b/rmf_site_editor/src/wireframe.rs @@ -19,7 +19,7 @@ use crate::menu_bar::{MenuEvent, MenuItem, ViewMenu}; use bevy::pbr::wireframe::{Wireframe, WireframePlugin}; use bevy::prelude::*; -use rmf_site_format::ModelMarker; +use rmf_site_format::{ModelMarker, PrimitiveShape}; #[derive(Default)] pub struct SiteWireframePlugin; @@ -53,7 +53,7 @@ fn handle_wireframe_menu_events( wireframe_menu: Res, meshes: Query>>, children: Query<&Children>, - models: Query>, + models: Query, With)>>, ) { for event in menu_events.iter() { if event.clicked() && event.source() == wireframe_menu.toggle_wireframe { @@ -86,7 +86,7 @@ fn add_wireframe_to_new_models( mut commands: Commands, new_meshes: Query>>, parents: Query<&Parent>, - models: Query>, + models: Query, With)>>, wireframe_menu: Res, menu_items: Query<&MenuItem>, meshes: Query>>, diff --git a/rmf_site_editor/src/workcell/frame.rs b/rmf_site_editor/src/workcell/frame.rs new file mode 100644 index 00000000..15bbb9da --- /dev/null +++ b/rmf_site_editor/src/workcell/frame.rs @@ -0,0 +1,34 @@ +/* + * 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::AnchorVisualization; +use bevy::prelude::*; +use rmf_site_format::FrameMarker; + +// TODO(luca) We should probably have a different mesh altogether for workcell anchors, rather than +// a scaled down version of site anchors. +pub fn scale_workcell_anchors( + parents: Query<&Parent>, + new_frames: Query<&AnchorVisualization, With>, + mut transforms: Query<&mut Transform>, +) { + for frame in new_frames.iter() { + if let Ok(mut tf) = transforms.get_mut(frame.body) { + tf.scale = Vec3::splat(0.25); + } + } +} diff --git a/rmf_site_editor/src/workcell/joint.rs b/rmf_site_editor/src/workcell/joint.rs new file mode 100644 index 00000000..47f7167e --- /dev/null +++ b/rmf_site_editor/src/workcell/joint.rs @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 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::site::{Delete, Dependents}; +use bevy::prelude::*; +use rmf_site_format::{FrameMarker, Joint, JointProperties, NameInWorkcell}; + +/// Event used to request the creation of a joint between a parent and a child frame +#[derive(Event)] +pub struct CreateJoint { + pub parent: Entity, + pub child: Entity, + // TODO(luca) Add different properties here such as JointType +} + +pub fn handle_create_joint_events( + mut commands: Commands, + mut events: EventReader, + mut dependents: Query<&mut Dependents>, + frames: Query<&NameInWorkcell, With>, +) { + for req in events.iter() { + let Ok(parent_name) = frames.get(req.parent) else { + error!( + "Requested to create a joint with a parent that is not a frame, \ + this is not valid and will be ignored" + ); + continue; + }; + let Ok(child_name) = frames.get(req.child) else { + error!( + "Requested to create a joint with a child that is not a frame, \ + this is not valid and will be ignored" + ); + continue; + }; + let joint_name = if !parent_name.is_empty() && !child_name.is_empty() { + format!("joint-{}-{}", **parent_name, **child_name) + } else { + "new_joint".into() + }; + let joint = Joint { + name: NameInWorkcell(joint_name), + properties: JointProperties::Fixed, + }; + let mut cmd = commands.spawn(Dependents::single(req.child)); + let joint_id = cmd.id(); + joint.add_bevy_components(&mut cmd); + // Now place the joint between the parent and child in the hierarchy + commands.entity(req.child).set_parent(joint_id); + commands.entity(joint_id).set_parent(req.parent); + if let Ok(mut deps) = dependents.get_mut(req.parent) { + deps.remove(&req.child); + deps.insert(joint_id); + } + } +} + +/// This system cleans up joints which don't have a child anymore because it was despawned +pub fn cleanup_orphaned_joints( + changed_joints: Query<(Entity, &Children), (Changed, With)>, + mut delete: EventWriter, +) { + for (e, children) in &changed_joints { + if children.is_empty() { + delete.send(Delete::new(e)); + } + } +} diff --git a/rmf_site_editor/src/workcell/keyboard.rs b/rmf_site_editor/src/workcell/keyboard.rs index f8ee6f7b..6327d5ae 100644 --- a/rmf_site_editor/src/workcell/keyboard.rs +++ b/rmf_site_editor/src/workcell/keyboard.rs @@ -15,12 +15,14 @@ * */ +use crate::{ExportFormat, SaveWorkspace, SaveWorkspaceDestination}; use bevy::prelude::*; use bevy_egui::EguiContexts; pub fn handle_workcell_keyboard_input( keyboard_input: Res>, mut egui_context: EguiContexts, + mut save_events: EventWriter, ) { let egui_context = egui_context.ctx_mut(); let ui_has_focus = egui_context.wants_pointer_input() @@ -31,21 +33,12 @@ pub fn handle_workcell_keyboard_input( return; } - /* - if keyboard_input.any_pressed([KeyCode::LShift, KeyCode::RShift]) { - if keyboard_input.just_pressed(KeyCode::V) { - info!("Toggling visuals"); - for mut v in visuals.iter_mut() { - v.is_visible = !v.is_visible; - } - } - - if keyboard_input.just_pressed(KeyCode::C) { - info!("Toggling collisions"); - for mut c in collisions.iter_mut() { - c.is_visible = !c.is_visible; - } + if keyboard_input.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]) { + if keyboard_input.just_pressed(KeyCode::E) { + save_events.send(SaveWorkspace { + destination: SaveWorkspaceDestination::Dialog, + format: ExportFormat::Urdf, + }); } } - */ } diff --git a/rmf_site_editor/src/workcell/load.rs b/rmf_site_editor/src/workcell/load.rs index 284f16fd..218ee00b 100644 --- a/rmf_site_editor/src/workcell/load.rs +++ b/rmf_site_editor/src/workcell/load.rs @@ -30,7 +30,7 @@ use bevy::prelude::*; use std::collections::HashSet; use rmf_site_format::{ - Category, ConstraintDependents, MeshConstraint, NameInWorkcell, SiteID, Workcell, + Category, ConstraintDependents, FrameMarker, MeshConstraint, NameInWorkcell, SiteID, Workcell, }; #[derive(Event)] @@ -54,7 +54,6 @@ fn generate_workcell_entities(commands: &mut Commands, workcell: &Workcell) -> E let root = commands .spawn(SpatialBundle::INHERITED_IDENTITY) .insert(workcell.properties.clone()) - .insert(NameInWorkcell(workcell.properties.name.clone())) .insert(SiteID(workcell.id)) .insert(Category::Workcell) .insert(WorkspaceMarker) @@ -65,9 +64,9 @@ fn generate_workcell_entities(commands: &mut Commands, workcell: &Workcell) -> E id_to_entity.insert(&workcell.id, root); for (id, parented_visual) in &workcell.visuals { - let cmd = commands.spawn((SiteID(*id), VisualMeshMarker)); + let mut cmd = commands.spawn((SiteID(*id), VisualMeshMarker, Category::Visual)); let e = cmd.id(); - parented_visual.bundle.add_bevy_components(cmd); + parented_visual.bundle.add_bevy_components(&mut cmd); // TODO(luca) this hashmap update is duplicated, refactor into function let child_entities: &mut Vec = parent_to_child_entities .entry(parented_visual.parent) @@ -77,9 +76,9 @@ fn generate_workcell_entities(commands: &mut Commands, workcell: &Workcell) -> E } for (id, parented_collision) in &workcell.collisions { - let cmd = commands.spawn((SiteID(*id), CollisionMeshMarker)); + let mut cmd = commands.spawn((SiteID(*id), CollisionMeshMarker, Category::Collision)); let e = cmd.id(); - parented_collision.bundle.add_bevy_components(cmd); + parented_collision.bundle.add_bevy_components(&mut cmd); // TODO(luca) this hashmap update is duplicated, refactor into function let child_entities: &mut Vec = parent_to_child_entities .entry(parented_collision.parent) @@ -92,6 +91,7 @@ fn generate_workcell_entities(commands: &mut Commands, workcell: &Workcell) -> E let e = commands .spawn(AnchorBundle::new(parented_anchor.bundle.anchor.clone()).visible(true)) .insert(SiteID(*id)) + .insert(FrameMarker) .id(); if let Some(c) = &parented_anchor.bundle.mesh_constraint { let model_entity = *id_to_entity @@ -118,6 +118,32 @@ fn generate_workcell_entities(commands: &mut Commands, workcell: &Workcell) -> E id_to_entity.insert(id, e); } + for (id, parented_inertia) in &workcell.inertias { + let e = commands + .spawn(SpatialBundle::INHERITED_IDENTITY) + .insert(parented_inertia.bundle.clone()) + .insert(Category::Inertia) + .insert(SiteID(*id)) + .id(); + let child_entities: &mut Vec = parent_to_child_entities + .entry(parented_inertia.parent) + .or_default(); + child_entities.push(e); + id_to_entity.insert(id, e); + } + + for (id, parented_joint) in &workcell.joints { + let joint = &parented_joint.bundle; + let mut cmd = commands.spawn(SiteID(*id)); + let e = cmd.id(); + joint.add_bevy_components(&mut cmd); + let child_entities: &mut Vec = parent_to_child_entities + .entry(parented_joint.parent) + .or_default(); + child_entities.push(e); + id_to_entity.insert(id, e); + } + // Add constraint dependents to models for (model, dependents) in model_to_constraint_dependent_entities { commands diff --git a/rmf_site_editor/src/workcell/menu.rs b/rmf_site_editor/src/workcell/menu.rs new file mode 100644 index 00000000..89618948 --- /dev/null +++ b/rmf_site_editor/src/workcell/menu.rs @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 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::menu_bar::{FileMenu, MenuEvent, MenuItem, MenuVisualizationStates}; +use crate::{AppState, ExportFormat, SaveWorkspace, SaveWorkspaceDestination}; +use bevy::prelude::*; +use std::collections::HashSet; + +/// Keeps track of which entity is associated to the export urdf button. +#[derive(Resource)] +pub struct ExportUrdfMenu { + export_urdf: Entity, +} + +impl FromWorld for ExportUrdfMenu { + fn from_world(world: &mut World) -> Self { + let workcell_states = HashSet::from([AppState::WorkcellEditor]); + // TODO(luca) add shortcut text for Ctrl-E + let file_header = world.resource::().get(); + let export_urdf = world + .spawn(( + MenuItem::Text("Export Urdf".to_string()), + MenuVisualizationStates(workcell_states), + )) + .set_parent(file_header) + .id(); + + ExportUrdfMenu { export_urdf } + } +} + +pub fn handle_export_urdf_menu_events( + mut commands: Commands, + mut menu_events: EventReader, + urdf_menu: Res, + mut save_events: EventWriter, +) { + for event in menu_events.iter() { + if event.clicked() && event.source() == urdf_menu.export_urdf { + save_events.send(SaveWorkspace { + destination: SaveWorkspaceDestination::Dialog, + format: ExportFormat::Urdf, + }); + } + } +} diff --git a/rmf_site_editor/src/workcell/mesh_constraint.rs b/rmf_site_editor/src/workcell/mesh_constraint.rs deleted file mode 100644 index e93c3120..00000000 --- a/rmf_site_editor/src/workcell/mesh_constraint.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2023 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::site::AnchorBundle; -use bevy::prelude::*; -use rmf_site_format::{Anchor, ConstraintDependents, MeshConstraint, ModelMarker, Pose}; - -pub fn update_constraint_dependents( - updated_models: Query< - (&ConstraintDependents, &Transform), - (Changed, With), - >, - mut transforms: Query<&mut Transform, Without>, - mesh_constraints: Query<&MeshConstraint>, -) { - // TODO(luca) Add widget for parent reassignment in models, otherwise Changed will - // never trigger - // When a mesh constraint is added we need to remove the Pose component and - // set the transform of the entity according to the entity contained in the MeshConstraint - // component - for (deps, model_tf) in updated_models.iter() { - for dep in deps.iter() { - if let Ok(mut anchor_tf) = transforms.get_mut(*dep) { - if let Ok(constraint) = mesh_constraints.get(*dep) { - // TODO(luca) should relative_pose be relative to model origin instead? - // constraint.relative_pose = tf.into(); - // Set the transform to be a combination of model's and constraint's relative_pose - *anchor_tf = *model_tf * constraint.relative_pose.transform(); - } - } - } - } -} - -pub fn add_anchors_for_new_mesh_constraints( - mut commands: Commands, - changed_constraints: Query<(Entity, &MeshConstraint), Changed>>, - transforms: Query<&Transform>, -) { - for (e, constraint) in changed_constraints.iter() { - if let Ok(model_tf) = transforms.get(constraint.entity) { - let tf = *model_tf * constraint.relative_pose.transform(); - let pose = Pose::default().align_with(&tf); - // TODO(luca) is this OK performance wise or should we detect if the component is - // already present and change its value? - commands - .entity(e) - .insert(AnchorBundle::new(Anchor::Pose3D(pose))); - } - } -} diff --git a/rmf_site_editor/src/workcell/mod.rs b/rmf_site_editor/src/workcell/mod.rs index b689d500..410dcc23 100644 --- a/rmf_site_editor/src/workcell/mod.rs +++ b/rmf_site_editor/src/workcell/mod.rs @@ -15,18 +15,29 @@ * */ +pub mod frame; +pub use frame::*; + +pub mod joint; +pub use joint::*; + pub mod load; pub use load::*; pub mod keyboard; pub use keyboard::*; -pub mod mesh_constraint; -pub use mesh_constraint::*; +pub mod menu; +pub use menu::*; + +pub mod model; +pub use model::*; pub mod save; pub use save::*; +pub mod urdf_package_exporter; + pub mod workcell; pub use workcell::*; @@ -39,7 +50,7 @@ use crate::AppState; use crate::{ shapes::make_infinite_grid, site::{ - clear_model_trashcan, handle_model_loaded_events, handle_new_mesh_primitives, + clear_model_trashcan, handle_model_loaded_events, handle_new_primitive_shapes, handle_new_sdf_roots, handle_update_fuel_cache_requests, make_models_selectable, propagate_model_render_layers, read_update_fuel_cache_results, reload_failed_models_with_new_api_key, update_anchor_transforms, update_model_scales, @@ -49,8 +60,6 @@ use crate::{ use rmf_site_format::ModelMarker; -use bevy_rapier3d::prelude::*; - #[derive(Default)] pub struct WorkcellEditorPlugin; @@ -67,6 +76,7 @@ fn delete_grid(mut commands: Commands, grids: Query>) impl Plugin for WorkcellEditorPlugin { fn build(&self, app: &mut App) { app.add_plugin(InfiniteGridPlugin) + .add_event::() .add_event::() .add_event::() .add_event::() @@ -75,20 +85,23 @@ impl Plugin for WorkcellEditorPlugin { .add_systems( Update, ( - update_constraint_dependents, handle_model_loaded_events, update_model_scenes, update_model_scales, + handle_new_primitive_shapes, update_model_tentative_formats, + replace_name_in_site_components, + handle_create_joint_events, + cleanup_orphaned_joints, propagate_model_render_layers, make_models_selectable, handle_update_fuel_cache_requests, read_update_fuel_cache_results, reload_failed_models_with_new_api_key, handle_workcell_keyboard_input, - handle_new_mesh_primitives, change_workcell.before(load_workcell), handle_new_sdf_roots, + handle_export_urdf_menu_events, ) .run_if(in_state(AppState::WorkcellEditor)), ) @@ -104,11 +117,20 @@ impl Plugin for WorkcellEditorPlugin { .add_systems( Update, ( + scale_workcell_anchors, update_anchor_transforms, - add_anchors_for_new_mesh_constraints, update_transforms_for_changed_poses, ) .run_if(in_state(AppState::WorkcellEditor)), + ) + .add_systems( + PostUpdate, + (flatten_loaded_models_hierarchy,).run_if(in_state(AppState::WorkcellEditor)), ); } + + // Put the UI dependent plugins in `finish` to make sure the interaction is initialized first + fn finish(&self, app: &mut App) { + app.init_resource::(); + } } diff --git a/rmf_site_editor/src/workcell/model.rs b/rmf_site_editor/src/workcell/model.rs new file mode 100644 index 00000000..cd4cabb5 --- /dev/null +++ b/rmf_site_editor/src/workcell/model.rs @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 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::site::{Dependents, ModelTrashcan, Pending}; +use bevy::prelude::*; +use rmf_site_format::{ + ModelMarker, NameInSite, NameInWorkcell, NameOfWorkcell, Pose, PrimitiveShape, +}; + +/// SDFs loaded through site editor wrap all the collisions and visuals into a single Model entity. +/// This doesn't quite work for URDF / workcells since we need to export and edit single visuals +/// and collisions, hence we process the loaded models to flatten them here +pub fn flatten_loaded_models_hierarchy( + mut commands: Commands, + new_models: Query< + (Entity, &Parent), + ( + Without, + Or<(Added, Added)>, + ), + >, + all_model_parents: Query<(Entity, &Parent), (With, Without)>, + mut poses: Query<&mut Pose>, + mut dependents: Query<&mut Dependents>, + parents: Query<&Parent>, + trashcan: Res, +) { + for (e, parent) in &new_models { + // Traverse up the hierarchy to find the first model parent and reassign it + if let Some((parent_entity, model_parent)) = + AncestorIter::new(&parents, **parent).find_map(|e| all_model_parents.get(e).ok()) + { + let Ok(parent_pose) = poses.get(parent_entity).cloned() else { + continue; + }; + let Ok(mut child_pose) = poses.get_mut(e) else { + continue; + }; + if let Ok(mut parent_dependents) = dependents.get_mut(**model_parent) { + parent_dependents.remove(&parent_entity); + } + commands.entity(**model_parent).add_child(e); + if let Ok(mut deps) = dependents.get_mut(**model_parent) { + deps.insert(e); + } + for (mut t1, t2) in child_pose.trans.iter_mut().zip(parent_pose.trans.iter()) { + *t1 += t2; + } + // Now despawn the unnecessary model + commands.entity(parent_entity).set_parent(trashcan.0); + } + } +} + +pub fn replace_name_in_site_components( + mut commands: Commands, + new_names: Query<(Entity, &NameInSite), Added>, + workcells: Query<(), With>, + parents: Query<&Parent>, +) { + for (e, name) in &new_names { + if AncestorIter::new(&parents, e).any(|p| workcells.get(p).is_ok()) { + commands + .entity(e) + .insert(NameInWorkcell(name.0.clone())) + .remove::(); + } + } +} diff --git a/rmf_site_editor/src/workcell/save.rs b/rmf_site_editor/src/workcell/save.rs index 30489d64..1b609186 100644 --- a/rmf_site_editor/src/workcell/save.rs +++ b/rmf_site_editor/src/workcell/save.rs @@ -17,9 +17,10 @@ use bevy::ecs::system::SystemState; use bevy::prelude::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use crate::site::{CollisionMeshMarker, Pending, VisualMeshMarker}; +use crate::workcell::urdf_package_exporter::{generate_package, PackageContext, Person}; use crate::ExportFormat; use thiserror::Error as ThisError; @@ -56,8 +57,9 @@ fn assign_site_ids(world: &mut World, workcell: Entity) { Entity, ( Or<( - With, - With, + With, + With, + With, With, With, )>, @@ -99,36 +101,45 @@ pub fn generate_workcell( ), Without, >, + Query<(Entity, &Pose, &Mass, &Moment, &SiteID, &Parent), Without>, Query< ( Entity, &NameInWorkcell, Option<&AssetSource>, - Option<&MeshPrimitive>, + Option<&PrimitiveShape>, &Pose, &SiteID, &Parent, - &Scale, + Option<&Scale>, ), ( Or<(With, With)>, Without, ), >, + Query<(Entity, &JointProperties, &NameInWorkcell, &SiteID, &Parent), Without>, Query<&VisualMeshMarker>, Query<&CollisionMeshMarker>, Query<&SiteID>, - Query<&WorkcellProperties>, + Query<&NameOfWorkcell>, Query<&Parent>, )> = SystemState::new(world); - let (q_anchors, q_models, q_visuals, q_collisions, q_site_id, q_properties, q_parents) = - state.get(world); + let ( + q_anchors, + q_inertials, + q_models, + q_joints, + q_visuals, + q_collisions, + q_site_id, + q_properties, + q_parents, + ) = state.get(world); let mut workcell = Workcell::default(); match q_properties.get(root) { - Ok(properties) => { - workcell.properties = properties.clone(); - } + Ok(name) => workcell.properties.name = name.clone(), Err(_) => { return Err(WorkcellGenerationError::InvalidWorkcellEntity(root)); } @@ -150,8 +161,8 @@ pub fn generate_workcell( let geom = if let Some(source) = source { // It's a model Geometry::Mesh { - filename: String::from(source), - scale: Some(**scale), + source: source.clone(), + scale: scale.map(|s| **s), } } else if let Some(primitive) = primitive { Geometry::Primitive(primitive.clone()) @@ -163,7 +174,7 @@ pub fn generate_workcell( workcell.visuals.insert( id.0, Parented { - parent: parent, + parent, bundle: WorkcellModel { name: name.0.clone(), geometry: geom, @@ -176,7 +187,7 @@ pub fn generate_workcell( workcell.collisions.insert( id.0, Parented { - parent: parent, + parent, bundle: WorkcellModel { name: name.0.clone(), geometry: geom, @@ -214,7 +225,7 @@ pub fn generate_workcell( workcell.frames.insert( id.0, Parented { - parent: parent, + parent, bundle: Frame { anchor: anchor.clone(), name: name.cloned(), @@ -225,6 +236,55 @@ pub fn generate_workcell( ); } + for (e, pose, mass, moment, id, parent) in &q_inertials { + if !parent_in_workcell(&q_parents, e, root) { + continue; + } + let parent = match q_site_id.get(parent.get()) { + Ok(parent) => parent.0, + Err(_) => { + error!("Parent not found for inertial {:?}", parent.get()); + continue; + } + }; + + workcell.inertias.insert( + id.0, + Parented { + parent, + bundle: Inertia { + center: pose.clone(), + mass: mass.clone(), + moment: moment.clone(), + }, + }, + ); + } + + for (e, properties, name, id, parent) in &q_joints { + if !parent_in_workcell(&q_parents, e, root) { + continue; + } + let parent = match q_site_id.get(parent.get()) { + Ok(parent) => parent.0, + Err(_) => { + error!("Parent not found for joint {:?}", parent.get()); + continue; + } + }; + + workcell.joints.insert( + id.0, + Parented { + parent, + bundle: Joint { + name: name.clone(), + properties: properties.clone(), + }, + }, + ); + } + Ok(workcell) } @@ -234,19 +294,6 @@ pub fn save_workcell(world: &mut World) { .drain() .collect(); for save_event in save_events { - let path = save_event.to_file; - info!( - "Saving to {}", - path.to_str().unwrap_or("") - ); - let f = match std::fs::File::create(path) { - Ok(f) => f, - Err(err) => { - error!("Unable to save file: {err}"); - continue; - } - }; - let workcell = match generate_workcell(world, save_event.root) { Ok(root) => root, Err(err) => { @@ -255,18 +302,61 @@ pub fn save_workcell(world: &mut World) { } }; + let path = save_event.to_file; match save_event.format { - ExportFormat::Default => match workcell.to_writer(f) { - Ok(()) => { - info!("Save successful"); - } - Err(err) => { - error!("Save failed: {err}"); + ExportFormat::Default => { + info!( + "Saving to {}", + path.to_str().unwrap_or("") + ); + let f = match std::fs::File::create(path) { + Ok(f) => f, + Err(err) => { + error!("Unable to save file: {err}"); + continue; + } + }; + match workcell.to_writer(f) { + Ok(()) => { + info!("Save successful"); + } + Err(err) => { + error!("Save failed: {err}"); + } } - }, + } ExportFormat::Urdf => { - info!("Saving to urdf"); + // TODO(luca) File name is ignored, change to pick folder instead of pick file + match export_package(&path, workcell) { + Ok(()) => { + info!("Successfully exported package"); + } + Err(err) => { + error!("Failed to export package: {err}"); + } + }; } } } } + +fn export_package(path: &PathBuf, workcell: Workcell) -> Result<(), Box> { + let output_directory = path.parent().ok_or("Not able to get parent")?; + + let package_context = PackageContext { + license: "TODO".to_string(), + maintainers: vec![Person { + name: "TODO".to_string(), + email: "todo@todo.com".to_string(), + }], + project_name: workcell.properties.name.0.clone() + "_description", + fixed_frame: "world".to_string(), + dependencies: vec![], + project_description: "TODO".to_string(), + project_version: "0.0.1".to_string(), + urdf_file_name: "robot.urdf".to_string(), + }; + + generate_package(workcell, package_context, &output_directory)?; + Ok(()) +} diff --git a/rmf_site_editor/src/workcell/urdf_package_exporter/generate_package.rs b/rmf_site_editor/src/workcell/urdf_package_exporter/generate_package.rs new file mode 100644 index 00000000..a338175b --- /dev/null +++ b/rmf_site_editor/src/workcell/urdf_package_exporter/generate_package.rs @@ -0,0 +1,138 @@ +use crate::site_asset_io::cache_path; +use crate::workcell::urdf_package_exporter::template::PackageContext; +use rmf_site_format::{AssetSource, Geometry, Workcell}; +use std::error::Error; +use std::io::{Error as IoError, ErrorKind as IoErrorKind}; +use std::path::{Path, PathBuf}; +use tera::Tera; + +pub fn generate_package( + workcell: Workcell, + package_context: PackageContext, + output_directory_path: &Path, +) -> Result<(), Box> { + let new_package_name = &package_context.project_name; + + let output_package_path = output_directory_path.join(new_package_name); + std::fs::create_dir_all(&output_package_path)?; + + // Create the package + write_urdf_and_copy_mesh_files(workcell, &new_package_name, &output_package_path)?; + generate_templates(package_context, &output_package_path)?; + + Ok(()) +} + +fn write_urdf_and_copy_mesh_files( + mut workcell: Workcell, + new_package_name: &str, + output_package_path: &Path, +) -> Result<(), Box> { + convert_and_copy_meshes(&mut workcell, new_package_name, output_package_path)?; + + let urdf_robot = workcell.to_urdf()?; + let urdf_directory_path = output_package_path.join("urdf"); + std::fs::create_dir_all(&urdf_directory_path)?; + let urdf_file_path = urdf_directory_path.join("robot.urdf"); + let urdf_string = urdf_rs::write_to_string(&urdf_robot)?; + std::fs::write(urdf_file_path, urdf_string)?; + + Ok(()) +} + +fn convert_and_copy_meshes( + workcell: &mut Workcell, + package_name: &str, + output_package_path: &Path, +) -> Result<(), Box> { + let meshes_directory_path = output_package_path.join("meshes"); + std::fs::create_dir_all(&meshes_directory_path)?; + for (_, model) in &mut workcell + .visuals + .iter_mut() + .chain(workcell.collisions.iter_mut()) + { + if let Geometry::Mesh { + source: asset_source, + .. + } = &mut model.bundle.geometry + { + let path = get_path_to_asset_file(&asset_source)?; + + let file_name = path + .file_name() + .ok_or(IoError::new( + IoErrorKind::InvalidInput, + "Unable to get file name from path", + ))? + .to_str() + .ok_or(IoError::new( + IoErrorKind::InvalidInput, + "Unable to convert file name to str", + ))?; + + std::fs::copy(&path, &meshes_directory_path.join(&file_name))?; + let package_path = format!("{}/meshes/{}", package_name, file_name); + *asset_source = AssetSource::Package(package_path); + } + } + Ok(()) +} + +fn get_path_to_asset_file(asset_source: &AssetSource) -> Result> { + match asset_source { + AssetSource::Package(_) => Ok(urdf_rs::utils::expand_package_path( + &(String::from(asset_source)), + None, + ) + .to_string() + .into()), + AssetSource::Remote(asset_name) => { + let mut asset_path = cache_path(); + asset_path.push(&asset_name); + Ok(asset_path) + } + AssetSource::Local(path) => Ok(path.into()), + AssetSource::Search(_) | AssetSource::OSMTile { .. } | AssetSource::Bundled(_) => { + Err(IoError::new( + IoErrorKind::Unsupported, + "Not a supported asset type for exporting a workcell to a package", + ))? + } + } +} + +fn generate_templates( + package_context: PackageContext, + package_directory: &Path, +) -> Result<(), Box> { + let context = tera::Context::from_serialize(package_context)?; + let mut tera = Tera::default(); + tera.add_raw_template("package.xml", include_str!("templates/package.xml.j2"))?; + tera.add_raw_template( + "CMakeLists.txt", + include_str!("templates/CMakeLists.txt.j2"), + )?; + tera.add_raw_template("urdf.rviz", include_str!("templates/urdf.rviz.j2"))?; + tera.add_raw_template( + "display.launch.py", + include_str!("templates/display.launch.py.j2"), + )?; + let f = std::fs::File::create(package_directory.join("package.xml"))?; + tera.render_to("package.xml", &context, f)?; + + let f = std::fs::File::create(package_directory.join("CMakeLists.txt"))?; + tera.render_to("CMakeLists.txt", &context, f)?; + + let rviz_directory = package_directory.join("rviz"); + std::fs::create_dir_all(&rviz_directory)?; + let f = std::fs::File::create(rviz_directory.join("urdf.rviz"))?; + tera.render_to("urdf.rviz", &context, f)?; + + let launch_directory = package_directory.join("launch"); + std::fs::create_dir_all(&launch_directory)?; + let f = std::fs::File::create(launch_directory.join("display.launch.py"))?; + tera.render_to("display.launch.py", &context, f)?; + + Ok(()) +} diff --git a/rmf_site_editor/src/workcell/urdf_package_exporter/mod.rs b/rmf_site_editor/src/workcell/urdf_package_exporter/mod.rs new file mode 100644 index 00000000..5d3e09b3 --- /dev/null +++ b/rmf_site_editor/src/workcell/urdf_package_exporter/mod.rs @@ -0,0 +1,5 @@ +pub mod generate_package; +pub use generate_package::generate_package; + +pub use template::{PackageContext, Person}; +pub mod template; diff --git a/rmf_site_editor/src/workcell/urdf_package_exporter/template.rs b/rmf_site_editor/src/workcell/urdf_package_exporter/template.rs new file mode 100644 index 00000000..f84eca0a --- /dev/null +++ b/rmf_site_editor/src/workcell/urdf_package_exporter/template.rs @@ -0,0 +1,27 @@ +use serde::Serialize; +use std::path::PathBuf; + +#[derive(Debug, Serialize)] +pub struct PackageContext { + pub project_name: String, + pub project_description: String, + pub project_version: String, + pub license: String, + pub maintainers: Vec, + pub dependencies: Vec, + pub fixed_frame: String, + pub urdf_file_name: String, +} + +#[derive(Debug, Serialize)] +pub struct Person { + pub name: String, + pub email: String, +} + +#[derive(Debug)] +pub struct Template { + pub name: String, + pub path: String, + pub output_path: PathBuf, +} diff --git a/rmf_site_editor/src/workcell/urdf_package_exporter/templates/CMakeLists.txt.j2 b/rmf_site_editor/src/workcell/urdf_package_exporter/templates/CMakeLists.txt.j2 new file mode 100644 index 00000000..063522fb --- /dev/null +++ b/rmf_site_editor/src/workcell/urdf_package_exporter/templates/CMakeLists.txt.j2 @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.16) + +project({{project_name}}) + +find_package(ament_cmake REQUIRED) +{%- for dependency in dependencies %} +find_package({{dependency}} REQUIRED) +{%- endfor %} + +install( + DIRECTORY launch meshes rviz urdf + DESTINATION share/${PROJECT_NAME} +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/rmf_site_editor/src/workcell/urdf_package_exporter/templates/display.launch.py.j2 b/rmf_site_editor/src/workcell/urdf_package_exporter/templates/display.launch.py.j2 new file mode 100644 index 00000000..8e8c9efd --- /dev/null +++ b/rmf_site_editor/src/workcell/urdf_package_exporter/templates/display.launch.py.j2 @@ -0,0 +1,31 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution +from launch_ros.substitutions import FindPackageShare + +def generate_launch_description(): + ld = LaunchDescription() + + package_path = FindPackageShare('{{project_name}}') + default_rviz_config_path = PathJoinSubstitution([package_path, 'rviz', 'urdf.rviz']) + + gui_arg = DeclareLaunchArgument(name='gui', default_value='true', choices=['true', 'false'], + description='Flag to enable joint_state_publisher_gui') + ld.add_action(gui_arg) + + rviz_arg = DeclareLaunchArgument(name='rvizconfig', default_value=default_rviz_config_path, + description='Absolute path to rviz config file') + ld.add_action(rviz_arg) + + ld.add_action(IncludeLaunchDescription( + PathJoinSubstitution( + [FindPackageShare('urdf_launch'), 'launch', 'display.launch.py']), + launch_arguments={ + 'urdf_package': '{{project_name}}', + 'urdf_package_path': 'urdf/{{urdf_file_name}}', + 'rviz_config': LaunchConfiguration('rvizconfig'), + 'jsp_gui': LaunchConfiguration('gui'), + }.items() + )) + + return ld diff --git a/rmf_site_editor/src/workcell/urdf_package_exporter/templates/package.xml.j2 b/rmf_site_editor/src/workcell/urdf_package_exporter/templates/package.xml.j2 new file mode 100644 index 00000000..737b2792 --- /dev/null +++ b/rmf_site_editor/src/workcell/urdf_package_exporter/templates/package.xml.j2 @@ -0,0 +1,25 @@ + + + + {{project_name}} + {{project_version}} + {{project_description}} + + {%- for maintainer in maintainers %} + {{maintainer.name}} + {%- endfor %} + + {{license}} + + ament_cmake + urdf_launch + ament_lint_auto + {%- for dependency in dependencies %} + {{dependency}} + {%- endfor %} + + + ament_cmake + + + diff --git a/rmf_site_editor/src/workcell/urdf_package_exporter/templates/urdf.rviz.j2 b/rmf_site_editor/src/workcell/urdf_package_exporter/templates/urdf.rviz.j2 new file mode 100644 index 00000000..bb8cc092 --- /dev/null +++ b/rmf_site_editor/src/workcell/urdf_package_exporter/templates/urdf.rviz.j2 @@ -0,0 +1,35 @@ +Panels: + - Class: rviz_common/Displays + Name: Displays + - Class: rviz_common/Views + Name: Views +Visualization Manager: + Displays: + - Class: rviz_default_plugins/Grid + Name: Grid + Value: true + - Alpha: 0.8 + Class: rviz_default_plugins/RobotModel + Description Topic: + Value: /robot_description + Name: RobotModel + Value: true + - Class: rviz_default_plugins/TF + Name: TF + Value: true + Global Options: + Fixed Frame: {{fixed_frame}} + Tools: + - Class: rviz_default_plugins/MoveCamera + Value: true + Views: + Current: + Class: rviz_default_plugins/Orbit + Distance: 1.7 + Name: Current View + Pitch: 0.33 + Value: Orbit (rviz) + Yaw: 5.5 +Window Geometry: + Height: 800 + Width: 1200 diff --git a/rmf_site_editor/src/workcell/workcell.rs b/rmf_site_editor/src/workcell/workcell.rs index a6326297..8a31d487 100644 --- a/rmf_site_editor/src/workcell/workcell.rs +++ b/rmf_site_editor/src/workcell/workcell.rs @@ -19,7 +19,7 @@ use crate::interaction::{InteractionAssets, Selectable}; use crate::site::SiteAssets; use crate::CurrentWorkspace; use bevy::prelude::*; -use rmf_site_format::WorkcellProperties; +use rmf_site_format::NameOfWorkcell; /// Used as an event to command that a new workcell should be made the current one #[derive(Clone, Copy, Debug, Event)] @@ -35,7 +35,7 @@ pub struct WorkcellVisualizationMarker; pub fn change_workcell( mut current_workspace: ResMut, mut change_current_workcell: EventReader, - open_workcells: Query>, + open_workcells: Query>, ) { if let Some(cmd) = change_current_workcell.iter().last() { if open_workcells.get(cmd.root).is_err() { @@ -53,7 +53,7 @@ pub fn change_workcell( pub fn add_workcell_visualization( mut commands: Commands, - new_workcells: Query>, + new_workcells: Query>, site_assets: Res, interaction_assets: Res, ) { diff --git a/rmf_site_editor/src/workspace.rs b/rmf_site_editor/src/workspace.rs index 449046fc..adba2269 100644 --- a/rmf_site_editor/src/workspace.rs +++ b/rmf_site_editor/src/workspace.rs @@ -63,6 +63,7 @@ pub enum WorkspaceData { LegacyBuilding(Vec), Site(Vec), Workcell(Vec), + WorkcellUrdf(Vec), } impl WorkspaceData { @@ -74,6 +75,8 @@ impl WorkspaceData { Some(WorkspaceData::Site(data)) } else if filename.ends_with("workcell.json") { Some(WorkspaceData::Workcell(data)) + } else if filename.ends_with("urdf") { + Some(WorkspaceData::WorkcellUrdf(data)) } else { error!("Unrecognized file type {:?}", filename); None @@ -371,6 +374,36 @@ fn workspace_file_load_complete( } } } + WorkspaceData::WorkcellUrdf(data) => { + info!("Importing urdf workcell"); + let Ok(utf) = std::str::from_utf8(&data) else { + error!("Failed converting urdf bytes to string"); + return; + }; + match urdf_rs::read_from_string(utf) { + Ok(urdf) => { + // TODO(luca) make this function return a result and this a match statement + match Workcell::from_urdf(&urdf) { + Ok(workcell) => { + // Switch state + app_state.set(AppState::WorkcellEditor); + load_workcell.send(LoadWorkcell { + workcell, + focus: true, + default_file, + }); + interaction_state.set(InteractionState::Enable); + } + Err(err) => { + error!("Failed converting urdf to workcell {:?}", err); + } + } + } + Err(err) => { + error!("Failed loading urdf workcell {:?}", err); + } + } + } } } } diff --git a/rmf_site_format/Cargo.toml b/rmf_site_format/Cargo.toml index b6709986..67def6f4 100644 --- a/rmf_site_format/Cargo.toml +++ b/rmf_site_format/Cargo.toml @@ -16,6 +16,10 @@ thiserror = "*" glam = { version = "0.24", features = ["serde"] } uuid = { version = "1.1", features = ["v4", "serde"] } # add features=["bevy"] to a dependent Cargo.toml to get the bevy-related features +# We depend on a bugfix released specifically in 0.7.3 bevy = { version = "0.11", optional = true } -urdf-rs = "0.7" +urdf-rs = "0.7.3" pathdiff = "*" + +[dev-dependencies] +float_eq = "1.0" diff --git a/rmf_site_format/src/category.rs b/rmf_site_format/src/category.rs index 8fe7d5d8..4df9a01b 100644 --- a/rmf_site_format/src/category.rs +++ b/rmf_site_format/src/category.rs @@ -30,9 +30,12 @@ pub enum Category { General, Site, Anchor, + Collision, Door, Wall, Floor, + Inertia, + Joint, Level, Lane, Lift, @@ -49,6 +52,7 @@ pub enum Category { Workcell, GeoReference, NavigationGraph, + Visual, } impl Category { @@ -57,9 +61,12 @@ impl Category { Self::General => "General", Self::Site => "Site", Self::Anchor => "Anchor", + Self::Collision => "Collision", Self::Door => "Door", Self::Wall => "Wall", Self::Floor => "Floor", + Self::Inertia => "Inertia", + Self::Joint => "Joint", Self::Level => "Level", Self::Lane => "Lane", Self::Lift => "Lift", @@ -76,6 +83,7 @@ impl Category { Self::Workcell => "Workcell", Self::GeoReference => "Georeference", Self::NavigationGraph => "Navigation Graph", + Self::Visual => "Visual", } } diff --git a/rmf_site_format/src/workcell.rs b/rmf_site_format/src/workcell.rs index 843a6d9c..f8efa167 100644 --- a/rmf_site_format/src/workcell.rs +++ b/rmf_site_format/src/workcell.rs @@ -15,19 +15,20 @@ * */ -use std::collections::{BTreeMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::io; +use crate::misc::Rotation; use crate::*; #[cfg(feature = "bevy")] use bevy::ecs::system::EntityCommands; #[cfg(feature = "bevy")] -use bevy::prelude::{Bundle, Component, Deref, DerefMut, Entity}; +use bevy::prelude::{Bundle, Component, Deref, DerefMut, Entity, SpatialBundle}; #[cfg(feature = "bevy")] use bevy::reflect::{TypePath, TypeUuid}; -use glam::Vec3; +use glam::{EulerRot, Vec3}; use serde::{Deserialize, Serialize}; -use urdf_rs::Robot; +use thiserror::Error as ThisError; /// Helper structure to serialize / deserialize entities with parents #[derive(Serialize, Deserialize, Clone, Debug)] @@ -73,10 +74,14 @@ pub enum MeshElement { #[derive(Component, Deref, DerefMut, Serialize, Deserialize, Debug, Default, Clone, PartialEq)] pub struct ConstraintDependents(pub HashSet); -#[derive(Serialize, Deserialize, Debug, Default, Clone)] +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] #[cfg_attr(feature = "bevy", derive(Component))] +pub struct NameOfWorkcell(pub String); + +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +#[cfg_attr(feature = "bevy", derive(Bundle))] pub struct WorkcellProperties { - pub name: String, + pub name: NameOfWorkcell, } #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] @@ -89,41 +94,190 @@ pub struct Mass(f32); #[derive(Serialize, Deserialize, Debug, Default, Clone)] #[cfg_attr(feature = "bevy", derive(Component))] -pub struct Inertia {} +pub struct Moment { + pub ixx: f32, + pub ixy: f32, + pub ixz: f32, + pub iyy: f32, + pub iyz: f32, + pub izz: f32, +} #[derive(Serialize, Deserialize, Debug, Default, Clone)] #[cfg_attr(feature = "bevy", derive(Bundle))] -pub struct Inertial { - pub origin: Pose, +pub struct Inertia { + pub center: Pose, pub mass: Mass, - pub inertia: Inertia, + pub moment: Moment, } -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -#[cfg_attr(feature = "bevy", derive(Bundle))] -pub struct Link { - pub name: NameInWorkcell, - pub inertial: Inertial, - #[serde(skip)] - pub marker: LinkMarker, +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct JointAxis([f32; 3]); + +impl From<&urdf_rs::Axis> for JointAxis { + fn from(axis: &urdf_rs::Axis) -> Self { + Self(axis.xyz.map(|t| t as f32)) + } } -#[derive(Debug, Default, Clone)] -#[cfg_attr(feature = "bevy", derive(Component))] -pub struct LinkMarker; +impl From<&JointAxis> for urdf_rs::Axis { + fn from(axis: &JointAxis) -> Self { + Self { + xyz: urdf_rs::Vec3(axis.0.map(|v| v as f64)), + } + } +} -#[derive(Serialize, Deserialize, Debug, Default, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] +enum RangeLimits { + None, + Symmetric(f32), + Asymmetric { + lower: Option, + upper: Option, + }, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct JointLimits { + position: RangeLimits, + effort: RangeLimits, + velocity: RangeLimits, +} + +impl From<&urdf_rs::JointLimit> for JointLimits { + fn from(limit: &urdf_rs::JointLimit) -> Self { + Self { + position: RangeLimits::Asymmetric { + lower: Some(limit.lower as f32), + upper: Some(limit.upper as f32), + }, + effort: RangeLimits::Symmetric(limit.effort as f32), + velocity: RangeLimits::Symmetric(limit.velocity as f32), + } + } +} + +impl From<&JointLimits> for urdf_rs::JointLimit { + fn from(limits: &JointLimits) -> Self { + const DEFAULT_EFFORT_LIMIT: f64 = 1e3; + const DEFAULT_VELOCITY_LIMIT: f64 = 10.0; + fn min_or_default(slice: [Option; 2], default: f64) -> f64 { + let mut vec = slice + .iter() + .filter_map(|v| v.map(|m| m as f64)) + .collect::>(); + vec.sort_by(|a, b| a.total_cmp(b)); + vec.first().cloned().unwrap_or(default) + } + // 0.0 is a valid default in urdf for lower and upper limits + let (lower, upper) = match limits.position { + RangeLimits::None => (0.0, 0.0), + RangeLimits::Symmetric(l) => (l as f64, l as f64), + RangeLimits::Asymmetric { lower, upper } => ( + lower.map(|v| v as f64).unwrap_or_default(), + upper.map(|v| v as f64).unwrap_or_default(), + ), + }; + let effort = match limits.effort { + RangeLimits::None => { + println!( + "No effort limit found when exporting to urdf, setting to {}", + DEFAULT_EFFORT_LIMIT + ); + DEFAULT_EFFORT_LIMIT + } + RangeLimits::Symmetric(l) => l as f64, + RangeLimits::Asymmetric { lower, upper } => { + let limit = min_or_default([lower, upper], DEFAULT_EFFORT_LIMIT); + println!( + "Asymmetric effort limit found when exporting to urdf, setting to {}", + limit + ); + limit + } + }; + let velocity = match limits.velocity { + RangeLimits::None => { + println!( + "No velocity limit found when exporting to urdf, setting to {}", + DEFAULT_VELOCITY_LIMIT + ); + DEFAULT_VELOCITY_LIMIT + } + RangeLimits::Symmetric(l) => l as f64, + RangeLimits::Asymmetric { lower, upper } => { + let limit = min_or_default([lower, upper], DEFAULT_VELOCITY_LIMIT); + println!( + "Asymmetric velocity limit found when exporting to urdf, setting to {}", + limit + ); + limit + } + }; + Self { + lower, + upper, + effort, + velocity, + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] #[cfg_attr(feature = "bevy", derive(Bundle))] pub struct Joint { pub name: NameInWorkcell, + pub properties: JointProperties, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[cfg_attr(feature = "bevy", derive(Component))] +pub enum JointProperties { + Fixed, + Prismatic(SingleDofJoint), + Revolute(SingleDofJoint), + Continuous(SingleDofJoint), +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SingleDofJoint { + pub limits: JointLimits, + pub axis: JointAxis, +} + +impl JointProperties { + pub fn label(&self) -> String { + match &self { + JointProperties::Fixed => "Fixed", + JointProperties::Revolute(_) => "Revolute", + JointProperties::Prismatic(_) => "Prismatic", + JointProperties::Continuous(_) => "Continuous", + } + .to_string() + } +} + +// TODO(luca) should commands implementation be in rmf_site_editor instead of rmf_site_format? +/// Custom spawning implementation since bundles don't allow options +#[cfg(feature = "bevy")] +impl Joint { + pub fn add_bevy_components(&self, commands: &mut EntityCommands) { + commands.insert(( + SpatialBundle::INHERITED_IDENTITY, + Category::Joint, + self.name.clone(), + self.properties.clone(), + )); + } } #[derive(Serialize, Deserialize, Debug, Clone)] pub enum Geometry { //#[serde(flatten)] - Primitive(MeshPrimitive), + Primitive(PrimitiveShape), Mesh { - filename: String, + source: AssetSource, #[serde(default, skip_serializing_if = "is_default")] scale: Option, }, @@ -131,20 +285,20 @@ pub enum Geometry { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[cfg_attr(feature = "bevy", derive(Component))] -pub enum MeshPrimitive { +pub enum PrimitiveShape { Box { size: [f32; 3] }, Cylinder { radius: f32, length: f32 }, Capsule { radius: f32, length: f32 }, Sphere { radius: f32 }, } -impl MeshPrimitive { +impl PrimitiveShape { pub fn label(&self) -> String { match &self { - MeshPrimitive::Box { .. } => "Box", - MeshPrimitive::Cylinder { .. } => "Cylinder", - MeshPrimitive::Capsule { .. } => "Capsule", - MeshPrimitive::Sphere { .. } => "Sphere", + PrimitiveShape::Box { .. } => "Box", + PrimitiveShape::Cylinder { .. } => "Cylinder", + PrimitiveShape::Capsule { .. } => "Capsule", + PrimitiveShape::Sphere { .. } => "Sphere", } .to_string() } @@ -152,7 +306,7 @@ impl MeshPrimitive { #[derive(Clone, Debug, Default, PartialEq)] #[cfg_attr(feature = "bevy", derive(Component))] -pub struct RecallMeshPrimitive { +pub struct RecallPrimitiveShape { pub box_size: Option<[f32; 3]>, pub cylinder_radius: Option, pub cylinder_length: Option, @@ -161,60 +315,76 @@ pub struct RecallMeshPrimitive { pub sphere_radius: Option, } -impl Recall for RecallMeshPrimitive { - type Source = MeshPrimitive; +impl Recall for RecallPrimitiveShape { + type Source = PrimitiveShape; - fn remember(&mut self, source: &MeshPrimitive) { + fn remember(&mut self, source: &PrimitiveShape) { match source { - MeshPrimitive::Box { size } => { + PrimitiveShape::Box { size } => { self.box_size = Some(*size); } - MeshPrimitive::Cylinder { radius, length } => { + PrimitiveShape::Cylinder { radius, length } => { self.cylinder_radius = Some(*radius); self.cylinder_length = Some(*length); } - MeshPrimitive::Capsule { radius, length } => { + PrimitiveShape::Capsule { radius, length } => { self.capsule_radius = Some(*radius); self.capsule_length = Some(*length); } - MeshPrimitive::Sphere { radius } => { + PrimitiveShape::Sphere { radius } => { self.sphere_radius = Some(*radius); } } } } -impl RecallMeshPrimitive { - pub fn assume_box(&self, current: &MeshPrimitive) -> MeshPrimitive { - MeshPrimitive::Box { - size: self.box_size.unwrap_or_default(), +impl RecallPrimitiveShape { + pub fn assume_box(&self, current: &PrimitiveShape) -> PrimitiveShape { + if matches!(current, PrimitiveShape::Box { .. }) { + current.clone() + } else { + PrimitiveShape::Box { + size: self.box_size.unwrap_or_default(), + } } } - pub fn assume_cylinder(&self, current: &MeshPrimitive) -> MeshPrimitive { - MeshPrimitive::Cylinder { - radius: self.cylinder_radius.unwrap_or_default(), - length: self.cylinder_length.unwrap_or_default(), + pub fn assume_cylinder(&self, current: &PrimitiveShape) -> PrimitiveShape { + if matches!(current, PrimitiveShape::Cylinder { .. }) { + current.clone() + } else { + PrimitiveShape::Cylinder { + radius: self.cylinder_radius.unwrap_or_default(), + length: self.cylinder_length.unwrap_or_default(), + } } } - pub fn assume_capsule(&self, current: &MeshPrimitive) -> MeshPrimitive { - MeshPrimitive::Capsule { - radius: self.capsule_radius.unwrap_or_default(), - length: self.capsule_length.unwrap_or_default(), + pub fn assume_capsule(&self, current: &PrimitiveShape) -> PrimitiveShape { + if matches!(current, PrimitiveShape::Capsule { .. }) { + current.clone() + } else { + PrimitiveShape::Capsule { + radius: self.capsule_radius.unwrap_or_default(), + length: self.capsule_length.unwrap_or_default(), + } } } - pub fn assume_sphere(&self, current: &MeshPrimitive) -> MeshPrimitive { - MeshPrimitive::Sphere { - radius: self.sphere_radius.unwrap_or_default(), + pub fn assume_sphere(&self, current: &PrimitiveShape) -> PrimitiveShape { + if matches!(current, PrimitiveShape::Sphere { .. }) { + current.clone() + } else { + PrimitiveShape::Sphere { + radius: self.sphere_radius.unwrap_or_default(), + } } } } impl Default for Geometry { fn default() -> Self { - Geometry::Primitive(MeshPrimitive::Box { size: [0.0; 3] }) + Geometry::Primitive(PrimitiveShape::Box { size: [0.0; 3] }) } } @@ -227,7 +397,7 @@ pub struct WorkcellModel { #[cfg(feature = "bevy")] impl WorkcellModel { - pub fn add_bevy_components(&self, mut commands: EntityCommands) { + pub fn add_bevy_components(&self, commands: &mut EntityCommands) { match &self.geometry { Geometry::Primitive(primitive) => { commands.insert(( @@ -236,12 +406,12 @@ impl WorkcellModel { NameInWorkcell(self.name.clone()), )); } - Geometry::Mesh { filename, scale } => { - let scale = Scale(scale.unwrap_or_default()); + Geometry::Mesh { source, scale } => { + let scale = Scale(scale.unwrap_or(Vec3::ONE)); // TODO(luca) Make a bundle for workcell models to avoid manual insertion here commands.insert(( NameInWorkcell(self.name.clone()), - AssetSource::from(filename.as_str()), + source.clone(), self.pose.clone(), ConstraintDependents::default(), scale, @@ -268,10 +438,208 @@ pub struct Workcell { pub visuals: BTreeMap>, /// Collisions, key is their id, used for hierarchy pub collisions: BTreeMap>, - // TODO(luca) Joints + /// Inertias, key is their id, used for hierarchy + pub inertias: BTreeMap>, + /// Joints, key is their id, used for hierarchy. They must have a frame as a parent and a frame + /// as a child + pub joints: BTreeMap>, +} + +#[derive(Debug, ThisError)] +pub enum UrdfImportError { + #[error("a joint refers to a non existing link [{0}]")] + BrokenJointReference(String), + // TODO(luca) Add urdf_rs::JointType to this error, it doesn't implement Display + #[error("unsupported joint type found")] + UnsupportedJointType, +} + +impl From for urdf_rs::Pose { + fn from(pose: Pose) -> Self { + urdf_rs::Pose { + rpy: match pose.rot { + Rotation::EulerExtrinsicXYZ(arr) => urdf_rs::Vec3(arr.map(|v| v.radians().into())), + Rotation::Yaw(v) => urdf_rs::Vec3([0.0, 0.0, v.radians().into()]), + Rotation::Quat([x, y, z, w]) => { + let (z, y, x) = glam::quat(x, y, z, w).to_euler(EulerRot::ZYX); + urdf_rs::Vec3([x as f64, y as f64, z as f64]) + } + }, + xyz: urdf_rs::Vec3(pose.trans.map(|v| v as f64)), + } + } +} + +impl From<&urdf_rs::Pose> for Pose { + fn from(pose: &urdf_rs::Pose) -> Self { + Pose { + trans: pose.xyz.map(|t| t as f32), + rot: Rotation::EulerExtrinsicXYZ(pose.rpy.map(|t| Angle::Rad(t as f32))), + } + } +} + +impl From for urdf_rs::Geometry { + fn from(geometry: Geometry) -> Self { + match geometry { + Geometry::Mesh { source, scale } => urdf_rs::Geometry::Mesh { + filename: (&source).into(), + scale: scale.map(|v| urdf_rs::Vec3([v.x as f64, v.y as f64, v.z as f64])), + }, + Geometry::Primitive(PrimitiveShape::Box { size: [x, y, z] }) => { + urdf_rs::Geometry::Box { + size: urdf_rs::Vec3([x as f64, y as f64, z as f64]), + } + } + Geometry::Primitive(PrimitiveShape::Cylinder { radius, length }) => { + urdf_rs::Geometry::Cylinder { + radius: radius as f64, + length: length as f64, + } + } + Geometry::Primitive(PrimitiveShape::Capsule { radius, length }) => { + urdf_rs::Geometry::Capsule { + radius: radius as f64, + length: length as f64, + } + } + Geometry::Primitive(PrimitiveShape::Sphere { radius }) => urdf_rs::Geometry::Sphere { + radius: radius as f64, + }, + } + } +} + +#[derive(Debug, ThisError)] +pub enum WorkcellToUrdfError { + #[error("Invalid anchor type {0:?}")] + InvalidAnchorType(Anchor), + #[error("Urdf error: {0}")] + WriteToStringError(#[from] urdf_rs::UrdfError), + #[error("Broken reference: {0}")] + BrokenReference(u32), + #[error("Frame {0} referred by joint {1} has no name, this is not allowed in URDF")] + MissingJointFrameName(u32, u32), } impl Workcell { + pub fn from_urdf(urdf: &urdf_rs::Robot) -> Result { + let mut frame_name_to_id = HashMap::new(); + let root_id = 0_u32; + let mut cur_id = 1u32..; + let mut frames = BTreeMap::new(); + let mut visuals = BTreeMap::new(); + let mut collisions = BTreeMap::new(); + let mut inertias = BTreeMap::new(); + let mut joints = BTreeMap::new(); + // Populate here + for link in &urdf.links { + let inertia = Inertia::from(&link.inertial); + // Add a frame with the link's name, then the inertia data as a child + let frame_id = cur_id.next().unwrap(); + let inertia_id = cur_id.next().unwrap(); + frame_name_to_id.insert(link.name.clone(), frame_id); + // Pose and parent will be overwritten by joints, if needed + frames.insert( + frame_id, + Parented { + parent: root_id, + bundle: Frame { + anchor: Anchor::Pose3D(Pose::default()), + name: Some(NameInWorkcell(link.name.clone())), + mesh_constraint: Default::default(), + marker: Default::default(), + }, + }, + ); + inertias.insert( + inertia_id, + Parented { + parent: frame_id, + bundle: inertia, + }, + ); + for visual in &link.visual { + let model = WorkcellModel::from(visual); + let visual_id = cur_id.next().unwrap(); + visuals.insert( + visual_id, + Parented { + parent: frame_id, + bundle: model, + }, + ); + } + for collision in &link.collision { + let model = WorkcellModel::from(collision); + let collision_id = cur_id.next().unwrap(); + collisions.insert( + collision_id, + Parented { + parent: frame_id, + bundle: model, + }, + ); + } + } + for joint in &urdf.joints { + let parent = frame_name_to_id.get(&joint.parent.link).ok_or( + UrdfImportError::BrokenJointReference(joint.parent.link.clone()), + )?; + let child = frame_name_to_id.get(&joint.child.link).ok_or( + UrdfImportError::BrokenJointReference(joint.child.link.clone()), + )?; + let properties = match joint.joint_type { + urdf_rs::JointType::Revolute => JointProperties::Revolute(SingleDofJoint { + axis: (&joint.axis).into(), + limits: (&joint.limit).into(), + }), + urdf_rs::JointType::Prismatic => JointProperties::Prismatic(SingleDofJoint { + axis: (&joint.axis).into(), + limits: (&joint.limit).into(), + }), + urdf_rs::JointType::Fixed => JointProperties::Fixed, + urdf_rs::JointType::Continuous => JointProperties::Continuous(SingleDofJoint { + axis: (&joint.axis).into(), + limits: (&joint.limit).into(), + }), + _ => { + return Err(UrdfImportError::UnsupportedJointType); + } + }; + let joint_id = cur_id.next().unwrap(); + // Reassign the child parenthood and pose to the joint + // If the frame didn't exist we would have returned an error when populating child + // hence this is safe. + let child_frame = frames.get_mut(child).unwrap(); + child_frame.parent = joint_id; + // In urdf, joint origin represents the coordinates of the joint in the + // parent frame. The child is always in the origin of the joint + child_frame.bundle.anchor = Anchor::Pose3D((&joint.origin).into()); + joints.insert( + joint_id, + Parented { + parent: *parent, + bundle: Joint { + name: NameInWorkcell(joint.name.clone()), + properties, + }, + }, + ); + } + + Ok(Workcell { + properties: WorkcellProperties { + name: NameOfWorkcell(urdf.name.clone()), + }, + id: root_id, + frames, + visuals, + collisions, + inertias, + joints, + }) + } pub fn to_writer(&self, writer: W) -> serde_json::Result<()> { serde_json::ser::to_writer_pretty(writer, self) } @@ -280,6 +648,191 @@ impl Workcell { serde_json::ser::to_string_pretty(self) } + pub fn to_urdf(&self) -> Result { + let mut parent_to_visuals = HashMap::new(); + for (_, visual) in self.visuals.iter() { + let parent = visual.parent; + let visual = &visual.bundle; + let visual = urdf_rs::Visual { + name: Some(visual.name.clone()), + origin: visual.pose.into(), + geometry: visual.geometry.clone().into(), + material: None, + }; + parent_to_visuals + .entry(parent) + .or_insert_with(Vec::new) + .push(visual); + } + + let mut parent_to_collisions = HashMap::new(); + for (_, collision) in self.collisions.iter() { + let parent = collision.parent; + let collision = &collision.bundle; + let collision = urdf_rs::Collision { + name: Some(collision.name.clone()), + origin: collision.pose.into(), + geometry: collision.geometry.clone().into(), + }; + parent_to_collisions + .entry(parent) + .or_insert_with(Vec::new) + .push(collision); + } + + // If the workcell has a single frame child we can use the child as the base link. + // Otherwise, we will need to spawn a new base link to contain all the workcell children + let workcell_child_frames = self + .frames + .iter() + .filter(|(_, frame)| frame.parent == self.id); + let num_children = workcell_child_frames.clone().count(); + let frames = if num_children != 1 { + // TODO(luca) remove hardcoding of base link name, it might in some cases create + // duplicates + let mut frames = self.frames.clone(); + let dummy_frame = Frame { + anchor: Anchor::Pose3D(Pose { + rot: Rotation::Quat([0.0, 0.0, 0.0, 0.0]), + trans: [0.0, 0.0, 0.0], + }), + name: Some(NameInWorkcell(String::from("world"))), + mesh_constraint: None, + marker: FrameMarker, + }; + frames.insert( + self.id, + Parented { + // Root has no parent, use placeholder of max u32 + parent: u32::MAX, + bundle: dummy_frame, + }, + ); + frames + } else { + // Flatten the hierarchy by making the only child the new workcell base link + self.frames.clone() + }; + + let mut parent_to_inertials = HashMap::new(); + for (_, inertia) in self.inertias.iter() { + let parent = inertia.parent; + let inertia = &inertia.bundle; + let inertial = urdf_rs::Inertial::from(inertia); + parent_to_inertials.insert(parent, inertial); + } + + // TODO(luca) combine multiple frames without a joint inbetween into a single link. + // For now as soon as a joint is missing the hierarchy will be broken + let links = frames + .iter() + .map(|(frame_id, parented_frame)| { + let name = match &parented_frame.bundle.name { + Some(name) => name.0.clone(), + None => format!("frame_{}", &frame_id), + }; + + let inertial = parent_to_inertials.remove(&frame_id).unwrap_or_default(); + let collision = parent_to_collisions.remove(&frame_id).unwrap_or_default(); + let visual = parent_to_visuals.remove(&frame_id).unwrap_or_default(); + + urdf_rs::Link { + name, + inertial, + collision, + visual, + } + }) + .collect::>(); + + let joints = self + .joints + .iter() + .map(|(joint_id, parented_joint)| { + let joint_parent = parented_joint.parent; + let joint = &parented_joint.bundle; + // The pose of the joint is the pose of the frame that has it as its parent + let parent_frame = self + .frames + .get(&joint_parent) + .ok_or(WorkcellToUrdfError::BrokenReference(joint_parent))?; + let (child_frame_id, child_frame) = self + .frames + .iter() + .find(|(_, frame)| frame.parent == *joint_id) + .ok_or(WorkcellToUrdfError::BrokenReference(*joint_id))?; + let parent_name = parent_frame.bundle.name.clone().ok_or( + WorkcellToUrdfError::MissingJointFrameName(joint_parent, *joint_id), + )?; + let child_name = child_frame.bundle.name.clone().ok_or( + WorkcellToUrdfError::MissingJointFrameName(*child_frame_id, *joint_id), + )?; + let Anchor::Pose3D(pose) = child_frame.bundle.anchor else { + return Err(WorkcellToUrdfError::InvalidAnchorType( + child_frame.bundle.anchor.clone(), + )); + }; + let (joint_type, axis, limit) = match &joint.properties { + JointProperties::Fixed => ( + urdf_rs::JointType::Fixed, + urdf_rs::Axis::default(), + urdf_rs::JointLimit::default(), + ), + JointProperties::Revolute(joint) => ( + urdf_rs::JointType::Revolute, + (&joint.axis).into(), + (&joint.limits).into(), + ), + JointProperties::Prismatic(joint) => ( + urdf_rs::JointType::Prismatic, + (&joint.axis).into(), + (&joint.limits).into(), + ), + JointProperties::Continuous(joint) => ( + urdf_rs::JointType::Continuous, + (&joint.axis).into(), + (&joint.limits).into(), + ), + }; + Ok(urdf_rs::Joint { + name: joint.name.0.clone(), + joint_type, + origin: pose.into(), + parent: urdf_rs::LinkName { + link: parent_name.0, + }, + child: urdf_rs::LinkName { link: child_name.0 }, + axis, + limit, + dynamics: None, + mimic: None, + safety_controller: None, + }) + }) + .collect::, WorkcellToUrdfError>>()?; + + // TODO(luca) implement materials + let robot = urdf_rs::Robot { + name: self.properties.name.0.clone(), + links, + joints, + materials: vec![], + }; + Ok(robot) + } + + pub fn to_urdf_string(&self) -> Result { + let urdf = self.to_urdf()?; + urdf_rs::write_to_string(&urdf).map_err(|e| WorkcellToUrdfError::WriteToStringError(e)) + } + + pub fn to_urdf_writer(&self, mut writer: impl io::Write) -> Result<(), std::io::Error> { + let urdf = self + .to_urdf_string() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + writer.write_all(urdf.as_bytes()) + } + pub fn from_reader(reader: R) -> serde_json::Result { serde_json::de::from_reader(reader) } @@ -298,28 +851,28 @@ impl Workcell { derive(Component, Clone, Debug, Deref, DerefMut, TypeUuid, TypePath) )] #[cfg_attr(feature = "bevy", uuid = "fe707f9e-c6f3-11ed-afa1-0242ac120002")] -pub struct UrdfRoot(pub Robot); +pub struct UrdfRoot(pub urdf_rs::Robot); // TODO(luca) feature gate urdf support impl From<&urdf_rs::Geometry> for Geometry { fn from(geom: &urdf_rs::Geometry) -> Self { match geom { - urdf_rs::Geometry::Box { size } => Geometry::Primitive(MeshPrimitive::Box { + urdf_rs::Geometry::Box { size } => Geometry::Primitive(PrimitiveShape::Box { size: (**size).map(|f| f as f32), }), urdf_rs::Geometry::Cylinder { radius, length } => { - Geometry::Primitive(MeshPrimitive::Cylinder { + Geometry::Primitive(PrimitiveShape::Cylinder { radius: *radius as f32, length: *length as f32, }) } urdf_rs::Geometry::Capsule { radius, length } => { - Geometry::Primitive(MeshPrimitive::Capsule { + Geometry::Primitive(PrimitiveShape::Capsule { radius: *radius as f32, length: *length as f32, }) } - urdf_rs::Geometry::Sphere { radius } => Geometry::Primitive(MeshPrimitive::Sphere { + urdf_rs::Geometry::Sphere { radius } => Geometry::Primitive(PrimitiveShape::Sphere { radius: *radius as f32, }), urdf_rs::Geometry::Mesh { filename, scale } => { @@ -327,7 +880,7 @@ impl From<&urdf_rs::Geometry> for Geometry { .clone() .and_then(|s| Some(Vec3::from_array(s.map(|v| v as f32)))); Geometry::Mesh { - filename: filename.clone(), + source: (&**filename).into(), scale, } } @@ -335,21 +888,44 @@ impl From<&urdf_rs::Geometry> for Geometry { } } -impl From<&urdf_rs::Link> for Link { - fn from(link: &urdf_rs::Link) -> Self { +impl From<&urdf_rs::Inertia> for Moment { + fn from(inertia: &urdf_rs::Inertia) -> Self { Self { - name: NameInWorkcell(link.name.clone()), - inertial: Inertial { - origin: Pose { - trans: link.inertial.origin.xyz.0.map(|v| v as f32), - rot: Rotation::EulerExtrinsicXYZ( - link.inertial.origin.rpy.map(|v| Angle::Rad(v as f32)), - ), - }, - mass: Mass(link.inertial.mass.value as f32), - inertia: Inertia::default(), + ixx: inertia.ixx as f32, + ixy: inertia.ixy as f32, + ixz: inertia.ixz as f32, + iyy: inertia.iyy as f32, + iyz: inertia.iyz as f32, + izz: inertia.izz as f32, + } + } +} + +impl From<&urdf_rs::Inertial> for Inertia { + fn from(inertial: &urdf_rs::Inertial) -> Self { + Self { + center: (&inertial.origin).into(), + mass: Mass(inertial.mass.value as f32), + moment: (&inertial.inertia).into(), + } + } +} + +impl From<&Inertia> for urdf_rs::Inertial { + fn from(inertia: &Inertia) -> Self { + Self { + origin: inertia.center.into(), + mass: urdf_rs::Mass { + value: inertia.mass.0 as f64, + }, + inertia: urdf_rs::Inertia { + ixx: inertia.moment.ixx as f64, + ixy: inertia.moment.ixy as f64, + ixz: inertia.moment.ixz as f64, + iyy: inertia.moment.iyy as f64, + iyz: inertia.moment.iyz as f64, + izz: inertia.moment.izz as f64, }, - marker: LinkMarker, } } } @@ -360,12 +936,10 @@ impl WorkcellModel { name: &Option, geometry: &urdf_rs::Geometry, ) -> Self { - let trans = pose.xyz.map(|t| t as f32); - let rot = Rotation::EulerExtrinsicXYZ(pose.rpy.map(|t| Angle::Rad(t as f32))); WorkcellModel { name: name.clone().unwrap_or_default(), geometry: geometry.into(), - pose: Pose { trans, rot }, + pose: pose.into(), } } } @@ -381,3 +955,192 @@ impl From<&urdf_rs::Collision> for WorkcellModel { WorkcellModel::from_urdf_data(&collision.origin, &collision.name, &collision.geometry) } } + +#[cfg(test)] +mod tests { + use super::*; + use float_eq::{assert_float_eq, float_eq}; + + fn frame_by_name( + frames: &BTreeMap>, + name: &str, + ) -> Option<(u32, Parented)> { + frames + .iter() + .find(|(_, parented_frame)| { + parented_frame.bundle.name == Some(NameInWorkcell(name.to_string())) + }) + .map(|(id, f)| (*id, f.clone())) + } + + fn element_by_parent( + models: &BTreeMap>, + parent: u32, + ) -> Option<(u32, Parented)> { + models + .iter() + .find(|(_, parented_element)| parented_element.parent == parent) + .map(|(id, e)| (*id, e.clone())) + } + + fn is_pose_eq(p1: &Pose, p2: &Pose) -> bool { + if !p1 + .trans + .iter() + .zip(p2.trans.iter()) + .map(|(t1, t2)| float_eq!(t1, t2, abs <= 1e-6)) + .all(|eq| eq) + { + return false; + } + match ( + p1.rot.as_euler_extrinsic_xyz(), + p2.rot.as_euler_extrinsic_xyz(), + ) { + (Rotation::EulerExtrinsicXYZ(r1), Rotation::EulerExtrinsicXYZ(r2)) => r1 + .iter() + .zip(r2.iter()) + .map(|(a1, a2)| float_eq!(a1.radians(), a2.radians(), abs <= 1e-6)) + .all(|eq| eq), + _ => false, + } + } + + fn is_inertia_eq(i1: &Inertia, i2: &Inertia) -> bool { + is_pose_eq(&i1.center, &i2.center) + && float_eq!(i1.mass.0, i2.mass.0, abs <= 1e6) + && float_eq!(i1.moment.ixx, i2.moment.ixx, abs <= 1e6) + && float_eq!(i1.moment.ixy, i2.moment.ixy, abs <= 1e6) + && float_eq!(i1.moment.ixz, i2.moment.ixz, abs <= 1e6) + && float_eq!(i1.moment.iyy, i2.moment.iyy, abs <= 1e6) + && float_eq!(i1.moment.iyz, i2.moment.iyz, abs <= 1e6) + && float_eq!(i1.moment.izz, i2.moment.izz, abs <= 1e6) + } + + #[test] + fn urdf_roundtrip() { + let urdf = urdf_rs::read_file("test/07-physics.urdf").unwrap(); + let workcell = Workcell::from_urdf(&urdf).unwrap(); + assert_eq!(workcell.visuals.len(), 16); + assert_eq!(workcell.collisions.len(), 16); + assert_eq!(workcell.frames.len(), 16); + assert_eq!(workcell.joints.len(), 15); + assert_eq!(workcell.properties.name.0, "physics"); + // Test that we convert poses from joints to frames + let (right_leg_id, right_leg) = frame_by_name(&workcell.frames, "right_leg").unwrap(); + let target_right_leg_pose = Pose { + trans: [0.0, -0.22, 0.25], + rot: Default::default(), + }; + assert!(right_leg + .bundle + .anchor + .is_close(&Anchor::Pose3D(target_right_leg_pose), 1e-6)); + // Test that we can parse parenthood and properties of visuals and collisions correctly + let (_, right_leg_visual) = element_by_parent(&workcell.visuals, right_leg_id).unwrap(); + let target_right_leg_model_pose = Pose { + trans: [0.0, 0.0, -0.3], + rot: Rotation::EulerExtrinsicXYZ([ + Angle::Rad(0.0), + Angle::Rad(1.57075), + Angle::Rad(0.0), + ]), + }; + assert!(is_pose_eq( + &right_leg_visual.bundle.pose, + &target_right_leg_model_pose + )); + assert!(matches!( + right_leg_visual.bundle.geometry, + Geometry::Primitive(PrimitiveShape::Box { .. }) + )); + let (_, right_leg_collision) = + element_by_parent(&workcell.collisions, right_leg_id).unwrap(); + assert!(is_pose_eq( + &right_leg_collision.bundle.pose, + &target_right_leg_model_pose + )); + assert!(matches!( + right_leg_collision.bundle.geometry, + Geometry::Primitive(PrimitiveShape::Box { .. }) + )); + // Test inertia parenthood and parsing + let (_, right_leg_inertia) = element_by_parent(&workcell.inertias, right_leg_id).unwrap(); + assert_float_eq!(right_leg_inertia.bundle.mass.0, 10.0, abs <= 1e6); + let target_right_leg_inertia = Inertia { + center: Pose::default(), + mass: Mass(10.0), + moment: Moment { + ixx: 1.0, + ixy: 0.0, + ixz: 0.0, + iyy: 1.0, + iyz: 0.0, + izz: 1.0, + }, + }; + assert!(is_inertia_eq( + &right_leg_inertia.bundle, + &target_right_leg_inertia + )); + // Test joint parenthood and parsing + let (_, right_leg_joint) = element_by_parent(&workcell.joints, right_leg_id).unwrap(); + assert!(matches!( + right_leg_joint.bundle.properties, + JointProperties::Fixed + )); + assert_eq!( + right_leg_joint.bundle.name, + NameInWorkcell("right_base_joint".to_string()) + ); + // Test that the new urdf contains the same data + let new_urdf = workcell.to_urdf().unwrap(); + assert_eq!(new_urdf.name, "physics"); + assert_eq!(new_urdf.links.len(), 16); + assert_eq!(new_urdf.joints.len(), 15); + // Check that link information is preserved + let right_leg_link = new_urdf + .links + .iter() + .find(|l| l.name == "right_leg") + .unwrap(); + assert!(is_inertia_eq( + &(&right_leg_link.inertial).into(), + &target_right_leg_inertia + )); + assert_eq!(right_leg_link.visual.len(), 1); + assert_eq!(right_leg_link.collision.len(), 1); + let right_leg_visual = right_leg_link.visual.get(0).unwrap(); + let right_leg_collision = right_leg_link.collision.get(0).unwrap(); + assert!(is_pose_eq( + &(&right_leg_visual.origin).into(), + &target_right_leg_model_pose + )); + assert!(is_pose_eq( + &(&right_leg_collision.origin).into(), + &target_right_leg_model_pose + )); + assert!(matches!( + right_leg_visual.geometry, + urdf_rs::Geometry::Box { .. } + )); + assert!(matches!( + right_leg_collision.geometry, + urdf_rs::Geometry::Box { .. } + )); + // Check that joint origin is preserved + let right_leg_joint = new_urdf + .joints + .iter() + .find(|l| l.name == "base_to_right_leg") + .unwrap(); + assert!(is_pose_eq( + &(&right_leg_joint.origin).into(), + &target_right_leg_pose + )); + assert!(matches!( + right_leg_joint.joint_type, + urdf_rs::JointType::Fixed + )); + } +} diff --git a/rmf_site_format/test/07-physics.urdf b/rmf_site_format/test/07-physics.urdf new file mode 100644 index 00000000..95c4419e --- /dev/null +++ b/rmf_site_format/test/07-physics.urdf @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +