A demo application using easel3d to draw in an HTML5 canvas element in a web page.
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

//
// 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()
}
}