1#![allow(
5 clippy::large_stack_arrays,
6 reason = "effectively-false positive on Arbitrary derive"
7)]
8use core::ops::Mul;
9
10use euclid::vec3;
11
12use crate::math::{Face6, GridCoordinate, GridMatrix, GridSize, GridVector, Gridgid, Vector3D};
13
14#[cfg(doc)]
15use crate::math::GridAab;
16
17#[doc = include_str!("../serde-warning.md")]
35#[rustfmt::skip]
36#[expect(clippy::exhaustive_enums, clippy::upper_case_acronyms)]
37#[allow(missing_docs)]
38#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
39#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41#[repr(u8)]
42pub enum GridRotation {
43 RXYZ, RXYz, RXyZ, RXyz, RxYZ, RxYz, RxyZ, Rxyz,
45 RXZY, RXZy, RXzY, RXzy, RxZY, RxZy, RxzY, Rxzy,
46 RYXZ, RYXz, RYxZ, RYxz, RyXZ, RyXz, RyxZ, Ryxz,
47 RYZX, RYZx, RYzX, RYzx, RyZX, RyZx, RyzX, Ryzx,
48 RZXY, RZXy, RZxY, RZxy, RzXY, RzXy, RzxY, Rzxy,
49 RZYX, RZYx, RZyX, RZyx, RzYX, RzYx, RzyX, Rzyx,
50}
51
52impl GridRotation {
53 #[rustfmt::skip]
58 pub const ALL: [Self; 48] = {
59 use GridRotation::*;
60 [
61 RXYZ, RXYz, RXyZ, RXyz, RxYZ, RxYz, RxyZ, Rxyz,
62 RXZY, RXZy, RXzY, RXzy, RxZY, RxZy, RxzY, Rxzy,
63 RYXZ, RYXz, RYxZ, RYxz, RyXZ, RyXz, RyxZ, Ryxz,
64 RYZX, RYZx, RYzX, RYzx, RyZX, RyZx, RyzX, Ryzx,
65 RZXY, RZXy, RZxY, RZxy, RzXY, RzXy, RzxY, Rzxy,
66 RZYX, RZYx, RZyX, RZyx, RzYX, RzYx, RzyX, Rzyx,
67 ]
68 };
69
70 #[rustfmt::skip]
74 pub const ALL_BUT_REFLECTIONS: [Self; 24] = {
75 use GridRotation::*;
76 [
77 RXYZ, RXyz, RxYz, RxyZ,
78 RXZy, RXzY, RxZY, Rxzy,
79 RYXz, RYxZ, RyXZ, Ryxz,
80 RYZX, RYzx, RyZx, RyzX,
81 RZXY, RZxy, RzXy, RzxY,
82 RZYx, RZyX, RzYX, Rzyx,
83 ]
84 };
85
86 pub const IDENTITY: Self = Self::RXYZ;
88
89 #[inline]
94 pub fn from_basis(basis: impl Into<[Face6; 3]>) -> Self {
95 let basis = basis.into();
96 Self::try_from_basis_const(basis)
97 .unwrap_or_else(|| panic!("Invalid basis given to GridRotation::from_basis: {basis:?}"))
98 }
99
100 #[doc(hidden)]
103 #[inline]
104 pub const fn try_from_basis_const(basis: [Face6; 3]) -> Option<Self> {
105 use {Face6::*, GridRotation::*};
106 Some(match basis {
107 [PX, PY, PZ] => RXYZ,
108 [PX, PZ, PY] => RXZY,
109 [PY, PX, PZ] => RYXZ,
110 [PY, PZ, PX] => RYZX,
111 [PZ, PX, PY] => RZXY,
112 [PZ, PY, PX] => RZYX,
113
114 [PX, PY, NZ] => RXYz,
115 [PX, PZ, NY] => RXZy,
116 [PY, PX, NZ] => RYXz,
117 [PY, PZ, NX] => RYZx,
118 [PZ, PX, NY] => RZXy,
119 [PZ, PY, NX] => RZYx,
120
121 [PX, NY, PZ] => RXyZ,
122 [PX, NZ, PY] => RXzY,
123 [PY, NX, PZ] => RYxZ,
124 [PY, NZ, PX] => RYzX,
125 [PZ, NX, PY] => RZxY,
126 [PZ, NY, PX] => RZyX,
127
128 [PX, NY, NZ] => RXyz,
129 [PX, NZ, NY] => RXzy,
130 [PY, NX, NZ] => RYxz,
131 [PY, NZ, NX] => RYzx,
132 [PZ, NX, NY] => RZxy,
133 [PZ, NY, NX] => RZyx,
134
135 [NX, PY, PZ] => RxYZ,
136 [NX, PZ, PY] => RxZY,
137 [NY, PX, PZ] => RyXZ,
138 [NY, PZ, PX] => RyZX,
139 [NZ, PX, PY] => RzXY,
140 [NZ, PY, PX] => RzYX,
141
142 [NX, PY, NZ] => RxYz,
143 [NX, PZ, NY] => RxZy,
144 [NY, PX, NZ] => RyXz,
145 [NY, PZ, NX] => RyZx,
146 [NZ, PX, NY] => RzXy,
147 [NZ, PY, NX] => RzYx,
148
149 [NX, NY, PZ] => RxyZ,
150 [NX, NZ, PY] => RxzY,
151 [NY, NX, PZ] => RyxZ,
152 [NY, NZ, PX] => RyzX,
153 [NZ, NX, PY] => RzxY,
154 [NZ, NY, PX] => RzyX,
155
156 [NX, NY, NZ] => Rxyz,
157 [NX, NZ, NY] => Rxzy,
158 [NY, NX, NZ] => Ryxz,
159 [NY, NZ, NX] => Ryzx,
160 [NZ, NX, NY] => Rzxy,
161 [NZ, NY, NX] => Rzyx,
162
163 _ => return None,
164 })
165 }
166
167 #[allow(clippy::missing_inline_in_public_items)]
173 pub fn from_to(source: Face6, destination: Face6, up: Face6) -> Option<Self> {
174 let perpendicular = source.cross(up);
175 if source == destination {
176 Some(Self::IDENTITY)
177 } else if let Ok(perpendicular) = Face6::try_from(perpendicular) {
178 let canonical_to_given = Self::from_basis([perpendicular, up, source.opposite()]);
180 let given_to_canonical = canonical_to_given.inverse();
181 debug_assert!(!canonical_to_given.is_reflection());
182
183 let canonical_destination = given_to_canonical.transform(destination);
185 use Face6::*;
187 let canonical_rotation = match canonical_destination {
188 NY | PY => {
189 return None;
191 }
192 NZ => Self::IDENTITY,
193 PX => PY.clockwise(),
194 PZ => PY.r180(),
195 NX => PY.counterclockwise(),
196 };
197 Some(canonical_to_given * canonical_rotation * given_to_canonical)
199 } else {
200 None
203 }
204 }
205
206 #[doc(hidden)]
210 #[inline]
211 pub const fn to_basis(self) -> Vector3D<Face6, ()> {
212 static TABLE: [Vector3D<Face6, ()>; 48] = {
216 let mut table = [Vector3D::new(Face6::PX, Face6::PX, Face6::PX); 48];
217 let mut i = 0;
218 while i < table.len() {
219 use {Face6::*, GridRotation::*};
220 let rot = GridRotation::ALL[i];
221 table[rot as usize] = match rot {
222 RXYZ => vec3(PX, PY, PZ),
225 RXZY => vec3(PX, PZ, PY),
226 RYXZ => vec3(PY, PX, PZ),
227 RYZX => vec3(PY, PZ, PX),
228 RZXY => vec3(PZ, PX, PY),
229 RZYX => vec3(PZ, PY, PX),
230
231 RXYz => vec3(PX, PY, NZ),
232 RXZy => vec3(PX, PZ, NY),
233 RYXz => vec3(PY, PX, NZ),
234 RYZx => vec3(PY, PZ, NX),
235 RZXy => vec3(PZ, PX, NY),
236 RZYx => vec3(PZ, PY, NX),
237
238 RXyZ => vec3(PX, NY, PZ),
239 RXzY => vec3(PX, NZ, PY),
240 RYxZ => vec3(PY, NX, PZ),
241 RYzX => vec3(PY, NZ, PX),
242 RZxY => vec3(PZ, NX, PY),
243 RZyX => vec3(PZ, NY, PX),
244
245 RXyz => vec3(PX, NY, NZ),
246 RXzy => vec3(PX, NZ, NY),
247 RYxz => vec3(PY, NX, NZ),
248 RYzx => vec3(PY, NZ, NX),
249 RZxy => vec3(PZ, NX, NY),
250 RZyx => vec3(PZ, NY, NX),
251
252 RxYZ => vec3(NX, PY, PZ),
253 RxZY => vec3(NX, PZ, PY),
254 RyXZ => vec3(NY, PX, PZ),
255 RyZX => vec3(NY, PZ, PX),
256 RzXY => vec3(NZ, PX, PY),
257 RzYX => vec3(NZ, PY, PX),
258
259 RxYz => vec3(NX, PY, NZ),
260 RxZy => vec3(NX, PZ, NY),
261 RyXz => vec3(NY, PX, NZ),
262 RyZx => vec3(NY, PZ, NX),
263 RzXy => vec3(NZ, PX, NY),
264 RzYx => vec3(NZ, PY, NX),
265
266 RxyZ => vec3(NX, NY, PZ),
267 RxzY => vec3(NX, NZ, PY),
268 RyxZ => vec3(NY, NX, PZ),
269 RyzX => vec3(NY, NZ, PX),
270 RzxY => vec3(NZ, NX, PY),
271 RzyX => vec3(NZ, NY, PX),
272
273 Rxyz => vec3(NX, NY, NZ),
274 Rxzy => vec3(NX, NZ, NY),
275 Ryxz => vec3(NY, NX, NZ),
276 Ryzx => vec3(NY, NZ, NX),
277 Rzxy => vec3(NZ, NX, NY),
278 Rzyx => vec3(NZ, NY, NX),
279 };
280 i += 1;
281 }
282 table
283 };
284
285 TABLE[self as usize]
286 }
287
288 #[inline]
323 pub const fn to_positive_octant_transform(self, size: GridCoordinate) -> Gridgid {
324 #[inline(always)]
325 const fn offset(face: Face6, size: GridCoordinate) -> GridVector {
326 if face.is_positive() {
327 GridVector::new(0, 0, 0)
328 } else {
329 let mut v = face.into7().normal_vector_const();
331 v.x *= -size;
332 v.y *= -size;
333 v.z *= -size;
334 v
335 }
336 }
337 #[inline(always)]
338 const fn add(mut a: GridVector, b: GridVector) -> GridVector {
339 a.x += b.x;
340 a.y += b.y;
341 a.z += b.z;
342 a
343 }
344 let basis = self.to_basis();
345 Gridgid {
346 rotation: self,
347 translation: add(
348 add(offset(basis.x, size), offset(basis.y, size)),
349 offset(basis.z, size),
350 ),
351 }
352 }
353
354 #[inline]
357 pub fn to_rotation_matrix(self) -> GridMatrix {
358 let basis = self.to_basis();
359 GridMatrix {
360 x: basis.x.normal_vector(),
361 y: basis.y.normal_vector(),
362 z: basis.z.normal_vector(),
363 w: Vector3D::zero(),
364 }
365 }
366
367 #[inline]
370 pub fn transform(self, face: Face6) -> Face6 {
371 let p = self.to_basis()[face.axis()];
374 if face.is_negative() { p.opposite() } else { p }
375 }
376
377 #[inline]
381 #[track_caller]
382 pub fn transform_vector(self, vector: GridVector) -> GridVector {
383 match self.checked_transform_vector(vector) {
387 Some(v) => v,
388 None => panic!(
389 "overflow due to sign change in GridVector::transform_vector({self:?}, {vector:?})"
390 ),
391 }
392 }
393
394 #[inline]
399 pub fn checked_transform_vector(self, vector: GridVector) -> Option<GridVector> {
400 let basis = self.to_basis();
401
402 let mut result = GridVector::zero();
403 result[basis.x.axis()] = vector.x.checked_mul(basis.x.signum())?;
404 result[basis.y.axis()] = vector.y.checked_mul(basis.y.signum())?;
405 result[basis.z.axis()] = vector.z.checked_mul(basis.z.signum())?;
406
407 Some(result)
408
409 }
422
423 #[inline]
428 pub fn transform_size(self, size: GridSize) -> GridSize {
429 let basis = self.to_basis();
430
431 let mut result = GridSize::zero();
432 result[basis.x.axis()] = size.width;
433 result[basis.y.axis()] = size.height;
434 result[basis.z.axis()] = size.depth;
435 result
436 }
437
438 #[inline]
449 pub const fn is_reflection(self) -> bool {
450 let Vector3D { x, y, z, _unit } = self.to_basis();
454 x.cross(y) as u8 != z as u8
456 }
457
458 #[rustfmt::skip]
469 #[must_use]
470 #[inline]
471 pub const fn inverse(self) -> Self {
472 use GridRotation::*;
473 match self {
474 RXYZ => RXYZ, RXYz => RXYz, RXyZ => RXyZ, RXyz => RXyz, RxYZ => RxYZ,
475 RxYz => RxYz, RxyZ => RxyZ, Rxyz => Rxyz, RXZY => RXZY, RXZy => RXzY,
476 RXzY => RXZy, RXzy => RXzy, RxZY => RxZY, RxZy => RxzY, RxzY => RxZy,
477 Rxzy => Rxzy, RYXZ => RYXZ, RYXz => RYXz, RYxZ => RyXZ, RYxz => RyXz,
478 RyXZ => RYxZ, RyXz => RYxz, RyxZ => RyxZ, Ryxz => Ryxz, RYZX => RZXY,
479 RYZx => RzXY, RYzX => RZXy, RYzx => RzXy, RyZX => RZxY, RyZx => RzxY,
480 RyzX => RZxy, Ryzx => Rzxy, RZXY => RYZX, RZXy => RYzX, RZxY => RyZX,
481 RZxy => RyzX, RzXY => RYZx, RzXy => RYzx, RzxY => RyZx, Rzxy => Ryzx,
482 RZYX => RZYX, RZYx => RzYX, RZyX => RZyX, RZyx => RzyX, RzYX => RZYx,
483 RzYx => RzYx, RzyX => RZyx, Rzyx => Rzyx,
484 }
485 }
486
487 #[allow(clippy::missing_inline_in_public_items)]
525 pub fn iterate(self) -> impl Iterator<Item = Self> {
526 let mut state = Some(Self::IDENTITY);
527 core::iter::from_fn(move || {
528 let current = state?;
529 let next = current * self;
530 if next == Self::IDENTITY {
531 state = None;
534 } else {
535 state = Some(next);
536 }
537 Some(current)
538 })
539 }
540}
541
542impl Default for GridRotation {
543 #[inline]
545 fn default() -> Self {
546 Self::IDENTITY
547 }
548}
549
550impl num_traits::One for GridRotation {
551 #[inline]
553 fn one() -> Self {
554 Self::IDENTITY
555 }
556}
557
558impl Mul<Self> for GridRotation {
559 type Output = Self;
560
561 #[inline]
580 fn mul(self, rhs: Self) -> Self::Output {
581 MULTIPLICATION_TABLE[rhs as usize][self as usize]
582 }
583}
584
585#[rustfmt::skip]
592static MULTIPLICATION_TABLE: [[GridRotation; 48]; 48] = {
593 use GridRotation::*;
594 [
595 [RXYZ,RXYz,RXyZ,RXyz,RxYZ,RxYz,RxyZ,Rxyz,RXZY,RXZy,RXzY,RXzy,RxZY,RxZy,RxzY,Rxzy,RYXZ,RYXz,RYxZ,RYxz,RyXZ,RyXz,RyxZ,Ryxz,RYZX,RYZx,RYzX,RYzx,RyZX,RyZx,RyzX,Ryzx,RZXY,RZXy,RZxY,RZxy,RzXY,RzXy,RzxY,Rzxy,RZYX,RZYx,RZyX,RZyx,RzYX,RzYx,RzyX,Rzyx,],
596 [RXYz,RXYZ,RXyz,RXyZ,RxYz,RxYZ,Rxyz,RxyZ,RXZy,RXZY,RXzy,RXzY,RxZy,RxZY,Rxzy,RxzY,RYXz,RYXZ,RYxz,RYxZ,RyXz,RyXZ,Ryxz,RyxZ,RYZx,RYZX,RYzx,RYzX,RyZx,RyZX,Ryzx,RyzX,RZXy,RZXY,RZxy,RZxY,RzXy,RzXY,Rzxy,RzxY,RZYx,RZYX,RZyx,RZyX,RzYx,RzYX,Rzyx,RzyX,],
597 [RXyZ,RXyz,RXYZ,RXYz,RxyZ,Rxyz,RxYZ,RxYz,RXzY,RXzy,RXZY,RXZy,RxzY,Rxzy,RxZY,RxZy,RYxZ,RYxz,RYXZ,RYXz,RyxZ,Ryxz,RyXZ,RyXz,RYzX,RYzx,RYZX,RYZx,RyzX,Ryzx,RyZX,RyZx,RZxY,RZxy,RZXY,RZXy,RzxY,Rzxy,RzXY,RzXy,RZyX,RZyx,RZYX,RZYx,RzyX,Rzyx,RzYX,RzYx,],
598 [RXyz,RXyZ,RXYz,RXYZ,Rxyz,RxyZ,RxYz,RxYZ,RXzy,RXzY,RXZy,RXZY,Rxzy,RxzY,RxZy,RxZY,RYxz,RYxZ,RYXz,RYXZ,Ryxz,RyxZ,RyXz,RyXZ,RYzx,RYzX,RYZx,RYZX,Ryzx,RyzX,RyZx,RyZX,RZxy,RZxY,RZXy,RZXY,Rzxy,RzxY,RzXy,RzXY,RZyx,RZyX,RZYx,RZYX,Rzyx,RzyX,RzYx,RzYX,],
599 [RxYZ,RxYz,RxyZ,Rxyz,RXYZ,RXYz,RXyZ,RXyz,RxZY,RxZy,RxzY,Rxzy,RXZY,RXZy,RXzY,RXzy,RyXZ,RyXz,RyxZ,Ryxz,RYXZ,RYXz,RYxZ,RYxz,RyZX,RyZx,RyzX,Ryzx,RYZX,RYZx,RYzX,RYzx,RzXY,RzXy,RzxY,Rzxy,RZXY,RZXy,RZxY,RZxy,RzYX,RzYx,RzyX,Rzyx,RZYX,RZYx,RZyX,RZyx,],
600 [RxYz,RxYZ,Rxyz,RxyZ,RXYz,RXYZ,RXyz,RXyZ,RxZy,RxZY,Rxzy,RxzY,RXZy,RXZY,RXzy,RXzY,RyXz,RyXZ,Ryxz,RyxZ,RYXz,RYXZ,RYxz,RYxZ,RyZx,RyZX,Ryzx,RyzX,RYZx,RYZX,RYzx,RYzX,RzXy,RzXY,Rzxy,RzxY,RZXy,RZXY,RZxy,RZxY,RzYx,RzYX,Rzyx,RzyX,RZYx,RZYX,RZyx,RZyX,],
601 [RxyZ,Rxyz,RxYZ,RxYz,RXyZ,RXyz,RXYZ,RXYz,RxzY,Rxzy,RxZY,RxZy,RXzY,RXzy,RXZY,RXZy,RyxZ,Ryxz,RyXZ,RyXz,RYxZ,RYxz,RYXZ,RYXz,RyzX,Ryzx,RyZX,RyZx,RYzX,RYzx,RYZX,RYZx,RzxY,Rzxy,RzXY,RzXy,RZxY,RZxy,RZXY,RZXy,RzyX,Rzyx,RzYX,RzYx,RZyX,RZyx,RZYX,RZYx,],
602 [Rxyz,RxyZ,RxYz,RxYZ,RXyz,RXyZ,RXYz,RXYZ,Rxzy,RxzY,RxZy,RxZY,RXzy,RXzY,RXZy,RXZY,Ryxz,RyxZ,RyXz,RyXZ,RYxz,RYxZ,RYXz,RYXZ,Ryzx,RyzX,RyZx,RyZX,RYzx,RYzX,RYZx,RYZX,Rzxy,RzxY,RzXy,RzXY,RZxy,RZxY,RZXy,RZXY,Rzyx,RzyX,RzYx,RzYX,RZyx,RZyX,RZYx,RZYX,],
603 [RXZY,RXzY,RXZy,RXzy,RxZY,RxzY,RxZy,Rxzy,RXYZ,RXyZ,RXYz,RXyz,RxYZ,RxyZ,RxYz,Rxyz,RYZX,RYzX,RYZx,RYzx,RyZX,RyzX,RyZx,Ryzx,RYXZ,RYxZ,RYXz,RYxz,RyXZ,RyxZ,RyXz,Ryxz,RZYX,RZyX,RZYx,RZyx,RzYX,RzyX,RzYx,Rzyx,RZXY,RZxY,RZXy,RZxy,RzXY,RzxY,RzXy,Rzxy,],
604 [RXZy,RXzy,RXZY,RXzY,RxZy,Rxzy,RxZY,RxzY,RXYz,RXyz,RXYZ,RXyZ,RxYz,Rxyz,RxYZ,RxyZ,RYZx,RYzx,RYZX,RYzX,RyZx,Ryzx,RyZX,RyzX,RYXz,RYxz,RYXZ,RYxZ,RyXz,Ryxz,RyXZ,RyxZ,RZYx,RZyx,RZYX,RZyX,RzYx,Rzyx,RzYX,RzyX,RZXy,RZxy,RZXY,RZxY,RzXy,Rzxy,RzXY,RzxY,],
605 [RXzY,RXZY,RXzy,RXZy,RxzY,RxZY,Rxzy,RxZy,RXyZ,RXYZ,RXyz,RXYz,RxyZ,RxYZ,Rxyz,RxYz,RYzX,RYZX,RYzx,RYZx,RyzX,RyZX,Ryzx,RyZx,RYxZ,RYXZ,RYxz,RYXz,RyxZ,RyXZ,Ryxz,RyXz,RZyX,RZYX,RZyx,RZYx,RzyX,RzYX,Rzyx,RzYx,RZxY,RZXY,RZxy,RZXy,RzxY,RzXY,Rzxy,RzXy,],
606 [RXzy,RXZy,RXzY,RXZY,Rxzy,RxZy,RxzY,RxZY,RXyz,RXYz,RXyZ,RXYZ,Rxyz,RxYz,RxyZ,RxYZ,RYzx,RYZx,RYzX,RYZX,Ryzx,RyZx,RyzX,RyZX,RYxz,RYXz,RYxZ,RYXZ,Ryxz,RyXz,RyxZ,RyXZ,RZyx,RZYx,RZyX,RZYX,Rzyx,RzYx,RzyX,RzYX,RZxy,RZXy,RZxY,RZXY,Rzxy,RzXy,RzxY,RzXY,],
607 [RxZY,RxzY,RxZy,Rxzy,RXZY,RXzY,RXZy,RXzy,RxYZ,RxyZ,RxYz,Rxyz,RXYZ,RXyZ,RXYz,RXyz,RyZX,RyzX,RyZx,Ryzx,RYZX,RYzX,RYZx,RYzx,RyXZ,RyxZ,RyXz,Ryxz,RYXZ,RYxZ,RYXz,RYxz,RzYX,RzyX,RzYx,Rzyx,RZYX,RZyX,RZYx,RZyx,RzXY,RzxY,RzXy,Rzxy,RZXY,RZxY,RZXy,RZxy,],
608 [RxZy,Rxzy,RxZY,RxzY,RXZy,RXzy,RXZY,RXzY,RxYz,Rxyz,RxYZ,RxyZ,RXYz,RXyz,RXYZ,RXyZ,RyZx,Ryzx,RyZX,RyzX,RYZx,RYzx,RYZX,RYzX,RyXz,Ryxz,RyXZ,RyxZ,RYXz,RYxz,RYXZ,RYxZ,RzYx,Rzyx,RzYX,RzyX,RZYx,RZyx,RZYX,RZyX,RzXy,Rzxy,RzXY,RzxY,RZXy,RZxy,RZXY,RZxY,],
609 [RxzY,RxZY,Rxzy,RxZy,RXzY,RXZY,RXzy,RXZy,RxyZ,RxYZ,Rxyz,RxYz,RXyZ,RXYZ,RXyz,RXYz,RyzX,RyZX,Ryzx,RyZx,RYzX,RYZX,RYzx,RYZx,RyxZ,RyXZ,Ryxz,RyXz,RYxZ,RYXZ,RYxz,RYXz,RzyX,RzYX,Rzyx,RzYx,RZyX,RZYX,RZyx,RZYx,RzxY,RzXY,Rzxy,RzXy,RZxY,RZXY,RZxy,RZXy,],
610 [Rxzy,RxZy,RxzY,RxZY,RXzy,RXZy,RXzY,RXZY,Rxyz,RxYz,RxyZ,RxYZ,RXyz,RXYz,RXyZ,RXYZ,Ryzx,RyZx,RyzX,RyZX,RYzx,RYZx,RYzX,RYZX,Ryxz,RyXz,RyxZ,RyXZ,RYxz,RYXz,RYxZ,RYXZ,Rzyx,RzYx,RzyX,RzYX,RZyx,RZYx,RZyX,RZYX,Rzxy,RzXy,RzxY,RzXY,RZxy,RZXy,RZxY,RZXY,],
611 [RYXZ,RYXz,RyXZ,RyXz,RYxZ,RYxz,RyxZ,Ryxz,RZXY,RZXy,RzXY,RzXy,RZxY,RZxy,RzxY,Rzxy,RXYZ,RXYz,RxYZ,RxYz,RXyZ,RXyz,RxyZ,Rxyz,RZYX,RZYx,RzYX,RzYx,RZyX,RZyx,RzyX,Rzyx,RXZY,RXZy,RxZY,RxZy,RXzY,RXzy,RxzY,Rxzy,RYZX,RYZx,RyZX,RyZx,RYzX,RYzx,RyzX,Ryzx,],
612 [RYXz,RYXZ,RyXz,RyXZ,RYxz,RYxZ,Ryxz,RyxZ,RZXy,RZXY,RzXy,RzXY,RZxy,RZxY,Rzxy,RzxY,RXYz,RXYZ,RxYz,RxYZ,RXyz,RXyZ,Rxyz,RxyZ,RZYx,RZYX,RzYx,RzYX,RZyx,RZyX,Rzyx,RzyX,RXZy,RXZY,RxZy,RxZY,RXzy,RXzY,Rxzy,RxzY,RYZx,RYZX,RyZx,RyZX,RYzx,RYzX,Ryzx,RyzX,],
613 [RYxZ,RYxz,RyxZ,Ryxz,RYXZ,RYXz,RyXZ,RyXz,RZxY,RZxy,RzxY,Rzxy,RZXY,RZXy,RzXY,RzXy,RXyZ,RXyz,RxyZ,Rxyz,RXYZ,RXYz,RxYZ,RxYz,RZyX,RZyx,RzyX,Rzyx,RZYX,RZYx,RzYX,RzYx,RXzY,RXzy,RxzY,Rxzy,RXZY,RXZy,RxZY,RxZy,RYzX,RYzx,RyzX,Ryzx,RYZX,RYZx,RyZX,RyZx,],
614 [RYxz,RYxZ,Ryxz,RyxZ,RYXz,RYXZ,RyXz,RyXZ,RZxy,RZxY,Rzxy,RzxY,RZXy,RZXY,RzXy,RzXY,RXyz,RXyZ,Rxyz,RxyZ,RXYz,RXYZ,RxYz,RxYZ,RZyx,RZyX,Rzyx,RzyX,RZYx,RZYX,RzYx,RzYX,RXzy,RXzY,Rxzy,RxzY,RXZy,RXZY,RxZy,RxZY,RYzx,RYzX,Ryzx,RyzX,RYZx,RYZX,RyZx,RyZX,],
615 [RyXZ,RyXz,RYXZ,RYXz,RyxZ,Ryxz,RYxZ,RYxz,RzXY,RzXy,RZXY,RZXy,RzxY,Rzxy,RZxY,RZxy,RxYZ,RxYz,RXYZ,RXYz,RxyZ,Rxyz,RXyZ,RXyz,RzYX,RzYx,RZYX,RZYx,RzyX,Rzyx,RZyX,RZyx,RxZY,RxZy,RXZY,RXZy,RxzY,Rxzy,RXzY,RXzy,RyZX,RyZx,RYZX,RYZx,RyzX,Ryzx,RYzX,RYzx,],
616 [RyXz,RyXZ,RYXz,RYXZ,Ryxz,RyxZ,RYxz,RYxZ,RzXy,RzXY,RZXy,RZXY,Rzxy,RzxY,RZxy,RZxY,RxYz,RxYZ,RXYz,RXYZ,Rxyz,RxyZ,RXyz,RXyZ,RzYx,RzYX,RZYx,RZYX,Rzyx,RzyX,RZyx,RZyX,RxZy,RxZY,RXZy,RXZY,Rxzy,RxzY,RXzy,RXzY,RyZx,RyZX,RYZx,RYZX,Ryzx,RyzX,RYzx,RYzX,],
617 [RyxZ,Ryxz,RYxZ,RYxz,RyXZ,RyXz,RYXZ,RYXz,RzxY,Rzxy,RZxY,RZxy,RzXY,RzXy,RZXY,RZXy,RxyZ,Rxyz,RXyZ,RXyz,RxYZ,RxYz,RXYZ,RXYz,RzyX,Rzyx,RZyX,RZyx,RzYX,RzYx,RZYX,RZYx,RxzY,Rxzy,RXzY,RXzy,RxZY,RxZy,RXZY,RXZy,RyzX,Ryzx,RYzX,RYzx,RyZX,RyZx,RYZX,RYZx,],
618 [Ryxz,RyxZ,RYxz,RYxZ,RyXz,RyXZ,RYXz,RYXZ,Rzxy,RzxY,RZxy,RZxY,RzXy,RzXY,RZXy,RZXY,Rxyz,RxyZ,RXyz,RXyZ,RxYz,RxYZ,RXYz,RXYZ,Rzyx,RzyX,RZyx,RZyX,RzYx,RzYX,RZYx,RZYX,Rxzy,RxzY,RXzy,RXzY,RxZy,RxZY,RXZy,RXZY,Ryzx,RyzX,RYzx,RYzX,RyZx,RyZX,RYZx,RYZX,],
619 [RYZX,RYzX,RyZX,RyzX,RYZx,RYzx,RyZx,Ryzx,RZYX,RZyX,RzYX,RzyX,RZYx,RZyx,RzYx,Rzyx,RXZY,RXzY,RxZY,RxzY,RXZy,RXzy,RxZy,Rxzy,RZXY,RZxY,RzXY,RzxY,RZXy,RZxy,RzXy,Rzxy,RXYZ,RXyZ,RxYZ,RxyZ,RXYz,RXyz,RxYz,Rxyz,RYXZ,RYxZ,RyXZ,RyxZ,RYXz,RYxz,RyXz,Ryxz,],
620 [RYZx,RYzx,RyZx,Ryzx,RYZX,RYzX,RyZX,RyzX,RZYx,RZyx,RzYx,Rzyx,RZYX,RZyX,RzYX,RzyX,RXZy,RXzy,RxZy,Rxzy,RXZY,RXzY,RxZY,RxzY,RZXy,RZxy,RzXy,Rzxy,RZXY,RZxY,RzXY,RzxY,RXYz,RXyz,RxYz,Rxyz,RXYZ,RXyZ,RxYZ,RxyZ,RYXz,RYxz,RyXz,Ryxz,RYXZ,RYxZ,RyXZ,RyxZ,],
621 [RYzX,RYZX,RyzX,RyZX,RYzx,RYZx,Ryzx,RyZx,RZyX,RZYX,RzyX,RzYX,RZyx,RZYx,Rzyx,RzYx,RXzY,RXZY,RxzY,RxZY,RXzy,RXZy,Rxzy,RxZy,RZxY,RZXY,RzxY,RzXY,RZxy,RZXy,Rzxy,RzXy,RXyZ,RXYZ,RxyZ,RxYZ,RXyz,RXYz,Rxyz,RxYz,RYxZ,RYXZ,RyxZ,RyXZ,RYxz,RYXz,Ryxz,RyXz,],
622 [RYzx,RYZx,Ryzx,RyZx,RYzX,RYZX,RyzX,RyZX,RZyx,RZYx,Rzyx,RzYx,RZyX,RZYX,RzyX,RzYX,RXzy,RXZy,Rxzy,RxZy,RXzY,RXZY,RxzY,RxZY,RZxy,RZXy,Rzxy,RzXy,RZxY,RZXY,RzxY,RzXY,RXyz,RXYz,Rxyz,RxYz,RXyZ,RXYZ,RxyZ,RxYZ,RYxz,RYXz,Ryxz,RyXz,RYxZ,RYXZ,RyxZ,RyXZ,],
623 [RyZX,RyzX,RYZX,RYzX,RyZx,Ryzx,RYZx,RYzx,RzYX,RzyX,RZYX,RZyX,RzYx,Rzyx,RZYx,RZyx,RxZY,RxzY,RXZY,RXzY,RxZy,Rxzy,RXZy,RXzy,RzXY,RzxY,RZXY,RZxY,RzXy,Rzxy,RZXy,RZxy,RxYZ,RxyZ,RXYZ,RXyZ,RxYz,Rxyz,RXYz,RXyz,RyXZ,RyxZ,RYXZ,RYxZ,RyXz,Ryxz,RYXz,RYxz,],
624 [RyZx,Ryzx,RYZx,RYzx,RyZX,RyzX,RYZX,RYzX,RzYx,Rzyx,RZYx,RZyx,RzYX,RzyX,RZYX,RZyX,RxZy,Rxzy,RXZy,RXzy,RxZY,RxzY,RXZY,RXzY,RzXy,Rzxy,RZXy,RZxy,RzXY,RzxY,RZXY,RZxY,RxYz,Rxyz,RXYz,RXyz,RxYZ,RxyZ,RXYZ,RXyZ,RyXz,Ryxz,RYXz,RYxz,RyXZ,RyxZ,RYXZ,RYxZ,],
625 [RyzX,RyZX,RYzX,RYZX,Ryzx,RyZx,RYzx,RYZx,RzyX,RzYX,RZyX,RZYX,Rzyx,RzYx,RZyx,RZYx,RxzY,RxZY,RXzY,RXZY,Rxzy,RxZy,RXzy,RXZy,RzxY,RzXY,RZxY,RZXY,Rzxy,RzXy,RZxy,RZXy,RxyZ,RxYZ,RXyZ,RXYZ,Rxyz,RxYz,RXyz,RXYz,RyxZ,RyXZ,RYxZ,RYXZ,Ryxz,RyXz,RYxz,RYXz,],
626 [Ryzx,RyZx,RYzx,RYZx,RyzX,RyZX,RYzX,RYZX,Rzyx,RzYx,RZyx,RZYx,RzyX,RzYX,RZyX,RZYX,Rxzy,RxZy,RXzy,RXZy,RxzY,RxZY,RXzY,RXZY,Rzxy,RzXy,RZxy,RZXy,RzxY,RzXY,RZxY,RZXY,Rxyz,RxYz,RXyz,RXYz,RxyZ,RxYZ,RXyZ,RXYZ,Ryxz,RyXz,RYxz,RYXz,RyxZ,RyXZ,RYxZ,RYXZ,],
627 [RZXY,RzXY,RZXy,RzXy,RZxY,RzxY,RZxy,Rzxy,RYXZ,RyXZ,RYXz,RyXz,RYxZ,RyxZ,RYxz,Ryxz,RZYX,RzYX,RZYx,RzYx,RZyX,RzyX,RZyx,Rzyx,RXYZ,RxYZ,RXYz,RxYz,RXyZ,RxyZ,RXyz,Rxyz,RYZX,RyZX,RYZx,RyZx,RYzX,RyzX,RYzx,Ryzx,RXZY,RxZY,RXZy,RxZy,RXzY,RxzY,RXzy,Rxzy,],
628 [RZXy,RzXy,RZXY,RzXY,RZxy,Rzxy,RZxY,RzxY,RYXz,RyXz,RYXZ,RyXZ,RYxz,Ryxz,RYxZ,RyxZ,RZYx,RzYx,RZYX,RzYX,RZyx,Rzyx,RZyX,RzyX,RXYz,RxYz,RXYZ,RxYZ,RXyz,Rxyz,RXyZ,RxyZ,RYZx,RyZx,RYZX,RyZX,RYzx,Ryzx,RYzX,RyzX,RXZy,RxZy,RXZY,RxZY,RXzy,Rxzy,RXzY,RxzY,],
629 [RZxY,RzxY,RZxy,Rzxy,RZXY,RzXY,RZXy,RzXy,RYxZ,RyxZ,RYxz,Ryxz,RYXZ,RyXZ,RYXz,RyXz,RZyX,RzyX,RZyx,Rzyx,RZYX,RzYX,RZYx,RzYx,RXyZ,RxyZ,RXyz,Rxyz,RXYZ,RxYZ,RXYz,RxYz,RYzX,RyzX,RYzx,Ryzx,RYZX,RyZX,RYZx,RyZx,RXzY,RxzY,RXzy,Rxzy,RXZY,RxZY,RXZy,RxZy,],
630 [RZxy,Rzxy,RZxY,RzxY,RZXy,RzXy,RZXY,RzXY,RYxz,Ryxz,RYxZ,RyxZ,RYXz,RyXz,RYXZ,RyXZ,RZyx,Rzyx,RZyX,RzyX,RZYx,RzYx,RZYX,RzYX,RXyz,Rxyz,RXyZ,RxyZ,RXYz,RxYz,RXYZ,RxYZ,RYzx,Ryzx,RYzX,RyzX,RYZx,RyZx,RYZX,RyZX,RXzy,Rxzy,RXzY,RxzY,RXZy,RxZy,RXZY,RxZY,],
631 [RzXY,RZXY,RzXy,RZXy,RzxY,RZxY,Rzxy,RZxy,RyXZ,RYXZ,RyXz,RYXz,RyxZ,RYxZ,Ryxz,RYxz,RzYX,RZYX,RzYx,RZYx,RzyX,RZyX,Rzyx,RZyx,RxYZ,RXYZ,RxYz,RXYz,RxyZ,RXyZ,Rxyz,RXyz,RyZX,RYZX,RyZx,RYZx,RyzX,RYzX,Ryzx,RYzx,RxZY,RXZY,RxZy,RXZy,RxzY,RXzY,Rxzy,RXzy,],
632 [RzXy,RZXy,RzXY,RZXY,Rzxy,RZxy,RzxY,RZxY,RyXz,RYXz,RyXZ,RYXZ,Ryxz,RYxz,RyxZ,RYxZ,RzYx,RZYx,RzYX,RZYX,Rzyx,RZyx,RzyX,RZyX,RxYz,RXYz,RxYZ,RXYZ,Rxyz,RXyz,RxyZ,RXyZ,RyZx,RYZx,RyZX,RYZX,Ryzx,RYzx,RyzX,RYzX,RxZy,RXZy,RxZY,RXZY,Rxzy,RXzy,RxzY,RXzY,],
633 [RzxY,RZxY,Rzxy,RZxy,RzXY,RZXY,RzXy,RZXy,RyxZ,RYxZ,Ryxz,RYxz,RyXZ,RYXZ,RyXz,RYXz,RzyX,RZyX,Rzyx,RZyx,RzYX,RZYX,RzYx,RZYx,RxyZ,RXyZ,Rxyz,RXyz,RxYZ,RXYZ,RxYz,RXYz,RyzX,RYzX,Ryzx,RYzx,RyZX,RYZX,RyZx,RYZx,RxzY,RXzY,Rxzy,RXzy,RxZY,RXZY,RxZy,RXZy,],
634 [Rzxy,RZxy,RzxY,RZxY,RzXy,RZXy,RzXY,RZXY,Ryxz,RYxz,RyxZ,RYxZ,RyXz,RYXz,RyXZ,RYXZ,Rzyx,RZyx,RzyX,RZyX,RzYx,RZYx,RzYX,RZYX,Rxyz,RXyz,RxyZ,RXyZ,RxYz,RXYz,RxYZ,RXYZ,Ryzx,RYzx,RyzX,RYzX,RyZx,RYZx,RyZX,RYZX,Rxzy,RXzy,RxzY,RXzY,RxZy,RXZy,RxZY,RXZY,],
635 [RZYX,RzYX,RZyX,RzyX,RZYx,RzYx,RZyx,Rzyx,RYZX,RyZX,RYzX,RyzX,RYZx,RyZx,RYzx,Ryzx,RZXY,RzXY,RZxY,RzxY,RZXy,RzXy,RZxy,Rzxy,RXZY,RxZY,RXzY,RxzY,RXZy,RxZy,RXzy,Rxzy,RYXZ,RyXZ,RYxZ,RyxZ,RYXz,RyXz,RYxz,Ryxz,RXYZ,RxYZ,RXyZ,RxyZ,RXYz,RxYz,RXyz,Rxyz,],
636 [RZYx,RzYx,RZyx,Rzyx,RZYX,RzYX,RZyX,RzyX,RYZx,RyZx,RYzx,Ryzx,RYZX,RyZX,RYzX,RyzX,RZXy,RzXy,RZxy,Rzxy,RZXY,RzXY,RZxY,RzxY,RXZy,RxZy,RXzy,Rxzy,RXZY,RxZY,RXzY,RxzY,RYXz,RyXz,RYxz,Ryxz,RYXZ,RyXZ,RYxZ,RyxZ,RXYz,RxYz,RXyz,Rxyz,RXYZ,RxYZ,RXyZ,RxyZ,],
637 [RZyX,RzyX,RZYX,RzYX,RZyx,Rzyx,RZYx,RzYx,RYzX,RyzX,RYZX,RyZX,RYzx,Ryzx,RYZx,RyZx,RZxY,RzxY,RZXY,RzXY,RZxy,Rzxy,RZXy,RzXy,RXzY,RxzY,RXZY,RxZY,RXzy,Rxzy,RXZy,RxZy,RYxZ,RyxZ,RYXZ,RyXZ,RYxz,Ryxz,RYXz,RyXz,RXyZ,RxyZ,RXYZ,RxYZ,RXyz,Rxyz,RXYz,RxYz,],
638 [RZyx,Rzyx,RZYx,RzYx,RZyX,RzyX,RZYX,RzYX,RYzx,Ryzx,RYZx,RyZx,RYzX,RyzX,RYZX,RyZX,RZxy,Rzxy,RZXy,RzXy,RZxY,RzxY,RZXY,RzXY,RXzy,Rxzy,RXZy,RxZy,RXzY,RxzY,RXZY,RxZY,RYxz,Ryxz,RYXz,RyXz,RYxZ,RyxZ,RYXZ,RyXZ,RXyz,Rxyz,RXYz,RxYz,RXyZ,RxyZ,RXYZ,RxYZ,],
639 [RzYX,RZYX,RzyX,RZyX,RzYx,RZYx,Rzyx,RZyx,RyZX,RYZX,RyzX,RYzX,RyZx,RYZx,Ryzx,RYzx,RzXY,RZXY,RzxY,RZxY,RzXy,RZXy,Rzxy,RZxy,RxZY,RXZY,RxzY,RXzY,RxZy,RXZy,Rxzy,RXzy,RyXZ,RYXZ,RyxZ,RYxZ,RyXz,RYXz,Ryxz,RYxz,RxYZ,RXYZ,RxyZ,RXyZ,RxYz,RXYz,Rxyz,RXyz,],
640 [RzYx,RZYx,Rzyx,RZyx,RzYX,RZYX,RzyX,RZyX,RyZx,RYZx,Ryzx,RYzx,RyZX,RYZX,RyzX,RYzX,RzXy,RZXy,Rzxy,RZxy,RzXY,RZXY,RzxY,RZxY,RxZy,RXZy,Rxzy,RXzy,RxZY,RXZY,RxzY,RXzY,RyXz,RYXz,Ryxz,RYxz,RyXZ,RYXZ,RyxZ,RYxZ,RxYz,RXYz,Rxyz,RXyz,RxYZ,RXYZ,RxyZ,RXyZ,],
641 [RzyX,RZyX,RzYX,RZYX,Rzyx,RZyx,RzYx,RZYx,RyzX,RYzX,RyZX,RYZX,Ryzx,RYzx,RyZx,RYZx,RzxY,RZxY,RzXY,RZXY,Rzxy,RZxy,RzXy,RZXy,RxzY,RXzY,RxZY,RXZY,Rxzy,RXzy,RxZy,RXZy,RyxZ,RYxZ,RyXZ,RYXZ,Ryxz,RYxz,RyXz,RYXz,RxyZ,RXyZ,RxYZ,RXYZ,Rxyz,RXyz,RxYz,RXYz,],
642 [Rzyx,RZyx,RzYx,RZYx,RzyX,RZyX,RzYX,RZYX,Ryzx,RYzx,RyZx,RYZx,RyzX,RYzX,RyZX,RYZX,Rzxy,RZxy,RzXy,RZXy,RzxY,RZxY,RzXY,RZXY,Rxzy,RXzy,RxZy,RXZy,RxzY,RXzY,RxZY,RXZY,Ryxz,RYxz,RyXz,RYXz,RyxZ,RYxZ,RyXZ,RYXZ,Rxyz,RXyz,RxYz,RXYz,RxyZ,RXyZ,RxYZ,RXYZ,],
643 ]
644};
645
646#[cfg(test)]
647mod tests {
648 use super::*;
649 use crate::math::{FaceMap, GridPoint};
650 use crate::util::MultiFailure;
651 use Face6::*;
652 use num_traits::One;
653 use std::collections::HashSet;
654
655 #[test]
656 fn identity() {
657 assert_eq!(GridRotation::IDENTITY, GridRotation::one());
658 assert_eq!(GridRotation::IDENTITY, GridRotation::default());
659 assert_eq!(
660 GridRotation::IDENTITY,
661 GridRotation::from_basis([PX, PY, PZ])
662 );
663 }
664
665 #[test]
666 fn inverse_axioms() {
667 assert_eq!(GridRotation::IDENTITY.inverse(), GridRotation::IDENTITY);
668 for rot in GridRotation::ALL {
669 assert_eq!(rot * rot.inverse(), GridRotation::IDENTITY, "{rot:?}");
670 assert_eq!(rot.inverse().inverse(), rot, "{rot:?}");
671 }
672 }
673
674 #[test]
675 fn inverse_effect() {
676 let v = GridVector::new(1, 5, 100);
677 for rot in GridRotation::ALL {
678 assert_eq!(
679 rot.transform_vector(rot.inverse().transform_vector(v)),
680 v,
681 "{rot:?}"
682 );
683 assert_eq!(
684 rot.inverse().transform_vector(rot.transform_vector(v)),
685 v,
686 "{rot:?}"
687 );
688 }
689 }
690
691 #[test]
694 fn inverse_from_iterate() {
695 let mut ok = true;
696 for rot in GridRotation::ALL {
697 let iter_inv = rot.iterate().last().unwrap();
698 let inv = rot.inverse();
699 println!("{rot:?} => {iter_inv:?}, // {inv:?}");
700 ok = ok && iter_inv == inv;
701 }
702 assert!(ok);
703 }
704
705 #[test]
708 fn composition_consistency() {
709 let mut f = MultiFailure::new();
710 for first in GridRotation::ALL {
711 for second in GridRotation::ALL {
712 f.catch(|| {
713 let composed = second * first;
714 assert_eq!(
715 FaceMap::from_fn(|face| { composed.transform(face) }),
716 FaceMap::from_fn(|face| { second.transform(first.transform(face)) }),
717 "{second:?} * {first:?}",
718 );
719 });
720 }
721 }
722 }
723
724 #[test]
725 fn is_reflection_consistency() {
726 for a in GridRotation::ALL {
727 for b in GridRotation::ALL {
728 assert_eq!(
729 a.is_reflection() ^ b.is_reflection(),
730 (a * b).is_reflection(),
731 "{a:?}, {b:?}",
732 );
733 }
734 }
735 }
736
737 #[test]
740 fn enumeration() {
741 let mut set = HashSet::new();
742 for rot in GridRotation::ALL {
743 set.insert(rot);
744 }
745 assert_eq!(set.len(), GridRotation::ALL.len());
746 assert_eq!(48, GridRotation::ALL.len());
747 }
748
749 #[test]
751 fn all_but_reflections() {
752 let mut set = HashSet::new();
753 for rot in GridRotation::ALL_BUT_REFLECTIONS {
754 assert!(!rot.is_reflection(), "{rot:?} is a reflection");
755 set.insert(rot);
756 }
757 assert_eq!(set.len(), GridRotation::ALL_BUT_REFLECTIONS.len());
758 assert_eq!(
760 GridRotation::ALL.len(),
761 GridRotation::ALL_BUT_REFLECTIONS.len() * 2,
762 );
763 }
764
765 #[test]
767 fn equivalent_rotation_matrix() {
768 for rot in GridRotation::ALL {
769 let point = GridPoint::new(1, 20, 300);
770 assert_eq!(
771 rot.transform_vector(point.to_vector()).to_point(),
772 rot.to_rotation_matrix().transform_point(point),
773 );
774 }
775 }
776
777 #[test]
778 fn equivalent_transform_vector_transform_size() {
779 for rot in GridRotation::ALL {
780 let vector = GridVector::new(1, 20, 300);
781 assert_eq!(
782 rot.transform_vector(vector).abs().to_u32(),
783 rot.transform_size(GridSize::from(vector.to_u32())).to_vector()
784 )
785 }
786 }
787
788 #[test]
790 fn from_to_exhaustive() {
791 let mut f = MultiFailure::new();
792 for from_face in Face6::ALL {
793 for to_face in Face6::ALL {
794 for up_face in Face6::ALL {
795 f.catch(|| {
796 let result = GridRotation::from_to(from_face, to_face, up_face);
797 let info = (from_face, to_face, up_face, result);
798 match result {
799 Some(result) => {
800 assert!(!result.is_reflection());
801 assert_eq!(
802 result.transform(from_face),
803 to_face,
804 "wrong from-to: {info:?}"
805 );
806 assert_eq!(
807 result.transform(up_face),
808 up_face,
809 "did not preserve up vector: {info:?}"
810 );
811 }
812 None => {
813 assert!(
814 up_face.axis() == from_face.axis()
815 || up_face.axis() == to_face.axis(),
816 "returned None incorrectly: {info:?}"
817 );
818 }
819 }
820 });
821 }
822 }
823 }
824 }
825
826 #[test]
827 fn regenerate_multiplication_table() {
828 let mut failed = false;
829 println!(indoc::indoc! {
830 "
831 #[rustfmt::skip]
832 static MULTIPLICATION_TABLE: [[GridRotation; 48]; 48] = {{
833 use GridRotation::*;
834 ["
835 });
836 for first in GridRotation::ALL {
837 print!(" [");
838 for second in GridRotation::ALL {
839 let result =
841 GridRotation::from_basis(first.to_basis().map(|v| second.transform(v)));
842
843 print!("{result:?},");
844 if result != second * first {
846 failed = true;
847 }
848 }
849 println!("],");
850 }
851 println!(" ]\n}};");
852 if failed {
853 panic!("multiplication results were not as expected");
854 }
855 }
856}