use super::{Vector3d, calculate_great_circle_atd, normalise, normalise_centroid, sq_distance};
use angle_sc::{Angle, Radians};
#[must_use]
pub fn calculate_intersection(
pole_0: &Vector3d,
pole_1: &Vector3d,
min_sq_value: f64,
) -> Option<Vector3d> {
normalise(&pole_0.cross(pole_1), min_sq_value)
}
#[must_use]
pub fn use_antipodal_point(point: &Vector3d, centroid: &Vector3d) -> bool {
sq_distance(centroid, &(-*point)) < sq_distance(centroid, point)
}
#[must_use]
pub fn closest_intersection_point(point: &Vector3d, centroid: &Vector3d) -> Vector3d {
if use_antipodal_point(point, centroid) {
-*point
} else {
*point
}
}
#[must_use]
pub fn calculate_reference_point_and_angle(
mid_point_0: &Vector3d,
pole_0: &Vector3d,
mid_point_1: &Vector3d,
pole_1: &Vector3d,
sq_sin_max_coincident_angle: f64,
) -> (Vector3d, Angle) {
let centroid = mid_point_0 + mid_point_1;
let point = pole_0.cross(pole_1);
normalise(&point, sq_sin_max_coincident_angle).map_or_else(
|| {
let c = normalise_centroid(¢roid, mid_point_0, pole_0);
(c, Angle::from_y_x(point.norm(), pole_0.dot(pole_1)))
},
|p| {
let x = closest_intersection_point(&p, ¢roid);
(x, Angle::from_y_x(point.norm(), pole_0.dot(pole_1)))
},
)
}
#[must_use]
pub fn calculate_arc_reference_distances_and_angle(
mid_point_0: &Vector3d,
pole_0: &Vector3d,
mid_point_1: &Vector3d,
pole_1: &Vector3d,
sq_sin_max_coincident_angle: f64,
) -> (Radians, Radians, Angle) {
let (point, angle) = calculate_reference_point_and_angle(
mid_point_0,
pole_0,
mid_point_1,
pole_1,
sq_sin_max_coincident_angle,
);
let distance_0 = calculate_great_circle_atd(mid_point_0, pole_0, &point);
let distance_1 = calculate_great_circle_atd(mid_point_1, pole_1, &point);
(distance_0, distance_1, angle)
}
#[cfg(test)]
mod tests {
use std::f64;
use super::*;
use crate::{LatLong, vector};
use angle_sc::{Angle, Degrees, is_within_tolerance};
#[test]
fn test_calculate_intersection() {
let lat_lon_south = LatLong::new(Degrees(-90.0), Degrees(0.0));
let south_pole = Vector3d::from(&lat_lon_south);
let lat_lon_north = LatLong::new(Degrees(90.0), Degrees(0.0));
let north_pole = Vector3d::from(&lat_lon_north);
let lat_lon_idl = LatLong::new(Degrees(0.0), Degrees(180.0));
let idl = Vector3d::from(&lat_lon_idl);
let equator_intersection =
calculate_intersection(&south_pole, &north_pole, vector::MIN_SQ_NORM);
assert!(equator_intersection.is_none());
let gc_intersection1 =
calculate_intersection(&idl, &north_pole, vector::MIN_SQ_NORM).unwrap();
let gc_intersection2 =
calculate_intersection(&idl, &south_pole, vector::MIN_SQ_NORM).unwrap();
assert_eq!(gc_intersection1, -gc_intersection2);
}
#[test]
fn test_calculate_arc_reference_distances_and_angle_coincident_great_circles() {
let point_1 = Vector3d::new(1.0, 0.0, 0.0);
let pole_1 = Vector3d::new(0.0, 0.0, 1.0);
let result = calculate_arc_reference_distances_and_angle(
&point_1,
&pole_1,
&point_1,
&pole_1,
vector::MIN_SQ_NORM,
);
assert_eq!(Radians(0.0), result.0);
assert_eq!(Radians(0.0), result.1);
assert_eq!(Degrees(0.0), Degrees::from(result.2));
let point_m1 = -point_1;
let result = calculate_arc_reference_distances_and_angle(
&point_1,
&pole_1,
&point_m1,
&pole_1,
vector::MIN_SQ_NORM,
);
assert!(is_within_tolerance(
-f64::consts::FRAC_PI_2,
result.0.0,
f64::EPSILON
));
assert!(is_within_tolerance(
f64::consts::FRAC_PI_2,
result.1.0,
f64::EPSILON
));
assert_eq!(Degrees(0.0), Degrees::from(result.2));
let pole_m1 = -pole_1;
let result = calculate_arc_reference_distances_and_angle(
&point_1,
&pole_1,
&point_m1,
&pole_m1,
vector::MIN_SQ_NORM,
);
assert!(is_within_tolerance(
-f64::consts::FRAC_PI_2,
result.0.0,
f64::EPSILON
));
assert!(is_within_tolerance(
-f64::consts::FRAC_PI_2,
result.1.0,
f64::EPSILON
));
assert_eq!(Degrees(180.0), Degrees::from(result.2));
}
#[test]
fn test_calculate_arc_reference_distances_and_angle_intersecting_great_circles() {
let point_1 = Vector3d::new(1.0, 0.0, 0.0);
let pole_1 = Vector3d::new(0.0, 0.0, 1.0);
let pole_2 = Vector3d::new(0.0, 1.0, 0.0);
let result = calculate_arc_reference_distances_and_angle(
&point_1,
&pole_1,
&point_1,
&pole_2,
vector::MIN_SQ_NORM,
);
assert_eq!(Radians(0.0), result.0);
assert_eq!(Radians(0.0), result.1);
assert_eq!(Degrees(90.0), Degrees::from(result.2));
let pole_3 = (pole_1 + pole_2).normalize();
let result = calculate_arc_reference_distances_and_angle(
&point_1,
&pole_1,
&point_1,
&pole_3,
vector::MIN_SQ_NORM,
);
assert_eq!(Radians(0.0), result.0);
assert_eq!(Radians(0.0), result.1);
assert_eq!(Degrees(45.0), Degrees::from(result.2));
let pole_m3 = -pole_3;
let result = calculate_arc_reference_distances_and_angle(
&point_1,
&pole_1,
&point_1,
&pole_m3,
vector::MIN_SQ_NORM,
);
assert_eq!(Radians(0.0), result.0);
assert_eq!(Radians(0.0), result.1);
assert_eq!(Degrees(135.0), Degrees::from(result.2));
let point_2 = vector::position(
&point_1,
&vector::direction(&point_1, &pole_3),
Angle::default().quarter_turn_cw(),
);
let result = calculate_arc_reference_distances_and_angle(
&point_1,
&pole_1,
&point_2,
&pole_3,
vector::MIN_SQ_NORM,
);
assert_eq!(Radians(0.0), result.0);
assert!(is_within_tolerance(
-f64::consts::FRAC_PI_2,
result.1.0,
f64::EPSILON
));
assert_eq!(Degrees(45.0), Degrees::from(result.2));
let result = calculate_arc_reference_distances_and_angle(
&point_1,
&pole_1,
&point_2,
&pole_m3,
vector::MIN_SQ_NORM,
);
assert_eq!(Radians(0.0), result.0);
assert!(is_within_tolerance(
f64::consts::FRAC_PI_2,
result.1.0,
f64::EPSILON
));
assert_eq!(Degrees(135.0), Degrees::from(result.2));
}
}