polarization 0.2.0

Simulate the polarization of a laser beam
Documentation
//! An element that has no effect.
//!
//! An identity element simply passes the incident beam straight through. In real life
//! this could simply be the beam traveling through a transparent medium. This element
//! is mostly useful for testing and other internal uses in `polarization`, but may
//! prove handy for other uses as well.

use num::complex::Complex;

use super::common::{rotate_matrix, Angle, ComplexMatrix, JonesMatrix};
#[cfg(test)]
use proptest::prelude::*;

/// An optical element that simply passes a beam through untouched.
#[derive(Debug, Copy, Clone)]
pub struct IdentityElement {
    mat: ComplexMatrix,
}

impl IdentityElement {
    /// Constructs a new `IdentityElement`.
    ///
    /// There are no parameters because this element doesn't really do anything.
    pub fn new() -> Self {
        let zero = Complex::new(0_f64, 0_f64);
        let one = Complex::new(1_f64, 0_f64);
        let mat = ComplexMatrix::new(one, zero, zero, one);
        IdentityElement { mat }
    }
}

impl JonesMatrix for IdentityElement {
    /// Returns an `IdentityElement` that has been rotated counter-clockwise by
    /// `angle`.
    ///
    /// Note that the identity element only passes the original beam through when the
    /// element and the beam are in the same coordinate system. For example, if you
    /// start with a given beam and pass it through an identity element that has
    /// been rotated, in general you will not get back the same beam. However, if you
    /// start with a given beam, rotate both the beam and the element by the same angle,
    /// then rotate the beam back to its original orientation, you will recover the beam
    /// that you started with.
    fn rotated(&self, angle: Angle) -> Self {
        let mat = rotate_matrix(&self.matrix(), &angle);
        IdentityElement { mat }
    }

    /// Replaces this element with one that has been rotated counter-clockwise by
    /// `angle`.
    ///
    /// See above for notes on the semantics of rotating an `IdentityElement`.
    fn rotate(&mut self, angle: Angle) {
        self.mat = rotate_matrix(&self.matrix(), &angle);
    }

    /// Returns the 2x2 Jones matrix of the element.
    fn matrix(&self) -> ComplexMatrix {
        self.mat
    }
}

#[cfg(test)]
impl Arbitrary for IdentityElement {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;

    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
        Just(IdentityElement::new()).boxed()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use jones::common::{are_rel_eq, float_angle, Beam, JonesVector};

    proptest! {
        #[test]
        fn test_identity_element_returns_same_beam(beam: Beam) {
            let ident = IdentityElement::new();
            let beam_after = beam.apply_element(ident);
            prop_assert_beam_approx_eq!(beam_after, beam);
        }

        #[test]
        fn test_multiple_identity_elements_preserves_beam(beam: Beam, elems: Vec<IdentityElement>) {
            let mut beam_after = beam.clone();
            for elem in elems {
                beam_after = beam_after.apply_element(elem);
            }
            prop_assert_beam_approx_eq!(beam_after, beam);
        }

        #[test]
        fn test_identity_preserved_under_rotation(theta in float_angle()) {
            let beam = Beam::linear(Angle::Degrees(theta));
            let ident = IdentityElement::new().rotated(Angle::Degrees(theta));
            let beam_after = beam.apply_element(ident);
            prop_assert_beam_approx_eq!(beam_after, beam);
        }

    }
}