parry2d/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)]
105pub struct SortedPair<T: PartialOrd>([T; 2]);
106
107impl<T: PartialOrd> SortedPair<T> {
108    /// Sorts two elements in increasing order into a new pair.
109    ///
110    /// # Arguments
111    ///
112    /// * `element1` - The first element
113    /// * `element2` - The second element
114    ///
115    /// # Returns
116    ///
117    /// A `SortedPair` where the smaller element comes first.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
123    /// # use parry2d::utils::SortedPair;
124    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
125    /// # use parry3d::utils::SortedPair;
126    ///
127    /// let pair = SortedPair::new(10, 5);
128    ///
129    /// // Elements are automatically sorted
130    /// assert_eq!(pair.0, 5);
131    /// assert_eq!(pair.1, 10);
132    /// # }
133    /// # }
134    /// ```
135    pub fn new(element1: T, element2: T) -> Self {
136        if element1 > element2 {
137            SortedPair([element2, element1])
138        } else {
139            SortedPair([element1, element2])
140        }
141    }
142}
143
144impl<T: PartialOrd> Deref for SortedPair<T> {
145    type Target = (T, T);
146
147    fn deref(&self) -> &(T, T) {
148        unsafe { mem::transmute(self) }
149    }
150}
151
152// TODO: can we avoid these manual impls of Hash/PartialEq/Eq for the archived types?
153#[cfg(feature = "rkyv")]
154impl<T: rkyv::Archive + PartialOrd> core::hash::Hash for ArchivedSortedPair<T>
155where
156    [<T as rkyv::Archive>::Archived; 2]: core::hash::Hash,
157{
158    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
159        self.0.hash(state)
160    }
161}
162
163#[cfg(feature = "rkyv")]
164impl<T: rkyv::Archive + PartialOrd> PartialEq for ArchivedSortedPair<T>
165where
166    [<T as rkyv::Archive>::Archived; 2]: PartialEq,
167{
168    fn eq(&self, other: &Self) -> bool {
169        self.0 == other.0
170    }
171}
172
173#[cfg(feature = "rkyv")]
174impl<T: rkyv::Archive + PartialOrd> Eq for ArchivedSortedPair<T> where
175    [<T as rkyv::Archive>::Archived; 2]: Eq
176{
177}