use crate::*;
pub fn crossed_cylinders(lens1: SpheroCyl, lens2: SpheroCyl) -> SpheroCyl {
let s1 = lens1.sphere;
let c1 = lens1.cylinder;
let a1 = lens1.axis_deg.to_radians();
let sin1 = a1.sin();
let cos1 = a1.cos();
let px1 = s1 + c1 * sin1 * sin1;
let pt1 = -c1 * sin1 * cos1;
let py1 = s1 + c1 * cos1 * cos1;
let s2 = lens2.sphere;
let c2 = lens2.cylinder;
let a2 = lens2.axis_deg.to_radians();
let sin2 = a2.sin();
let cos2 = a2.cos();
let px2 = s2 + c2 * sin2 * sin2;
let pt2 = -c2 * sin2 * cos2;
let py2 = s2 + c2 * cos2 * cos2;
let px = px1 + px2;
let pt = pt1 + pt2;
let py = py1 + py2;
let trace = px + py;
let determinant = (px * py) - (pt * pt);
let delta = (trace * trace - 4.0 * determinant).max(0.0).sqrt();
let cylinder = -delta;
let sphere = (trace - cylinder) / 2.0;
let axis_rad = (sphere - px).atan2(pt);
let mut axis_deg = axis_rad.to_degrees();
axis_deg = axis_deg % 180.0;
if axis_deg < 0.0 {
axis_deg += 180.0;
}
SpheroCyl {
sphere,
cylinder,
axis_deg,
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_abs_diff_eq;
fn matrix_from_lens(l: SpheroCyl) -> (f64, f64, f64) {
let a = l.axis_deg.to_radians();
let s = l.sphere;
let c = l.cylinder;
let sin = a.sin();
let cos = a.cos();
let px = s + c * sin * sin;
let pt = -c * sin * cos;
let py = s + c * cos * cos;
(px, pt, py)
}
#[test]
fn pure_spheres_add() {
let l1 = SpheroCyl {
sphere: 2.0,
cylinder: 0.0,
axis_deg: 0.0,
};
let l2 = SpheroCyl {
sphere: -1.25,
cylinder: 0.0,
axis_deg: 90.0,
};
let r = crossed_cylinders(l1, l2);
assert_abs_diff_eq!(r.sphere, 0.75, epsilon = 1e-12);
assert_abs_diff_eq!(r.cylinder, 0.0, epsilon = 1e-12);
assert!(r.axis_deg >= 0.0 && r.axis_deg < 180.0);
}
#[test]
fn aligned_minus_cylinders_add() {
let l1 = SpheroCyl {
sphere: 0.0,
cylinder: -2.0,
axis_deg: 90.0,
};
let l2 = SpheroCyl {
sphere: 0.0,
cylinder: -1.0,
axis_deg: 90.0,
};
let r = crossed_cylinders(l1, l2);
assert_abs_diff_eq!(r.sphere, 0.0, epsilon = 1e-12);
assert_abs_diff_eq!(r.cylinder, -3.0, epsilon = 1e-12);
assert_abs_diff_eq!(r.axis_deg, 90.0, epsilon = 1e-12);
}
#[test]
fn matrix_consistency_oblique_case() {
let l1 = SpheroCyl {
sphere: -4.0,
cylinder: 2.0,
axis_deg: 45.0,
};
let l2 = SpheroCyl {
sphere: -5.0,
cylinder: -3.0,
axis_deg: 120.0,
};
let (px1, pt1, py1) = matrix_from_lens(l1);
let (px2, pt2, py2) = matrix_from_lens(l2);
let px_sum = px1 + px2;
let pt_sum = pt1 + pt2;
let py_sum = py1 + py2;
let r = crossed_cylinders(l1, l2);
let (pxr, ptr, pyr) = matrix_from_lens(r);
assert_abs_diff_eq!(pxr, px_sum, epsilon = 1e-9);
assert_abs_diff_eq!(ptr, pt_sum, epsilon = 1e-9);
assert_abs_diff_eq!(pyr, py_sum, epsilon = 1e-9);
}
}