diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f6007fcf..122dd4dbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix crash when removing a multibody joint, or a rigid-body with a multipody-joint attached to it. - Fix crash when inserting multibody joints in an arbitrary order (instead of incrementally from root to leaf). +- Fix `BroadphaseMultiSap` not being able to serialize correctly with serde_json. ### Added @@ -14,6 +15,7 @@ - Divided by two the value of each `QueryFilterFlags` variant so that the smallest one is 1 instead of 2 (fixes a bug in rapier.js). +- `BroadphaseMultiSap` now serializes its `colliders_proxy_ids` as `Vec[(ColliderHandle, BroadPhaseProxyIndex)]`. ## v0.21.0 (23 June 2024) diff --git a/crates/rapier2d-f64/Cargo.toml b/crates/rapier2d-f64/Cargo.toml index 2606f7d23..e5347a5d4 100644 --- a/crates/rapier2d-f64/Cargo.toml +++ b/crates/rapier2d-f64/Cargo.toml @@ -7,7 +7,13 @@ documentation = "https://docs.rs/rapier2d" homepage = "https://rapier.rs" repository = "https://github.com/dimforge/rapier" readme = "README.md" -categories = ["science", "game-development", "mathematics", "simulation", "wasm"] +categories = [ + "science", + "game-development", + "mathematics", + "simulation", + "wasm", +] keywords = ["physics", "dynamics", "rigid", "real-time", "impulse_joints"] license = "Apache-2.0" edition = "2021" @@ -26,7 +32,13 @@ simd-nightly = ["simba/portable_simd", "simd-is-enabled"] # enabled with the "simd-stable" or "simd-nightly" feature. simd-is-enabled = ["dep:vec_map"] wasm-bindgen = ["instant/wasm-bindgen"] -serde-serialize = ["nalgebra/serde-serialize", "parry2d-f64/serde-serialize", "dep:serde", "bit-vec/serde", "arrayvec/serde"] +serde-serialize = [ + "nalgebra/serde-serialize", + "parry2d-f64/serde-serialize", + "dep:serde", + "bit-vec/serde", + "arrayvec/serde", +] enhanced-determinism = ["simba/libm_force", "parry2d-f64/enhanced-determinism"] debug-render = [] profiler = ["dep:instant"] # Enables the internal profiler. @@ -70,5 +82,6 @@ thiserror = "1" [dev-dependencies] bincode = "1" +serde_json = "1" serde = { version = "1", features = ["derive"] } oorandom = { version = "11", default-features = false } diff --git a/crates/rapier2d/Cargo.toml b/crates/rapier2d/Cargo.toml index ec363c1fe..e8e1ad26a 100644 --- a/crates/rapier2d/Cargo.toml +++ b/crates/rapier2d/Cargo.toml @@ -7,7 +7,13 @@ documentation = "https://docs.rs/rapier2d" homepage = "https://rapier.rs" repository = "https://github.com/dimforge/rapier" readme = "README.md" -categories = ["science", "game-development", "mathematics", "simulation", "wasm"] +categories = [ + "science", + "game-development", + "mathematics", + "simulation", + "wasm", +] keywords = ["physics", "dynamics", "rigid", "real-time", "impulse_joints"] license = "Apache-2.0" edition = "2021" @@ -26,7 +32,13 @@ simd-nightly = ["simba/portable_simd", "simd-is-enabled"] # enabled with the "simd-stable" or "simd-nightly" feature. simd-is-enabled = ["dep:vec_map"] wasm-bindgen = ["instant/wasm-bindgen"] -serde-serialize = ["nalgebra/serde-serialize", "parry2d/serde-serialize", "dep:serde", "bit-vec/serde", "arrayvec/serde"] +serde-serialize = [ + "nalgebra/serde-serialize", + "parry2d/serde-serialize", + "dep:serde", + "bit-vec/serde", + "arrayvec/serde", +] enhanced-determinism = ["simba/libm_force", "parry2d/enhanced-determinism"] debug-render = [] profiler = ["dep:instant"] # Enables the internal profiler. @@ -70,5 +82,6 @@ thiserror = "1" [dev-dependencies] bincode = "1" +serde_json = "1" serde = { version = "1", features = ["derive"] } -oorandom = { version = "11", default-features = false } \ No newline at end of file +oorandom = { version = "11", default-features = false } diff --git a/crates/rapier3d-f64/Cargo.toml b/crates/rapier3d-f64/Cargo.toml index 17390e73f..c1d9ce7e2 100644 --- a/crates/rapier3d-f64/Cargo.toml +++ b/crates/rapier3d-f64/Cargo.toml @@ -7,7 +7,13 @@ documentation = "https://docs.rs/rapier3d" homepage = "https://rapier.rs" repository = "https://github.com/dimforge/rapier" readme = "README.md" -categories = ["science", "game-development", "mathematics", "simulation", "wasm"] +categories = [ + "science", + "game-development", + "mathematics", + "simulation", + "wasm", +] keywords = ["physics", "dynamics", "rigid", "real-time", "impulse_joints"] license = "Apache-2.0" edition = "2021" @@ -21,12 +27,21 @@ dim3 = [] f64 = [] parallel = ["dep:rayon"] simd-stable = ["parry3d-f64/simd-stable", "simba/wide", "simd-is-enabled"] -simd-nightly = ["parry3d-f64/simd-nightly", "simba/portable_simd", "simd-is-enabled"] +simd-nightly = [ + "parry3d-f64/simd-nightly", + "simba/portable_simd", + "simd-is-enabled", +] # Do not enable this feature directly. It is automatically # enabled with the "simd-stable" or "simd-nightly" feature. simd-is-enabled = ["dep:vec_map"] wasm-bindgen = ["instant/wasm-bindgen"] -serde-serialize = ["nalgebra/serde-serialize", "parry3d-f64/serde-serialize", "dep:serde", "bit-vec/serde"] +serde-serialize = [ + "nalgebra/serde-serialize", + "parry3d-f64/serde-serialize", + "dep:serde", + "bit-vec/serde", +] enhanced-determinism = ["simba/libm_force", "parry3d-f64/enhanced-determinism"] debug-render = [] profiler = ["dep:instant"] # Enables the internal profiler. @@ -70,5 +85,6 @@ thiserror = "1" [dev-dependencies] bincode = "1" +serde_json = "1" serde = { version = "1", features = ["derive"] } oorandom = { version = "11", default-features = false } diff --git a/crates/rapier3d/Cargo.toml b/crates/rapier3d/Cargo.toml index 3f45d7bad..03947ef9b 100644 --- a/crates/rapier3d/Cargo.toml +++ b/crates/rapier3d/Cargo.toml @@ -7,7 +7,13 @@ documentation = "https://docs.rs/rapier3d" homepage = "https://rapier.rs" repository = "https://github.com/dimforge/rapier" readme = "README.md" -categories = ["science", "game-development", "mathematics", "simulation", "wasm"] +categories = [ + "science", + "game-development", + "mathematics", + "simulation", + "wasm", +] keywords = ["physics", "dynamics", "rigid", "real-time", "impulse_joints"] license = "Apache-2.0" edition = "2021" @@ -21,12 +27,21 @@ dim3 = [] f32 = [] parallel = ["dep:rayon"] simd-stable = ["parry3d/simd-stable", "simba/wide", "simd-is-enabled"] -simd-nightly = ["parry3d/simd-nightly", "simba/portable_simd", "simd-is-enabled"] +simd-nightly = [ + "parry3d/simd-nightly", + "simba/portable_simd", + "simd-is-enabled", +] # Do not enable this feature directly. It is automatically # enabled with the "simd-stable" or "simd-nightly" feature. simd-is-enabled = ["dep:vec_map"] wasm-bindgen = ["instant/wasm-bindgen"] -serde-serialize = ["nalgebra/serde-serialize", "parry3d/serde-serialize", "dep:serde", "bit-vec/serde"] +serde-serialize = [ + "nalgebra/serde-serialize", + "parry3d/serde-serialize", + "dep:serde", + "bit-vec/serde", +] enhanced-determinism = ["simba/libm_force", "parry3d/enhanced-determinism"] debug-render = [] profiler = ["dep:instant"] # Enables the internal profiler. @@ -70,5 +85,6 @@ thiserror = "1" [dev-dependencies] bincode = "1" +serde_json = "1" serde = { version = "1", features = ["derive"] } oorandom = { version = "11", default-features = false } diff --git a/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs index 87b2269f3..1d0f2a4ce 100644 --- a/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs +++ b/src/geometry/broad_phase_multi_sap/broad_phase_multi_sap.rs @@ -90,6 +90,13 @@ pub struct BroadPhaseMultiSap { // Another alternative would be to remove ColliderProxyId and // just use a Coarena. But this seems like it could use too // much memory. + #[cfg_attr( + feature = "serde-serialize", + serde( + serialize_with = "crate::utils::serde::serialize_to_vec_tuple", + deserialize_with = "crate::utils::serde::deserialize_from_vec_tuple" + ) + )] colliders_proxy_ids: HashMap, #[cfg_attr(feature = "serde-serialize", serde(skip))] region_pool: SAPRegionPool, // To avoid repeated allocations. diff --git a/src/utils.rs b/src/utils.rs index 102c85f2d..84acb21de 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -667,3 +667,75 @@ pub fn smallest_abs_diff_between_angles(a: N, b: N) -> N { let s_err_is_smallest = s_err.simd_abs().simd_lt(s_err_complement.simd_abs()); s_err.select(s_err_is_smallest, s_err_complement) } + +/// Helpers around serialization. +#[cfg(feature = "serde-serialize")] +pub mod serde { + use serde::{Deserialize, Serialize}; + use std::iter::FromIterator; + + /// Serializes to a `Vec<(K, V)>`. + /// + /// Useful for [`std::collections::HashMap`] with a non-string key, + /// which is unsupported by [`serde_json`]. + pub fn serialize_to_vec_tuple< + 'a, + S: serde::Serializer, + T: IntoIterator, + K: Serialize + 'a, + V: Serialize + 'a, + >( + target: T, + s: S, + ) -> Result { + let container: Vec<_> = target.into_iter().collect(); + serde::Serialize::serialize(&container, s) + } + + /// Deserializes from a `Vec<(K, V)>`. + /// + /// Useful for [`std::collections::HashMap`] with a non-string key, + /// which is unsupported by [`serde_json`]. + pub fn deserialize_from_vec_tuple< + 'de, + D: serde::Deserializer<'de>, + T: FromIterator<(K, V)>, + K: Deserialize<'de>, + V: Deserialize<'de>, + >( + d: D, + ) -> Result { + let hashmap_as_vec: Vec<(K, V)> = Deserialize::deserialize(d)?; + Ok(T::from_iter(hashmap_as_vec)) + } + + #[cfg(test)] + mod test { + use std::collections::HashMap; + + /// This test uses serde_json because json doesn't support non string + /// keys in hashmaps, which requires a custom serialization. + #[test] + fn serde_json_hashmap() { + #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] + struct Test { + #[cfg_attr( + feature = "serde-serialize", + serde( + serialize_with = "crate::utils::serde::serialize_to_vec_tuple", + deserialize_with = "crate::utils::serde::deserialize_from_vec_tuple" + ) + )] + pub map: HashMap, + } + + let s = Test { + map: [(42, "Forty-Two".to_string())].into(), + }; + let j = serde_json::to_string(&s).unwrap(); + assert_eq!(&j, "{\"map\":[[42,\"Forty-Two\"]]}"); + let p: Test = serde_json::from_str(&j).unwrap(); + assert_eq!(&p, &s); + } + } +}