use super::Vector2;
use crate::core::traits::Real;
#[inline]
pub fn min_max<T>(v1: T, v2: T) -> (T, T)
where
T: PartialOrd,
{
if v1 < v2 { (v1, v2) } else { (v2, v1) }
}
#[inline]
pub fn normalize_radians<T>(angle: T) -> T
where
T: Real,
{
if angle >= T::zero() && angle <= T::tau() {
return angle;
}
angle - (angle / T::tau()).floor() * T::tau()
}
#[inline]
pub fn delta_angle<T>(angle1: T, angle2: T) -> T
where
T: Real,
{
let mut diff = normalize_radians(angle2 - angle1);
if diff > T::pi() {
diff = diff - T::tau();
}
diff
}
#[inline]
pub fn delta_angle_signed<T>(angle1: T, angle2: T, negative: bool) -> T
where
T: Real,
{
let diff = delta_angle(angle1, angle2);
if negative { -diff.abs() } else { diff.abs() }
}
#[inline]
pub fn angle_is_between_eps<T>(test_angle: T, start_angle: T, end_angle: T, epsilon: T) -> bool
where
T: Real,
{
let end_sweep = normalize_radians(end_angle - start_angle);
let mid_sweep = normalize_radians(test_angle - start_angle);
mid_sweep < end_sweep + epsilon
}
#[inline]
pub fn angle_is_between<T>(test_angle: T, start_angle: T, end_angle: T) -> bool
where
T: Real,
{
angle_is_between_eps(test_angle, start_angle, end_angle, T::fuzzy_epsilon())
}
#[inline]
pub fn angle_is_within_sweep_eps<T>(
test_angle: T,
start_angle: T,
sweep_angle: T,
epsilon: T,
) -> bool
where
T: Real,
{
let end_angle = start_angle + sweep_angle;
if sweep_angle < T::zero() {
return angle_is_between_eps(test_angle, end_angle, start_angle, epsilon);
}
angle_is_between_eps(test_angle, start_angle, end_angle, epsilon)
}
#[inline]
pub fn angle_is_within_sweep<T>(test_angle: T, start_angle: T, sweep_angle: T) -> bool
where
T: Real,
{
angle_is_within_sweep_eps(test_angle, start_angle, sweep_angle, T::fuzzy_epsilon())
}
#[inline]
pub fn quadratic_solutions<T>(a: T, b: T, c: T, sqrt_discriminant: T) -> (T, T)
where
T: Real,
{
debug_assert!(
(b * b - T::four() * a * c)
.sqrt()
.fuzzy_eq(sqrt_discriminant),
"discriminant is not valid"
);
let denom = T::two() * a;
let sol1 = if b < T::zero() {
(-b + sqrt_discriminant) / denom
} else {
(-b - sqrt_discriminant) / denom
};
let sol2 = (c / a) / sol1;
(sol1, sol2)
}
#[inline]
pub fn dist_squared<T>(p0: Vector2<T>, p1: Vector2<T>) -> T
where
T: Real,
{
let d = p0 - p1;
d.dot(d)
}
#[inline]
pub fn angle<T>(p0: Vector2<T>, p1: Vector2<T>) -> T
where
T: Real,
{
T::atan2(p1.y - p0.y, p1.x - p0.x)
}
#[inline]
pub fn midpoint<T>(p0: Vector2<T>, p1: Vector2<T>) -> Vector2<T>
where
T: Real,
{
Vector2::new((p0.x + p1.x) / T::two(), (p0.y + p1.y) / T::two())
}
#[inline]
pub fn point_on_circle<T>(radius: T, center: Vector2<T>, angle: T) -> Vector2<T>
where
T: Real,
{
let (s, c) = angle.sin_cos();
Vector2::new(center.x + radius * c, center.y + radius * s)
}
#[inline]
pub fn point_from_parametric<T>(p0: Vector2<T>, p1: Vector2<T>, t: T) -> Vector2<T>
where
T: Real,
{
p0 + (p1 - p0).scale(t)
}
#[inline]
pub fn parametric_from_point<T>(p0: Vector2<T>, p1: Vector2<T>, point: Vector2<T>, epsilon: T) -> T
where
T: Real,
{
let x_diff = p1.x - p0.x;
let y_diff = p1.y - p0.y;
debug_assert!(
((x_diff * (p0.y - point.y) - (p0.x - point.x) * y_diff)
/ (x_diff * x_diff + y_diff * y_diff).sqrt())
.fuzzy_eq_zero_eps(epsilon),
"point does not lie on the line defined by p0 to p1 (based on distance)"
);
if x_diff.abs() < y_diff.abs() {
(point.y - p0.y) / y_diff
} else {
(point.x - p0.x) / x_diff
}
}
#[inline]
pub fn line_seg_closest_point<T>(p0: Vector2<T>, p1: Vector2<T>, point: Vector2<T>) -> Vector2<T>
where
T: Real,
{
let v = p1 - p0;
let w = point - p0;
let c1 = w.dot(v);
if c1 < T::fuzzy_epsilon() {
return p0;
}
let c2 = v.length_squared();
if c2 < c1 + T::fuzzy_epsilon() {
return p1;
}
let b = c1 / c2;
p0 + v.scale(b)
}
#[inline]
fn perp_dot_test_value<T>(p0: Vector2<T>, p1: Vector2<T>, point: Vector2<T>) -> T
where
T: Real,
{
(p1.x - p0.x) * (point.y - p0.y) - (p1.y - p0.y) * (point.x - p0.x)
}
#[inline]
pub fn is_left<T>(p0: Vector2<T>, p1: Vector2<T>, point: Vector2<T>) -> bool
where
T: Real,
{
perp_dot_test_value(p0, p1, point) > T::zero()
}
#[inline]
pub fn is_left_or_equal<T>(p0: Vector2<T>, p1: Vector2<T>, point: Vector2<T>) -> bool
where
T: Real,
{
perp_dot_test_value(p0, p1, point) >= T::zero()
}
#[inline]
pub fn is_left_or_coincident_eps<T>(
p0: Vector2<T>,
p1: Vector2<T>,
point: Vector2<T>,
epsilon: T,
) -> bool
where
T: Real,
{
debug_assert!(epsilon > T::zero());
perp_dot_test_value(p0, p1, point) > -epsilon
}
#[inline]
pub fn is_left_or_coincident<T>(p0: Vector2<T>, p1: Vector2<T>, point: Vector2<T>) -> bool
where
T: Real,
{
is_left_or_coincident_eps(p0, p1, point, T::fuzzy_epsilon())
}
#[inline]
pub fn is_right_or_coincident_eps<T>(
p0: Vector2<T>,
p1: Vector2<T>,
point: Vector2<T>,
epsilon: T,
) -> bool
where
T: Real,
{
debug_assert!(epsilon > T::zero());
perp_dot_test_value(p0, p1, point) < epsilon
}
#[inline]
pub fn is_right_or_coincident<T>(p0: Vector2<T>, p1: Vector2<T>, point: Vector2<T>) -> bool
where
T: Real,
{
is_right_or_coincident_eps(p0, p1, point, T::fuzzy_epsilon())
}
#[inline]
pub fn point_within_arc_sweep<T>(
center: Vector2<T>,
arc_start: Vector2<T>,
arc_end: Vector2<T>,
is_clockwise: bool,
point: Vector2<T>,
epsilon: T,
) -> bool
where
T: Real,
{
if is_clockwise {
is_right_or_coincident_eps(center, arc_start, point, epsilon)
&& is_left_or_coincident_eps(center, arc_end, point, epsilon)
} else {
is_left_or_coincident_eps(center, arc_start, point, epsilon)
&& is_right_or_coincident_eps(center, arc_end, point, epsilon)
}
}
#[inline]
pub fn bulge_from_angle<T>(angle: T) -> T
where
T: Real,
{
(angle / T::four()).tan()
}
#[inline]
pub fn angle_from_bulge<T>(bulge: T) -> T
where
T: Real,
{
T::four() * bulge.atan()
}