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}