1use std::fmt::Debug;
4use std::hash::Hash;
5
6use bevy::prelude::*;
7use serde::{Deserialize, Serialize};
8
9use super::DualAxisProcessor;
10use crate::input_processing::single_axis::*;
11
12#[doc(alias("SquareBounds", "AxialBounds"))]
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
46#[must_use]
47pub struct DualAxisBounds {
48 pub bounds_x: AxisBounds,
50
51 pub bounds_y: AxisBounds,
53}
54
55impl DualAxisBounds {
56 pub const FULL_RANGE: Self = AxisBounds::FULL_RANGE.extend_dual();
58
59 #[inline]
69 pub fn new((x_min, x_max): (f32, f32), (y_min, y_max): (f32, f32)) -> Self {
70 Self {
71 bounds_x: AxisBounds::new(x_min, x_max),
72 bounds_y: AxisBounds::new(y_min, y_max),
73 }
74 }
75
76 #[inline]
86 pub fn all(min: f32, max: f32) -> Self {
87 let range = (min, max);
88 Self::new(range, range)
89 }
90
91 #[inline]
101 pub fn only_x(min: f32, max: f32) -> Self {
102 Self {
103 bounds_x: AxisBounds::new(min, max),
104 ..Self::FULL_RANGE
105 }
106 }
107
108 #[inline]
118 pub fn only_y(min: f32, max: f32) -> Self {
119 Self {
120 bounds_y: AxisBounds::new(min, max),
121 ..Self::FULL_RANGE
122 }
123 }
124
125 #[doc(alias = "magnitude")]
135 #[inline]
136 pub fn symmetric(threshold_x: f32, threshold_y: f32) -> Self {
137 Self {
138 bounds_x: AxisBounds::symmetric(threshold_x),
139 bounds_y: AxisBounds::symmetric(threshold_y),
140 }
141 }
142
143 #[doc(alias = "magnitude_all")]
153 #[inline]
154 pub fn symmetric_all(threshold: f32) -> Self {
155 Self::symmetric(threshold, threshold)
156 }
157
158 #[doc(alias = "magnitude_only_x")]
168 #[inline]
169 pub fn symmetric_only_x(threshold: f32) -> Self {
170 Self {
171 bounds_x: AxisBounds::symmetric(threshold),
172 ..Self::FULL_RANGE
173 }
174 }
175
176 #[doc(alias = "magnitude_only_y")]
186 #[inline]
187 pub fn symmetric_only_y(threshold: f32) -> Self {
188 Self {
189 bounds_y: AxisBounds::symmetric(threshold),
190 ..Self::FULL_RANGE
191 }
192 }
193
194 #[inline]
196 pub const fn at_least(x_min: f32, y_min: f32) -> Self {
197 Self {
198 bounds_x: AxisBounds::at_least(x_min),
199 bounds_y: AxisBounds::at_least(y_min),
200 }
201 }
202
203 #[inline]
205 pub const fn at_least_all(min: f32) -> Self {
206 AxisBounds::at_least(min).extend_dual()
207 }
208
209 #[inline]
211 pub const fn at_least_only_x(min: f32) -> Self {
212 Self {
213 bounds_x: AxisBounds::at_least(min),
214 ..Self::FULL_RANGE
215 }
216 }
217
218 #[inline]
220 pub const fn at_least_only_y(min: f32) -> Self {
221 Self {
222 bounds_y: AxisBounds::at_least(min),
223 ..Self::FULL_RANGE
224 }
225 }
226
227 #[inline]
229 pub const fn at_most(x_max: f32, y_max: f32) -> Self {
230 Self {
231 bounds_x: AxisBounds::at_most(x_max),
232 bounds_y: AxisBounds::at_most(y_max),
233 }
234 }
235
236 #[inline]
238 pub const fn at_most_all(max: f32) -> Self {
239 AxisBounds::at_most(max).extend_dual()
240 }
241
242 #[inline]
244 pub const fn at_most_only_x(max: f32) -> Self {
245 Self {
246 bounds_x: AxisBounds::at_most(max),
247 ..Self::FULL_RANGE
248 }
249 }
250
251 #[inline]
253 pub const fn at_most_only_y(max: f32) -> Self {
254 Self {
255 bounds_y: AxisBounds::at_most(max),
256 ..Self::FULL_RANGE
257 }
258 }
259
260 #[inline]
262 pub fn bounds(&self) -> (AxisBounds, AxisBounds) {
263 (self.bounds_x, self.bounds_y)
264 }
265
266 #[inline]
268 pub fn bounds_x(&self) -> AxisBounds {
269 self.bounds().0
270 }
271
272 #[inline]
274 pub fn bounds_y(&self) -> AxisBounds {
275 self.bounds().1
276 }
277
278 #[must_use]
280 #[inline]
281 pub fn contains(&self, input_value: Vec2) -> BVec2 {
282 BVec2::new(
283 self.bounds_x.contains(input_value.x),
284 self.bounds_y.contains(input_value.y),
285 )
286 }
287
288 #[must_use]
290 #[inline]
291 pub fn clamp(&self, input_value: Vec2) -> Vec2 {
292 Vec2::new(
293 self.bounds_x.clamp(input_value.x),
294 self.bounds_y.clamp(input_value.y),
295 )
296 }
297}
298
299impl Default for DualAxisBounds {
300 #[inline]
302 fn default() -> Self {
303 AxisBounds::default().extend_dual()
304 }
305}
306
307impl From<DualAxisBounds> for DualAxisProcessor {
308 fn from(value: DualAxisBounds) -> Self {
309 Self::ValueBounds(value)
310 }
311}
312
313impl AxisBounds {
314 #[inline]
316 pub const fn extend_dual(self) -> DualAxisBounds {
317 DualAxisBounds {
318 bounds_x: self,
319 bounds_y: self,
320 }
321 }
322
323 #[inline]
325 pub const fn extend_dual_only_x(self) -> DualAxisBounds {
326 DualAxisBounds {
327 bounds_x: self,
328 ..DualAxisBounds::FULL_RANGE
329 }
330 }
331
332 #[inline]
334 pub const fn extend_dual_only_y(self) -> DualAxisBounds {
335 DualAxisBounds {
336 bounds_y: self,
337 ..DualAxisBounds::FULL_RANGE
338 }
339 }
340
341 #[inline]
343 pub const fn extend_dual_with_x(self, bounds_x: Self) -> DualAxisBounds {
344 DualAxisBounds {
345 bounds_x,
346 bounds_y: self,
347 }
348 }
349
350 #[inline]
352 pub const fn extend_dual_with_y(self, bounds_y: Self) -> DualAxisBounds {
353 DualAxisBounds {
354 bounds_x: self,
355 bounds_y,
356 }
357 }
358}
359
360impl From<AxisBounds> for DualAxisProcessor {
361 fn from(bounds: AxisBounds) -> Self {
362 Self::ValueBounds(bounds.extend_dual())
363 }
364}
365
366#[doc(alias("CrossExclusion", "AxialExclusion"))]
399#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
400#[must_use]
401pub struct DualAxisExclusion {
402 pub exclusion_x: AxisExclusion,
404
405 pub exclusion_y: AxisExclusion,
407}
408
409impl DualAxisExclusion {
410 pub const ZERO: Self = AxisExclusion::ZERO.extend_dual();
412
413 #[inline]
423 pub fn new(
424 (x_negative_max, x_positive_min): (f32, f32),
425 (y_negative_max, y_positive_min): (f32, f32),
426 ) -> Self {
427 Self {
428 exclusion_x: AxisExclusion::new(x_negative_max, x_positive_min),
429 exclusion_y: AxisExclusion::new(y_negative_max, y_positive_min),
430 }
431 }
432
433 #[inline]
443 pub fn all(negative_max: f32, positive_min: f32) -> Self {
444 AxisExclusion::new(negative_max, positive_min).extend_dual()
445 }
446
447 #[inline]
457 pub fn only_x(negative_max: f32, positive_min: f32) -> Self {
458 Self {
459 exclusion_x: AxisExclusion::new(negative_max, positive_min),
460 ..Self::ZERO
461 }
462 }
463
464 #[inline]
474 pub fn only_y(negative_max: f32, positive_min: f32) -> Self {
475 Self {
476 exclusion_y: AxisExclusion::new(negative_max, positive_min),
477 ..Self::ZERO
478 }
479 }
480
481 #[doc(alias = "magnitude")]
491 #[inline]
492 pub fn symmetric(threshold_x: f32, threshold_y: f32) -> Self {
493 Self {
494 exclusion_x: AxisExclusion::symmetric(threshold_x),
495 exclusion_y: AxisExclusion::symmetric(threshold_y),
496 }
497 }
498
499 #[doc(alias = "magnitude_all")]
509 #[inline]
510 pub fn symmetric_all(threshold: f32) -> Self {
511 AxisExclusion::symmetric(threshold).extend_dual()
512 }
513
514 #[doc(alias = "magnitude_only_x")]
524 #[inline]
525 pub fn symmetric_only_x(threshold: f32) -> Self {
526 Self {
527 exclusion_x: AxisExclusion::symmetric(threshold),
528 ..Self::ZERO
529 }
530 }
531
532 #[doc(alias = "magnitude_only_y")]
542 #[inline]
543 pub fn symmetric_only_y(threshold: f32) -> Self {
544 Self {
545 exclusion_y: AxisExclusion::symmetric(threshold),
546 ..Self::ZERO
547 }
548 }
549
550 #[inline]
560 pub fn only_positive(x_positive_min: f32, y_positive_min: f32) -> Self {
561 Self {
562 exclusion_x: AxisExclusion::only_positive(x_positive_min),
563 exclusion_y: AxisExclusion::only_positive(y_positive_min),
564 }
565 }
566
567 #[inline]
577 pub fn only_positive_all(positive_min: f32) -> Self {
578 AxisExclusion::only_positive(positive_min).extend_dual()
579 }
580
581 #[inline]
591 pub fn only_positive_x(positive_min: f32) -> Self {
592 Self {
593 exclusion_x: AxisExclusion::only_positive(positive_min),
594 ..Self::ZERO
595 }
596 }
597
598 #[inline]
608 pub fn only_positive_y(positive_min: f32) -> Self {
609 Self {
610 exclusion_y: AxisExclusion::only_positive(positive_min),
611 ..Self::ZERO
612 }
613 }
614
615 #[inline]
625 pub fn only_negative(x_negative_max: f32, y_negative_max: f32) -> Self {
626 Self {
627 exclusion_x: AxisExclusion::only_negative(x_negative_max),
628 exclusion_y: AxisExclusion::only_negative(y_negative_max),
629 }
630 }
631
632 #[inline]
642 pub fn only_negative_all(negative_max: f32) -> Self {
643 AxisExclusion::only_negative(negative_max).extend_dual()
644 }
645
646 #[inline]
656 pub fn only_negative_x(negative_max: f32) -> Self {
657 Self {
658 exclusion_x: AxisExclusion::only_negative(negative_max),
659 ..Self::ZERO
660 }
661 }
662
663 #[inline]
673 pub fn only_negative_y(negative_max: f32) -> Self {
674 Self {
675 exclusion_y: AxisExclusion::only_negative(negative_max),
676 ..Self::ZERO
677 }
678 }
679
680 #[inline]
682 pub fn exclusions(&self) -> (AxisExclusion, AxisExclusion) {
683 (self.exclusion_x, self.exclusion_y)
684 }
685
686 #[inline]
688 pub fn exclusion_x(&self) -> AxisExclusion {
689 self.exclusions().0
690 }
691
692 #[inline]
694 pub fn exclusion_y(&self) -> AxisExclusion {
695 self.exclusions().1
696 }
697
698 #[must_use]
700 #[inline]
701 pub fn contains(&self, input_value: Vec2) -> BVec2 {
702 BVec2::new(
703 self.exclusion_x.contains(input_value.x),
704 self.exclusion_y.contains(input_value.y),
705 )
706 }
707
708 #[must_use]
710 #[inline]
711 pub fn exclude(&self, input_value: Vec2) -> Vec2 {
712 Vec2::new(
713 self.exclusion_x.exclude(input_value.x),
714 self.exclusion_y.exclude(input_value.y),
715 )
716 }
717
718 pub fn scaled(self) -> DualAxisDeadZone {
720 DualAxisDeadZone::new(self.exclusion_x.min_max(), self.exclusion_y.min_max())
721 }
722}
723
724impl Default for DualAxisExclusion {
725 #[inline]
727 fn default() -> Self {
728 AxisExclusion::default().extend_dual()
729 }
730}
731
732impl From<DualAxisExclusion> for DualAxisProcessor {
733 fn from(value: DualAxisExclusion) -> Self {
734 Self::Exclusion(value)
735 }
736}
737
738impl AxisExclusion {
739 #[inline]
741 pub const fn extend_dual(self) -> DualAxisExclusion {
742 DualAxisExclusion {
743 exclusion_x: self,
744 exclusion_y: self,
745 }
746 }
747
748 #[inline]
750 pub const fn extend_dual_only_x(self) -> DualAxisExclusion {
751 DualAxisExclusion {
752 exclusion_x: self,
753 ..DualAxisExclusion::ZERO
754 }
755 }
756
757 #[inline]
759 pub const fn extend_dual_only_y(self) -> DualAxisExclusion {
760 DualAxisExclusion {
761 exclusion_y: self,
762 ..DualAxisExclusion::ZERO
763 }
764 }
765
766 #[inline]
768 pub const fn extend_dual_with_x(self, exclusion_x: Self) -> DualAxisExclusion {
769 DualAxisExclusion {
770 exclusion_x,
771 exclusion_y: self,
772 }
773 }
774
775 #[inline]
777 pub const fn extend_dual_with_y(self, exclusion_y: Self) -> DualAxisExclusion {
778 DualAxisExclusion {
779 exclusion_x: self,
780 exclusion_y,
781 }
782 }
783}
784
785impl From<AxisExclusion> for DualAxisProcessor {
786 fn from(exclusion: AxisExclusion) -> Self {
787 Self::Exclusion(exclusion.extend_dual())
788 }
789}
790
791#[doc(alias("CrossDeadZone", "AxialDeadZone"))]
831#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
832#[must_use]
833pub struct DualAxisDeadZone {
834 pub deadzone_x: AxisDeadZone,
836
837 pub deadzone_y: AxisDeadZone,
839}
840
841impl DualAxisDeadZone {
842 pub const ZERO: Self = AxisDeadZone::ZERO.extend_dual();
844
845 #[inline]
855 pub fn new(
856 (x_negative_max, x_positive_min): (f32, f32),
857 (y_negative_max, y_positive_min): (f32, f32),
858 ) -> Self {
859 Self {
860 deadzone_x: AxisDeadZone::new(x_negative_max, x_positive_min),
861 deadzone_y: AxisDeadZone::new(y_negative_max, y_positive_min),
862 }
863 }
864
865 #[inline]
875 pub fn all(negative_max: f32, positive_min: f32) -> Self {
876 AxisDeadZone::new(negative_max, positive_min).extend_dual()
877 }
878
879 #[inline]
889 pub fn only_x(negative_max: f32, positive_min: f32) -> Self {
890 Self {
891 deadzone_x: AxisDeadZone::new(negative_max, positive_min),
892 ..Self::ZERO
893 }
894 }
895
896 #[inline]
906 pub fn only_y(negative_max: f32, positive_min: f32) -> Self {
907 Self {
908 deadzone_y: AxisDeadZone::new(negative_max, positive_min),
909 ..Self::ZERO
910 }
911 }
912
913 #[doc(alias = "magnitude")]
923 #[inline]
924 pub fn symmetric(threshold_x: f32, threshold_y: f32) -> Self {
925 Self {
926 deadzone_x: AxisDeadZone::symmetric(threshold_x),
927 deadzone_y: AxisDeadZone::symmetric(threshold_y),
928 }
929 }
930
931 #[doc(alias = "magnitude_all")]
941 #[inline]
942 pub fn symmetric_all(threshold: f32) -> Self {
943 AxisDeadZone::symmetric(threshold).extend_dual()
944 }
945
946 #[doc(alias = "magnitude_only_x")]
956 #[inline]
957 pub fn symmetric_only_x(threshold: f32) -> Self {
958 Self {
959 deadzone_x: AxisDeadZone::symmetric(threshold),
960 ..Self::ZERO
961 }
962 }
963
964 #[doc(alias = "magnitude_only_y")]
974 #[inline]
975 pub fn symmetric_only_y(threshold: f32) -> Self {
976 Self {
977 deadzone_y: AxisDeadZone::symmetric(threshold),
978 ..Self::ZERO
979 }
980 }
981
982 #[inline]
992 pub fn only_positive(x_positive_min: f32, y_positive_min: f32) -> Self {
993 Self {
994 deadzone_x: AxisDeadZone::only_positive(x_positive_min),
995 deadzone_y: AxisDeadZone::only_positive(y_positive_min),
996 }
997 }
998
999 #[inline]
1009 pub fn only_positive_all(positive_min: f32) -> Self {
1010 AxisDeadZone::only_positive(positive_min).extend_dual()
1011 }
1012
1013 #[inline]
1023 pub fn only_positive_x(positive_min: f32) -> Self {
1024 Self {
1025 deadzone_x: AxisDeadZone::only_positive(positive_min),
1026 ..Self::ZERO
1027 }
1028 }
1029
1030 #[inline]
1040 pub fn only_positive_y(positive_min: f32) -> Self {
1041 Self {
1042 deadzone_y: AxisDeadZone::only_positive(positive_min),
1043 ..Self::ZERO
1044 }
1045 }
1046
1047 #[inline]
1057 pub fn only_negative(x_negative_max: f32, y_negative_max: f32) -> Self {
1058 Self {
1059 deadzone_x: AxisDeadZone::only_negative(x_negative_max),
1060 deadzone_y: AxisDeadZone::only_negative(y_negative_max),
1061 }
1062 }
1063
1064 #[inline]
1074 pub fn only_negative_all(negative_max: f32) -> Self {
1075 AxisDeadZone::only_negative(negative_max).extend_dual()
1076 }
1077
1078 #[inline]
1088 pub fn only_negative_x(negative_max: f32) -> Self {
1089 Self {
1090 deadzone_x: AxisDeadZone::only_negative(negative_max),
1091 ..Self::ZERO
1092 }
1093 }
1094
1095 #[inline]
1105 pub fn only_negative_y(negative_max: f32) -> Self {
1106 Self {
1107 deadzone_y: AxisDeadZone::only_negative(negative_max),
1108 ..Self::ZERO
1109 }
1110 }
1111
1112 #[inline]
1114 pub fn deadzones(&self) -> (AxisDeadZone, AxisDeadZone) {
1115 (self.deadzone_x, self.deadzone_y)
1116 }
1117
1118 #[inline]
1120 pub fn deadzone_x(&self) -> AxisDeadZone {
1121 self.deadzones().0
1122 }
1123
1124 #[inline]
1126 pub fn deadzone_y(&self) -> AxisDeadZone {
1127 self.deadzones().1
1128 }
1129
1130 #[inline]
1132 pub fn exclusion(&self) -> DualAxisExclusion {
1133 DualAxisExclusion {
1134 exclusion_x: self.deadzone_x.exclusion(),
1135 exclusion_y: self.deadzone_y.exclusion(),
1136 }
1137 }
1138
1139 #[inline]
1141 pub fn bounds(&self) -> DualAxisBounds {
1142 DualAxisBounds::default()
1143 }
1144
1145 #[must_use]
1147 #[inline]
1148 pub fn within_exclusion(&self, input_value: Vec2) -> BVec2 {
1149 BVec2::new(
1150 self.deadzone_x.within_exclusion(input_value.x),
1151 self.deadzone_y.within_exclusion(input_value.y),
1152 )
1153 }
1154
1155 #[must_use]
1157 #[inline]
1158 pub fn within_bounds(&self, input_value: Vec2) -> BVec2 {
1159 BVec2::new(
1160 self.deadzone_x.within_bounds(input_value.x),
1161 self.deadzone_y.within_bounds(input_value.y),
1162 )
1163 }
1164
1165 #[must_use]
1167 #[inline]
1168 pub fn within_livezone_lower(&self, input_value: Vec2) -> BVec2 {
1169 BVec2::new(
1170 self.deadzone_x.within_livezone_lower(input_value.x),
1171 self.deadzone_y.within_livezone_lower(input_value.y),
1172 )
1173 }
1174
1175 #[must_use]
1177 #[inline]
1178 pub fn within_livezone_upper(&self, input_value: Vec2) -> BVec2 {
1179 BVec2::new(
1180 self.deadzone_x.within_livezone_upper(input_value.x),
1181 self.deadzone_y.within_livezone_upper(input_value.y),
1182 )
1183 }
1184
1185 #[must_use]
1187 #[inline]
1188 pub fn normalize(&self, input_value: Vec2) -> Vec2 {
1189 Vec2::new(
1190 self.deadzone_x.normalize(input_value.x),
1191 self.deadzone_y.normalize(input_value.y),
1192 )
1193 }
1194}
1195
1196impl Default for DualAxisDeadZone {
1197 fn default() -> Self {
1199 AxisDeadZone::default().extend_dual()
1200 }
1201}
1202
1203impl From<DualAxisDeadZone> for DualAxisProcessor {
1204 fn from(value: DualAxisDeadZone) -> Self {
1205 Self::DeadZone(value)
1206 }
1207}
1208
1209impl AxisDeadZone {
1210 #[inline]
1212 pub const fn extend_dual(self) -> DualAxisDeadZone {
1213 DualAxisDeadZone {
1214 deadzone_x: self,
1215 deadzone_y: self,
1216 }
1217 }
1218
1219 #[inline]
1221 pub const fn extend_dual_only_x(self) -> DualAxisDeadZone {
1222 DualAxisDeadZone {
1223 deadzone_x: self,
1224 ..DualAxisDeadZone::ZERO
1225 }
1226 }
1227
1228 #[inline]
1230 pub const fn extend_dual_only_y(self) -> DualAxisDeadZone {
1231 DualAxisDeadZone {
1232 deadzone_y: self,
1233 ..DualAxisDeadZone::ZERO
1234 }
1235 }
1236
1237 #[inline]
1239 pub const fn extend_dual_with_x(self, deadzone_x: Self) -> DualAxisDeadZone {
1240 DualAxisDeadZone {
1241 deadzone_x,
1242 deadzone_y: self,
1243 }
1244 }
1245
1246 #[inline]
1248 pub const fn extend_dual_with_y(self, deadzone_y: Self) -> DualAxisDeadZone {
1249 DualAxisDeadZone {
1250 deadzone_x: self,
1251 deadzone_y,
1252 }
1253 }
1254}
1255
1256impl From<AxisDeadZone> for DualAxisProcessor {
1257 fn from(deadzone: AxisDeadZone) -> Self {
1258 Self::DeadZone(deadzone.extend_dual())
1259 }
1260}
1261
1262impl From<DualAxisExclusion> for DualAxisDeadZone {
1263 fn from(exclusion: DualAxisExclusion) -> Self {
1264 Self::new(
1265 exclusion.exclusion_x.min_max(),
1266 exclusion.exclusion_y.min_max(),
1267 )
1268 }
1269}
1270
1271#[cfg(test)]
1272mod tests {
1273 use super::*;
1274
1275 #[test]
1276 fn test_dual_axis_value_bounds() {
1277 fn test_bounds(
1278 bounds: DualAxisBounds,
1279 (x_min, x_max): (f32, f32),
1280 (y_min, y_max): (f32, f32),
1281 ) {
1282 assert_eq!(bounds.bounds_x().min_max(), (x_min, x_max));
1283 assert_eq!(bounds.bounds_y().min_max(), (y_min, y_max));
1284
1285 let bounds_x = AxisBounds::new(x_min, x_max);
1286 let bounds_y = AxisBounds::new(y_min, y_max);
1287 assert_eq!(bounds_x.extend_dual_with_y(bounds_y), bounds);
1288 assert_eq!(bounds_y.extend_dual_with_x(bounds_x), bounds);
1289
1290 let (bx, by) = bounds.bounds();
1291 assert_eq!(bx, bounds_x);
1292 assert_eq!(by, bounds_y);
1293
1294 assert_eq!(
1295 DualAxisProcessor::from(bounds_x),
1296 DualAxisProcessor::ValueBounds(DualAxisBounds::all(x_min, x_max))
1297 );
1298
1299 let processor = DualAxisProcessor::ValueBounds(bounds);
1300 assert_eq!(DualAxisProcessor::from(bounds), processor);
1301
1302 for x in -300..300 {
1303 let x = x as f32 * 0.01;
1304 for y in -300..300 {
1305 let y = y as f32 * 0.01;
1306 let value = Vec2::new(x, y);
1307
1308 assert_eq!(processor.process(value), bounds.clamp(value));
1309
1310 let expected = BVec2::new(bounds_x.contains(x), bounds_y.contains(y));
1311 assert_eq!(bounds.contains(value), expected);
1312
1313 let expected = Vec2::new(bounds_x.clamp(x), bounds_y.clamp(y));
1314 assert_eq!(bounds.clamp(value), expected);
1315 }
1316 }
1317 }
1318
1319 let full_range = (f32::MIN, f32::MAX);
1320
1321 let bounds = DualAxisBounds::FULL_RANGE;
1322 test_bounds(bounds, full_range, full_range);
1323
1324 let bounds = DualAxisBounds::default();
1325 test_bounds(bounds, (-1.0, 1.0), (-1.0, 1.0));
1326
1327 let bounds = DualAxisBounds::new((-2.0, 2.5), (-1.0, 1.5));
1328 test_bounds(bounds, (-2.0, 2.5), (-1.0, 1.5));
1329
1330 let bounds = DualAxisBounds::all(-2.0, 2.5);
1331 test_bounds(bounds, (-2.0, 2.5), (-2.0, 2.5));
1332
1333 let bounds = DualAxisBounds::only_x(-2.0, 2.5);
1334 test_bounds(bounds, (-2.0, 2.5), full_range);
1335
1336 let bounds = DualAxisBounds::only_y(-1.0, 1.5);
1337 test_bounds(bounds, full_range, (-1.0, 1.5));
1338
1339 let bounds = DualAxisBounds::symmetric(2.0, 2.5);
1340 test_bounds(bounds, (-2.0, 2.0), (-2.5, 2.5));
1341
1342 let bounds = DualAxisBounds::symmetric_all(2.5);
1343 test_bounds(bounds, (-2.5, 2.5), (-2.5, 2.5));
1344
1345 let bounds = DualAxisBounds::symmetric_only_x(2.5);
1346 test_bounds(bounds, (-2.5, 2.5), full_range);
1347
1348 let bounds = DualAxisBounds::symmetric_only_y(2.5);
1349 test_bounds(bounds, full_range, (-2.5, 2.5));
1350
1351 let bounds = DualAxisBounds::at_least(2.0, 2.5);
1352 test_bounds(bounds, (2.0, f32::MAX), (2.5, f32::MAX));
1353
1354 let bounds = DualAxisBounds::at_least_all(2.5);
1355 test_bounds(bounds, (2.5, f32::MAX), (2.5, f32::MAX));
1356
1357 let bounds = DualAxisBounds::at_least_only_x(2.5);
1358 test_bounds(bounds, (2.5, f32::MAX), full_range);
1359
1360 let bounds = DualAxisBounds::at_least_only_y(2.5);
1361 test_bounds(bounds, full_range, (2.5, f32::MAX));
1362
1363 let bounds = DualAxisBounds::at_most(2.0, 2.5);
1364 test_bounds(bounds, (f32::MIN, 2.0), (f32::MIN, 2.5));
1365
1366 let bounds = DualAxisBounds::at_most_all(2.5);
1367 test_bounds(bounds, (f32::MIN, 2.5), (f32::MIN, 2.5));
1368
1369 let bounds = DualAxisBounds::at_most_only_x(2.5);
1370 test_bounds(bounds, (f32::MIN, 2.5), full_range);
1371
1372 let bounds = DualAxisBounds::at_most_only_y(2.5);
1373 test_bounds(bounds, full_range, (f32::MIN, 2.5));
1374
1375 let bounds_x = AxisBounds::new(-2.0, 2.5);
1376 let bounds_y = AxisBounds::new(-1.0, 1.5);
1377
1378 test_bounds(bounds_x.extend_dual(), (-2.0, 2.5), (-2.0, 2.5));
1379
1380 test_bounds(bounds_y.extend_dual(), (-1.0, 1.5), (-1.0, 1.5));
1381
1382 test_bounds(bounds_x.extend_dual_only_x(), (-2.0, 2.5), full_range);
1383
1384 test_bounds(bounds_y.extend_dual_only_y(), full_range, (-1.0, 1.5));
1385
1386 test_bounds(
1387 bounds_x.extend_dual_with_y(bounds_y),
1388 (-2.0, 2.5),
1389 (-1.0, 1.5),
1390 );
1391
1392 test_bounds(
1393 bounds_y.extend_dual_with_x(bounds_x),
1394 (-2.0, 2.5),
1395 (-1.0, 1.5),
1396 );
1397 }
1398
1399 #[test]
1400 fn test_dual_axis_exclusion() {
1401 fn test_exclusion(
1402 exclusion: DualAxisExclusion,
1403 (x_negative_max, x_positive_min): (f32, f32),
1404 (y_negative_max, y_positive_min): (f32, f32),
1405 ) {
1406 assert_eq!(
1407 exclusion.exclusion_x.min_max(),
1408 (x_negative_max, x_positive_min)
1409 );
1410 assert_eq!(
1411 exclusion.exclusion_y.min_max(),
1412 (y_negative_max, y_positive_min)
1413 );
1414
1415 let exclusion_x = AxisExclusion::new(x_negative_max, x_positive_min);
1416 let exclusion_y = AxisExclusion::new(y_negative_max, y_positive_min);
1417 assert_eq!(exclusion_x.extend_dual_with_y(exclusion_y), exclusion);
1418
1419 let (ex, ey) = exclusion.exclusions();
1420 assert_eq!(ex, exclusion_x);
1421 assert_eq!(ey, exclusion_y);
1422
1423 assert_eq!(
1424 DualAxisProcessor::from(exclusion_x),
1425 DualAxisProcessor::Exclusion(DualAxisExclusion::all(
1426 x_negative_max,
1427 x_positive_min
1428 ))
1429 );
1430
1431 let processor = DualAxisProcessor::Exclusion(exclusion);
1432 assert_eq!(DualAxisProcessor::from(exclusion), processor);
1433
1434 for x in -300..300 {
1435 let x = x as f32 * 0.01;
1436 for y in -300..300 {
1437 let y = y as f32 * 0.01;
1438 let value = Vec2::new(x, y);
1439
1440 assert_eq!(processor.process(value), exclusion.exclude(value));
1441
1442 assert_eq!(
1443 exclusion.contains(value),
1444 BVec2::new(exclusion_x.contains(x), exclusion_y.contains(y))
1445 );
1446
1447 assert_eq!(
1448 exclusion.exclude(value),
1449 Vec2::new(exclusion_x.exclude(x), exclusion_y.exclude(y))
1450 );
1451 }
1452 }
1453 }
1454
1455 let zero_size = (0.0, 0.0);
1456
1457 let exclusion = DualAxisExclusion::ZERO;
1458 test_exclusion(exclusion, zero_size, zero_size);
1459
1460 let exclusion = DualAxisExclusion::default();
1461 test_exclusion(exclusion, (-0.1, 0.1), (-0.1, 0.1));
1462
1463 let exclusion = DualAxisExclusion::new((-0.2, 0.3), (-0.1, 0.4));
1464 test_exclusion(exclusion, (-0.2, 0.3), (-0.1, 0.4));
1465
1466 let exclusion = DualAxisExclusion::all(-0.2, 0.3);
1467 test_exclusion(exclusion, (-0.2, 0.3), (-0.2, 0.3));
1468
1469 let exclusion = DualAxisExclusion::only_x(-0.2, 0.3);
1470 test_exclusion(exclusion, (-0.2, 0.3), zero_size);
1471
1472 let exclusion = DualAxisExclusion::only_y(-0.1, 0.4);
1473 test_exclusion(exclusion, zero_size, (-0.1, 0.4));
1474
1475 let exclusion = DualAxisExclusion::symmetric(0.2, 0.3);
1476 test_exclusion(exclusion, (-0.2, 0.2), (-0.3, 0.3));
1477
1478 let exclusion = DualAxisExclusion::symmetric_all(0.3);
1479 test_exclusion(exclusion, (-0.3, 0.3), (-0.3, 0.3));
1480
1481 let exclusion = DualAxisExclusion::symmetric_only_x(0.3);
1482 test_exclusion(exclusion, (-0.3, 0.3), zero_size);
1483
1484 let exclusion = DualAxisExclusion::symmetric_only_y(0.3);
1485 test_exclusion(exclusion, zero_size, (-0.3, 0.3));
1486
1487 let exclusion_x = AxisExclusion::new(-0.2, 0.3);
1488 let exclusion_y = AxisExclusion::new(-0.1, 0.4);
1489
1490 test_exclusion(exclusion_x.extend_dual(), (-0.2, 0.3), (-0.2, 0.3));
1491
1492 test_exclusion(exclusion_y.extend_dual(), (-0.1, 0.4), (-0.1, 0.4));
1493
1494 test_exclusion(exclusion_x.extend_dual_only_x(), (-0.2, 0.3), zero_size);
1495
1496 test_exclusion(exclusion_y.extend_dual_only_y(), zero_size, (-0.1, 0.4));
1497
1498 test_exclusion(
1499 exclusion_x.extend_dual_with_y(exclusion_y),
1500 (-0.2, 0.3),
1501 (-0.1, 0.4),
1502 );
1503
1504 test_exclusion(
1505 exclusion_y.extend_dual_with_x(exclusion_x),
1506 (-0.2, 0.3),
1507 (-0.1, 0.4),
1508 );
1509 }
1510
1511 #[test]
1512 fn test_dual_axis_deadzone() {
1513 fn test_deadzone(
1514 deadzone: DualAxisDeadZone,
1515 (x_negative_max, x_positive_min): (f32, f32),
1516 (y_negative_max, y_positive_min): (f32, f32),
1517 ) {
1518 assert_eq!(
1519 deadzone.deadzone_x.exclusion().min_max(),
1520 (x_negative_max, x_positive_min)
1521 );
1522 assert_eq!(
1523 deadzone.deadzone_y.exclusion().min_max(),
1524 (y_negative_max, y_positive_min)
1525 );
1526
1527 let deadzone_x = AxisDeadZone::new(x_negative_max, x_positive_min);
1528 let deadzone_y = AxisDeadZone::new(y_negative_max, y_positive_min);
1529 assert_eq!(deadzone_x.extend_dual_with_y(deadzone_y), deadzone);
1530
1531 let exclusion = DualAxisExclusion::new(
1532 (x_negative_max, x_positive_min),
1533 (y_negative_max, y_positive_min),
1534 );
1535 assert_eq!(exclusion.scaled(), deadzone);
1536
1537 let (dx, dy) = deadzone.deadzones();
1538 assert_eq!(dx, deadzone_x);
1539 assert_eq!(dy, deadzone_y);
1540
1541 assert_eq!(
1542 DualAxisProcessor::from(deadzone_x),
1543 DualAxisProcessor::DeadZone(DualAxisDeadZone::all(x_negative_max, x_positive_min))
1544 );
1545
1546 let processor = DualAxisProcessor::DeadZone(deadzone);
1547 assert_eq!(DualAxisProcessor::from(deadzone), processor);
1548
1549 for x in -300..300 {
1550 let x = x as f32 * 0.01;
1551 for y in -300..300 {
1552 let y = y as f32 * 0.01;
1553 let value = Vec2::new(x, y);
1554
1555 assert_eq!(processor.process(value), deadzone.normalize(value));
1556
1557 assert_eq!(
1558 deadzone.within_exclusion(value),
1559 BVec2::new(
1560 deadzone_x.within_exclusion(x),
1561 deadzone_y.within_exclusion(y)
1562 )
1563 );
1564
1565 assert_eq!(
1566 deadzone.within_bounds(value),
1567 BVec2::new(deadzone_x.within_bounds(x), deadzone_y.within_bounds(y))
1568 );
1569
1570 assert_eq!(
1571 deadzone.within_livezone_lower(value),
1572 BVec2::new(
1573 deadzone_x.within_livezone_lower(x),
1574 deadzone_y.within_livezone_lower(y)
1575 )
1576 );
1577
1578 assert_eq!(
1579 deadzone.within_livezone_upper(value),
1580 BVec2::new(
1581 deadzone_x.within_livezone_upper(x),
1582 deadzone_y.within_livezone_upper(y)
1583 )
1584 );
1585
1586 assert_eq!(
1587 deadzone.normalize(value),
1588 Vec2::new(deadzone_x.normalize(x), deadzone_y.normalize(y))
1589 );
1590 }
1591 }
1592 }
1593
1594 let zero_size = (0.0, 0.0);
1595
1596 let deadzone = DualAxisDeadZone::ZERO;
1597 test_deadzone(deadzone, zero_size, zero_size);
1598
1599 let deadzone = DualAxisDeadZone::default();
1600 test_deadzone(deadzone, (-0.1, 0.1), (-0.1, 0.1));
1601
1602 let deadzone = DualAxisDeadZone::new((-0.2, 0.3), (-0.1, 0.4));
1603 test_deadzone(deadzone, (-0.2, 0.3), (-0.1, 0.4));
1604
1605 let deadzone = DualAxisDeadZone::all(-0.2, 0.3);
1606 test_deadzone(deadzone, (-0.2, 0.3), (-0.2, 0.3));
1607
1608 let deadzone = DualAxisDeadZone::only_x(-0.2, 0.3);
1609 test_deadzone(deadzone, (-0.2, 0.3), zero_size);
1610
1611 let deadzone = DualAxisDeadZone::only_y(-0.1, 0.4);
1612 test_deadzone(deadzone, zero_size, (-0.1, 0.4));
1613
1614 let deadzone = DualAxisDeadZone::symmetric(0.2, 0.3);
1615 test_deadzone(deadzone, (-0.2, 0.2), (-0.3, 0.3));
1616
1617 let deadzone = DualAxisDeadZone::symmetric_all(0.3);
1618 test_deadzone(deadzone, (-0.3, 0.3), (-0.3, 0.3));
1619
1620 let deadzone = DualAxisDeadZone::symmetric_only_x(0.3);
1621 test_deadzone(deadzone, (-0.3, 0.3), zero_size);
1622
1623 let deadzone = DualAxisDeadZone::symmetric_only_y(0.3);
1624 test_deadzone(deadzone, zero_size, (-0.3, 0.3));
1625
1626 let deadzone_x = AxisDeadZone::new(-0.2, 0.3);
1627 let deadzone_y = AxisDeadZone::new(-0.1, 0.4);
1628
1629 test_deadzone(deadzone_x.extend_dual(), (-0.2, 0.3), (-0.2, 0.3));
1630
1631 test_deadzone(deadzone_y.extend_dual(), (-0.1, 0.4), (-0.1, 0.4));
1632
1633 test_deadzone(deadzone_x.extend_dual_only_x(), (-0.2, 0.3), zero_size);
1634
1635 test_deadzone(deadzone_y.extend_dual_only_y(), zero_size, (-0.1, 0.4));
1636
1637 test_deadzone(
1638 deadzone_x.extend_dual_with_y(deadzone_y),
1639 (-0.2, 0.3),
1640 (-0.1, 0.4),
1641 );
1642
1643 test_deadzone(
1644 deadzone_y.extend_dual_with_x(deadzone_x),
1645 (-0.2, 0.3),
1646 (-0.1, 0.4),
1647 );
1648 }
1649}