src/cube.rs

changeset 14
d0f20c6cb49c
parent 13
f67949050a32
child 15
2f4af30af476
--- a/src/cube.rs	Tue Oct 22 08:39:46 2024 -0500
+++ b/src/cube.rs	Tue Oct 22 09:34:12 2024 -0500
@@ -187,31 +187,63 @@
     /// Indicates whether an unfolded point `p` is on this face, i.e.,
     /// has coordinates in [0,1]².
     pub fn is_in_face(&self, p: &Point) -> bool {
-        p.iter().map(|t| t.abs()).all(|t| 0.0 <= t && t <= 1.0)
+        p.iter().all(|t| 0.0 <= *t && *t <= 1.0)
     }
 
-    /// Given an unfolded point `p`, possibly outside this face, finds
-    /// the edge, presented by an adjacent face, in whose direction it is.
+    /// Given an unfolded point `p` and a destination point `d` in unfolded coordinates,
+    /// but possibly outside this face, find the crossing point of the line between
+    /// `p` and `d` on an edge of (`self`). Return the point and the edge presented
+    /// by an adjacent face.
     ///
-    /// **TODO:** this does not correctly handle corners, i.e., when the point is not in
-    /// the direction of an adjacent face.
-    pub fn find_crossing(&self, p : &Point) -> Face {
+    /// Crossing at corners is decided arbitrarily.
+    pub fn find_crossing(&self, p :& Point, d : &Point) -> (Face, Point) {
+        //assert!(self.is_in_face(p));
+        
+        if self.is_in_face(d) {
+            return (*self, *p)
+        }
+
+        use std::cmp::Ordering::*;
+
         let &Loc([x, y]) = p;
-        use std::cmp::Ordering::*;
-        let crossing = |t| match (0.0 <= t, t<=1.0) {
+        let &Loc([xd, yd]) = d;
+        let tx = xd - x;
+        let ty = yd - y;
+
+        // Move towards tangent as (x + s tx, y + s ty) for the largest s<=1.0 for which
+        // both coordinates is within [0, 1].
+        let sx = match tx.partial_cmp(&0.0) {
+            Some(Less) =>  1.0f64.min(-x/tx),
+            Some(Greater) => 1.0f64.min((1.0-x)/tx),
+            _ => 1.0,
+        };
+        let sy = match ty.partial_cmp(&0.0) {
+            Some(Less) => 1.0f64.min(-y/ty),
+            Some(Greater) => 1.0f64.min((1.0-y)/ty),
+            _ => 1.0,
+        };
+        // We use the point where one coordinate comes within the range to decide on
+        // the edge.
+        let s = sx.max(sy);
+        // But move such that both coordinates are within the range.
+        let c = sx.min(sy);
+        let (cx, cy) = (x + c*tx, y + c*ty);
+
+        let crossing = |t| match (0.0 <= t, t <= 1.0) {
             (false, _) => Less,
             (_, false) => Greater,
             _ => Equal,
         };
 
         // TODO: how to properly handle corners? Just throw an error?
-        match (crossing(x), crossing(y)) {
+        let crossing = match (crossing(x + s*tx), crossing(y + s*ty)) {
             (Equal, Equal) => *self,
             (Less, _) => self.adjacent_faces()[0],
             (Greater, _) => self.adjacent_faces()[1],
             (Equal, Less) => self.adjacent_faces()[2],
             (Equal, Greater) => self.adjacent_faces()[3],
-        }
+        };
+        (crossing, Loc([cx, cy]))
     }
 
     /// Get embedded 3D coordinates
@@ -278,16 +310,18 @@
 
     fn exp(self, tangent : &Self::Tangent) -> Self {
         let mut face = self.face;
-        let mut point = self.point + tangent;
+        let mut point = self.point;
+        let mut dest = self.point + tangent;
         loop {
-            let next_face = face.find_crossing(&point);
+            let (next_face, cross) = face.find_crossing(&point, &dest);
             if next_face == face {
                 break
             }
-            point = next_face.convert_adjacent(face, &point).unwrap();
+            point = next_face.convert_adjacent(face, &cross).unwrap();
+            dest = next_face.convert_adjacent(face, &dest).unwrap();
             face = next_face;
         }
-        OnCube { face, point }
+        OnCube { face, point : dest }
     }
 
     fn log(&self, other : &Self) -> Self::Tangent {
@@ -311,8 +345,8 @@
     fn convert_adjacent() {
         let point = Loc([0.4, 0.6]);
 
-        for f1 in [F1, F2, F3, F4, F5, F6] {
-            for f2 in [F1, F2, F3, F4, F5, F6] {
+        for f1 in Face::all() {
+            for f2 in Face::all() {
                 println!("{:?}-{:?}", f1, f2);
                 match f1.convert_adjacent(f2, &point) {
                     None => assert_eq!(f2.opposing_face(), f1),
@@ -333,8 +367,8 @@
     // fn convert_paths() {
     //     let point = Loc([0.4, 0.6]);
 
-    //     for f1 in [F1, F2, F3, F4, F5, F6] {
-    //         for f2 in [F1, F2, F3, F4, F5, F6] {
+    //     for f1 in Face::all() {
+    //         for f2 in Face::all() {
     //             for p1 in f2.paths(f1) {
     //                 for p2 in f1.paths(f2) {
     //                     println!("{:?}-{:?}; {:?} {:?}", f1, f2, p1, p2);

mercurial