use crate::s1::{Angle, ChordAngle};
use crate::s2::Point;
use crate::s2::edge_crossings::{self, Crossing};
use crate::s2::predicates;
#[inline]
pub fn distance_from_segment(x: Point, a: Point, b: Point) -> Angle {
let (min_dist, _) = update_min_distance_impl(x, a, b, ChordAngle::ZERO, true);
min_dist.to_angle()
}
#[inline]
pub fn is_distance_less(x: Point, a: Point, b: Point, limit: ChordAngle) -> bool {
let (_, less) = update_min_distance(x, a, b, limit);
less
}
#[inline]
pub fn update_min_distance(
x: Point,
a: Point,
b: Point,
min_dist: ChordAngle,
) -> (ChordAngle, bool) {
update_min_distance_impl(x, a, b, min_dist, false)
}
#[inline]
pub fn update_max_distance(
x: Point,
a: Point,
b: Point,
max_dist: ChordAngle,
) -> (ChordAngle, bool) {
let ca = x.chord_angle(a);
let cb = x.chord_angle(b);
let mut dist = if ca > cb { ca } else { cb };
if dist > ChordAngle::RIGHT {
let (d, _) = update_min_distance_impl(-x, a, b, dist, true);
dist = ChordAngle::STRAIGHT - d;
}
if max_dist < dist {
(dist, true)
} else {
(max_dist, false)
}
}
#[inline]
pub fn is_interior_distance_less(x: Point, a: Point, b: Point, limit: ChordAngle) -> bool {
let (_, less) = update_min_interior_distance(x, a, b, limit);
less
}
#[inline]
pub fn update_min_interior_distance(
x: Point,
a: Point,
b: Point,
min_dist: ChordAngle,
) -> (ChordAngle, bool) {
interior_dist(x, a, b, min_dist, false)
}
#[inline]
pub fn project(x: Point, a: Point, b: Point) -> Point {
let a_xb = a.point_cross(b);
let p = x.0 - a_xb.0 * (x.0.dot(a_xb.0) / a_xb.0.norm2());
if predicates::sign(a_xb, a, Point(p)) && predicates::sign(Point(p), b, a_xb) {
return Point(p.normalize());
}
if (x.0 - a.0).norm2() <= (x.0 - b.0).norm2() {
a
} else {
b
}
}
pub fn distance_fraction(x: Point, a: Point, b: Point) -> f64 {
debug_assert!(a != b);
let d0 = x.0.angle(a.0);
let d1 = x.0.angle(b.0);
d0 / (d0 + d1)
}
#[inline]
pub fn interpolate(t: f64, a: Point, b: Point) -> Point {
if t == 0.0 {
return a;
}
if t == 1.0 {
return b;
}
let ab = Angle::from_radians(a.0.angle(b.0));
interpolate_at_distance(Angle::from_radians(t * ab.radians()), a, b)
}
#[inline]
pub fn interpolate_at_distance(ax: Angle, a: Point, b: Point) -> Point {
let a_rad = ax.radians();
let normal = a.point_cross(b);
let tangent = normal.0.cross(a.0);
Point((a.0 * a_rad.cos() + tangent * (a_rad.sin() / tangent.norm())).normalize())
}
pub fn min_update_distance_max_error(dist: ChordAngle) -> f64 {
dist.max_point_error()
.max(min_update_interior_distance_max_error(dist))
}
pub fn min_update_interior_distance_max_error(dist: ChordAngle) -> f64 {
if dist >= ChordAngle::RIGHT {
return 0.0;
}
let b = 1.0_f64.min(0.5 * dist.length2());
let a = (b * (2.0 - b)).sqrt();
((2.5 + 2.0 * predicates::SQRT3 + 8.5 * a) * a
+ (2.0 + 2.0 * predicates::SQRT3 / 3.0 + 6.5 * (1.0 - b)) * b
+ (23.0 + 16.0 / predicates::SQRT3) * predicates::DBL_EPSILON)
* predicates::DBL_EPSILON
}
pub fn update_min_distance_max_error(dist: ChordAngle) -> f64 {
min_update_interior_distance_max_error(dist).max(dist.max_point_error())
}
#[inline]
pub(crate) fn update_edge_pair_min_distance(
a0: Point,
a1: Point,
b0: Point,
b1: Point,
mut min_dist: ChordAngle,
) -> (ChordAngle, bool) {
if min_dist == ChordAngle::ZERO {
return (ChordAngle::ZERO, false);
}
if edge_crossings::crossing_sign(a0, a1, b0, b1) != Crossing::DoNotCross {
return (ChordAngle::ZERO, true);
}
let mut updated = false;
let (d, ok1) = update_min_distance(a0, b0, b1, min_dist);
min_dist = d;
updated |= ok1;
let (d, ok2) = update_min_distance(a1, b0, b1, min_dist);
min_dist = d;
updated |= ok2;
let (d, ok3) = update_min_distance(b0, a0, a1, min_dist);
min_dist = d;
updated |= ok3;
let (d, ok4) = update_min_distance(b1, a0, a1, min_dist);
min_dist = d;
updated |= ok4;
(min_dist, updated)
}
#[inline]
pub(crate) fn update_edge_pair_max_distance(
a0: Point,
a1: Point,
b0: Point,
b1: Point,
mut max_dist: ChordAngle,
) -> (ChordAngle, bool) {
if max_dist == ChordAngle::STRAIGHT {
return (ChordAngle::STRAIGHT, false);
}
if edge_crossings::crossing_sign(a0, a1, -b0, -b1) != Crossing::DoNotCross {
return (ChordAngle::STRAIGHT, true);
}
let mut updated = false;
let (d, ok1) = update_max_distance(a0, b0, b1, max_dist);
max_dist = d;
updated |= ok1;
let (d, ok2) = update_max_distance(a1, b0, b1, max_dist);
max_dist = d;
updated |= ok2;
let (d, ok3) = update_max_distance(b0, a0, a1, max_dist);
max_dist = d;
updated |= ok3;
let (d, ok4) = update_max_distance(b1, a0, a1, max_dist);
max_dist = d;
updated |= ok4;
(max_dist, updated)
}
#[inline]
pub fn edge_pair_closest_points(a0: Point, a1: Point, b0: Point, b1: Point) -> (Point, Point) {
if edge_crossings::crossing_sign(a0, a1, b0, b1) == Crossing::Cross {
let x = edge_crossings::intersection(a0, a1, b0, b1);
return (x, x);
}
let (mut min_dist, _) = update_min_distance_impl(a0, b0, b1, ChordAngle::ZERO, true);
let mut closest_vertex = 0;
if let (d, true) = update_min_distance(a1, b0, b1, min_dist) {
min_dist = d;
closest_vertex = 1;
}
if let (d, true) = update_min_distance(b0, a0, a1, min_dist) {
min_dist = d;
closest_vertex = 2;
}
if let (_, true) = update_min_distance(b1, a0, a1, min_dist) {
closest_vertex = 3;
}
match closest_vertex {
0 => (a0, project(a0, b0, b1)),
1 => (a1, project(a1, b0, b1)),
2 => (project(b0, a0, a1), b0),
3 => (project(b1, a0, a1), b1),
_ => unreachable!(),
}
}
pub fn point_on_line(a: Point, b: Point, r: Angle) -> Point {
let dir = Point(a.point_cross(b).0.cross(a.0).normalize());
point_on_ray(a, dir, r)
}
pub fn point_to_left(a: Point, b: Point, r: Angle) -> Point {
point_on_ray(a, Point(a.point_cross(b).0.normalize()), r)
}
pub fn point_to_right(a: Point, b: Point, r: Angle) -> Point {
point_on_ray(a, Point(b.point_cross(a).0.normalize()), r)
}
pub fn point_on_ray(origin: Point, dir: Point, r: Angle) -> Point {
Point((origin.0 * r.cos() + dir.0 * r.sin()).normalize())
}
pub fn point_on_ray_chord(origin: Point, dir: Point, r: ChordAngle) -> Point {
Point((origin.0 * r.cos() + dir.0 * r.sin()).normalize())
}
pub fn get_point_to_left(a: Point, b: Point) -> Point {
Point(a.0.cross(b.0).normalize())
}
pub fn get_point_to_right(a: Point, b: Point) -> Point {
get_point_to_left(b, a)
}
fn update_min_distance_impl(
x: Point,
a: Point,
b: Point,
min_dist: ChordAngle,
always_update: bool,
) -> (ChordAngle, bool) {
if let (d, true) = interior_dist(x, a, b, min_dist, always_update) {
return (d, true);
}
let xa2 = (x.0 - a.0).norm2();
let xb2 = (x.0 - b.0).norm2();
let dist = ChordAngle::from_length2(xa2.min(xb2));
if !always_update && dist >= min_dist {
return (min_dist, false);
}
(dist, true)
}
fn interior_dist(
x: Point,
a: Point,
b: Point,
min_dist: ChordAngle,
always_update: bool,
) -> (ChordAngle, bool) {
let xa2 = (x.0 - a.0).norm2();
let xb2 = (x.0 - b.0).norm2();
let ab2 = (a.0 - b.0).norm2();
let max_error = 4.75 * predicates::DBL_EPSILON * (xa2 + xb2 + ab2)
+ 8.0 * predicates::DBL_EPSILON * predicates::DBL_EPSILON;
if (xa2 - xb2).abs() >= ab2 + max_error {
return (min_dist, false);
}
let c = a.point_cross(b);
let c2 = c.0.norm2();
let x_dot_c = x.0.dot(c.0);
let x_dot_c2 = x_dot_c * x_dot_c;
if !always_update && x_dot_c2 > c2 * min_dist.length2() {
return (min_dist, false);
}
let cx = c.0.cross(x.0);
if (a.0 - x.0).dot(cx) >= 0.0 || (b.0 - x.0).dot(cx) <= 0.0 {
return (min_dist, false);
}
let qr = 1.0 - (cx.norm2() / c2).sqrt();
let dist = ChordAngle::from_length2((x_dot_c2 / c2) + (qr * qr));
if !always_update && dist >= min_dist {
return (min_dist, false);
}
(dist, true)
}
pub fn is_edge_pair_distance_less(
a0: Point,
a1: Point,
b0: Point,
b1: Point,
distance: ChordAngle,
) -> bool {
use super::edge_crossings;
if edge_crossings::crossing_sign(a0, a1, b0, b1) != Crossing::DoNotCross {
return distance != ChordAngle::ZERO;
}
is_distance_less(a0, b0, b1, distance)
|| is_distance_less(a1, b0, b1, distance)
|| is_distance_less(b0, a0, a1, distance)
|| is_distance_less(b1, a0, a1, distance)
}
pub fn is_edge_b_near_edge_a(a0: Point, a1: Point, b0: Point, b1: Point, tolerance: Angle) -> bool {
debug_assert!(tolerance.radians() > 0.0);
debug_assert!(tolerance.radians() < std::f64::consts::FRAC_PI_2);
use predicates::Direction;
let mut a_ortho = a0.point_cross(a1).0.normalize();
let a_nearest_b0 = project(b0, a0, a1);
let a_nearest_b1 = project(b1, a0, a1);
if predicates::robust_sign(Point(a_ortho), a_nearest_b0, a_nearest_b1) == Direction::Clockwise {
a_ortho = a_ortho * -1.0;
}
let b0_distance = Angle::from_radians(b0.0.angle(a_nearest_b0.0));
let b1_distance = Angle::from_radians(b1.0.angle(a_nearest_b1.0));
if b0_distance > tolerance || b1_distance > tolerance {
return false;
}
let b_ortho = b0.point_cross(b1).0.normalize();
let planar_angle = Angle::from_radians(a_ortho.angle(b_ortho));
if planar_angle <= tolerance {
return true;
}
if planar_angle >= Angle::from_radians(std::f64::consts::FRAC_PI_2) {
return (Angle::from_radians(b0.0.angle(a0.0)) < Angle::from_radians(b0.0.angle(a1.0)))
== (Angle::from_radians(b1.0.angle(a0.0)) < Angle::from_radians(b1.0.angle(a1.0)));
}
let furthest = Point(
b_ortho
.cross(a0.point_cross(a1).0.normalize().cross(b_ortho))
.normalize(),
);
let furthest_inv = -furthest;
!((predicates::robust_sign(Point(b_ortho), b0, furthest) == Direction::CounterClockwise
&& predicates::robust_sign(furthest, b1, Point(b_ortho)) == Direction::CounterClockwise)
|| (predicates::robust_sign(Point(b_ortho), b0, furthest_inv)
== Direction::CounterClockwise
&& predicates::robust_sign(furthest_inv, b1, Point(b_ortho))
== Direction::CounterClockwise))
}
#[cfg(test)]
mod tests {
use super::*;
use std::f64::consts::PI;
fn float64_near(a: f64, b: f64, eps: f64) -> bool {
(a - b).abs() <= eps
}
#[test]
fn test_distance_from_segment_basic() {
let x = Point::from_coords(0.0, 0.0, 1.0);
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let dist = distance_from_segment(x, a, b);
assert!(
float64_near(dist.radians(), PI / 2.0, 1e-14),
"distance = {}, want π/2 = {}",
dist.radians(),
PI / 2.0,
);
}
#[test]
fn test_distance_from_segment_on_edge() {
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let mid = Point::from_coords(1.0, 1.0, 0.0);
let dist = distance_from_segment(mid, a, b);
assert!(
dist.radians() < 1e-14,
"distance from midpoint = {}, want ~0",
dist.radians(),
);
}
#[test]
fn test_distance_from_segment_degenerate() {
let x = Point::from_coords(0.0, 0.0, 1.0);
let a = Point::from_coords(1.0, 0.0, 0.0);
let dist = distance_from_segment(x, a, a);
assert!(
float64_near(dist.radians(), PI / 2.0, 1e-14),
"degenerate distance = {}, want π/2",
dist.radians(),
);
}
#[test]
fn test_is_distance_less() {
let x = Point::from_coords(0.0, 0.0, 1.0);
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
assert!(is_distance_less(x, a, b, ChordAngle::STRAIGHT));
assert!(!is_distance_less(
x,
a,
b,
ChordAngle::from_radians(PI / 4.0)
));
}
#[test]
fn test_update_min_distance() {
let x = Point::from_coords(0.0, 0.0, 1.0);
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let (d, ok) = update_min_distance(x, a, b, ChordAngle::STRAIGHT);
assert!(ok);
assert!(
float64_near(d.to_angle().radians(), PI / 2.0, 1e-14),
"updated dist = {}, want π/2",
d.to_angle().radians(),
);
let (_, ok) = update_min_distance(x, a, b, ChordAngle::ZERO);
assert!(!ok);
}
#[test]
fn test_update_max_distance() {
let x = Point::from_coords(0.0, 0.0, 1.0);
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let (_, ok) = update_max_distance(x, a, b, ChordAngle::ZERO);
assert!(ok);
}
#[test]
fn test_project() {
let x = Point::from_coords(0.0, 0.0, 1.0);
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let p = project(x, a, b);
assert!(p == a || p == b, "projected point should be an endpoint");
let mid = Point::from_coords(1.0, 1.0, 0.01);
let p = project(mid, a, b);
let expected = Point::from_coords(1.0, 1.0, 0.0);
assert!(
p.approx_eq_angle(expected, Angle::from_radians(0.01)),
"project({mid}) on AB = {p}, want ≈ {expected}",
);
}
#[test]
fn test_distance_fraction() {
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
assert!(
float64_near(distance_fraction(a, a, b), 0.0, 1e-14),
"fraction at A = {}, want 0",
distance_fraction(a, a, b),
);
assert!(
float64_near(distance_fraction(b, a, b), 1.0, 1e-14),
"fraction at B = {}, want 1",
distance_fraction(b, a, b),
);
}
#[test]
fn test_interpolate() {
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
assert_eq!(interpolate(0.0, a, b), a);
assert_eq!(interpolate(1.0, a, b), b);
let mid = interpolate(0.5, a, b);
let expected = Point::from_coords(1.0, 1.0, 0.0);
assert!(
mid.approx_eq_angle(expected, Angle::from_radians(1e-14)),
"midpoint = {mid}, want ≈ {expected}",
);
}
#[test]
fn test_interpolate_at_distance() {
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let p0 = interpolate_at_distance(Angle::ZERO, a, b);
assert!(
p0.approx_eq(a),
"interpolate_at_distance(0, a, b) = {p0}, want ≈ {a}",
);
}
#[test]
fn test_edge_pair_closest_points_crossing() {
let a0 = Point::from_coords(1.0, 0.0, 1.0);
let a1 = Point::from_coords(-1.0, 0.0, 1.0);
let b0 = Point::from_coords(0.0, 1.0, 1.0);
let b1 = Point::from_coords(0.0, -1.0, 1.0);
let (pa, pb) = edge_pair_closest_points(a0, a1, b0, b1);
assert!(
pa.approx_eq_angle(pb, Angle::from_radians(1e-10)),
"crossing edges: pa = {pa}, pb = {pb}, should be same",
);
}
#[test]
fn test_edge_pair_min_distance_crossing() {
let a0 = Point::from_coords(1.0, 0.0, 1.0);
let a1 = Point::from_coords(-1.0, 0.0, 1.0);
let b0 = Point::from_coords(0.0, 1.0, 1.0);
let b1 = Point::from_coords(0.0, -1.0, 1.0);
let (d, ok) = update_edge_pair_min_distance(a0, a1, b0, b1, ChordAngle::STRAIGHT);
assert!(ok);
assert_eq!(d, ChordAngle::ZERO);
}
#[test]
fn test_point_on_line() {
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let p = point_on_line(a, b, Angle::ZERO);
assert!(p.approx_eq(a), "point_on_line(a, b, 0) = {p}, want ≈ {a}",);
}
#[test]
fn test_point_on_ray() {
let origin = Point::from_coords(1.0, 0.0, 0.0);
let dir = Point::from_coords(0.0, 1.0, 0.0);
let p = point_on_ray(origin, dir, Angle::from_radians(PI / 2.0));
assert!(
p.approx_eq_angle(dir, Angle::from_radians(1e-14)),
"point_on_ray at π/2 = {p}, want ≈ {dir}",
);
}
#[test]
fn test_min_update_distance_max_error() {
assert!(min_update_distance_max_error(ChordAngle::ZERO) >= 0.0);
assert!(min_update_distance_max_error(ChordAngle::RIGHT) >= 0.0);
assert!(min_update_distance_max_error(ChordAngle::STRAIGHT) >= 0.0);
}
#[test]
fn test_min_update_interior_distance_max_error() {
assert_eq!(
min_update_interior_distance_max_error(ChordAngle::RIGHT),
0.0
);
assert_eq!(
min_update_interior_distance_max_error(ChordAngle::STRAIGHT),
0.0
);
assert!(min_update_interior_distance_max_error(ChordAngle::from_radians(0.1)) > 0.0);
}
#[test]
fn test_update_min_interior_distance() {
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let mid = Point::from_coords(1.0, 1.0, 0.001);
let (d, ok) = update_min_interior_distance(mid, a, b, ChordAngle::STRAIGHT);
assert!(ok, "should find interior distance for near-midpoint");
assert!(
d.to_angle().radians() < 0.01,
"interior distance = {}, want < 0.01",
d.to_angle().radians(),
);
}
#[test]
fn test_point_to_left_right() {
let a = Point::from_coords(1.0, 0.0, 0.0);
let b = Point::from_coords(0.0, 1.0, 0.0);
let r = Angle::from_radians(0.1);
let left = point_to_left(a, b, r);
let right = point_to_right(a, b, r);
assert!(
left.distance(right).radians() > 0.0,
"left and right should be distinct",
);
assert!(
float64_near(left.distance(a).radians(), 0.1, 0.01),
"left distance from a = {}, want ≈ 0.1",
left.distance(a).radians(),
);
assert!(
float64_near(right.distance(a).radians(), 0.1, 0.01),
"right distance from a = {}, want ≈ 0.1",
right.distance(a).radians(),
);
}
#[test]
fn test_edge_pair_max_distance() {
use crate::s2::LatLng;
let a0 = LatLng::from_degrees(88.0, 0.0).to_point();
let a1 = LatLng::from_degrees(89.0, 10.0).to_point();
let b0 = LatLng::from_degrees(-88.0, 180.0).to_point();
let b1 = LatLng::from_degrees(-89.0, 190.0).to_point();
let (dist, updated) = update_edge_pair_max_distance(a0, a1, b0, b1, ChordAngle::ZERO);
assert!(updated, "should have updated max distance");
let angle = dist.to_angle().radians();
assert!(
angle > PI - 0.1,
"max distance = {angle}, expected close to PI = {PI}"
);
let (dist2, updated2) = update_edge_pair_max_distance(a0, a1, b0, b1, ChordAngle::STRAIGHT);
assert!(
!updated2,
"should not update when max_dist is already STRAIGHT"
);
assert_eq!(dist2, ChordAngle::STRAIGHT);
}
#[test]
fn test_update_min_interior_distance_rejection_conservative() {
use crate::r3::Vector;
{
let x = Point(Vector::new(
1.0,
-4.6547732744037044e-11,
-5.6374428459823598e-89,
));
let a = Point(Vector::new(1.0, -8.9031850507928352e-11, 0.0));
let b = Point(Vector::new(
-0.99999999999996347,
2.7030110029169596e-07,
1.555092348806121e-99,
));
let min_dist = ChordAngle::from_length2(6.3897233584120815e-26);
let (_, updated) = update_min_interior_distance(x, a, b, min_dist);
assert!(updated, "case 1: should update interior distance");
}
{
let x = Point(Vector::new(1.0, -4.7617930898495072e-13, 0.0));
let a = Point(Vector::new(-1.0, -1.6065916409055676e-10, 0.0));
let b = Point(Vector::new(1.0, 0.0, 9.9964883247706732e-35));
let min_dist = ChordAngle::from_length2(6.3897233584120815e-26);
let (_, updated) = update_min_interior_distance(x, a, b, min_dist);
assert!(updated, "case 2: should update interior distance");
}
{
let x = Point(Vector::new(1.0, 0.0, 0.0));
let a = Point(Vector::new(1.0, -8.4965026896454536e-11, 0.0));
let b = Point(Vector::new(
-0.99999999999966138,
8.2297529603339328e-07,
9.6070344113320997e-21,
));
let min_dist = ChordAngle::from_length2(6.3897233584120815e-26);
let (_, updated) = update_min_interior_distance(x, a, b, min_dist);
assert!(updated, "case 3: should update interior distance");
}
}
fn is_edge_b_near_a(a_str: &str, b_str: &str, max_error_degrees: f64) -> bool {
use crate::s2::text_format::make_polyline;
let a = make_polyline(a_str);
let b = make_polyline(b_str);
is_edge_b_near_edge_a(
a.vertex(0),
a.vertex(1),
b.vertex(0),
b.vertex(1),
Angle::from_degrees(max_error_degrees),
)
}
#[test]
fn test_edge_b_near_edge_a() {
assert!(is_edge_b_near_a("5:5, 10:-5", "5:5, 10:-5", 1e-6));
assert!(is_edge_b_near_a("5:5, 10:-5", "10:-5, 5:5", 1e-6));
assert!(is_edge_b_near_a("10:0, -10:0", "2:1, -2:1", 1.0));
assert!(!is_edge_b_near_a("2:1, -2:1", "10:0, -10:0", 1.0));
assert!(!is_edge_b_near_a("10:0, -10:0", "0:1.5, 0:-1.5", 1.0));
assert!(is_edge_b_near_a("10:0, -10:0", "0:1.5, 0:-1.5", 2.0));
assert!(!is_edge_b_near_a("89:1, -89:1", "89:2, -89:2", 0.5));
assert!(is_edge_b_near_a("89:1, -89:1", "89:2, -89:2", 1.5));
assert!(is_edge_b_near_a("89:1, -89:1", "-89:2, 89:2", 1.5));
assert!(!is_edge_b_near_a("0:-100, 0:100", "5:-80, -5:80", 70.0));
assert!(!is_edge_b_near_a("0:-100, 0:100", "1:-35, 10:35", 70.0));
assert!(!is_edge_b_near_a("0:-100, 0:100", "5:80, -5:-80", 70.0));
assert!(!is_edge_b_near_a(
"0:-179.75, 0:-0.25",
"0:179.75, 0:0.25",
1.0
));
assert!(is_edge_b_near_a("40:0, -5:0", "39:0.975, -1:0.975", 1.0));
assert!(is_edge_b_near_a("10:0, -10:0", "-.4:0.975, 0.4:0.975", 1.0));
assert!(is_edge_b_near_a("0:0, 1:0", "0.9:0, 1.1:0", 0.25));
assert!(is_edge_b_near_a("0:0, 1:0", "1.1:0, 1.2:0", 0.25));
assert!(is_edge_b_near_a("0:0, 1:0", "1.2:0, 1.1:0", 0.25));
}
#[test]
fn test_interpolate_midpoint() {
let i = Point::from_coords(1.0, 0.0, 0.0);
let j = Point::from_coords(0.0, 1.0, 0.0);
let mid = interpolate(0.5, i, j);
let expected = Point::from_coords(1.0, 1.0, 0.0).normalize();
assert!(
mid.approx_eq(expected),
"interpolate(0.5, i, j) should be ~(1,1,0).normalize()"
);
}
#[test]
fn test_interpolate_same_point() {
let p = Point::from_coords(0.1, 1e-30, 0.3).normalize();
assert_eq!(interpolate(0.0, p, p), p);
assert_eq!(interpolate(0.5, p, p), p);
assert_eq!(interpolate(1.0, p, p), p);
}
#[test]
fn test_is_edge_pair_distance_less_crossing() {
let a0 = Point::from_coords(1.0, 0.0, 0.1);
let a1 = Point::from_coords(1.0, 0.0, -0.1);
let b0 = Point::from_coords(1.0, -0.1, 0.0);
let b1 = Point::from_coords(1.0, 0.1, 0.0);
assert!(is_edge_pair_distance_less(
a0,
a1,
b0,
b1,
ChordAngle::from_degrees(1.0)
));
assert!(!is_edge_pair_distance_less(
a0,
a1,
b0,
b1,
ChordAngle::ZERO
));
}
#[test]
fn test_is_edge_pair_distance_less_far() {
let a0 = Point::from_coords(1.0, 0.0, 0.0);
let a1 = Point::from_coords(1.0, 0.1, 0.0);
let b0 = Point::from_coords(-1.0, 0.0, 0.0);
let b1 = Point::from_coords(-1.0, 0.0, 0.1);
assert!(!is_edge_pair_distance_less(
a0,
a1,
b0,
b1,
ChordAngle::from_degrees(90.0)
));
assert!(is_edge_pair_distance_less(
a0,
a1,
b0,
b1,
ChordAngle::STRAIGHT
));
}
}