use crate::*;
pub fn induced_prism(eye: Eye, lens: SpheroCyl, dec: Decentration) -> CombinedPrism {
let s: f64 = lens.sphere;
let c = lens.cylinder;
let axis_rad = lens.axis_deg.to_radians();
let sin_axis = axis_rad.sin();
let cos_axis = axis_rad.cos();
let px = s + c * sin_axis * sin_axis; let pt = -c * sin_axis * cos_axis; let py = s + c * cos_axis * cos_axis;
let dec_up_mm = dec.vertical_mm; let dec_in_mm = dec.horizontal_mm; let dec_in_adjusted_mm = match eye {
Eye::OD => dec_in_mm,
Eye::OS => -dec_in_mm, };
let horiz_value = (px * (-dec_in_adjusted_mm) / 10.0) + (pt * (-dec_up_mm) / 10.0);
let vert_value = (pt * dec_in_adjusted_mm / 10.0) + (py * dec_up_mm / 10.0);
let horizontal_base = match eye {
Eye::OD => {
if horiz_value < 0.0 {
HorizontalBase::In
} else {
HorizontalBase::Out
}
}
Eye::OS => {
if horiz_value < 0.0 {
HorizontalBase::Out
} else {
HorizontalBase::In
}
}
};
let horizontal = HorizontalPrism::new(horiz_value.abs(), horizontal_base);
let vertical_base = if vert_value >= 0.0 {
VerticalBase::Up
} else {
VerticalBase::Down
};
let vertical = VerticalPrism::new(vert_value.abs(), vertical_base);
CombinedPrism {
horizontal,
vertical,
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::{assert_abs_diff_eq, assert_relative_eq};
#[test]
fn test_1() {
let lens = SpheroCyl {
sphere: 2.0,
cylinder: -1.0,
axis_deg: 26.0,
};
let dec = Decentration {
horizontal_mm: 1.0,
vertical_mm: 3.0,
};
let p = induced_prism(Eye::OS, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), -0.0625, epsilon = 1e-4);
assert_relative_eq!(p.vertical.signed(), 0.31825, epsilon = 1e-4);
assert_relative_eq!(p.horizontal.amount(), 0.0625, epsilon = 1e-4);
assert!(matches!(p.horizontal.base(), Some(HorizontalBase::In)));
assert_relative_eq!(p.vertical.amount(), 0.31825, epsilon = 1e-4);
assert!(matches!(p.vertical.base(), Some(VerticalBase::Up)));
let p = induced_prism(Eye::OD, lens, dec);
assert_relative_eq!(p.vertical.signed(), 0.397, epsilon = 1e-4);
assert_relative_eq!(p.vertical.amount(), 0.397, epsilon = 1e-4);
assert!(matches!(p.vertical.base(), Some(VerticalBase::Up)));
assert_abs_diff_eq!(p.horizontal.signed(), -0.299, epsilon = 1e-4);
assert_relative_eq!(p.horizontal.amount(), 0.299, epsilon = 1e-4);
assert!(matches!(p.horizontal.base(), Some(HorizontalBase::In)));
}
#[test]
fn test_3() {
let lens = SpheroCyl {
sphere: -2.00,
cylinder: -3.40,
axis_deg: -5.0,
};
let dec = Decentration {
horizontal_mm: -1.5,
vertical_mm: -3.0,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_relative_eq!(p.horizontal.amount(), 0.392434, epsilon = 1e-4);
assert!(matches!(p.horizontal.base(), Some(HorizontalBase::In)));
assert_relative_eq!(p.vertical.amount(), 1.6565, epsilon = 1e-4);
assert!(matches!(p.vertical.base(), Some(VerticalBase::Up)));
let p = induced_prism(Eye::OS, lens, dec);
assert_relative_eq!(p.horizontal.signed(), -0.21531, epsilon = 1e-4);
assert_relative_eq!(p.horizontal.amount(), 0.21531, epsilon = 1e-4);
assert!(matches!(p.horizontal.base(), Some(HorizontalBase::In)));
assert_relative_eq!(p.vertical.signed(), 1.568, epsilon = 1e-4);
assert_relative_eq!(p.vertical.amount(), 1.568, epsilon = 1e-4);
assert!(matches!(p.vertical.base(), Some(VerticalBase::Up)));
}
#[test]
fn test_4() {
let lens = SpheroCyl {
sphere: 0.0,
cylinder: 0.0,
axis_deg: 0.0,
};
let dec = Decentration {
horizontal_mm: 0.0,
vertical_mm: 0.0,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p.horizontal.amount(), 0.0, epsilon = 1e-4);
assert_relative_eq!(p.vertical.amount(), 0.0, epsilon = 1e-4);
assert!(p.horizontal.base().is_none());
assert!(p.vertical.base().is_none());
}
#[test]
fn pure_sphere_horizontal_decentration_od_vs_os() {
let lens = SpheroCyl {
sphere: 3.0,
cylinder: 0.0,
axis_deg: 0.0,
};
let dec = Decentration {
horizontal_mm: 5.0,
vertical_mm: 0.0,
};
let p_od = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p_od.horizontal.signed(), -1.5, epsilon = 1e-6);
assert_abs_diff_eq!(p_od.vertical.signed(), 0.0, epsilon = 1e-6);
assert_abs_diff_eq!(p_od.horizontal.amount(), 1.5, epsilon = 1e-6);
assert!(matches!(p_od.horizontal.base(), Some(HorizontalBase::In)));
let p_os = induced_prism(Eye::OS, lens, dec);
assert_abs_diff_eq!(p_os.horizontal.signed(), -1.5, epsilon = 1e-6);
assert_abs_diff_eq!(p_os.vertical.signed(), 0.0, epsilon = 1e-6);
assert_abs_diff_eq!(p_os.horizontal.amount(), 1.5, epsilon = 1e-6);
assert!(matches!(p_os.horizontal.base(), Some(HorizontalBase::In)));
}
#[test]
fn pure_cylinder_vertical_decentration_axis_0_or_180_equivalence() {
let lens = SpheroCyl {
sphere: 0.0,
cylinder: -2.0,
axis_deg: 180.0,
};
let dec = Decentration {
horizontal_mm: 0.0,
vertical_mm: 4.0,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), 0.0, epsilon = 1e-6);
assert_abs_diff_eq!(p.vertical.signed(), -0.8, epsilon = 1e-6);
assert!(matches!(p.vertical.base(), Some(VerticalBase::Down)));
assert_eq!(p.horizontal.base(), None);
let lens0 = SpheroCyl {
sphere: 0.0,
cylinder: -2.0,
axis_deg: 0.0,
};
let p0 = induced_prism(Eye::OD, lens0, dec);
assert_abs_diff_eq!(p0.vertical.signed(), p.vertical.signed(), epsilon = 1e-6);
}
#[test]
fn pure_cylinder_horizontal_decentration_axis_90() {
let lens = SpheroCyl {
sphere: 0.0,
cylinder: -2.0,
axis_deg: 90.0,
};
let dec = Decentration {
horizontal_mm: 3.0,
vertical_mm: 0.0,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), 0.6, epsilon = 1e-6);
assert_abs_diff_eq!(p.vertical.signed(), 0.0, epsilon = 1e-6);
assert!(matches!(p.horizontal.base(), Some(HorizontalBase::Out)));
}
#[test]
fn mixed_sphero_cyl_os_out_and_up() {
let lens = SpheroCyl {
sphere: -4.0,
cylinder: 2.0,
axis_deg: 45.0,
};
let dec = Decentration {
horizontal_mm: -2.0,
vertical_mm: 1.0,
};
let p = induced_prism(Eye::OS, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), -0.7, epsilon = 1e-6);
assert_abs_diff_eq!(p.vertical.signed(), -0.5, epsilon = 1e-6);
assert!(matches!(p.horizontal.base(), Some(HorizontalBase::In)));
assert!(matches!(p.vertical.base(), Some(VerticalBase::Down)));
}
#[test]
fn down_only_with_toric_cross_term() {
let lens = SpheroCyl {
sphere: -1.5,
cylinder: -1.5,
axis_deg: 30.0,
};
let dec = Decentration {
horizontal_mm: 0.0,
vertical_mm: -3.0,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), 0.19485571585149866, epsilon = 1e-9);
assert_abs_diff_eq!(p.vertical.signed(), 0.7875, epsilon = 1e-9);
assert_eq!(p.horizontal.base(), Some(HorizontalBase::Out));
assert_eq!(p.vertical.base(), Some(VerticalBase::Up));
}
#[test]
fn equal_in_and_down_at_45_has_equal_components() {
let lens = SpheroCyl {
sphere: 0.0,
cylinder: -2.0,
axis_deg: 45.0,
};
let dec = Decentration {
horizontal_mm: 2.5,
vertical_mm: -2.5,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), 0.5, epsilon = 1e-9);
assert_abs_diff_eq!(p.vertical.signed(), 0.5, epsilon = 1e-9);
assert_abs_diff_eq!(p.magnitude(), 0.7071067811865476, epsilon = 1e-12);
}
#[test]
fn axis_135_cancels_components_for_symmetric_decentration() {
let lens = SpheroCyl {
sphere: 0.0,
cylinder: -2.0,
axis_deg: 135.0,
};
let dec = Decentration {
horizontal_mm: 2.5,
vertical_mm: -2.5,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), 0.0, epsilon = 1e-12);
assert_abs_diff_eq!(p.vertical.signed(), 0.0, epsilon = 1e-12);
assert_abs_diff_eq!(p.magnitude(), 0.0, epsilon = 1e-12);
}
#[test]
fn zero_decentration_yields_zero_prism_even_with_power() {
let lens = SpheroCyl {
sphere: 1.25,
cylinder: -3.50,
axis_deg: 72.0,
};
let dec = Decentration {
horizontal_mm: 0.0,
vertical_mm: 0.0,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), 0.0, epsilon = 1e-9);
assert_abs_diff_eq!(p.vertical.signed(), 0.0, epsilon = 1e-9);
assert_abs_diff_eq!(p.magnitude(), 0.0, epsilon = 1e-9);
}
#[test]
fn one_centimeter_in_scales_linearly() {
let lens = SpheroCyl {
sphere: 1.0,
cylinder: -1.0,
axis_deg: 0.0,
};
let dec = Decentration {
horizontal_mm: 10.0,
vertical_mm: 0.0,
};
let p = induced_prism(Eye::OD, lens, dec);
assert_abs_diff_eq!(p.horizontal.signed(), -1.0, epsilon = 1e-9);
assert_abs_diff_eq!(p.vertical.signed(), 0.0, epsilon = 1e-9);
assert_abs_diff_eq!(p.magnitude(), 1.0, epsilon = 1e-9);
}
#[test]
fn power_at_matches_definition() {
let lens = SpheroCyl {
sphere: -2.0,
cylinder: 3.0,
axis_deg: 25.0,
};
assert_abs_diff_eq!(lens.power_at(25.0), -2.0, epsilon = 1e-12);
assert_abs_diff_eq!(lens.power_at(115.0), 1.0, epsilon = 1e-12);
let axis = lens.axis_deg.to_radians();
let px = lens.sphere + lens.cylinder * axis.sin().powi(2);
let py = lens.sphere + lens.cylinder * axis.cos().powi(2);
assert_abs_diff_eq!(lens.power_at(0.0), px, epsilon = 1e-12);
assert_abs_diff_eq!(lens.power_at(90.0), py, epsilon = 1e-12);
}
}