#[cfg(feature = "std")]
mod imp {
pub(crate) fn sin(x: f64) -> f64 {
x.sin()
}
pub(crate) fn cos(x: f64) -> f64 {
x.cos()
}
pub(crate) fn asin(x: f64) -> f64 {
x.asin()
}
pub(crate) fn atan2(y: f64, x: f64) -> f64 {
y.atan2(x)
}
pub(crate) fn sqrt(x: f64) -> f64 {
x.sqrt()
}
pub(crate) fn abs(x: f64) -> f64 {
x.abs()
}
pub(crate) fn copysign(magnitude: f64, sign: f64) -> f64 {
magnitude.copysign(sign)
}
pub(crate) fn tan(x: f64) -> f64 {
x.tan()
}
pub(crate) fn atan(x: f64) -> f64 {
x.atan()
}
pub(crate) fn acos(x: f64) -> f64 {
x.acos()
}
pub(crate) fn ln(x: f64) -> f64 {
x.ln()
}
pub(crate) fn exp(x: f64) -> f64 {
x.exp()
}
pub(crate) fn hypot(x: f64, y: f64) -> f64 {
x.hypot(y)
}
pub(crate) fn round(x: f64) -> f64 {
x.round()
}
pub(crate) fn ceil(x: f64) -> f64 {
x.ceil()
}
pub(crate) fn trunc(x: f64) -> f64 {
x.trunc()
}
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
mod imp {
pub(crate) fn sin(x: f64) -> f64 {
libm::sin(x)
}
pub(crate) fn cos(x: f64) -> f64 {
libm::cos(x)
}
pub(crate) fn asin(x: f64) -> f64 {
libm::asin(x)
}
pub(crate) fn atan2(y: f64, x: f64) -> f64 {
libm::atan2(y, x)
}
pub(crate) fn sqrt(x: f64) -> f64 {
libm::sqrt(x)
}
pub(crate) fn abs(x: f64) -> f64 {
libm::fabs(x)
}
pub(crate) fn copysign(magnitude: f64, sign: f64) -> f64 {
libm::copysign(magnitude, sign)
}
pub(crate) fn tan(x: f64) -> f64 {
libm::tan(x)
}
pub(crate) fn atan(x: f64) -> f64 {
libm::atan(x)
}
pub(crate) fn acos(x: f64) -> f64 {
libm::acos(x)
}
pub(crate) fn ln(x: f64) -> f64 {
libm::log(x)
}
pub(crate) fn exp(x: f64) -> f64 {
libm::exp(x)
}
pub(crate) fn hypot(x: f64, y: f64) -> f64 {
libm::hypot(x, y)
}
pub(crate) fn round(x: f64) -> f64 {
libm::round(x)
}
pub(crate) fn ceil(x: f64) -> f64 {
libm::ceil(x)
}
pub(crate) fn trunc(x: f64) -> f64 {
libm::trunc(x)
}
}
#[cfg(not(any(feature = "std", feature = "libm")))]
compile_error!(
"kinavis-kernel needs floating point math: enable the default `std` feature, \
or build with `--no-default-features --features libm` for `no_std` targets"
);
#[must_use]
#[inline]
pub fn sin(x: f64) -> f64 {
imp::sin(x)
}
#[must_use]
#[inline]
pub fn cos(x: f64) -> f64 {
imp::cos(x)
}
#[must_use]
#[inline]
pub fn tan(x: f64) -> f64 {
imp::tan(x)
}
#[must_use]
#[inline]
pub fn asin(x: f64) -> f64 {
imp::asin(x)
}
#[must_use]
#[inline]
pub fn acos(x: f64) -> f64 {
imp::acos(x)
}
#[must_use]
#[inline]
pub fn atan(x: f64) -> f64 {
imp::atan(x)
}
#[must_use]
#[inline]
pub fn atan2(y: f64, x: f64) -> f64 {
imp::atan2(y, x)
}
#[must_use]
#[inline]
pub fn sqrt(x: f64) -> f64 {
imp::sqrt(x)
}
#[must_use]
#[inline]
pub fn abs(x: f64) -> f64 {
imp::abs(x)
}
#[must_use]
#[inline]
pub fn copysign(magnitude: f64, sign: f64) -> f64 {
imp::copysign(magnitude, sign)
}
#[must_use]
#[inline]
pub fn ln(x: f64) -> f64 {
imp::ln(x)
}
#[must_use]
#[inline]
pub fn exp(x: f64) -> f64 {
imp::exp(x)
}
#[must_use]
#[inline]
pub fn hypot(x: f64, y: f64) -> f64 {
imp::hypot(x, y)
}
#[must_use]
#[inline]
pub fn round(x: f64) -> f64 {
imp::round(x)
}
#[must_use]
#[inline]
pub fn ceil(x: f64) -> f64 {
imp::ceil(x)
}
#[must_use]
#[inline]
pub fn trunc(x: f64) -> f64 {
imp::trunc(x)
}
const DEGREES_PER_RADIAN: f64 = 180.0 / core::f64::consts::PI;
#[must_use]
pub fn to_radians(degrees: f64) -> f64 {
degrees / DEGREES_PER_RADIAN
}
#[must_use]
pub fn to_degrees(radians: f64) -> f64 {
radians * DEGREES_PER_RADIAN
}
#[allow(clippy::float_cmp)]
#[must_use]
pub fn is_integral(value: f64) -> bool {
value == trunc(value)
}
pub const RELATIVE_TOLERANCE: f64 = 1e-12;
#[must_use]
pub fn is_effectively_zero(value: f64, scale: f64) -> bool {
abs(value) <= abs(scale) * RELATIVE_TOLERANCE
}
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn round_to_i32(value: f64) -> i32 {
let rounded = round(value);
if rounded > f64::from(i32::MAX) || rounded < f64::from(i32::MIN) {
return 0;
}
rounded as i32
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
#[must_use]
pub fn to_usize(value: f64) -> usize {
if !(0.0..=1e9).contains(&value) {
return 0;
}
value as usize
}
#[allow(clippy::cast_precision_loss)]
#[must_use]
pub fn count_to_f64(count: usize) -> f64 {
count as f64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn radian_conversion_round_trips() {
for degrees in [0.0, 1.0, 45.0, 90.0, 180.0, 359.9] {
let back = to_degrees(to_radians(degrees));
assert!((back - degrees).abs() < 1e-12);
}
}
#[test]
fn trig_matches_known_values() {
assert!(abs(sin(to_radians(90.0)) - 1.0) < 1e-12);
assert!(abs(cos(to_radians(180.0)) + 1.0) < 1e-12);
assert!(abs(to_degrees(atan2(1.0, 0.0)) - 90.0) < 1e-12);
assert!(abs(hypot(3.0, 4.0) - 5.0) < 1e-12);
assert!(abs(sqrt(9.0) - 3.0) < 1e-12);
assert!(abs(to_degrees(asin(0.5)) - 30.0) < 1e-12);
}
}