rotate 0.4.0

Small program to align a distance vector defined through two atoms to a cartesian axis.
Documentation
use nalgebra::{Rotation3, UnitVector3, Vector3};
use std::ops::{Div, Mul};

/// Function to get the unit vector i, j, k in along the specified axis.
///
/// # Arguments:
///
/// * `unit` - which unit vector `i`, `j` or `k` we want.
///
/// # Panics:
///
/// Panics if `unit` is none of `i`, `j` or `k`
pub fn get_unit(unit: char) -> UnitVector3<f32> {
    match unit {
        'i' => Vector3::x_axis(),
        'j' => Vector3::y_axis(),
        'k' => Vector3::z_axis(),
        _ => {
            panic!("{} unkown unit vector", unit)
        }
    }
}

/// Function to compute the angle of an arbitrary vector with one of the cartesian axes in natural
/// basis, i.e. (1,0,0), (0,1,0) and (0,0,1)
///
/// # Arguments
///
/// - `user` - 3D-Vector supplied by user.
/// - `unit` - unit vector chosen by user.
///
pub fn angle_with_unit_vector(user: Vector3<f32>, unit: char) -> f32 {
    let user_length = user.norm();
    let unit_lengths = 1.0_f32;
    let unit = &get_unit(unit);
    user.dot(unit).div(user_length.mul(unit_lengths)).acos()
}

/// Function to compute angle between to user supplied 3D-Vectors.
///
/// # Arguments:
///
/// - `user` - 3D-Vector supplied by user.
/// - `user_other` - 3D-Vector supplied by user.
///
pub fn angle_with_other_vector(user: Vector3<f32>, user_other: Vector3<f32>) -> f32 {
    let user_length = user.norm();
    let user_o_length = user_other.norm();
    user.dot(&user_other)
        .div(user_length.mul(user_o_length))
        .acos()
}

/// Wrapper function to get the rotation matrix constructed from an angle and a cartesian-axis in natural
/// basis.
///
/// # Arguments
///
/// - `angle` - User supplied value for an angle in rad (!)
/// - `unit` - The unit axis to rotate around in counter-clockwise(!) direction.
///
pub fn get_rotation_matrix(angle: f32, unit: UnitVector3<f32>) -> Rotation3<f32> {
    Rotation3::from_axis_angle(&unit, angle)
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use std::f32::consts::{FRAC_PI_2, FRAC_PI_4};
    #[test]
    fn test_angle() {
        let test_vector = Vector3::z_axis();
        assert_eq!(1.0_f32, test_vector.norm());
        assert_eq!(
            0.0_f32,
            test_vector.dot(&Vector3::new(1.0_f32, 0.0_f32, 0.0_f32))
        );
        assert_eq!(FRAC_PI_2, angle_with_unit_vector(*test_vector, 'i'));
    }
    #[test]
    fn test_rotation() {
        let test_vector = Vector3::z_axis();
        let rot_matrix = get_rotation_matrix(FRAC_PI_2, Vector3::y_axis());
        assert_relative_eq!(
            rot_matrix * test_vector,
            Vector3::x_axis(),
            epsilon = f32::EPSILON
        );
        let rot_matrix = get_rotation_matrix(FRAC_PI_4, Vector3::x_axis());
        let expected: Vector3<f32> =
            Vector3::new(0.0_f32, -(1.0.div(2.0_f32.sqrt())), 1.0.div(2.0_f32.sqrt()));
        assert_relative_eq!(
            *(rot_matrix * test_vector),
            expected,
            epsilon = f32::EPSILON
        );
    }

    #[test]
    fn test_angle_wo() {
        let test_1: Vector3<f32> =
            Vector3::new(0.0_f32, -(1.0.div(2.0_f32.sqrt())), 1.0.div(2.0_f32.sqrt()));
        let test_2: Vector3<f32> =
            Vector3::new(0.0_f32, 1.0.div(2.0_f32.sqrt()), 1.0.div(2.0_f32.sqrt()));
        assert_eq!(FRAC_PI_2, angle_with_other_vector(test_1, test_2));
    }
}