Fri, 18 Oct 2024 13:47:28 -0500
Basic cube logarithm
use core::f64; use alg_tools::loc::Loc; use alg_tools::norms::{Norm, L2}; use crate::manifold::ManifoldPoint; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Face {F1, F2, F3, F4, F5, F6} use Face::*; pub type Point = Loc<f64, 2>; pub type AdjacentFaces = [Face; 4]; pub enum Path { Direct { destination : Face }, Indirect { destination : Face, intermediate : Face }, } /// An iterator over paths on a cube, from a source face to a destination face. #[derive(Clone, Debug)] pub enum PathIter { Direct(Face), Indirect{ destination : Face, intermediate : AdjacentFaces, current : usize}, Exhausted, } impl std::iter::Iterator for PathIter { type Item = Path; fn next(&mut self) -> Option<Self::Item> { use PathIter::*; match self { &mut Exhausted => None, &mut Direct(destination) => { *self = Exhausted; Some(Path::Direct { destination }) }, &mut Indirect{destination, intermediate : ref i, ref mut current} => { if *current < i.len() { let intermediate = i[*current]; *current += 1; Some(Path::Indirect{ destination, intermediate }) } else { *self = Exhausted; None } } } } } impl Face { /// Returns an array of the four faces adjacent to `self`. pub fn adjacent_faces(&self) -> AdjacentFaces { match *self { F1 => [F2, F3, F4, F5], F2 => [F1, F4, F5, F6], F3 => [F1, F4, F5, F6], F4 => [F1, F2, F3, F6], F5 => [F1, F2, F3, F6], F6 => [F2, F3, F4, F5], } } /// Returns the face opposing `self`. pub fn opposing_face(&self) -> Face { match *self { F1 => F6, F2 => F3, F3 => F2, F4 => F6, F5 => F4, F6 => F1, } } /// Converts a point on an adjacent face to the coordinate system of `self`. pub fn convert_adjacent(&self, adjacent : Face, p: &Point) -> Option<Point> { let Loc([x, y]) = *p; let mk = |x, y| Some(Loc([x, y])); match adjacent { F1 => match *self { F2 => mk(y, x - 1.0), F3 => mk(1.0 - y, -x), F4 => mk(x, -y), F5 => mk(1.0 - x, y - 1.0), F1 => mk(x, y), F6 => None, }, F2 => match *self { F1 => mk(y + 1.0, x), F4 => mk(x + 1.0, y), F5 => mk(x - 1.0, y), F6 => mk(2.0 - y, x), F2 => mk(x, y), F3 => None, }, F3 => match *self { F1 => mk(-y, 1.0 - x), F4 => mk(x - 1.0, y), F5 => mk(x + 1.0, y), F6 => mk(y - 1.0, 1.0 - x), F3 => mk(x, y), F2 => None, }, F4 => match *self { F1 => mk(x, -y), F2 => mk(x - 1.0, y), F3 => mk(x + 1.0, y), F6 => mk(x, y - 1.0), F4 => mk(x, y), F5 => None, }, F5 => match *self { F1 => mk(1.0 -x, y + 1.0), F2 => mk(x + 1.0, y), F3 => mk(x - 1.0, y), F6 => mk(1.0 -x, 2.0 - y), F5 => mk(x, y), F4 => None, }, F6 => match *self { F2 => mk(y, 2.0 - x), F3 => mk(1.0 - y, x + 1.0), F4 => mk(x, y + 1.0), F5 => mk(1.0 - x, 2.0 - y), F6 => mk(x, y), F1 => None, } } } /// Converts a point behind a path to the coordinate system of `self`. pub fn convert(&self, path : &Path, p: &Point) -> Point { use Path::*; match path { &Direct{ destination : d} => self.convert_adjacent(d, p), &Indirect{ destination : d, intermediate : i } => {dbg!((d,i)); dbg!(self.convert_adjacent(i, dbg!(&i.convert_adjacent(d, p).unwrap())))} }.unwrap() } /// Returns an iterator over all the paths from `self` to `other`. fn paths(&self, other : Face) -> PathIter { if self.opposing_face() == other { PathIter::Indirect { intermediate : self.adjacent_faces(), destination : other, current : 0 } } else { PathIter::Direct(other) } } } #[derive(Clone, Debug, PartialEq)] pub struct OnCube { face : Face, point : Point, } impl ManifoldPoint for OnCube { type Tangent = Point; fn exp(&self, tangent : &Self::Tangent) -> Self { unimplemented!(); } fn log(&self, other : &Self) -> Self::Tangent { let mut best_len = f64::INFINITY; let mut best_tan = Loc([0.0, 0.0]); for path in self.face.paths(other.face) { let tan = self.face.convert(&path, &other.point) - &self.point; let len = tan.norm(L2); if len < best_len { best_tan = tan; best_len = len; } } best_tan } } #[cfg(test)] mod tests { use super::*; #[test] fn log_test_adjacent() { let p1 = OnCube{ face : F1, point : Loc([0.5, 0.5])}; let p2 = OnCube{ face : F2, point : Loc([0.5, 0.5])}; assert_eq!(p1.log(&p2).norm(L2), 1.0); } #[test] fn log_test_opposing_equal() { let p1 = OnCube{ face : F1, point : Loc([0.5, 0.5])}; let p2 = OnCube{ face : F6, point : Loc([0.5, 0.5])}; assert_eq!(p1.log(&p2).norm(L2), 2.0); } #[test] fn log_test_opposing_unique_shortest() { let p1 = OnCube{ face : F1, point : Loc([0.3, 0.25])}; let p2 = OnCube{ face : F6, point : Loc([0.3, 0.25])}; assert_eq!(p1.log(&p2).norm(L2), 1.5); } }