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}