You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
4.4 KiB
143 lines
4.4 KiB
//
|
|
// Some trigonometic functions with Fractions results.
|
|
// Currently only sin, cos and tan are implemented.
|
|
// As I was unable to find a really good integral approximation for them I
|
|
// implement them as a table which is predefined using the floating point
|
|
// function f64::sin and then transformed into a fraction of a given
|
|
// PRECISION.
|
|
// These approximations are quite good and for a few edge cases
|
|
// even better than the floating point implementations.
|
|
//
|
|
// Georg Hopp <georg@steffers.org>
|
|
//
|
|
// Copyright © 2019 Georg Hopp
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
use std::ops::Div;
|
|
use std::ops::Neg;
|
|
use std::marker::Sized;
|
|
use crate::Error;
|
|
|
|
pub trait Trig {
|
|
fn pi() -> Self;
|
|
fn recip(self) -> Self;
|
|
fn round(&self) -> i32;
|
|
fn sqrt(self) -> Result<Self, Error> where Self: Sized;
|
|
fn sintab() -> Vec<Self> where Self: Sized;
|
|
fn tantab() -> Vec<Self> where Self: Sized;
|
|
|
|
fn sin(d :i32) -> Self
|
|
where Self: Sized + Neg<Output = Self> + Copy {
|
|
match d {
|
|
0 ..=90 => Self::sintab()[d as usize],
|
|
91 ..=180 => Self::sintab()[180 - d as usize],
|
|
181..=270 => -Self::sintab()[d as usize - 180],
|
|
271..=359 => -Self::sintab()[360 - d as usize],
|
|
_ => {
|
|
Self::sin(if d < 0 { d % 360 + 360 } else { d % 360 })
|
|
},
|
|
}
|
|
}
|
|
|
|
fn cos(d :i32) -> Self
|
|
where Self: Sized + Neg<Output = Self> + Copy {
|
|
match d {
|
|
0 ..=90 => Self::sintab()[90 - d as usize],
|
|
91 ..=180 => -Self::sintab()[90 - (180 - d as usize)],
|
|
181..=270 => -Self::sintab()[90 - (d as usize - 180)],
|
|
271..=359 => Self::sintab()[90 - (360 - d as usize)],
|
|
_ => {
|
|
Self::cos(if d < 0 { d % 360 + 360 } else { d % 360 })
|
|
},
|
|
}
|
|
}
|
|
|
|
fn tan(d :i32) -> Self where Self: Sized + Copy {
|
|
match d {
|
|
0 ..=179 => Self::tantab()[d as usize],
|
|
180..=359 => Self::tantab()[d as usize - 180],
|
|
_ => {
|
|
Self::tan(if d < 0 { d % 360 + 360 } else { d % 360 })
|
|
},
|
|
}
|
|
}
|
|
|
|
fn cot(d :i32) -> Self
|
|
where Self: Sized + Copy + From<i32> + Div<Output = Self> {
|
|
Into::<Self>::into(1) / Self::tan(d)
|
|
}
|
|
}
|
|
|
|
impl Trig for f64 {
|
|
fn pi() -> Self {
|
|
std::f64::consts::PI
|
|
}
|
|
|
|
fn recip(self) -> Self {
|
|
self.recip()
|
|
}
|
|
|
|
fn round(&self) -> i32 {
|
|
f64::round(*self) as i32
|
|
}
|
|
|
|
fn sqrt(self) -> Result<Self, Error> {
|
|
let x = self.sqrt();
|
|
match x.is_nan() {
|
|
true => Err("sqrt on negative undefined"),
|
|
false => Ok(x),
|
|
}
|
|
}
|
|
|
|
fn sintab() -> Vec<Self> {
|
|
lazy_static::lazy_static! {
|
|
static ref SINTAB :Vec<f64> =
|
|
(0..=90).map(|x| _sin(x)).collect();
|
|
}
|
|
|
|
// f64 sin. (From 0° to 90°)
|
|
fn _sin(d: u32) -> f64 {
|
|
match d {
|
|
0 => 0.0,
|
|
90 => 1.0,
|
|
_ => (d as f64).to_radians().sin(),
|
|
}
|
|
}
|
|
|
|
SINTAB.to_vec()
|
|
}
|
|
|
|
fn tantab() -> Vec<Self> {
|
|
// This table exists only because the sin(α) / cos(α) method
|
|
// yields very large unreducable denominators in a lot of cases.
|
|
lazy_static::lazy_static! {
|
|
static ref TANTAB :Vec<f64> =
|
|
(0..180).map(|x| _tan(x)).collect();
|
|
}
|
|
|
|
// fractional tan from f64 tan. (From 0° to 179°)
|
|
fn _tan(d: u32) -> f64 {
|
|
match d {
|
|
0 => 0.0,
|
|
45 => 1.0,
|
|
90 => std::f64::INFINITY,
|
|
135 => -1.0,
|
|
_ => (d as f64).to_radians().tan(),
|
|
}
|
|
}
|
|
|
|
TANTAB.to_vec()
|
|
}
|
|
}
|