parry2d_f64/utils/
sorted_pair.rs

1use core::cmp::PartialOrd;
2use core::mem;
3use core::ops::Deref;
4
5/// A pair of elements sorted in increasing order.
6///
7/// This structure guarantees that the first element is always less than or equal to
8/// the second element. It is useful for representing edges, connections, or any
9/// unordered pair where you want canonical ordering (e.g., ensuring that edge (A, B)
10/// and edge (B, A) are treated as the same edge).
11///
12/// The sorted pair implements `Deref` to `(T, T)` for convenient access to the elements.
13///
14/// # Examples
15///
16/// ## Creating a Sorted Pair
17///
18/// ```
19/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
20/// # use parry2d::utils::SortedPair;
21/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
22/// # use parry3d::utils::SortedPair;
23///
24/// // Create a pair - the elements will be sorted automatically
25/// let pair1 = SortedPair::new(5, 2);
26/// let pair2 = SortedPair::new(2, 5);
27///
28/// // Both pairs are equal because they contain the same sorted elements
29/// assert_eq!(pair1, pair2);
30///
31/// // Access elements via dereferencing
32/// assert_eq!(pair1.0, 2);
33/// assert_eq!(pair1.1, 5);
34/// # }
35/// # }
36/// ```
37///
38/// ## Using as HashMap Keys
39///
40/// ```
41/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
42/// # use parry2d::utils::SortedPair;
43/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
44/// # use parry3d::utils::SortedPair;
45/// use std::collections::HashMap;
46///
47/// let mut edge_weights = HashMap::new();
48///
49/// // These represent the same edge, so they'll map to the same entry
50/// edge_weights.insert(SortedPair::new(1, 3), 10.0);
51/// edge_weights.insert(SortedPair::new(3, 1), 20.0); // Overwrites previous
52///
53/// assert_eq!(edge_weights.len(), 1);
54/// assert_eq!(edge_weights.get(&SortedPair::new(1, 3)), Some(&20.0));
55/// # }
56/// # }
57/// ```
58///
59/// ## Representing Graph Edges
60///
61/// ```
62/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
63/// # use parry2d::utils::SortedPair;
64/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
65/// # use parry3d::utils::SortedPair;
66///
67/// // Represent undirected edges in a graph
68/// let edges = vec![
69///     SortedPair::new(0, 1),  // Edge between vertices 0 and 1
70///     SortedPair::new(1, 2),  // Edge between vertices 1 and 2
71///     SortedPair::new(2, 0),  // Edge between vertices 2 and 0
72/// ];
73///
74/// // Check if a specific edge exists (order doesn't matter)
75/// let query_edge = SortedPair::new(2, 1);
76/// assert!(edges.contains(&query_edge));
77/// # }
78/// # }
79/// ```
80///
81/// ## Ordering
82///
83/// ```
84/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
85/// # use parry2d::utils::SortedPair;
86/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
87/// # use parry3d::utils::SortedPair;
88///
89/// let pair1 = SortedPair::new(1, 5);
90/// let pair2 = SortedPair::new(2, 3);
91/// let pair3 = SortedPair::new(1, 6);
92///
93/// // Pairs are compared lexicographically (first element, then second)
94/// assert!(pair1 < pair2);  // (1, 5) < (2, 3)
95/// assert!(pair1 < pair3);  // (1, 5) < (1, 6)
96/// # }
97/// # }
98/// ```
99#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
100#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
101#[cfg_attr(
102    feature = "rkyv",
103    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
104    archive(check_bytes)
105)]
106pub struct SortedPair<T: PartialOrd>([T; 2]);
107
108impl<T: PartialOrd> SortedPair<T> {
109    /// Sorts two elements in increasing order into a new pair.
110    ///
111    /// # Arguments
112    ///
113    /// * `element1` - The first element
114    /// * `element2` - The second element
115    ///
116    /// # Returns
117    ///
118    /// A `SortedPair` where the smaller element comes first.
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
124    /// # use parry2d::utils::SortedPair;
125    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
126    /// # use parry3d::utils::SortedPair;
127    ///
128    /// let pair = SortedPair::new(10, 5);
129    ///
130    /// // Elements are automatically sorted
131    /// assert_eq!(pair.0, 5);
132    /// assert_eq!(pair.1, 10);
133    /// # }
134    /// # }
135    /// ```
136    pub fn new(element1: T, element2: T) -> Self {
137        if element1 > element2 {
138            SortedPair([element2, element1])
139        } else {
140            SortedPair([element1, element2])
141        }
142    }
143}
144
145impl<T: PartialOrd> Deref for SortedPair<T> {
146    type Target = (T, T);
147
148    fn deref(&self) -> &(T, T) {
149        unsafe { mem::transmute(self) }
150    }
151}
152
153// TODO: can we avoid these manual impls of Hash/PartialEq/Eq for the archived types?
154#[cfg(feature = "rkyv")]
155impl<T: rkyv::Archive + PartialOrd> core::hash::Hash for ArchivedSortedPair<T>
156where
157    [<T as rkyv::Archive>::Archived; 2]: core::hash::Hash,
158{
159    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
160        self.0.hash(state)
161    }
162}
163
164#[cfg(feature = "rkyv")]
165impl<T: rkyv::Archive + PartialOrd> PartialEq for ArchivedSortedPair<T>
166where
167    [<T as rkyv::Archive>::Archived; 2]: PartialEq,
168{
169    fn eq(&self, other: &Self) -> bool {
170        self.0 == other.0
171    }
172}
173
174#[cfg(feature = "rkyv")]
175impl<T: rkyv::Archive + PartialOrd> Eq for ArchivedSortedPair<T> where
176    [<T as rkyv::Archive>::Archived; 2]: Eq
177{
178}