Cylinder numerical stability hacks

Fri, 06 Dec 2024 13:27:48 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Fri, 06 Dec 2024 13:27:48 -0500
changeset 41
56d609b70a3b
parent 40
1d865db9d3e4
child 42
271fba635bce

Cylinder numerical stability hacks

src/cylinder.rs file | annotate | diff | comparison | revisions
--- a/src/cylinder.rs	Fri Dec 06 13:10:57 2024 -0500
+++ b/src/cylinder.rs	Fri Dec 06 13:27:48 2024 -0500
@@ -501,10 +501,16 @@
             let sideedge = SidePoint{ angle : φ, z };
             let t1 = cap.log(&capedge);
             let t2 = sideedge.log(r, side);
-            // Either option should give the same result, but the first one avoids division.
-            t1 + capedge.tangent_from_side(top, t2)
-            // let n = t1.norm(L2);
-            // (t1/n)*(n + t2.norm(L2))
+            // Either option should give the same result.
+            // The first one avoids division, but seems numerically more unstable due to
+            // sines and cosines.
+            //t1 + capedge.tangent_from_side(top, t2)
+            let n = t1.norm(L2);
+            if n < f64::EPSILON {
+                capedge.tangent_from_side(top, t2)
+            } else {
+                (t1/n)*(n + t2.norm(L2))
+            }
         }
     }
 
@@ -528,10 +534,16 @@
             let sideedge = SidePoint{ angle : φ, z };
             let t1 = side.log(r, &sideedge);
             let t2 = capedge.log(cap);
-            // Either option should give the same result, but the first one avoids division.
-            t1 + capedge.tangent_to_side(top, t2)
-            // let n = t1.norm(L2);
-            // (t1/n)*(n + t2.norm(L2))
+            // Either option should give the same result.
+            // The first one avoids division, but seems numerically more unstable due to
+            // sines and cosines.
+            // t1 + capedge.tangent_to_side(top, t2)
+            let n = t1.norm(L2);
+            if n < f64::EPSILON {
+                capedge.tangent_to_side(top, t2)
+            } else {
+                (t1/n)*(n + t2.norm(L2))
+            }
         }
     }
 
@@ -563,18 +575,24 @@
             let t1 = side1.log(r, &sideedge1);
             let t2 = capedge1.log(&capedge2);
             let t3 = sideedge2.log(r, &side2);
-            // Any option should give the same result, but the first one avoids division.
-            // t1 + capedge1.tangent_to_side(top, t2 + capedge2.tangent_from_side(top, t3))
-            //
-            // let n = t2.norm(L2);
-            // t1 + capedge1.tangent_to_side(top, (t2/n)*(n + t3.norm(L2)))
+            // Either option should give the same result.
+            // The first one avoids division, but seems numerically more unstable due to
+            // sines and cosines.
             //
+            // t1 + capedge1.tangent_to_side(top, t2 + capedge2.tangent_from_side(top, t3))
             let n = t1.norm(L2);
-            (t1/n)*(n + t2.norm(L2) + t3.norm(L2))
-            //
-            // let n = t1.norm(L2);
-            // let t23 = t2 + capedge2.tangent_from_side(top, t3);
-            // (t1/n)*(n + t23.norm(L2))
+            if n < f64::EPSILON {
+                let n2 = t2.norm(L2);
+                capedge1.tangent_to_side(top,
+                    if n2 < f64::EPSILON {
+                        capedge2.tangent_from_side(top, t3)
+                    } else {
+                        (t2/n2)*(n2 + t3.norm(L2))
+                    }
+                )
+            } else {
+                (t1/n)*(n + t2.norm(L2) + t3.norm(L2))
+            }
         }
     }
 
@@ -606,10 +624,23 @@
             let t1 = cap1.log(&capedge1);
             let t2 = sideedge1.log(r, &sideedge2);
             let t3 = capedge2.log(cap2);
-            // Either option should give the same result, but the first one avoids division.
-            t1 + capedge1.tangent_from_side(top1, t2 + capedge2.tangent_to_side(top2, t3))
-            //let n = t1.norm(L2);
-            //(t1/n)*(n + t2.norm(L2) + t3.norm(L2))
+            // Either option should give the same result.
+            // The first one avoids division, but seems numerically more unstable due to
+            // sines and cosines.
+            // t1 + capedge1.tangent_from_side(top1, t2 + capedge2.tangent_to_side(top2, t3))
+            let n = t1.norm(L2);
+            if n < f64::EPSILON {
+                let n2 = t2.norm(L2);
+                capedge1.tangent_from_side(top1,
+                    if n2 < f64::EPSILON {
+                        capedge2.tangent_to_side(top2, t3)
+                    } else {
+                        (t2/n2)*(n2 + t3.norm(L2))
+                    }
+                )
+            } else {
+                (t1/n)*(n + t2.norm(L2) + t3.norm(L2))
+            }
         }
     }
 
@@ -851,7 +882,7 @@
 mod tests {
     use super::*;
 
-    static TOL : f64 = 1e-8;
+    static TOL : f64 = 1e-7;
 
     macro_rules! check_distance {
         ($distance : expr, $expected : expr) => {

mercurial